[alsa-devel] [PATCH RFC 000/112] HD-audio generic parser improvements
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs. It's based on the Realtek codec parser, so I started hacking patch_realtek.c for more generalization and improvements. Then it's merged to the existing generic parser code, hda_generic.c, and let Realtek parser just uses this new parser code. In the end, patch_realtek.c contains only the device specific fixups and the call of generic parser functions.
The series contains a few improvements in the HD-audio core code, some of which aren't really dependent on the generic parser code. They are just there as I've developed this for a couple of weeks.
After this patchset, most of other codecs can be migrated as well. Currently analog, ca0110, cirrus, cmedia, conexant and via codecs have been already rewritten (i.e. the own parser codes dropped). patch_sigmatel.c is still not converted yet, but can be done soon later. I'm going to send the conversion patches for these codecs right after this series.
I don't expect to merge all conversions of codec parsers in 3.9 kernel, but at least, the conversion of Realtek and this generic parser code addition can be merged to 3.9. Maybe minor and easy ones like ca0110 and cmedia can be merged as well.
Takashi
Use the helper function snd_hda_get_conn_index() instead of open codes. This also improves the detection of some routes to DAC on ALC260 (although the difference doesn't influence on the end results of the mapping).
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 71ae23d..d8118c9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2938,19 +2938,9 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) static bool alc_auto_is_dac_reachable(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - hda_nid_t srcs[5]; - int i, num; - if (!pin || !dac) return false; - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid == dac) - return true; - } - return false; + return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; }
static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin)
In the probing phase, create a list of all available DACs in the codec and use it for checking the single DAC connections. This list will be used in more other places in the later commits, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d8118c9..50e079f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -172,6 +172,10 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin;
+ /* DAC list */ + int num_all_dacs; + hda_nid_t all_dacs[16]; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2916,48 +2920,42 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) return false; }
+/* check whether the DAC is reachable from the pin */ +static bool alc_auto_is_dac_reachable(struct hda_codec *codec, + hda_nid_t pin, hda_nid_t dac) +{ + if (!pin || !dac) + return false; + return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; +} + /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { - hda_nid_t srcs[5]; - int i, num; + struct alc_spec *spec = codec->spec; + int i;
- pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - hda_nid_t nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (!nid) + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (!alc_is_dac_already_used(codec, nid)) + if (alc_auto_is_dac_reachable(codec, pin, nid)) return nid; } return 0; }
-/* check whether the DAC is reachable from the pin */ -static bool alc_auto_is_dac_reachable(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - if (!pin || !dac) - return false; - return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; -} - static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; - hda_nid_t sel = alc_go_down_to_selector(codec, pin); - hda_nid_t nid, nid_found, srcs[5]; - int i, num = snd_hda_get_connections(codec, sel, srcs, - ARRAY_SIZE(srcs)); - if (num == 1) - return alc_auto_look_for_dac(codec, pin); - nid_found = 0; - for (i = 0; i < num; i++) { - if (srcs[i] == spec->mixer_nid) + int i; + hda_nid_t nid_found = 0; + + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || alc_is_dac_already_used(codec, nid)) continue; - nid = alc_auto_mix_to_dac(codec, srcs[i]); - if (nid && !alc_is_dac_already_used(codec, nid)) { + if (alc_auto_is_dac_reachable(codec, pin, nid)) { if (nid_found) return 0; nid_found = nid; @@ -3308,6 +3306,26 @@ static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) spec->multiout.extra_out_nid[3]); }
+/* find all available DACs of the codec */ +static void alc_fill_all_nids(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + hda_nid_t nid = codec->start_nid; + + spec->num_all_dacs = 0; + memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); + for (i = 0; i < codec->num_nodes; i++, nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) + continue; + if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { + snd_printk(KERN_ERR "hda: Too many DACs!\n"); + break; + } + spec->all_dacs[spec->num_all_dacs++] = nid; + } +} + static int alc_auto_fill_dac_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -3319,6 +3337,8 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) bool best_wired = true, best_mio = true; bool hp_spk_swapped = false;
+ alc_fill_all_nids(codec); + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); if (!best_cfg) return -ENOMEM;
Add the output path parser to Realtek codec driver as we already have in patch_via.c. The nid_path struct represents the complete output path from a DAC to a pin. The alc_spec contains an array of these paths, and a new path is added at each time when a new DAC is assigned.
So far, this path list is used only in limited codes: namely in this patch, only alc_is_dac_already_used() checks the list instead of dac arrays in all possible outputs. In the later development, the path list will be referred from more places, such as the mixer control assignment / check, the mute/unmute of active routes, etc.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 139 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 11 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 50e079f..9e0516d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -99,6 +99,23 @@ enum { #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
+#define MAX_NID_PATH_DEPTH 5 + +/* output-path: DAC -> ... -> pin + * idx[] contains the source index number of the next widget; + * e.g. idx[0] is the index of the DAC selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int ctls[2]; /* 0 = volume, 1 = mute */ +}; + struct alc_spec { struct hda_gen_spec gen;
@@ -176,6 +193,9 @@ struct alc_spec { int num_all_dacs; hda_nid_t all_dacs[16];
+ /* output paths */ + struct snd_array out_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2407,6 +2427,7 @@ static void alc_free(struct hda_codec *codec)
alc_free_kctls(codec); alc_free_bind_ctls(codec); + snd_array_free(&spec->out_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2906,15 +2927,10 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; int i; - if (found_in_nid_list(nid, spec->multiout.dac_nids, - ARRAY_SIZE(spec->private_dac_nids)) || - found_in_nid_list(nid, spec->multiout.hp_out_nid, - ARRAY_SIZE(spec->multiout.hp_out_nid)) || - found_in_nid_list(nid, spec->multiout.extra_out_nid, - ARRAY_SIZE(spec->multiout.extra_out_nid))) - return true; - for (i = 0; i < spec->multi_ios; i++) { - if (spec->multi_io[i].dac == nid) + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->path[0] == nid) return true; } return false; @@ -2945,6 +2961,75 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) return 0; }
+/* called recursively */ +static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path, int depth) +{ + struct alc_spec *spec = codec->spec; + hda_nid_t conn[8]; + int i, nums; + + if (nid == spec->mixer_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ + } + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) + continue; + if (conn[i] == target_dac || + (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) { + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; + } + } + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT) + continue; + if (__parse_output_path(codec, conn[i], target_dac, + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + path->multi[path->depth] = 1; + path->depth++; + return true; +} + +/* parse the output path from the given nid to the target DAC; + * when target_dac is 0, try to find an empty DAC; + * when with_aa_mix is 0, paths with spec->mixer_nid are excluded + */ +static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, + hda_nid_t target_dac, int with_aa_mix, + struct nid_path *path) +{ + if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { + path->path[path->depth] = nid; + path->depth++; +#if 0 + snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); +#endif + return true; + } + return false; +} + static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { struct alc_spec *spec = codec->spec; @@ -3016,6 +3101,23 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac);
+static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + struct nid_path *path; + + path = snd_array_new(&spec->out_path); + if (!path) + return false; + memset(path, 0, sizeof(*path)); + if (parse_output_path(codec, pin, dac, 0, path)) + return true; + /* push back */ + spec->out_path.used--; + return false; +} + static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { @@ -3127,6 +3229,8 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } + if (!add_new_out_path(codec, pin, dac)) + dac = dacs[i] = 0; if (dac) badness += eval_shared_vol_badness(codec, pin, dac); } @@ -3144,11 +3248,16 @@ static bool alc_map_singles(struct hda_codec *codec, int outs, int i; bool found = false; for (i = 0; i < outs; i++) { + hda_nid_t dac; if (dacs[i]) continue; - dacs[i] = get_dac_if_single(codec, pins[i]); - if (dacs[i]) + dac = get_dac_if_single(codec, pins[i]); + if (!dac) + continue; + if (add_new_out_path(codec, pins[i], dac)) { + dacs[i] = dac; found = true; + } } return found; } @@ -3169,6 +3278,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; + snd_array_free(&spec->out_path); clear_vol_marks(codec); badness = 0;
@@ -3882,6 +3992,10 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, badness++; continue; } + if (!add_new_out_path(codec, nid, dac)) { + badness++; + continue; + } spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->multi_ios++; @@ -3899,6 +4013,8 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, return badness; /* no badness if nothing found */ } if (!hardwired && spec->multi_ios < 2) { + /* cancel newly assigned paths */ + spec->out_path.used -= spec->multi_ios - old_pins; spec->multi_ios = old_pins; return badness; } @@ -4388,6 +4504,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); + snd_array_init(&spec->out_path, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec); if (err < 0) {
As we parse the output paths more precisely now, we can use this path list for parsing the widgets for volume and mute mixer controls. The spec->vol_ctls[] and sw_ctls[] bitmasks are replaced with the ctls[] in each output path instance.
Interestingly, this move alone automagically fixes some bugs that the conflicting volume or mute NIDs weren't properly detected. Also, by parsing the whole path, there are more chances to get a free widget for volume/mute controls.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 230 ++++++++++++++++++++++++------------------ 1 file changed, 131 insertions(+), 99 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9e0516d..298e046 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -116,6 +116,8 @@ struct nid_path { unsigned int ctls[2]; /* 0 = volume, 1 = mute */ };
+enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; + struct alc_spec { struct hda_gen_spec gen;
@@ -150,8 +152,6 @@ struct alc_spec { const hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */ - DECLARE_BITMAP(vol_ctls, MAX_VOL_NIDS << 1); - DECLARE_BITMAP(sw_ctls, MAX_VOL_NIDS << 1);
/* capture setup for dynamic dual-adc switch */ hda_nid_t cur_adc; @@ -2886,22 +2886,6 @@ static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) return srcs[0]; }
-/* get MIX nid connected to the given pin targeted to DAC */ -static hda_nid_t alc_auto_dac_to_mix(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) - return mix[i]; - } - return 0; -} - /* select the connection from pin to DAC if needed */ static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) @@ -3049,29 +3033,28 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) return nid_found; }
-/* mark up volume and mute control NIDs: used during badness parsing and - * at creating actual controls - */ -static inline unsigned int get_ctl_pos(unsigned int data) +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) { - hda_nid_t nid = get_amp_nid_(data); - unsigned int dir; - if (snd_BUG_ON(nid >= MAX_VOL_NIDS)) - return 0; - dir = get_amp_direction_(data); - return (nid << 1) | dir; -} + struct alc_spec *spec = codec->spec; + int i;
-#define is_ctl_used(bits, data) \ - test_bit(get_ctl_pos(data), bits) -#define mark_ctl_usage(bits, data) \ - set_bit(get_ctl_pos(data), bits) + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->ctls[type] == val) + return true; + } + return false; +}
static void clear_vol_marks(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - memset(spec->vol_ctls, 0, sizeof(spec->vol_ctls)); - memset(spec->sw_ctls, 0, sizeof(spec->sw_ctls)); + int i; + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + path->ctls[0] = path->ctls[1] = 0; + } }
/* badness definition */ @@ -3097,9 +3080,9 @@ enum { };
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); + struct nid_path *path); static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac); + struct nid_path *path);
static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) @@ -3118,34 +3101,56 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, return false; }
+/* get the path pointing from the given dac to pin; + * passing 0 to either @pin or @dac behaves as a wildcard + */ +static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *path = snd_array_elem(&spec->out_path, i); + if (path->depth <= 0) + continue; + if ((!dac || path->path[0] == dac) && + (!pin || path->path[path->depth - 1] == pin)) + return path; + } + return NULL; +} + static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - struct alc_spec *spec = codec->spec; + struct nid_path *path = get_out_path(codec, pin, dac); hda_nid_t nid; unsigned int val; int badness = 0;
- nid = alc_look_for_out_vol_nid(codec, pin, dac); + if (!path) + return BAD_SHARED_VOL * 2; + nid = alc_look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, nid)) + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) badness += BAD_SHARED_VOL; else - mark_ctl_usage(spec->vol_ctls, val); + path->ctls[NID_PATH_VOL_CTL] = val; } else badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, pin, dac); + nid = alc_look_for_out_mute_nid(codec, path); if (nid) { unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(spec->sw_ctls, val)) + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) badness += BAD_SHARED_VOL; else - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; } else badness += BAD_SHARED_VOL; return badness; @@ -3279,7 +3284,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; snd_array_free(&spec->out_path); - clear_vol_marks(codec); badness = 0;
/* fill hard-wired DACs first */ @@ -3521,10 +3525,13 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) cfg->line_out_type, best_wired, best_mio); debug_show_configs(spec, cfg);
- if (cfg->line_out_pins[0]) - spec->vmaster_nid = - alc_look_for_out_vol_nid(codec, cfg->line_out_pins[0], - spec->multiout.dac_nids[0]); + if (cfg->line_out_pins[0]) { + struct nid_path *path = get_out_path(codec, + cfg->line_out_pins[0], + spec->multiout.dac_nids[0]); + if (path) + spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); + }
/* clear the bitmap flags for creating controls */ clear_vol_marks(codec); @@ -3533,43 +3540,43 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) }
static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs, + struct nid_path *path) { - struct alc_spec *spec = codec->spec; unsigned int val; - if (!nid) + if (!nid || !path) return 0; val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - if (is_ctl_used(spec->vol_ctls, val) && chs != 2) /* exclude LFE */ + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */ return 0; - mark_ctl_usage(spec->vol_ctls, val); + path->ctls[NID_PATH_VOL_CTL] = val; return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); }
static int alc_auto_add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid) + hda_nid_t nid, struct nid_path *path) { int chs = 1; if (get_wcaps(codec, nid) & AC_WCAP_STEREO) chs = 3; - return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs); + return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path); }
/* create a mute-switch for the given mixer widget; * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs) + const char *pfx, int cidx, + hda_nid_t nid, unsigned int chs, + struct nid_path *path) { - struct alc_spec *spec = codec->spec; int wid_type; int type; unsigned long val; - if (!nid) + if (!nid || !path) return 0; wid_type = get_wcaps_type(get_wcaps(codec, nid)); if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { @@ -3582,44 +3589,46 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, type = ALC_CTL_BIND_MUTE; val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); } - if (is_ctl_used(spec->sw_ctls, val) && chs != 2) /* exclude LFE */ + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */ return 0; - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); }
static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, hda_nid_t nid) + int cidx, hda_nid_t nid, + struct nid_path *path) { int chs = 1; if (get_wcaps(codec, nid) & AC_WCAP_STEREO) chs = 3; - return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs); + return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path); }
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) + struct nid_path *path) { - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_mute(codec, pin, HDA_OUTPUT)) - return pin; - else if (mix && nid_has_mute(codec, mix, HDA_INPUT)) - return mix; - else if (nid_has_mute(codec, dac, HDA_OUTPUT)) - return dac; + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + if (i != path->depth - 1 && i != 0 && + nid_has_mute(codec, path->path[i], HDA_INPUT)) + return path->path[i]; + } return 0; }
static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - hda_nid_t mix = alc_auto_dac_to_mix(codec, pin, dac); - if (nid_has_volume(codec, dac, HDA_OUTPUT)) - return dac; - else if (nid_has_volume(codec, mix, HDA_OUTPUT)) - return mix; - else if (nid_has_volume(codec, pin, HDA_OUTPUT)) - return pin; + struct nid_path *path) +{ + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + } return 0; }
@@ -3639,6 +3648,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, int index; hda_nid_t dac, pin; hda_nid_t sw, vol; + struct nid_path *path;
dac = spec->multiout.dac_nids[i]; if (!dac) @@ -3652,27 +3662,36 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); }
- sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); + path = get_out_path(codec, pin, dac); + if (!path) + continue; + sw = alc_look_for_out_mute_nid(codec, path); + vol = alc_look_for_out_vol_nid(codec, path); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1); + err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1, + path); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2, + path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1); + err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1, + path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2, + path); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, vol); + err = alc_auto_add_stereo_vol(codec, name, index, vol, + path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, sw); + err = alc_auto_add_stereo_sw(codec, name, index, sw, + path); if (err < 0) return err; } @@ -3685,9 +3704,14 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, int cidx) { struct alc_spec *spec = codec->spec; + struct nid_path *path; hda_nid_t sw, vol; int err;
+ path = get_out_path(codec, pin, dac); + if (!path) + return 0; + if (!dac) { unsigned int val; /* the corresponding DAC is already occupied */ @@ -3695,18 +3719,18 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, return 0; /* no way */ /* create a switch only */ val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); - if (is_ctl_used(spec->sw_ctls, val)) + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) return 0; /* already created */ - mark_ctl_usage(spec->sw_ctls, val); + path->ctls[NID_PATH_MUTE_CTL] = val; return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); }
- sw = alc_look_for_out_mute_nid(codec, pin, dac); - vol = alc_look_for_out_vol_nid(codec, pin, dac); - err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol); + sw = alc_look_for_out_mute_nid(codec, path); + vol = alc_look_for_out_vol_nid(codec, path); + err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw); + err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path); if (err < 0) return err; return 0; @@ -3780,9 +3804,13 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, n = 0; for (i = 0; i < num_pins; i++) { hda_nid_t vol; + struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - vol = alc_look_for_out_vol_nid(codec, pins[i], dacs[i]); + path = get_out_path(codec, pins[i], dacs[i]); + if (!path) + continue; + vol = alc_look_for_out_vol_nid(codec, path); if (vol) ctl->values[n++] = HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); @@ -3821,6 +3849,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, int i, num; hda_nid_t nid, mix = 0; hda_nid_t srcs[HDA_MAX_CONNECTIONS]; + struct nid_path *path;
alc_set_pin_output(codec, pin, pin_type); nid = alc_go_down_to_selector(codec, pin); @@ -3845,13 +3874,16 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, AMP_IN_UNMUTE(1)); } /* initialize volume */ - nid = alc_look_for_out_vol_nid(codec, pin, dac); + path = get_out_path(codec, pin, dac); + if (!path) + return; + nid = alc_look_for_out_vol_nid(codec, path); if (nid) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
/* unmute DAC if it's not assigned to a mixer */ - nid = alc_look_for_out_mute_nid(codec, pin, dac); + nid = alc_look_for_out_mute_nid(codec, path); if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
The mono widget is always connected to the left channel, thus the left channel amp value also should be referred for mono widgets instead of the right channel.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_proc.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c index 045e5d3..740f46a 100644 --- a/sound/pci/hda/hda_proc.c +++ b/sound/pci/hda/hda_proc.c @@ -138,16 +138,17 @@ static void print_amp_vals(struct snd_info_buffer *buffer, dir = dir == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT; for (i = 0; i < indices; i++) { snd_iprintf(buffer, " ["); + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_AMP_GAIN_MUTE, + AC_AMP_GET_LEFT | dir | i); + snd_iprintf(buffer, "0x%02x", val); if (stereo) { val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_LEFT | dir | i); - snd_iprintf(buffer, "0x%02x ", val); + AC_AMP_GET_RIGHT | dir | i); + snd_iprintf(buffer, " 0x%02x", val); } - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_AMP_GAIN_MUTE, - AC_AMP_GET_RIGHT | dir | i); - snd_iprintf(buffer, "0x%02x]", val); + snd_iprintf(buffer, "]"); } snd_iprintf(buffer, "\n"); }
So far, Realtek codec driver evaluates the NIDs for volume and mute controls twice, once while parsing the DACs and evaluating the assignment, and another while creating the mixer elements. This is utterly redundant and even fragile, as it's assuming that the ctl element evaluation is identical between both parsing DACs and creating mixer elements.
This patch simplifies the code flow by doing the volume / mute controls evaluation only once while parsing the DACs. The patch ended up in larger changes than expected because of some cleanups became mandatory.
As a gratis bonus, this patch also fixes some cases where the stereo channels are used wrongly for mono amps.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 168 ++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 90 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 298e046..f8dd753 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3046,17 +3046,6 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) return false; }
-static void clear_vol_marks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); - path->ctls[0] = path->ctls[1] = 0; - } -} - /* badness definition */ enum { /* No primary DAC is found for the main output */ @@ -3121,8 +3110,15 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, return NULL; }
-static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +/* look for widgets in the path between the given NIDs appropriate for + * volume and mute controls, and assign the values to ctls[]. + * + * When no appropriate widget is found in the path, the badness value + * is incremented depending on the situation. The function returns the + * total badness for both volume and mute controls. + */ +static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { struct nid_path *path = get_out_path(codec, pin, dac); hda_nid_t nid; @@ -3143,7 +3139,8 @@ static int eval_shared_vol_badness(struct hda_codec *codec, hda_nid_t pin, nid = alc_look_for_out_mute_nid(codec, path); if (nid) { unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || + nid_has_mute(codec, nid, HDA_OUTPUT)) val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); else val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); @@ -3237,7 +3234,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, if (!add_new_out_path(codec, pin, dac)) dac = dacs[i] = 0; if (dac) - badness += eval_shared_vol_badness(codec, pin, dac); + badness += assign_out_path_ctls(codec, pin, dac); }
return badness; @@ -3533,36 +3530,52 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); }
- /* clear the bitmap flags for creating controls */ - clear_vol_marks(codec); kfree(best_cfg); return 0; }
+/* replace the channels in the composed amp value with the given number */ +static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) +{ + val &= ~(0x3U << 16); + val |= chs << 16; + return val; +} + static int alc_auto_add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs, + unsigned int chs, struct nid_path *path) { unsigned int val; - if (!nid || !path) + if (!path) return 0; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL) && chs != 2) /* exclude LFE */ + val = path->ctls[NID_PATH_VOL_CTL]; + if (!val) return 0; - path->ctls[NID_PATH_VOL_CTL] = val; - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, - val); + val = amp_val_replace_channels(val, chs); + return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); +} + +/* return the channel bits suitable for the given path->ctls[] */ +static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, + int type) +{ + int chs = 1; /* mono (left only) */ + if (path) { + hda_nid_t nid = get_amp_nid_(path->ctls[type]); + if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) + chs = 3; /* stereo */ + } + return chs; }
static int alc_auto_add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, struct nid_path *path) + struct nid_path *path) { - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_vol_ctl(codec, pfx, cidx, nid, chs, path); + int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); + return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path); }
/* create a mute-switch for the given mixer widget; @@ -3570,39 +3583,33 @@ static int alc_auto_add_stereo_vol(struct hda_codec *codec, */ static int alc_auto_add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, - hda_nid_t nid, unsigned int chs, + unsigned int chs, struct nid_path *path) { - int wid_type; - int type; - unsigned long val; - if (!nid || !path) + unsigned int val; + int type = ALC_CTL_WIDGET_MUTE; + + if (!path) return 0; - wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); - } else if (snd_hda_get_num_conns(codec, nid) == 1) { - type = ALC_CTL_WIDGET_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); - } else { - type = ALC_CTL_BIND_MUTE; - val = HDA_COMPOSE_AMP_VAL(nid, chs, 2, HDA_INPUT); - } - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL) && chs != 2) /* exclude LFE */ + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) return 0; - path->ctls[NID_PATH_MUTE_CTL] = val; + val = amp_val_replace_channels(val, chs); + if (get_amp_direction_(val) == HDA_INPUT) { + hda_nid_t nid = get_amp_nid_(val); + if (snd_hda_get_num_conns(codec, nid) > 1) { + type = ALC_CTL_BIND_MUTE; + val |= 2 << 19; /* FIXME: fixed two widgets, so far */ + } + } return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); }
static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, hda_nid_t nid, - struct nid_path *path) + int cidx, struct nid_path *path) { - int chs = 1; - if (get_wcaps(codec, nid) & AC_WCAP_STEREO) - chs = 3; - return alc_auto_add_sw_ctl(codec, pfx, cidx, nid, chs, path); + int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); + return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path); }
static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, @@ -3647,7 +3654,6 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, const char *name; int index; hda_nid_t dac, pin; - hda_nid_t sw, vol; struct nid_path *path;
dac = spec->multiout.dac_nids[i]; @@ -3665,33 +3671,25 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, path = get_out_path(codec, pin, dac); if (!path) continue; - sw = alc_look_for_out_mute_nid(codec, path); - vol = alc_look_for_out_vol_nid(codec, path); if (!name || !strcmp(name, "CLFE")) { /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, vol, 1, - path); + err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, vol, 2, - path); + err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, sw, 1, - path); + err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path); if (err < 0) return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, sw, 2, - path); + err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path); if (err < 0) return err; } else { - err = alc_auto_add_stereo_vol(codec, name, index, vol, - path); + err = alc_auto_add_stereo_vol(codec, name, index, path); if (err < 0) return err; - err = alc_auto_add_stereo_sw(codec, name, index, sw, - path); + err = alc_auto_add_stereo_sw(codec, name, index, path); if (err < 0) return err; } @@ -3703,34 +3701,19 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac, const char *pfx, int cidx) { - struct alc_spec *spec = codec->spec; struct nid_path *path; - hda_nid_t sw, vol; int err;
path = get_out_path(codec, pin, dac); if (!path) return 0; - - if (!dac) { - unsigned int val; - /* the corresponding DAC is already occupied */ - if (!(get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)) - return 0; /* no way */ - /* create a switch only */ - val = HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - return 0; /* already created */ - path->ctls[NID_PATH_MUTE_CTL] = val; - return __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, pfx, cidx, val); + /* bind volume control will be created in the case of dac = 0 */ + if (dac) { + err = alc_auto_add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; } - - sw = alc_look_for_out_mute_nid(codec, path); - vol = alc_look_for_out_vol_nid(codec, path); - err = alc_auto_add_stereo_vol(codec, pfx, cidx, vol, path); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, pfx, cidx, sw, path); + err = alc_auto_add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; return 0; @@ -4051,7 +4034,12 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, return badness; }
- return 0; + /* assign volume and mute controls */ + for (i = old_pins; i < spec->multi_ios; i++) + badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, + spec->multi_io[i].dac); + + return badness; }
static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol,
Simplify the output path initialization using the existing path information instead of assuming the topology specific to Realtek codecs. This is also implicitly a fix for some amp values on output pins where the old parser missed (e.g. ALC260 output pins).
The same function alc_auto_set_output_and_unmute() can be used now for the multi-io activation, since the output selection means nothing but activating the given output path.
And, finally at this stage, we can get rid of alc_go_down_to_selector() and other functions that are codec really specifically to Realtek codecs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 182 +++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 89 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f8dd753..a7899d1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2858,55 +2858,6 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) } }
-/* convert from MIX nid to DAC */ -static hda_nid_t alc_auto_mix_to_dac(struct hda_codec *codec, hda_nid_t nid) -{ - hda_nid_t list[5]; - int i, num; - - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_AUD_OUT) - return nid; - num = snd_hda_get_connections(codec, nid, list, ARRAY_SIZE(list)); - for (i = 0; i < num; i++) { - if (get_wcaps_type(get_wcaps(codec, list[i])) == AC_WID_AUD_OUT) - return list[i]; - } - return 0; -} - -/* go down to the selector widget before the mixer */ -static hda_nid_t alc_go_down_to_selector(struct hda_codec *codec, hda_nid_t pin) -{ - hda_nid_t srcs[5]; - int num = snd_hda_get_connections(codec, pin, srcs, - ARRAY_SIZE(srcs)); - if (num != 1 || - get_wcaps_type(get_wcaps(codec, srcs[0])) != AC_WID_AUD_SEL) - return pin; - return srcs[0]; -} - -/* select the connection from pin to DAC if needed */ -static int alc_auto_select_dac(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - hda_nid_t mix[5]; - int i, num; - - pin = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, pin, mix, ARRAY_SIZE(mix)); - if (num < 2) - return 0; - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, mix[i]) == dac) { - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_CONNECT_SEL, i); - return 0; - } - } - return 0; -} - static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec; @@ -3825,51 +3776,102 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); }
-static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac) +/* is a volume or mute control already present? */ +static bool __is_out_ctl_present(struct hda_codec *codec, + struct nid_path *exclude_path, + hda_nid_t nid, int dir, int types) { - int i, num; - hda_nid_t nid, mix = 0; - hda_nid_t srcs[HDA_MAX_CONNECTIONS]; - struct nid_path *path; + struct alc_spec *spec = codec->spec; + int i, type;
- alc_set_pin_output(codec, pin, pin_type); - nid = alc_go_down_to_selector(codec, pin); - num = snd_hda_get_connections(codec, nid, srcs, ARRAY_SIZE(srcs)); - for (i = 0; i < num; i++) { - if (alc_auto_mix_to_dac(codec, srcs[i]) != dac) + for (i = 0; i < spec->out_path.used; i++) { + struct nid_path *p = snd_array_elem(&spec->out_path, i); + if (p == exclude_path || p->depth <= 0) continue; - mix = srcs[i]; - break; + for (type = 0; type < 2; type++) { + if (types & (1 << type)) { + unsigned int val = p->ctls[type]; + if (get_amp_nid_(val) == nid && + get_amp_direction_(val) == dir) + return true; + } + } } - if (!mix) - return; + return false; +} + +#define is_out_ctl_present(codec, path, nid, dir) \ + __is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */ +#define is_out_vol_ctl_present(codec, nid, dir) \ + __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL) +#define is_out_mute_ctl_present(codec, nid, dir) \ + __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL) + +static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir) +{ + unsigned int caps, offset; + unsigned int val = 0; + + caps = query_amp_caps(codec, nid, dir); + if (caps & AC_AMPCAP_NUM_STEPS) { + offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; + /* if a volume control is assigned, set the lowest level + * as default; otherwise set to 0dB + */ + if (is_out_vol_ctl_present(codec, nid, dir)) + val = 0; + else + val = offset; + } + if (caps & AC_AMPCAP_MUTE) { + /* if a mute control is assigned, mute as default */ + if (is_out_mute_ctl_present(codec, nid, dir)) + val |= HDA_AMP_MUTE; + } + return val; +} + +/* configure the path from the given dac to the pin as the proper output */ +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac, bool force) +{ + int i, val; + struct nid_path *path;
- /* need the manual connection? */ - if (num > 1) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, i); - /* unmute mixer widget inputs */ - if (nid_has_mute(codec, mix, HDA_INPUT)) { - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0)); - snd_hda_codec_write(codec, mix, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1)); - } - /* initialize volume */ + alc_set_pin_output(codec, pin, pin_type); path = get_out_path(codec, pin, dac); if (!path) return; - nid = alc_look_for_out_vol_nid(codec, path); - if (nid) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO);
- /* unmute DAC if it's not assigned to a mixer */ - nid = alc_look_for_out_mute_nid(codec, path); - if (nid == mix && nid_has_mute(codec, dac, HDA_OUTPUT)) - snd_hda_codec_write(codec, dac, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_ZERO); + for (i = path->depth - 1; i >= 0; i--) { + hda_nid_t nid = path->path[i]; + if (i > 0 && path->multi[i - 1]) + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_CONNECT_SEL, + path->idx[i - 1]); + + if (i != 0 && i != path->depth - 1 && + (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && + (force || !is_out_ctl_present(codec, path, nid, + HDA_INPUT))) { + val = get_default_amp_val(codec, nid, HDA_INPUT); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(0) | val); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_IN_UNMUTE(1) | val); + } + if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && + (force || !is_out_ctl_present(codec, path, nid, + HDA_OUTPUT))) { + val = get_default_amp_val(codec, nid, HDA_OUTPUT); + snd_hda_codec_write(codec, nid, 0, + AC_VERB_SET_AMP_GAIN_MUTE, + AMP_OUT_UNMUTE | val); + } + } }
static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -3882,7 +3884,8 @@ static void alc_auto_init_multi_out(struct hda_codec *codec) hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); + spec->multiout.dac_nids[i], true); + } }
@@ -3905,7 +3908,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true); } for (i = 0; i < spec->autocfg.speaker_outs; i++) { if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -3920,7 +3923,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true); } }
@@ -4081,7 +4084,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); - alc_auto_select_dac(codec, nid, spec->multi_io[idx].dac); + alc_auto_set_output_and_unmute(codec, nid, PIN_OUT, + spec->multi_io[idx].dac, false); } else { if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
So far, idx[i] and multi[i] indicate the attribute of the widget path[i - 1]. This was just for simplifying the code in __parse_output_path(), but this is rather confusing for later use. It's more natural if both idx[i] and multi[i] point to the same widget of path[i]. This patch changes to that way.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a7899d1..5b8308d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -102,8 +102,8 @@ enum { #define MAX_NID_PATH_DEPTH 5
/* output-path: DAC -> ... -> pin - * idx[] contains the source index number of the next widget; - * e.g. idx[0] is the index of the DAC selected by path[1] widget + * idx[i] contains the source index number to select on of the widget path[i]; + * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget * multi[] indicates whether it's a selector widget with multi-connectors * (i.e. the connection selection is mandatory) * vol_ctl and mute_ctl contains the NIDs for the assigned mixers @@ -2937,9 +2937,9 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
found: path->path[path->depth] = conn[i]; - path->idx[path->depth] = i; + path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) - path->multi[path->depth] = 1; + path->multi[path->depth + 1] = 1; path->depth++; return true; } @@ -3846,10 +3846,10 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
for (i = path->depth - 1; i >= 0; i--) { hda_nid_t nid = path->path[i]; - if (i > 0 && path->multi[i - 1]) + if (path->multi[i]) snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, - path->idx[i - 1]); + path->idx[i]);
if (i != 0 && i != path->depth - 1 && (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) &&
Just like the output paths, parse the whole paths for inputs as well and store in a path list. For that purpose, rewrite the output parser code to be generically usable.
The input path list is not referred at all in this patch. It'll be used to replace the fixed adc/capsrc array in later patches for more flexible input path selections.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 103 ++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 30 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5b8308d..9d983a2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -101,7 +101,11 @@ enum {
#define MAX_NID_PATH_DEPTH 5
-/* output-path: DAC -> ... -> pin +/* Widget connection path + * + * For output, stored in the order of DAC -> ... -> pin, + * for input, pin -> ... -> ADC. + * * idx[i] contains the source index number to select on of the widget path[i]; * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget * multi[] indicates whether it's a selector widget with multi-connectors @@ -196,6 +200,9 @@ struct alc_spec { /* output paths */ struct snd_array out_path;
+ /* input paths */ + struct snd_array in_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2428,6 +2435,7 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); snd_array_free(&spec->out_path); + snd_array_free(&spec->in_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2628,6 +2636,10 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; }
+static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); + #ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) @@ -2666,6 +2678,28 @@ static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, return 0; }
+static int new_capture_source(struct hda_codec *codec, int adc_idx, + hda_nid_t pin, int idx, const char *label) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + + path = snd_array_new(&spec->in_path); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) { + snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", + pin, spec->adc_nids[adc_idx]); + return -EINVAL; + } + + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, idx, NULL); + return 0; +} + static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); @@ -2767,8 +2801,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) hda_nid_t cap = get_capsrc(spec, c); idx = get_connection_index(codec, cap, pin); if (idx >= 0) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, idx, NULL); + err = new_capture_source(codec, c, pin, idx, label); + if (err < 0) + return err; break; } } @@ -2897,40 +2932,45 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) }
/* called recursively */ -static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path, int depth) +static bool __parse_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix, struct nid_path *path, int depth) { struct alc_spec *spec = codec->spec; - hda_nid_t conn[8]; + hda_nid_t conn[16]; int i, nums;
- if (nid == spec->mixer_nid) { + if (to_nid == spec->mixer_nid) { if (!with_aa_mix) return false; with_aa_mix = 2; /* mark aa-mix is included */ }
- nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); for (i = 0; i < nums; i++) { - if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) - continue; - if (conn[i] == target_dac || - (!target_dac && !alc_is_dac_already_used(codec, conn[i]))) { - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) - goto found; + if (conn[i] != from_nid) { + /* special case: when from_nid is 0, + * try to find an empty DAC + */ + if (from_nid || + get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || + alc_is_dac_already_used(codec, conn[i])) + continue; } + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; } if (depth >= MAX_NID_PATH_DEPTH) return false; for (i = 0; i < nums; i++) { unsigned int type; type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT) + if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || + type == AC_WID_PIN) continue; - if (__parse_output_path(codec, conn[i], target_dac, - with_aa_mix, path, depth + 1)) + if (__parse_nid_path(codec, from_nid, conn[i], + with_aa_mix, path, depth + 1)) goto found; } return false; @@ -2938,25 +2978,27 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, found: path->path[path->depth] = conn[i]; path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) + if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; path->depth++; return true; }
-/* parse the output path from the given nid to the target DAC; - * when target_dac is 0, try to find an empty DAC; - * when with_aa_mix is 0, paths with spec->mixer_nid are excluded +/* parse the widget path from the given nid to the target nid; + * when @from_nid is 0, try to find an empty DAC; + * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. + * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. + * when @with_aa_mix is 2, no special handling about spec->mixer_nid. */ -static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, - hda_nid_t target_dac, int with_aa_mix, - struct nid_path *path) +static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path) { - if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { - path->path[path->depth] = nid; + if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + path->path[path->depth] = to_nid; path->depth++; #if 0 - snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", path->depth, path->path[0], path->path[1], path->path[2], path->path[3], path->path[4]); #endif @@ -3034,7 +3076,7 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, if (!path) return false; memset(path, 0, sizeof(*path)); - if (parse_output_path(codec, pin, dac, 0, path)) + if (parse_nid_path(codec, dac, pin, 0, path)) return true; /* push back */ spec->out_path.used--; @@ -4529,6 +4571,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->in_path, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec); if (err < 0) {
Improve the parser of analog loopback paths and handle in a more generic way. The following changes are included in this patch:
- Instead of assuming direct connections between pins and the mixer widget, track the whole path between them. This fixes some missing connections like ALC660.
- Introduce the path list for loopback paths like input and output path lists. Currently it's not used for any real purposes, yet.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9d983a2..6b3e67c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -203,6 +203,9 @@ struct alc_spec { /* input paths */ struct snd_array in_path;
+ /* analog loopback paths */ + struct snd_array loopback_path; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2436,6 +2439,7 @@ static void alc_free(struct hda_codec *codec) alc_free_bind_ctls(codec); snd_array_free(&spec->out_path); snd_array_free(&spec->in_path); + snd_array_free(&spec->loopback_path); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2660,12 +2664,22 @@ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) #endif
/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct alc_spec *spec, hda_nid_t pin, +static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, const char *ctlname, int ctlidx, - int idx, hda_nid_t mix_nid) + hda_nid_t mix_nid) { - int err; + struct alc_spec *spec = codec->spec; + struct nid_path *path; + int err, idx; + + path = snd_array_new(&spec->loopback_path); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, mix_nid, 2, path)) + return -EINVAL;
+ idx = path->idx[path->depth - 1]; err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); if (err < 0) @@ -2706,6 +2720,15 @@ static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) return (pincap & AC_PINCAP_IN) != 0; }
+/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +} + /* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ static int alc_auto_fill_adc_caps(struct hda_codec *codec) { @@ -2787,11 +2810,9 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) prev_label = label;
if (mixer) { - idx = get_connection_index(codec, mixer, pin); - if (idx >= 0) { - err = new_analog_input(spec, pin, - label, type_idx, - idx, mixer); + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, pin, + label, type_idx, mixer); if (err < 0) return err; } @@ -4572,6 +4593,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); snd_array_init(&spec->in_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec); if (err < 0) {
For handling the analog-loopback paths more generically, check the amp capabilities of the aa-mixer widget, and create only the appropriate mixer elements.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6b3e67c..1abcd8e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2672,6 +2672,10 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path; int err, idx;
+ if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && + !nid_has_mute(codec, mix_nid, HDA_INPUT)) + return 0; /* no need for analog loopback */ + path = snd_array_new(&spec->loopback_path); if (!path) return -ENOMEM; @@ -2680,14 +2684,20 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, return -EINVAL;
idx = path->idx[path->depth - 1]; - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, + if (err < 0) + return err; + } + + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); - if (err < 0) - return err; + if (err < 0) + return err; + } + add_loopback_list(spec, mix_nid, idx); return 0; }
When initializing the output paths, we assumed the input amps have almost two inputs blindly. It's not only generic but even incorrect for some codecs like ALC268 & co. Also, the same assumption (two sources) exists for the bind input-amp controls.
This patch changes the codes in these places to handle the input connections in a more generic way.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1abcd8e..981d505 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3621,9 +3621,10 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec, val = amp_val_replace_channels(val, chs); if (get_amp_direction_(val) == HDA_INPUT) { hda_nid_t nid = get_amp_nid_(val); - if (snd_hda_get_num_conns(codec, nid) > 1) { + int nums = snd_hda_get_num_conns(codec, nid); + if (nums > 1) { type = ALC_CTL_BIND_MUTE; - val |= 2 << 19; /* FIXME: fixed two widgets, so far */ + val |= nums << 19; } } return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); @@ -3909,6 +3910,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac, bool force) { + struct alc_spec *spec = codec->spec; int i, val; struct nid_path *path;
@@ -3928,13 +3930,19 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && (force || !is_out_ctl_present(codec, path, nid, HDA_INPUT))) { + hda_nid_t conn[16]; + int n, nums; + nums = snd_hda_get_connections(codec, nid, conn, + ARRAY_SIZE(conn)); val = get_default_amp_val(codec, nid, HDA_INPUT); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(0) | val); - snd_hda_codec_write(codec, nid, 0, + for (n = 0; n < nums; n++) { + if (n != path->idx[i] && + conn[n] != spec->mixer_nid) + continue; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(1) | val); + AMP_IN_UNMUTE(n) | val); + } } if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && (force || !is_out_ctl_present(codec, path, nid,
It's used only in one place in patch_analog.c, and it can be replaced with others better.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_analog.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 89fc503..308a5b9 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -995,15 +995,7 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - long *valp = ucontrol->value.integer.value; - int change; - - change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[0] ? 0 : HDA_AMP_MUTE); - change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, - HDA_AMP_MUTE, - valp[1] ? 0 : HDA_AMP_MUTE); + int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); if (change) ad1986a_update_hp(codec); return change;
For optimizing the verb executions, a new mechanism to cache the verbs and amp update commands is introduced. With the new "write to cache and flush" way, you can reduce the same verbs that have been written multiple times.
When codec->cached_write flag is set, the further snd_hda_codec_write_cache() and snd_hda_codec_amp_stereo() calls will be performed only on the command or amp cache table, but not sent to the hardware yet. Once after you call all commands and update amps, call snd_hda_codec_resume_amp() and snd_hda_codec_resume_cache(). Then all cached writes and amp updates will be written to the hardware, and the dirty flags are cleared.
In this implementation, the existing cache table is reused, so actually no big code change is seen here. Each cache entry has a new dirty flag now (so the cache key is now reduced to 31bit).
As a good side-effect by this change, snd_hda_codec_resume_*() will no longer execute verbs that have been already issued during the resume phase by checking the dirty flags.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 74 +++++++++++++++++++++++++++++++++++++---------- sound/pci/hda/hda_codec.h | 12 +++----- sound/pci/hda/hda_local.h | 2 -- 3 files changed, 63 insertions(+), 25 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b8fb0a5..3aa20dc 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1610,6 +1610,7 @@ static struct hda_cache_head *get_alloc_hash(struct hda_cache_rec *cache, cur = snd_array_index(&cache->buf, info); info->key = key; info->val = 0; + info->dirty = 0; idx = key % (u16)ARRAY_SIZE(cache->hash); info->next = cache->hash[idx]; cache->hash[idx] = cur; @@ -1873,8 +1874,11 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; + if (codec->cached_write) + info->head.dirty = 1; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, info, nid, ch, direction, idx, val); + if (!codec->cached_write) + put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update); @@ -1905,7 +1909,6 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
-#ifdef CONFIG_PM /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec @@ -1914,13 +1917,17 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo); */ void snd_hda_codec_resume_amp(struct hda_codec *codec) { - struct hda_amp_info *buffer = codec->amp_cache.buf.list; int i;
- for (i = 0; i < codec->amp_cache.buf.used; i++, buffer++) { - u32 key = buffer->head.key; + mutex_lock(&codec->hash_mutex); + for (i = 0; i < codec->amp_cache.buf.used; i++) { + struct hda_amp_info *buffer; + u32 key; hda_nid_t nid; unsigned int idx, dir, ch; + + buffer = snd_array_elem(&codec->amp_cache.buf, i); + key = buffer->head.key; if (!key) continue; nid = key & 0xff; @@ -1929,13 +1936,18 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) for (ch = 0; ch < 2; ch++) { if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; + if (!buffer->head.dirty) + continue; + buffer->head.dirty = 0; + mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, buffer, nid, ch, dir, idx, buffer->vol[ch]); + mutex_lock(&codec->hash_mutex); } } + mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_amp); -#endif /* CONFIG_PM */
static u32 get_amp_max_value(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int ofs) @@ -3375,12 +3387,11 @@ int snd_hda_create_spdif_in_ctls(struct hda_codec *codec, hda_nid_t nid) } EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls);
-#ifdef CONFIG_PM /* * command cache */
-/* build a 32bit cache key with the widget id and the command parameter */ +/* build a 31bit cache key with the widget id and the command parameter */ #define build_cmd_cache_key(nid, verb) ((verb << 8) | nid) #define get_cmd_cache_nid(key) ((key) & 0xff) #define get_cmd_cache_cmd(key) (((key) >> 8) & 0xffff) @@ -3400,12 +3411,16 @@ EXPORT_SYMBOL_HDA(snd_hda_create_spdif_in_ctls); int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { - int err = snd_hda_codec_write(codec, nid, direct, verb, parm); + int err; struct hda_cache_head *c; u32 key;
- if (err < 0) - return err; + if (!codec->cached_write) { + err = snd_hda_codec_write(codec, nid, direct, verb, parm); + if (err < 0) + return err; + } + /* parm may contain the verb stuff for get/set amp */ verb = verb | (parm >> 8); parm &= 0xff; @@ -3414,6 +3429,8 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, c = get_alloc_hash(&codec->cmd_cache, key); if (c) c->val = parm; + if (codec->cached_write) + c->dirty = 1; mutex_unlock(&codec->bus->cmd_mutex); return 0; } @@ -3462,16 +3479,26 @@ EXPORT_SYMBOL_HDA(snd_hda_codec_update_cache); */ void snd_hda_codec_resume_cache(struct hda_codec *codec) { - struct hda_cache_head *buffer = codec->cmd_cache.buf.list; int i;
- for (i = 0; i < codec->cmd_cache.buf.used; i++, buffer++) { - u32 key = buffer->key; + mutex_lock(&codec->hash_mutex); + for (i = 0; i < codec->cmd_cache.buf.used; i++) { + struct hda_cache_head *buffer; + u32 key; + + buffer = snd_array_elem(&codec->cmd_cache.buf, i); + key = buffer->key; if (!key) continue; + if (!buffer->dirty) + continue; + buffer->dirty = 0; + mutex_unlock(&codec->hash_mutex); snd_hda_codec_write(codec, get_cmd_cache_nid(key), 0, get_cmd_cache_cmd(key), buffer->val); + mutex_lock(&codec->hash_mutex); } + mutex_unlock(&codec->hash_mutex); } EXPORT_SYMBOL_HDA(snd_hda_codec_resume_cache);
@@ -3492,7 +3519,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, seq->param); } EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache); -#endif /* CONFIG_PM */
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg, unsigned int power_state, @@ -3640,6 +3666,22 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec, bool in_wq) return state; }
+/* mark all entries of cmd and amp caches dirty */ +static void hda_mark_cmd_cache_dirty(struct hda_codec *codec) +{ + int i; + for (i = 0; i < codec->cmd_cache.buf.used; i++) { + struct hda_cache_head *cmd; + cmd = snd_array_elem(&codec->cmd_cache.buf, i); + cmd->dirty = 1; + } + for (i = 0; i < codec->amp_cache.buf.used; i++) { + struct hda_amp_info *amp; + amp = snd_array_elem(&codec->cmd_cache.buf, i); + amp->head.dirty = 1; + } +} + /* * kick up codec; used both from PM and power-save */ @@ -3647,6 +3689,8 @@ static void hda_call_codec_resume(struct hda_codec *codec) { codec->in_pm = 1;
+ hda_mark_cmd_cache_dirty(codec); + /* set as if powered on for avoiding re-entering the resume * in the resume / power-save sequence */ diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 8665540..cab39b2 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -719,9 +719,10 @@ struct hda_codec_ops {
/* record for amp information cache */ struct hda_cache_head { - u32 key; /* hash key */ + u32 key:31; /* hash key */ + u32 dirty:1; u16 val; /* assigned value */ - u16 next; /* next link; -1 = terminal */ + u16 next; };
struct hda_amp_info { @@ -867,6 +868,7 @@ struct hda_codec { unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ + unsigned int cached_write:1; /* write only to caches */ #ifdef CONFIG_PM unsigned int power_on :1; /* current (global) power-state */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ @@ -952,7 +954,6 @@ void snd_hda_sequence_write(struct hda_codec *codec, int snd_hda_queue_unsol_event(struct hda_bus *bus, u32 res, u32 res_ex);
/* cached write */ -#ifdef CONFIG_PM int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_sequence_write_cache(struct hda_codec *codec, @@ -960,11 +961,6 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec, int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec); -#else -#define snd_hda_codec_write_cache snd_hda_codec_write -#define snd_hda_codec_update_cache snd_hda_codec_write -#define snd_hda_sequence_write_cache snd_hda_sequence_write -#endif
/* the struct for codec->pin_configs */ struct hda_pincfg { diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 4b40a5e..f765296 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -133,9 +133,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); -#ifdef CONFIG_PM void snd_hda_codec_resume_amp(struct hda_codec *codec); -#endif
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv);
The new function snd_hda_codec_amp_init() (and the stereo variant) initializes the amp value only once at the first access. If the amp was already initialized or updated, this won't do anything more.
It's useful for initializing the input amps that are in the part of the path but never used.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 71 ++++++++++++++++++++++++++++++++++------------- sound/pci/hda/hda_local.h | 4 +++ 2 files changed, 56 insertions(+), 19 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 3aa20dc..7383746 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1765,7 +1765,7 @@ EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps); */ static struct hda_amp_info * update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int index) + int direction, int index, bool init_only) { struct hda_amp_info *info; unsigned int parm, val = 0; @@ -1791,7 +1791,8 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; info->head.val |= INFO_AMP_VOL(ch); - } + } else if (init_only) + return NULL; return info; }
@@ -1832,7 +1833,7 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, unsigned int val = 0;
mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, index); + info = update_amp_hash(codec, nid, ch, direction, index, false); if (info) val = info->vol[ch]; mutex_unlock(&codec->hash_mutex); @@ -1840,21 +1841,9 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
-/** - * snd_hda_codec_amp_update - update the AMP value - * @codec: HD-audio codec - * @nid: NID to read the AMP value - * @ch: channel (left=0 or right=1) - * @direction: #HDA_INPUT or #HDA_OUTPUT - * @idx: the index value (only for input direction) - * @mask: bit mask to set - * @val: the bits value to set - * - * Update the AMP value with a bit mask. - * Returns 0 if the value is unchanged, 1 if changed. - */ -int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, - int direction, int idx, int mask, int val) +static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val, + bool init_only) { struct hda_amp_info *info;
@@ -1863,7 +1852,7 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, val &= mask;
mutex_lock(&codec->hash_mutex); - info = update_amp_hash(codec, nid, ch, direction, idx); + info = update_amp_hash(codec, nid, ch, direction, idx, init_only); if (!info) { mutex_unlock(&codec->hash_mutex); return 0; @@ -1881,6 +1870,25 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } + +/** + * snd_hda_codec_amp_update - update the AMP value + * @codec: HD-audio codec + * @nid: NID to read the AMP value + * @ch: channel (left=0 or right=1) + * @direction: #HDA_INPUT or #HDA_OUTPUT + * @idx: the index value (only for input direction) + * @mask: bit mask to set + * @val: the bits value to set + * + * Update the AMP value with a bit mask. + * Returns 0 if the value is unchanged, 1 if changed. + */ +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, direction, idx, mask, val, false); +} EXPORT_SYMBOL_HDA(snd_hda_codec_amp_update);
/** @@ -1909,6 +1917,31 @@ int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_codec_amp_stereo);
+/* Works like snd_hda_codec_amp_update() but it writes the value only at + * the first access. If the amp was already initialized / updated beforehand, + * this does nothing. + */ +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int dir, int idx, int mask, int val) +{ + return codec_amp_update(codec, nid, ch, dir, idx, mask, val, true); +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init); + +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val) +{ + int ch, ret = 0; + + if (snd_BUG_ON(mask & ~0xff)) + mask &= 0xff; + for (ch = 0; ch < 2; ch++) + ret |= snd_hda_codec_amp_init(codec, nid, ch, dir, + idx, mask, val); + return ret; +} +EXPORT_SYMBOL_HDA(snd_hda_codec_amp_init_stereo); + /** * snd_hda_codec_resume_amp - Resume all AMP commands from the cache * @codec: HD-audio codec diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index f765296..e38519b 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -133,6 +133,10 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); +int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val); +int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx, int mask, int val); void snd_hda_codec_resume_amp(struct hda_codec *codec);
void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir,
We are using only AUTOMUTE_MODE_PIN in patch_realtek.c and all others have been already dropped. Let's remove the old superfluous codes.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 46 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 981d505..d964c88 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -73,12 +73,6 @@ struct alc_multi_io { unsigned int ctl_in; /* cached input-pin control value */ };
-enum { - ALC_AUTOMUTE_PIN, /* change the pin control */ - ALC_AUTOMUTE_AMP, /* mute/unmute the pin AMP */ - ALC_AUTOMUTE_MIXER, /* mute/unmute mixer widget AMP */ -}; - #define MAX_VOL_NIDS 0x40
/* make compatible with old code */ @@ -542,7 +536,6 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, bool mute, bool hp_out) { struct alc_spec *spec = codec->spec; - unsigned int mute_bits = mute ? HDA_AMP_MUTE : 0; unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); int i;
@@ -551,34 +544,17 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, unsigned int val; if (!nid) break; - switch (spec->automute_mode) { - case ALC_AUTOMUTE_PIN: - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); - break; - case ALC_AUTOMUTE_AMP: - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, mute_bits); - break; - case ALC_AUTOMUTE_MIXER: - nid = spec->automute_mixer_nid[i]; - if (!nid) - break; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 0, - HDA_AMP_MUTE, mute_bits); - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, 1, - HDA_AMP_MUTE, mute_bits); - break; - } + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; + snd_hda_set_pin_ctl(codec, nid, val); } }
@@ -979,8 +955,6 @@ static int alc_init_automute(struct hda_codec *codec) cfg->hp_outs = cfg->line_outs; }
- spec->automute_mode = ALC_AUTOMUTE_PIN; - for (i = 0; i < cfg->hp_outs; i++) { hda_nid_t nid = cfg->hp_pins[i]; if (!is_jack_detectable(codec, nid))
... and rewrite the initialization of output paths as a generic function that is applicable for both i/o directions.
The new flag, active, is introduced to each nid_path entry. This indicates whether the given path is active, and it's used for checking whether a certain widget can be turned off or changed when a path is no longer used or newly enabled.
It's still used only in the output paths. More wider adaption for input and loopback paths will be achieved in the later patch.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_local.h | 3 +- sound/pci/hda/patch_realtek.c | 261 ++++++++++++++++++++++++++++-------------- 2 files changed, 176 insertions(+), 88 deletions(-)
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index e38519b..ff56da8 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -598,7 +598,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec, #define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3) #define get_amp_direction_(pv) (((pv) >> 18) & 0x1) #define get_amp_direction(kc) get_amp_direction_((kc)->private_value) -#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf) +#define get_amp_index_(pv) (((pv) >> 19) & 0xf) +#define get_amp_index(kc) get_amp_index_((kc)->private_value) #define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f) #define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d964c88..84f6781 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -112,6 +112,7 @@ struct nid_path { unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[2]; /* 0 = volume, 1 = mute */ + bool active; };
enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; @@ -2853,16 +2854,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; }
-static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid, - unsigned int pin_type) -{ - snd_hda_set_pin_ctl(codec, nid, pin_type); - /* unmute pin */ - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); -} - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -3824,23 +3815,21 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); }
-/* is a volume or mute control already present? */ -static bool __is_out_ctl_present(struct hda_codec *codec, - struct nid_path *exclude_path, - hda_nid_t nid, int dir, int types) +static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid, + int dir, int idx, int types) { - struct alc_spec *spec = codec->spec; int i, type;
- for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *p = snd_array_elem(&spec->out_path, i); - if (p == exclude_path || p->depth <= 0) + for (i = 0; i < array->used; i++) { + struct nid_path *p = snd_array_elem(array, i); + if (p->depth <= 0) continue; for (type = 0; type < 2; type++) { if (types & (1 << type)) { unsigned int val = p->ctls[type]; if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir) + get_amp_direction_(val) == dir && + get_amp_index_(val) == idx) return true; } } @@ -3848,85 +3837,183 @@ static bool __is_out_ctl_present(struct hda_codec *codec, return false; }
-#define is_out_ctl_present(codec, path, nid, dir) \ - __is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */ -#define is_out_vol_ctl_present(codec, nid, dir) \ - __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL) -#define is_out_mute_ctl_present(codec, nid, dir) \ - __is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) +{ + struct alc_spec *spec = codec->spec; + return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) || + is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) || + is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3); +}
-static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir) +/* can have the amp-in capability? */ +static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { - unsigned int caps, offset; + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_IN_AMP)) + return false; + if (type == AC_WID_PIN && idx > 0) /* only for input pins */ + return false; + return true; +} + +/* can have the amp-out capability? */ +static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) +{ + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_OUT_AMP)) + return false; + if (type == AC_WID_PIN && !idx) /* only for output pins */ + return false; + return true; +} + +static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, + hda_nid_t nid, int dir, int idx) +{ + int i, n; + + for (n = 0; n < array->used; n++) { + struct nid_path *path = snd_array_elem(array, n); + if (!path->active) + continue; + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) { + if (dir == HDA_OUTPUT || path->idx[i] == idx) + return true; + break; + } + } + } + return false; +} + +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) +{ + struct alc_spec *spec = codec->spec; + return is_active_in_list(codec, &spec->out_path, nid, idx, dir) || + is_active_in_list(codec, &spec->in_path, nid, idx, dir) || + is_active_in_list(codec, &spec->loopback_path, nid, idx, dir); +} + +/* get the default amp value for the target state */ +static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, + int dir, bool enable) +{ + unsigned int caps; unsigned int val = 0;
caps = query_amp_caps(codec, nid, dir); if (caps & AC_AMPCAP_NUM_STEPS) { - offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - /* if a volume control is assigned, set the lowest level - * as default; otherwise set to 0dB - */ - if (is_out_vol_ctl_present(codec, nid, dir)) - val = 0; - else - val = offset; + /* set to 0dB */ + if (enable) + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } if (caps & AC_AMPCAP_MUTE) { - /* if a mute control is assigned, mute as default */ - if (is_out_mute_ctl_present(codec, nid, dir)) + if (!enable) val |= HDA_AMP_MUTE; } return val; }
-/* configure the path from the given dac to the pin as the proper output */ -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac, bool force) +/* initialize the amp value (only at the first time) */ +static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) +{ + int val = get_amp_val_to_activate(codec, nid, dir, false); + snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, + int idx, bool enable) +{ + int val; + if (is_ctl_associated(codec, nid, dir, idx) || + is_active_nid(codec, nid, dir, idx)) + return; + val = get_amp_val_to_activate(codec, nid, dir, enable); + snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) +{ + hda_nid_t nid = path->path[i]; + init_amp(codec, nid, HDA_OUTPUT, 0); + activate_amp(codec, nid, HDA_OUTPUT, 0, enable); +} + +static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) { struct alc_spec *spec = codec->spec; - int i, val; - struct nid_path *path; + hda_nid_t conn[16]; + int n, nums; + hda_nid_t nid = path->path[i];
- alc_set_pin_output(codec, pin, pin_type); - path = get_out_path(codec, pin, dac); - if (!path) + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + for (n = 0; n < nums; n++) + init_amp(codec, nid, HDA_INPUT, n); + + if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i])) return;
+ /* here is a little bit tricky in comparison with activate_amp_out(); + * when aa-mixer is available, we need to enable the path as well + */ + for (n = 0; n < nums; n++) { + if (n != path->idx[i] && conn[n] != spec->mixer_nid) + continue; + activate_amp(codec, nid, HDA_INPUT, n, enable); + } +} + +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable) +{ + int i; + + if (path->active == enable) + return; + + if (!enable) + path->active = false; + for (i = path->depth - 1; i >= 0; i--) { - hda_nid_t nid = path->path[i]; if (path->multi[i]) - snd_hda_codec_write(codec, nid, 0, + snd_hda_codec_write_cache(codec, path->path[i], 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]); - - if (i != 0 && i != path->depth - 1 && - (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) && - (force || !is_out_ctl_present(codec, path, nid, - HDA_INPUT))) { - hda_nid_t conn[16]; - int n, nums; - nums = snd_hda_get_connections(codec, nid, conn, - ARRAY_SIZE(conn)); - val = get_default_amp_val(codec, nid, HDA_INPUT); - for (n = 0; n < nums; n++) { - if (n != path->idx[i] && - conn[n] != spec->mixer_nid) - continue; - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_UNMUTE(n) | val); - } - } - if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) && - (force || !is_out_ctl_present(codec, path, nid, - HDA_OUTPUT))) { - val = get_default_amp_val(codec, nid, HDA_OUTPUT); - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE | val); - } + if (has_amp_in(codec, path, i)) + activate_amp_in(codec, path, i, enable); + if (has_amp_out(codec, path, i)) + activate_amp_out(codec, path, i, enable); } + + if (enable) + path->active = true; +} + +/* configure the path from the given dac to the pin as the proper output */ +static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac) +{ + struct nid_path *path; + + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); + path = get_out_path(codec, pin, dac); + if (!path) + return; + activate_path(codec, path, true); }
static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -3939,7 +4026,7 @@ static void alc_auto_init_multi_out(struct hda_codec *codec) hda_nid_t nid = spec->autocfg.line_out_pins[i]; if (nid) alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i], true); + spec->multiout.dac_nids[i]);
} } @@ -3963,7 +4050,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true); + alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); } for (i = 0; i < spec->autocfg.speaker_outs; i++) { if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) @@ -3978,7 +4065,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec) else dac = spec->multiout.dac_nids[0]; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } }
@@ -4129,22 +4216,22 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) { struct alc_spec *spec = codec->spec; hda_nid_t nid = spec->multi_io[idx].pin; + struct nid_path *path; + + path = get_out_path(codec, nid, spec->multi_io[idx].dac); + if (!path) + return -EINVAL;
if (!spec->multi_io[idx].ctl_in) spec->multi_io[idx].ctl_in = - snd_hda_codec_read(codec, nid, 0, + snd_hda_codec_update_cache(codec, nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, 0); - alc_auto_set_output_and_unmute(codec, nid, PIN_OUT, - spec->multi_io[idx].dac, false); + activate_path(codec, path, true); } else { - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0, - HDA_AMP_MUTE, HDA_AMP_MUTE); + activate_path(codec, path, false); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); }
alc_auto_is_dac_reachable() can be replaced fully with is_reachable_path(). The only difference is the order of arguments.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 84f6781..fc5f80b5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2902,15 +2902,6 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) return false; }
-/* check whether the DAC is reachable from the pin */ -static bool alc_auto_is_dac_reachable(struct hda_codec *codec, - hda_nid_t pin, hda_nid_t dac) -{ - if (!pin || !dac) - return false; - return snd_hda_get_conn_index(codec, pin, dac, true) >= 0; -} - /* look for an empty DAC slot */ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) { @@ -2921,7 +2912,7 @@ static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (alc_auto_is_dac_reachable(codec, pin, nid)) + if (is_reachable_path(codec, nid, pin)) return nid; } return 0; @@ -3013,7 +3004,7 @@ static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; - if (alc_auto_is_dac_reachable(codec, pin, nid)) { + if (is_reachable_path(codec, nid, pin)) { if (nid_found) return 0; nid_found = nid; @@ -3189,7 +3180,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, dacs[i] = alc_auto_look_for_dac(codec, pin); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[j])) { + if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; break; @@ -3198,11 +3189,10 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, } dac = dacs[i]; if (!dac) { - if (alc_auto_is_dac_reachable(codec, pin, dacs[0])) + if (is_reachable_path(codec, dacs[0], pin)) dac = dacs[0]; else if (cfg->line_outs > i && - alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[i])) + is_reachable_path(codec, spec->private_dac_nids[i], pin)) dac = spec->private_dac_nids[i]; if (dac) { if (!i) @@ -3211,8 +3201,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, badness += bad->shared_surr; else badness += bad->shared_clfe; - } else if (alc_auto_is_dac_reachable(codec, pin, - spec->private_dac_nids[0])) { + } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { dac = spec->private_dac_nids[0]; badness += bad->shared_surr_main; } else if (!i) @@ -4141,7 +4130,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec,
if (offset && offset + spec->multi_ios < dacs) { dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!alc_auto_is_dac_reachable(codec, nid, dac)) + if (!is_reachable_path(codec, dac, nid)) dac = 0; } if (hardwired)
We don't have to keep three individual path lists for input, output and loopback.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 94 +++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 61 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fc5f80b5..47f5c4f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -192,14 +192,8 @@ struct alc_spec { int num_all_dacs; hda_nid_t all_dacs[16];
- /* output paths */ - struct snd_array out_path; - - /* input paths */ - struct snd_array in_path; - - /* analog loopback paths */ - struct snd_array loopback_path; + /* path list */ + struct snd_array paths;
/* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -2412,9 +2406,7 @@ static void alc_free(struct hda_codec *codec)
alc_free_kctls(codec); alc_free_bind_ctls(codec); - snd_array_free(&spec->out_path); - snd_array_free(&spec->in_path); - snd_array_free(&spec->loopback_path); + snd_array_free(&spec->paths); snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); @@ -2651,7 +2643,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */
- path = snd_array_new(&spec->loopback_path); + path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); @@ -2684,7 +2676,7 @@ static int new_capture_source(struct hda_codec *codec, int adc_idx, struct hda_input_mux *imux = &spec->private_imux[0]; struct nid_path *path;
- path = snd_array_new(&spec->in_path); + path = snd_array_new(&spec->paths); if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); @@ -2894,8 +2886,8 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) struct alc_spec *spec = codec->spec; int i;
- for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->path[0] == nid) return true; } @@ -3018,8 +3010,8 @@ static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) struct alc_spec *spec = codec->spec; int i;
- for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->ctls[type] == val) return true; } @@ -3059,14 +3051,14 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, struct alc_spec *spec = codec->spec; struct nid_path *path;
- path = snd_array_new(&spec->out_path); + path = snd_array_new(&spec->paths); if (!path) return false; memset(path, 0, sizeof(*path)); if (parse_nid_path(codec, dac, pin, 0, path)) return true; /* push back */ - spec->out_path.used--; + spec->paths.used--; return false; }
@@ -3079,8 +3071,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, struct alc_spec *spec = codec->spec; int i;
- for (i = 0; i < spec->out_path.used; i++) { - struct nid_path *path = snd_array_elem(&spec->out_path, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->depth <= 0) continue; if ((!dac || path->path[0] == dac) && @@ -3258,7 +3250,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; - snd_array_free(&spec->out_path); + snd_array_free(&spec->paths); badness = 0;
/* fill hard-wired DACs first */ @@ -3804,38 +3796,28 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec) "Speaker"); }
-static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid, - int dir, int idx, int types) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) { + struct alc_spec *spec = codec->spec; int i, type;
- for (i = 0; i < array->used; i++) { - struct nid_path *p = snd_array_elem(array, i); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *p = snd_array_elem(&spec->paths, i); if (p->depth <= 0) continue; for (type = 0; type < 2; type++) { - if (types & (1 << type)) { - unsigned int val = p->ctls[type]; - if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir && - get_amp_index_(val) == idx) - return true; - } + unsigned int val = p->ctls[type]; + if (get_amp_nid_(val) == nid && + get_amp_direction_(val) == dir && + get_amp_index_(val) == idx) + return true; } } return false; }
-/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - struct alc_spec *spec = codec->spec; - return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) || - is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) || - is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3); -} - /* can have the amp-in capability? */ static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { @@ -3864,13 +3846,15 @@ static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) return true; }
-static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, - hda_nid_t nid, int dir, int idx) +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) { + struct alc_spec *spec = codec->spec; int i, n;
- for (n = 0; n < array->used; n++) { - struct nid_path *path = snd_array_elem(array, n); + for (n = 0; n < spec->paths.used; n++) { + struct nid_path *path = snd_array_elem(&spec->paths, n); if (!path->active) continue; for (i = 0; i < path->depth; i++) { @@ -3884,16 +3868,6 @@ static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array, return false; }
-/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int idx, unsigned int dir) -{ - struct alc_spec *spec = codec->spec; - return is_active_in_list(codec, &spec->out_path, nid, idx, dir) || - is_active_in_list(codec, &spec->in_path, nid, idx, dir) || - is_active_in_list(codec, &spec->loopback_path, nid, idx, dir); -} - /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, int dir, bool enable) @@ -4163,7 +4137,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, } if (!hardwired && spec->multi_ios < 2) { /* cancel newly assigned paths */ - spec->out_path.used -= spec->multi_ios - old_pins; + spec->paths.used -= spec->multi_ios - old_pins; spec->multi_ios = old_pins; return badness; } @@ -4659,9 +4633,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); - snd_array_init(&spec->out_path, sizeof(struct nid_path), 8); - snd_array_init(&spec->in_path, sizeof(struct nid_path), 8); - snd_array_init(&spec->loopback_path, sizeof(struct nid_path), 8); + snd_array_init(&spec->paths, sizeof(struct nid_path), 8);
err = alc_codec_rename_from_preset(codec); if (err < 0) {
Similar like analog output paths, use the path list for parsing and initializing digital outputs as well.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 47f5c4f..206b983 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1424,6 +1424,14 @@ static unsigned int alc_get_coef0(struct hda_codec *codec) return spec->coef0; }
+static void alc_auto_set_output_and_unmute(struct hda_codec *codec, + hda_nid_t pin, int pin_type, + hda_nid_t dac); +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital); +static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac); + /* * Digital I/O handling */ @@ -1433,22 +1441,13 @@ static void alc_auto_init_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i; - hda_nid_t pin, dac; + hda_nid_t pin;
for (i = 0; i < spec->autocfg.dig_outs; i++) { pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - snd_hda_set_pin_ctl(codec, pin, PIN_OUT); - if (!i) - dac = spec->multiout.dig_out_nid; - else - dac = spec->slave_dig_outs[i - 1]; - if (!dac || !(get_wcaps(codec, dac) & AC_WCAP_OUT_AMP)) - continue; - snd_hda_codec_write(codec, dac, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_UNMUTE); + alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); } pin = spec->autocfg.dig_in_pin; if (pin) @@ -1465,13 +1464,10 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* support multiple SPDIFs; the secondary is set up as a slave */ nums = 0; for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t conn[4]; - err = snd_hda_get_connections(codec, - spec->autocfg.dig_out_pins[i], - conn, ARRAY_SIZE(conn)); - if (err <= 0) + hda_nid_t pin = spec->autocfg.dig_out_pins[i]; + dig_nid = alc_auto_look_for_dac(codec, pin, true); + if (!dig_nid) continue; - dig_nid = conn[0]; /* assume the first element is audio-out */ if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -1481,6 +1477,7 @@ static void alc_auto_parse_digital(struct hda_codec *codec) break; spec->slave_dig_outs[nums - 1] = dig_nid; } + add_new_out_path(codec, pin, dig_nid); nums++; }
@@ -2895,15 +2892,20 @@ static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) }
/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin) +static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital) { struct alc_spec *spec = codec->spec; + bool cap_digital; int i;
for (i = 0; i < spec->num_all_dacs; i++) { hda_nid_t nid = spec->all_dacs[i]; if (!nid || alc_is_dac_already_used(codec, nid)) continue; + cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); + if (is_digital != cap_digital) + continue; if (is_reachable_path(codec, nid, pin)) return nid; } @@ -3169,7 +3171,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, for (i = 0; i < num_outs; i++) { hda_nid_t pin = pins[i]; if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin); + dacs[i] = alc_auto_look_for_dac(codec, pin, false); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) { @@ -4110,7 +4112,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, if (hardwired) dac = get_dac_if_single(codec, nid); else if (!dac) - dac = alc_auto_look_for_dac(codec, nid); + dac = alc_auto_look_for_dac(codec, nid, false); if (!dac) { badness++; continue;
The function can be used not only for output paths but generically. Also swap the argument order.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 206b983..419979d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3064,11 +3064,11 @@ static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, return false; }
-/* get the path pointing from the given dac to pin; +/* get the path between the given NIDs; * passing 0 to either @pin or @dac behaves as a wildcard */ -static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) { struct alc_spec *spec = codec->spec; int i; @@ -3077,8 +3077,8 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path = snd_array_elem(&spec->paths, i); if (path->depth <= 0) continue; - if ((!dac || path->path[0] == dac) && - (!pin || path->path[path->depth - 1] == pin)) + if ((!from_nid || path->path[0] == from_nid) && + (!to_nid || path->path[path->depth - 1] == to_nid)) return path; } return NULL; @@ -3094,7 +3094,7 @@ static struct nid_path *get_out_path(struct hda_codec *codec, hda_nid_t pin, static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { - struct nid_path *path = get_out_path(codec, pin, dac); + struct nid_path *path = get_nid_path(codec, dac, pin); hda_nid_t nid; unsigned int val; int badness = 0; @@ -3495,9 +3495,9 @@ static int alc_auto_fill_dac_nids(struct hda_codec *codec) debug_show_configs(spec, cfg);
if (cfg->line_out_pins[0]) { - struct nid_path *path = get_out_path(codec, - cfg->line_out_pins[0], - spec->multiout.dac_nids[0]); + struct nid_path *path = get_nid_path(codec, + spec->multiout.dac_nids[0], + cfg->line_out_pins[0]); if (path) spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); } @@ -3641,7 +3641,7 @@ static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, name = alc_get_line_out_pfx(spec, i, true, &index); }
- path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) continue; if (!name || !strcmp(name, "CLFE")) { @@ -3677,7 +3677,7 @@ static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, struct nid_path *path; int err;
- path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) return 0; /* bind volume control will be created in the case of dac = 0 */ @@ -3763,7 +3763,7 @@ static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - path = get_out_path(codec, pins[i], dacs[i]); + path = get_nid_path(codec, dacs[i], pins[i]); if (!path) continue; vol = alc_look_for_out_vol_nid(codec, path); @@ -3975,7 +3975,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, struct nid_path *path;
snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = get_out_path(codec, pin, dac); + path = get_nid_path(codec, dac, pin); if (!path) return; activate_path(codec, path, true); @@ -4183,7 +4183,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) hda_nid_t nid = spec->multi_io[idx].pin; struct nid_path *path;
- path = get_out_path(codec, nid, spec->multi_io[idx].dac); + path = get_nid_path(codec, spec->multi_io[idx].dac, nid); if (!path) return -EINVAL;
The pin widget has only a single amp value for the input even if it has multiple "sources". Handle the situation in activate_path().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 419979d..3f39ba5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3921,21 +3921,27 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, { struct alc_spec *spec = codec->spec; hda_nid_t conn[16]; - int n, nums; + int n, nums, idx; hda_nid_t nid = path->path[i];
nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + nums = 1; + idx = 0; + } else + idx = path->idx[i]; + for (n = 0; n < nums; n++) init_amp(codec, nid, HDA_INPUT, n);
- if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i])) + if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) return;
/* here is a little bit tricky in comparison with activate_amp_out(); * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { - if (n != path->idx[i] && conn[n] != spec->mixer_nid) + if (n != idx && conn[n] != spec->mixer_nid) continue; activate_amp(codec, nid, HDA_INPUT, n, enable); }
The paths used for multi-io haven't been initialized properly, so far. It's usually no big matter because the pins are set to input as default, but it's still cleaner to initialize the paths properly.
Now with the path active/inactive check, we can do it easily.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3f39ba5..398b25b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3952,9 +3952,6 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, { int i;
- if (path->active == enable) - return; - if (!enable) path->active = false;
@@ -3984,6 +3981,8 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, path = get_nid_path(codec, dac, pin); if (!path) return; + if (path->active) + return; activate_path(codec, path, true); }
@@ -4193,10 +4192,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) if (!path) return -EINVAL;
- if (!spec->multi_io[idx].ctl_in) - spec->multi_io[idx].ctl_in = - snd_hda_codec_update_cache(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + if (path->active == output) + return 0;
if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); @@ -4251,6 +4248,25 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) return 0; }
+static void alc_auto_init_multi_io(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multi_ios; i++) { + hda_nid_t pin = spec->multi_io[i].pin; + struct nid_path *path; + path = get_nid_path(codec, spec->multi_io[i].dac, pin); + if (!path) + continue; + if (!spec->multi_io[i].ctl_in) + spec->multi_io[i].ctl_in = + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + activate_path(codec, path, path->active); + } +} + /* filter out invalid adc_nids (and capsrc_nids) that don't give all * active input pins */ @@ -4489,6 +4505,7 @@ static void alc_auto_init_std(struct hda_codec *codec) { alc_auto_init_multi_out(codec); alc_auto_init_extra_out(codec); + alc_auto_init_multi_io(codec); alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec);
Don't forget to take boost volumes into account in the managed path list. Since it's an additional volume, we need to extend the ctls[] array.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 398b25b..eadf326d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -95,6 +95,13 @@ struct alc_multi_io {
#define MAX_NID_PATH_DEPTH 5
+enum { + NID_PATH_VOL_CTL, + NID_PATH_MUTE_CTL, + NID_PATH_BOOST_CTL, + NID_PATH_NUM_CTLS +}; + /* Widget connection path * * For output, stored in the order of DAC -> ... -> pin, @@ -111,12 +118,10 @@ struct nid_path { hda_nid_t path[MAX_NID_PATH_DEPTH]; unsigned char idx[MAX_NID_PATH_DEPTH]; unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[2]; /* 0 = volume, 1 = mute */ + unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; };
-enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 }; - struct alc_spec { struct hda_gen_spec gen;
@@ -3809,7 +3814,7 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, struct nid_path *p = snd_array_elem(&spec->paths, i); if (p->depth <= 0) continue; - for (type = 0; type < 2; type++) { + for (type = 0; type < NID_PATH_NUM_CTLS; type++) { unsigned int val = p->ctls[type]; if (get_amp_nid_(val) == nid && get_amp_direction_(val) == dir && @@ -4388,6 +4393,8 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { const char *label; char boost_label[32]; + struct nid_path *path; + unsigned int val;
label = hda_get_autocfg_input_label(codec, cfg, i); if (spec->shared_mic_hp && !strcmp(label, "Misc")) @@ -4400,11 +4407,15 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec)
snprintf(boost_label, sizeof(boost_label), "%s Boost Volume", label); + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT)); + boost_label, type_idx, val); if (err < 0) return err; + + path = get_nid_path(codec, nid, 0); + if (path) + path->ctls[NID_PATH_BOOST_CTL] = val; } } return 0;
Now we have a complete list of loopback paths, thus we can initialize the paths more completely based on it, instead of assuming a direct connection from pin to mixer.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eadf326d..a41f048 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2639,6 +2639,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, { struct alc_spec *spec = codec->spec; struct nid_path *path; + unsigned int val; int err, idx;
if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && @@ -2654,19 +2655,22 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin,
idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val); if (err < 0) return err; + path->ctls[NID_PATH_VOL_CTL] = val; }
if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, - HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val); if (err < 0) return err; + path->ctls[NID_PATH_MUTE_CTL] = val; }
+ path->active = true; add_loopback_list(spec, mix_nid, idx); return 0; } @@ -2848,6 +2852,11 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; }
+static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable); + static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -2871,15 +2880,14 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE); } - }
- /* mute all loopback inputs */ - if (spec->mixer_nid) { - int nums = snd_hda_get_num_conns(codec, spec->mixer_nid); - for (i = 0; i < nums; i++) - snd_hda_codec_write(codec, spec->mixer_nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(i)); + /* mute loopback inputs */ + if (spec->mixer_nid) { + struct nid_path *path; + path = get_nid_path(codec, nid, spec->mixer_nid); + if (path) + activate_path(codec, path, path->active); + } } }
The widget connection selection must be changed only when the path is enabled.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a41f048..eccb386 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -3969,7 +3969,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, path->active = false;
for (i = path->depth - 1; i >= 0; i--) { - if (path->multi[i]) + if (enable && path->multi[i]) snd_hda_codec_write_cache(codec, path->path[i], 0, AC_VERB_SET_CONNECT_SEL, path->idx[i]);
Now we reached to the final big piece of parser rewrite: the input paths. While the old parser code assumes the more-or-less direct and similar connections from input pin to ADC, the new code handles the complete input paths. The capture source is switched by simple calls of activate_path() function.
The parsing of capture volume and capture switches is, however, not fully generalized. It assumes that amps are available in the vicinity of ADCs (in three depth). This isn't perfect but it should cover all codecs I know of.
Also, this commit removes some NID mapping of capture-related controls temporarily for simplicity. It'll be restored in later commits.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 997 ++++++++++++++++-------------------------- 1 file changed, 374 insertions(+), 623 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eccb386..111b82c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -278,6 +278,11 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, #define nid_has_volume(codec, nid, dir) \ check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS)
+static struct nid_path * +get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +static void activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix); + /* * input MUX handling */ @@ -286,12 +291,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id); - if (mux_idx >= spec->num_mux_defs) - mux_idx = 0; - if (!spec->input_mux[mux_idx].num_items && mux_idx > 0) - mux_idx = 0; - return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo); + return snd_hda_input_mux_info(&spec->input_mux[0], uinfo); }
static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, @@ -305,6 +305,14 @@ static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, return 0; }
+static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +{ + struct alc_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; +} + static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) { struct alc_spec *spec = codec->spec; @@ -322,14 +330,9 @@ static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) return false; }
-static inline hda_nid_t get_capsrc(struct alc_spec *spec, int idx) -{ - return spec->capsrc_nids ? - spec->capsrc_nids[idx] : spec->adc_nids[idx]; -} - static void call_update_outputs(struct hda_codec *codec); static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); +static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx);
/* for shared I/O, change the pin-control accordingly */ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) @@ -369,56 +372,39 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; - unsigned int mux_idx; - int i, type, num_conns; - hda_nid_t nid; - - if (!spec->input_mux) - return 0; + struct nid_path *path;
- mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx; - imux = &spec->input_mux[mux_idx]; - if (!imux->num_items && mux_idx > 0) - imux = &spec->input_mux[0]; - if (!imux->num_items) + imux = spec->input_mux; + if (!imux || !imux->num_items) return 0;
if (idx >= imux->num_items) idx = imux->num_items - 1; if (spec->cur_mux[adc_idx] == idx && !force) return 0; + + path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], + spec->adc_nids[adc_idx]); + if (!path) + return 0; + if (path->active) + activate_path(codec, path, false, false); + spec->cur_mux[adc_idx] = idx;
if (spec->shared_mic_hp) update_shared_mic_hp(codec, spec->cur_mux[adc_idx]);
- if (spec->dyn_adc_switch) { + if (spec->dyn_adc_switch) alc_dyn_adc_pcm_resetup(codec, idx); - adc_idx = spec->dyn_adc_idx[idx]; - } - - nid = get_capsrc(spec, adc_idx);
- /* no selection? */ - num_conns = snd_hda_get_num_conns(codec, nid); - if (num_conns <= 1) - return 1; - - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_AUD_MIX) { - /* Matrix-mixer style (e.g. ALC882) */ - int active = imux->items[idx].index; - for (i = 0; i < num_conns; i++) { - unsigned int v = (i == active) ? 0 : HDA_AMP_MUTE; - snd_hda_codec_amp_stereo(codec, nid, HDA_INPUT, i, - HDA_AMP_MUTE, v); - } - } else { - /* MUX style (e.g. ALC880) */ - snd_hda_codec_write_cache(codec, nid, 0, - AC_VERB_SET_CONNECT_SEL, - imux->items[idx].index); - } + path = get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, adc_idx, idx)); + if (!path) + return 0; + if (path->active) + return 0; + activate_path(codec, path, true, false); alc_inv_dmic_sync(codec, true); return 1; } @@ -1006,65 +992,12 @@ static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) return -1; }
-/* check whether dynamic ADC-switching is available */ -static bool alc_check_dyn_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - int i, n, idx; - hda_nid_t cap, pin; - - if (imux != spec->input_mux) /* no dynamic imux? */ - return false; - - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!pin) - return false; - if (get_connection_index(codec, cap, pin) < 0) - break; - } - if (i >= imux->num_items) - return true; /* no ADC-switch is needed */ - } - - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - cap = spec->private_capsrc_nids[n]; - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - imux->items[i].index = idx; - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - return true; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux;
- if (!spec->auto_mic) - return false; - if (spec->auto_mic_valid_imux) - return true; /* already checked */ - - /* fill up imux indices */ - if (!alc_check_dyn_adc_switch(codec)) { - spec->auto_mic = 0; - return false; - } - imux = spec->input_mux; spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, spec->imux_pins, imux->num_items); @@ -1072,10 +1005,8 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) spec->imux_pins, imux->num_items); spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, spec->imux_pins, imux->num_items); - if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) { - spec->auto_mic = 0; + if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) return false; /* no corresponding imux */ - }
snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, ALC_MIC_EVENT, alc_mic_automute); @@ -1085,7 +1016,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) alc_mic_automute);
spec->auto_mic_valid_imux = 1; - spec->auto_mic = 1; return true; }
@@ -1100,9 +1030,6 @@ static int alc_init_auto_mic(struct hda_codec *codec) hda_nid_t fixed, ext, dock; int i;
- if (spec->shared_mic_hp) - return 0; /* no auto-mic for the shared I/O */ - spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1;
fixed = ext = dock = 0; @@ -1152,30 +1079,18 @@ static int alc_init_auto_mic(struct hda_codec *codec) spec->int_mic_pin = fixed; spec->dock_mic_pin = dock;
- spec->auto_mic = 1; if (!alc_auto_mic_check_imux(codec)) return 0;
+ spec->auto_mic = 1; + spec->num_adc_nids = 1; + spec->cur_mux[0] = spec->int_mic_idx; snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", ext, fixed, dock);
return 0; }
-/* check the availabilities of auto-mute and auto-mic switches */ -static int alc_auto_check_switches(struct hda_codec *codec) -{ - int err; - - err = alc_init_automute(codec); - if (err < 0) - return err; - err = alc_init_auto_mic(codec); - if (err < 0) - return err; - return 0; -} - /* * Realtek SSID verification */ @@ -1509,169 +1424,101 @@ static void alc_auto_parse_digital(struct hda_codec *codec) /* * capture mixer elements */ -static int alc_cap_vol_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_volume_info(kcontrol, uinfo); - mutex_unlock(&codec->control_mutex); - return err; -} - -static int alc_cap_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned long val; - int err; - - mutex_lock(&codec->control_mutex); - if (spec->vol_in_capsrc) - val = HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[0], 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(spec->adc_nids[0], 3, 0, HDA_INPUT); - kcontrol->private_value = val; - err = snd_hda_mixer_amp_tlv(kcontrol, op_flag, size, tlv); - mutex_unlock(&codec->control_mutex); - return err; -} +#define alc_cap_vol_info snd_hda_mixer_amp_volume_info +#define alc_cap_vol_get snd_hda_mixer_amp_volume_get +#define alc_cap_vol_tlv snd_hda_mixer_amp_tlv
-typedef int (*getput_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); +typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol);
-static int alc_cap_getput_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - getput_call_t func, bool is_put) +static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + put_call_t func, int type) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, err = 0; + const struct hda_input_mux *imux; + struct nid_path *path; + int i, adc_idx, err = 0;
+ imux = spec->input_mux; + adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); - if (is_put && spec->dyn_adc_switch) { - for (i = 0; i < spec->num_adc_nids; i++) { - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - } else { - i = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - if (spec->vol_in_capsrc) - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->capsrc_nids[i], - 3, 0, HDA_OUTPUT); - else - kcontrol->private_value = - HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], - 3, 0, HDA_INPUT); + codec->cached_write = 1; + for (i = 0; i < imux->num_items; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, adc_idx, i)); + if (!path->ctls[type]) + continue; + kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); + if (err < 0) + goto error; } - if (err >= 0 && is_put) - alc_inv_dmic_sync(codec, false); error: + codec->cached_write = 0; mutex_unlock(&codec->control_mutex); + snd_hda_codec_resume_amp(codec); + if (err >= 0 && type == NID_PATH_MUTE_CTL && + spec->inv_dmic_fixup && spec->inv_dmic_muted) + alc_inv_dmic_sync_adc(codec, adc_idx); return err; }
-static int alc_cap_vol_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_get, false); -} - static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, true); + return alc_cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put, + NID_PATH_VOL_CTL); }
/* capture mixer elements */ #define alc_cap_sw_info snd_ctl_boolean_stereo_info +#define alc_cap_sw_get snd_hda_mixer_amp_switch_get
-static int alc_cap_sw_get(struct snd_kcontrol *kcontrol, +static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_get, false); + return alc_cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put, + NID_PATH_MUTE_CTL); }
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { - return alc_cap_getput_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, true); -} - -#define _DEFINE_CAPMIX(num) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Capture Switch", \ - .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ - .count = num, \ - .info = alc_cap_sw_info, \ - .get = alc_cap_sw_get, \ - .put = alc_cap_sw_put, \ - }, \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - .name = "Capture Volume", \ - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), \ - .count = num, \ - .info = alc_cap_vol_info, \ - .get = alc_cap_vol_get, \ - .put = alc_cap_vol_put, \ - .tlv = { .c = alc_cap_vol_tlv }, \ - } - -#define _DEFINE_CAPSRC(num) \ - { \ - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ - /* .name = "Capture Source", */ \ - .name = "Input Source", \ - .count = num, \ - .info = alc_mux_enum_info, \ - .get = alc_mux_enum_get, \ - .put = alc_mux_enum_put, \ - } - -#define DEFINE_CAPMIX(num) \ -static const struct snd_kcontrol_new alc_capture_mixer ## num[] = { \ - _DEFINE_CAPMIX(num), \ - _DEFINE_CAPSRC(num), \ - { } /* end */ \ -} - -#define DEFINE_CAPMIX_NOSRC(num) \ -static const struct snd_kcontrol_new alc_capture_mixer_nosrc ## num[] = { \ - _DEFINE_CAPMIX(num), \ - { } /* end */ \ -} - -/* up to three ADCs */ -DEFINE_CAPMIX(1); -DEFINE_CAPMIX(2); -DEFINE_CAPMIX(3); -DEFINE_CAPMIX_NOSRC(1); -DEFINE_CAPMIX_NOSRC(2); -DEFINE_CAPMIX_NOSRC(3); + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + hda_nid_t nid; + int i, dir, parm; + unsigned int val; + + for (i = 0; i < imux->num_items; i++) { + if (spec->imux_pins[i] == spec->inv_dmic_pin) + break; + } + if (i >= imux->num_items) + return; + + path = get_nid_path(codec, spec->inv_dmic_pin, + get_adc_nid(codec, adc_idx, i)); + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) + return; + nid = get_amp_nid_(val); + dir = get_amp_direction_(val); + parm = AC_AMP_SET_RIGHT | + (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT); + + /* we care only right channel */ + val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); + if (val & 0x80) /* if already muted, we don't need to touch */ + return; + val |= 0x80; + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, + parm | val); +}
/* * Inverted digital-mic handling @@ -1691,40 +1538,22 @@ DEFINE_CAPMIX_NOSRC(3); static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) { struct alc_spec *spec = codec->spec; - int i; + int src, nums;
if (!spec->inv_dmic_fixup) return; if (!spec->inv_dmic_muted && !force) return; - for (i = 0; i < spec->num_adc_nids; i++) { - int src = spec->dyn_adc_switch ? 0 : i; + nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids; + for (src = 0; src < nums; src++) { bool dmic_fixup = false; - hda_nid_t nid; - int parm, dir, v;
if (spec->inv_dmic_muted && spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; - if (spec->vol_in_capsrc) { - nid = spec->capsrc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_OUTPUT; - dir = HDA_OUTPUT; - } else { - nid = spec->adc_nids[i]; - parm = AC_AMP_SET_RIGHT | AC_AMP_SET_INPUT; - dir = HDA_INPUT; - } - /* we care only right channel */ - v = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); - if (v & 0x80) /* if already muted, we don't need to touch */ - continue; - if (dmic_fixup) /* add mute for d-mic */ - v |= 0x80; - snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, - parm | v); + alc_inv_dmic_sync_adc(codec, src); } }
@@ -1800,13 +1629,6 @@ static const char * const alc_slave_pfxs[] = {
#define NID_MAPPING (-1)
-#define SUBDEV_SPEAKER_ (0 << 6) -#define SUBDEV_HP_ (1 << 6) -#define SUBDEV_LINE_ (2 << 6) -#define SUBDEV_SPEAKER(x) (SUBDEV_SPEAKER_ | ((x) & 0x3f)) -#define SUBDEV_HP(x) (SUBDEV_HP_ | ((x) & 0x3f)) -#define SUBDEV_LINE(x) (SUBDEV_LINE_ | ((x) & 0x3f)) - static void alc_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP @@ -1821,11 +1643,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { static int __alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct snd_kcontrol *kctl = NULL; - const struct snd_kcontrol_new *knew; - int i, j, err; - unsigned int u; - hda_nid_t nid; + int i, err;
for (i = 0; i < spec->num_mixers; i++) { err = snd_hda_add_new_ctls(codec, spec->mixers[i]); @@ -1897,75 +1715,6 @@ static int __alc_build_controls(struct hda_codec *codec) return err; }
- /* assign Capture Source enums to NID */ - if (spec->capsrc_nids || spec->adc_nids) { - kctl = snd_hda_find_mixer_ctl(codec, "Capture Source"); - if (!kctl) - kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - get_capsrc(spec, i)); - if (err < 0) - return err; - } - } - if (spec->cap_mixer && spec->adc_nids) { - const char *kname = kctl ? kctl->id.name : NULL; - for (knew = spec->cap_mixer; knew->name; knew++) { - if (kname && strcmp(knew->name, kname) == 0) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - for (i = 0; kctl && i < kctl->count; i++) { - err = snd_hda_add_nid(codec, kctl, i, - spec->adc_nids[i]); - if (err < 0) - return err; - } - } - } - - /* other nid->control mapping */ - for (i = 0; i < spec->num_mixers; i++) { - for (knew = spec->mixers[i]; knew->name; knew++) { - if (knew->iface != NID_MAPPING) - continue; - kctl = snd_hda_find_mixer_ctl(codec, knew->name); - if (kctl == NULL) - continue; - u = knew->subdevice; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0x3f; - if (nid == 0) - continue; - switch (u & 0xc0) { - case SUBDEV_SPEAKER_: - nid = spec->autocfg.speaker_pins[nid]; - break; - case SUBDEV_LINE_: - nid = spec->autocfg.line_out_pins[nid]; - break; - case SUBDEV_HP_: - nid = spec->autocfg.hp_pins[nid]; - break; - default: - continue; - } - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - u = knew->private_value; - for (j = 0; j < 4; j++, u >>= 8) { - nid = u & 0xff; - if (nid == 0) - continue; - err = snd_hda_add_nid(codec, kctl, 0, nid); - if (err < 0) - return err; - } - } - } - alc_free_kctls(codec); /* no longer needed */
return 0; @@ -2007,7 +1756,6 @@ static int alc_build_controls(struct hda_codec *codec) * Common callbacks */
-static void alc_init_special_input_src(struct hda_codec *codec); static void alc_auto_init_std(struct hda_codec *codec);
static int alc_init(struct hda_codec *codec) @@ -2021,7 +1769,6 @@ static int alc_init(struct hda_codec *codec) alc_auto_init_amp(codec, spec->init_amp);
snd_hda_gen_apply_verbs(codec); - alc_init_special_input_src(codec); alc_auto_init_std(codec);
alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); @@ -2675,28 +2422,6 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, return 0; }
-static int new_capture_source(struct hda_codec *codec, int adc_idx, - hda_nid_t pin, int idx, const char *label) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - struct nid_path *path; - - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, spec->adc_nids[adc_idx], 2, path)) { - snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", - pin, spec->adc_nids[adc_idx]); - return -EINVAL; - } - - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, idx, NULL); - return 0; -} - static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) { unsigned int pincap = snd_hda_query_pin_caps(codec, nid); @@ -2712,54 +2437,217 @@ static bool is_reachable_path(struct hda_codec *codec, return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; }
-/* Parse the codec tree and retrieve ADCs and corresponding capsrc MUXs */ -static int alc_auto_fill_adc_caps(struct hda_codec *codec) +/* Parse the codec tree and retrieve ADCs */ +static int alc_auto_fill_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; hda_nid_t *adc_nids = spec->private_adc_nids; - hda_nid_t *cap_nids = spec->private_capsrc_nids; int max_nums = ARRAY_SIZE(spec->private_adc_nids); int i, nums = 0;
nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, nid++) { - hda_nid_t src; unsigned int caps = get_wcaps(codec, nid); int type = get_wcaps_type(caps);
if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) continue; adc_nids[nums] = nid; - cap_nids[nums] = nid; - src = nid; - for (;;) { - int n; - type = get_wcaps_type(get_wcaps(codec, src)); - if (type == AC_WID_PIN) - break; - if (type == AC_WID_AUD_SEL) { - cap_nids[nums] = src; - break; - } - n = snd_hda_get_num_conns(codec, src); - if (n > 1) { - cap_nids[nums] = src; - break; - } else if (n != 1) - break; - if (snd_hda_get_connections(codec, src, &src, 1) != 1) - break; - } if (++nums >= max_nums) break; } spec->adc_nids = spec->private_adc_nids; - spec->capsrc_nids = spec->private_capsrc_nids; spec->num_adc_nids = nums; return nums; }
+/* filter out invalid adc_nids that don't give all active input pins; + * if needed, check whether dynamic ADC-switching is available + */ +static int check_dyn_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + int i, n, nums; + hda_nid_t pin, adc; + + again: + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!is_reachable_path(codec, pin, adc)) + break; + } + if (i >= imux->num_items) + adc_nids[nums++] = adc; + } + + if (!nums) { + if (spec->shared_mic_hp) { + spec->shared_mic_hp = 0; + spec->private_imux[0].num_items = 1; + goto again; + } + + /* check whether ADC-switch is possible */ + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + if (is_reachable_path(codec, pin, adc)) { + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("realtek: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + } else if (nums != spec->num_adc_nids) { + memcpy(spec->private_adc_nids, adc_nids, + nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) { + snd_printdd("realtek: reducing to a single ADC\n"); + spec->num_adc_nids = 1; /* reduce to a single ADC */ + } + + return 0; +} + +/* templates for capture controls */ +static const struct snd_kcontrol_new cap_src_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = alc_mux_enum_info, + .get = alc_mux_enum_get, + .put = alc_mux_enum_put, +}; + +static const struct snd_kcontrol_new cap_vol_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), + .info = alc_cap_vol_info, + .get = alc_cap_vol_get, + .put = alc_cap_vol_put, + .tlv = { .c = alc_cap_vol_tlv }, +}; + +static const struct snd_kcontrol_new cap_sw_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", + .info = alc_cap_sw_info, + .get = alc_cap_sw_get, + .put = alc_cap_sw_put, +}; + +static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) +{ + hda_nid_t nid; + int i, depth; + + path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; + for (depth = 0; depth < 3; depth++) { + if (depth >= path->depth) + return -EINVAL; + i = path->depth - depth - 1; + nid = path->path[i]; + if (!path->ctls[NID_PATH_VOL_CTL]) { + if (nid_has_volume(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_volume(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + if (!path->ctls[NID_PATH_MUTE_CTL]) { + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_mute(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + } + return 0; +} + +static int create_capture_mixers(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct snd_kcontrol_new *knew; + int i, n, nums; + + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + if (!spec->auto_mic && imux->num_items > 1) { + knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp); + if (!knew) + return -ENOMEM; + knew->count = nums; + } + + for (n = 0; n < nums; n++) { + int vol, sw; + + vol = sw = 0; + for (i = 0; i < imux->num_items; i++) { + struct nid_path *path; + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, n, i)); + if (!path) + continue; + parse_capvol_in_path(codec, path); + if (!vol) + vol = path->ctls[NID_PATH_VOL_CTL]; + if (!sw) + sw = path->ctls[NID_PATH_MUTE_CTL]; + } + + if (vol) { + knew = alc_kcontrol_new(spec, "Capture Volume", + &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = n; + knew->private_value = vol; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw) { + knew = alc_kcontrol_new(spec, "Capture Switch", + &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = n; + knew->private_value = sw; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + } + + return 0; +} + /* create playback/capture controls for input pins */ static int alc_auto_create_input_ctls(struct hda_codec *codec) { @@ -2768,16 +2656,17 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) hda_nid_t mixer = spec->mixer_nid; struct hda_input_mux *imux = &spec->private_imux[0]; int num_adcs; - int i, c, err, idx, type_idx = 0; + int i, c, err, type_idx = 0; const char *prev_label = NULL;
- num_adcs = alc_auto_fill_adc_caps(codec); + num_adcs = alc_auto_fill_adc_nids(codec); if (num_adcs < 0) return 0;
for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; + bool imux_added;
pin = cfg->inputs[i].pin; if (!alc_is_input_pin(codec, pin)) @@ -2801,21 +2690,35 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) } }
+ imux_added = false; for (c = 0; c < num_adcs; c++) { - hda_nid_t cap = get_capsrc(spec, c); - idx = get_connection_index(codec, cap, pin); - if (idx >= 0) { - err = new_capture_source(codec, c, pin, idx, label); - if (err < 0) - return err; - break; + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + if (!is_reachable_path(codec, pin, adc)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!parse_nid_path(codec, pin, adc, 2, path)) { + snd_printd(KERN_ERR + "invalid input path 0x%x -> 0x%x\n", + pin, adc); + spec->paths.used--; + continue; + } + + if (!imux_added) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, + imux->num_items, NULL); + imux_added = true; } } }
- spec->num_mux_defs = 1; spec->input_mux = imux; - return 0; }
@@ -2852,11 +2755,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec) return 0; }
-static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable); - static int get_pin_type(int line_out_type) { if (line_out_type == AUTO_PIN_HP_OUT) @@ -2886,7 +2784,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec) struct nid_path *path; path = get_nid_path(codec, nid, spec->mixer_nid); if (path) - activate_path(codec, path, path->active); + activate_path(codec, path, path->active, false); } } } @@ -3930,15 +3828,18 @@ static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, }
static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) + int i, bool enable, bool add_aamix) { struct alc_spec *spec = codec->spec; hda_nid_t conn[16]; int n, nums, idx; + int type; hda_nid_t nid = path->path[i];
nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_PIN) { + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN || + (type == AC_WID_AUD_IN && codec->single_adc_amp)) { nums = 1; idx = 0; } else @@ -3954,14 +3855,14 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, * when aa-mixer is available, we need to enable the path as well */ for (n = 0; n < nums; n++) { - if (n != idx && conn[n] != spec->mixer_nid) + if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) continue; activate_amp(codec, nid, HDA_INPUT, n, enable); } }
static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable) + bool enable, bool add_aamix) { int i;
@@ -3974,7 +3875,7 @@ static void activate_path(struct hda_codec *codec, struct nid_path *path, AC_VERB_SET_CONNECT_SEL, path->idx[i]); if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable); + activate_amp_in(codec, path, i, enable, add_aamix); if (has_amp_out(codec, path, i)) activate_amp_out(codec, path, i, enable); } @@ -3996,7 +3897,7 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, return; if (path->active) return; - activate_path(codec, path, true); + activate_path(codec, path, true, true); }
static void alc_auto_init_multi_out(struct hda_codec *codec) @@ -4210,9 +4111,9 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - activate_path(codec, path, true); + activate_path(codec, path, true, true); } else { - activate_path(codec, path, false); + activate_path(codec, path, false, true); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); } @@ -4276,112 +4177,41 @@ static void alc_auto_init_multi_io(struct hda_codec *codec) spec->multi_io[i].ctl_in = snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - activate_path(codec, path, path->active); - } -} - -/* filter out invalid adc_nids (and capsrc_nids) that don't give all - * active input pins - */ -static void alc_remove_invalid_adc_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - hda_nid_t capsrc_nids[ARRAY_SIZE(spec->private_adc_nids)]; - int i, n, nums; - - imux = spec->input_mux; - if (!imux) - return; - if (spec->dyn_adc_switch) - return; - - again: - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - hda_nid_t cap = spec->private_capsrc_nids[n]; - int num_conns = snd_hda_get_num_conns(codec, cap); - for (i = 0; i < imux->num_items; i++) { - hda_nid_t pin = spec->imux_pins[i]; - if (pin) { - if (get_connection_index(codec, cap, pin) < 0) - break; - } else if (num_conns <= imux->items[i].index) - break; - } - if (i >= imux->num_items) { - adc_nids[nums] = spec->private_adc_nids[n]; - capsrc_nids[nums++] = cap; - } + activate_path(codec, path, path->active, true); } - if (!nums) { - /* check whether ADC-switch is possible */ - if (!alc_check_dyn_adc_switch(codec)) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; - goto again; - } - printk(KERN_WARNING "hda_codec: %s: no valid ADC found;" - " using fallback 0x%x\n", - codec->chip_name, spec->private_adc_nids[0]); - spec->num_adc_nids = 1; - spec->auto_mic = 0; - return; - } - } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); - memcpy(spec->private_capsrc_nids, capsrc_nids, - nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; - } - - if (spec->auto_mic) - alc_auto_mic_check_imux(codec); /* check auto-mic setups */ - else if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) - spec->num_adc_nids = 1; /* reduce to a single ADC */ }
/* * initialize ADC paths */ -static void alc_auto_init_adc(struct hda_codec *codec, int adc_idx) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - - nid = spec->adc_nids[adc_idx]; - /* mute ADC */ - if (nid_has_mute(codec, nid, HDA_INPUT)) { - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_IN_MUTE(0)); - return; - } - if (!spec->capsrc_nids) - return; - nid = spec->capsrc_nids[adc_idx]; - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); -} - static void alc_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int c, nums; + struct hda_input_mux *imux = &spec->private_imux[0]; + struct nid_path *path; + int i, c, nums;
- for (c = 0; c < spec->num_adc_nids; c++) - alc_auto_init_adc(codec, c); if (spec->dyn_adc_switch) nums = 1; else nums = spec->num_adc_nids; - for (c = 0; c < nums; c++) - alc_mux_select(codec, c, spec->cur_mux[c], true); + + for (c = 0; c < nums; c++) { + for (i = 0; i < imux->num_items; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, c, i)); + if (path) { + bool active = path->active; + if (i == spec->cur_mux[c]) + active = true; + activate_path(codec, path, active, false); + } + } + } + + alc_inv_dmic_sync(codec, true); + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[0]); }
/* add mic boosts if needed */ @@ -4429,94 +4259,6 @@ static int alc_auto_add_mic_boost(struct hda_codec *codec) return 0; }
-/* select or unmute the given capsrc route */ -static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap, - int idx) -{ - if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { - snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, - HDA_AMP_MUTE, 0); - } else if (snd_hda_get_num_conns(codec, cap) > 1) { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); - } -} - -/* set the default connection to that pin */ -static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int i; - - if (!pin) - return 0; - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = get_capsrc(spec, i); - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; - select_or_unmute_capsrc(codec, cap, idx); - return i; /* return the found index */ - } - return -1; /* not found */ -} - -/* initialize some special cases for input sources */ -static void alc_init_special_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->autocfg.num_inputs; i++) - init_capsrc_for_pin(codec, spec->autocfg.inputs[i].pin); -} - -/* assign appropriate capture mixers */ -static void set_capture_mixer(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - static const struct snd_kcontrol_new *caps[2][3] = { - { alc_capture_mixer_nosrc1, - alc_capture_mixer_nosrc2, - alc_capture_mixer_nosrc3 }, - { alc_capture_mixer1, - alc_capture_mixer2, - alc_capture_mixer3 }, - }; - - /* check whether either of ADC or MUX has a volume control */ - if (!nid_has_volume(codec, spec->adc_nids[0], HDA_INPUT)) { - if (!spec->capsrc_nids) - return; /* no volume */ - if (!nid_has_volume(codec, spec->capsrc_nids[0], HDA_OUTPUT)) - return; /* no volume in capsrc, too */ - spec->vol_in_capsrc = 1; - } - - if (spec->num_adc_nids > 0) { - int mux = 0; - int num_adcs = 0; - - if (spec->input_mux && spec->input_mux->num_items > 1) - mux = 1; - if (spec->auto_mic) { - num_adcs = 1; - mux = 0; - } else if (spec->dyn_adc_switch) - num_adcs = 1; - if (!num_adcs) { - if (spec->num_adc_nids > 3) - spec->num_adc_nids = 3; - else if (!spec->num_adc_nids) - return; - num_adcs = spec->num_adc_nids; - } - spec->cap_mixer = caps[mux][num_adcs - 1]; - } -} - /* * standard auto-parser initializations */ @@ -4639,16 +4381,28 @@ static int alc_parse_auto_config(struct hda_codec *codec, dig_only: alc_auto_parse_digital(codec);
- if (!spec->no_analog) - alc_remove_invalid_adc_nids(codec); - if (ssid_nids) alc_ssid_check(codec, ssid_nids);
if (!spec->no_analog) { - err = alc_auto_check_switches(codec); + err = alc_init_automute(codec); if (err < 0) return err; + + err = check_dyn_adc_switch(codec); + if (err < 0) + return err; + + if (!spec->shared_mic_hp) { + err = alc_init_auto_mic(codec); + if (err < 0) + return err; + } + + err = create_capture_mixers(codec); + if (err < 0) + return err; + err = alc_auto_add_mic_boost(codec); if (err < 0) return err; @@ -4657,9 +4411,6 @@ static int alc_parse_auto_config(struct hda_codec *codec, if (spec->kctls.list) add_mixer(spec, spec->kctls.list);
- if (!spec->no_analog && !spec->cap_mixer) - set_capture_mixer(codec); - return 1; }
Remove some fields from struct alc_spec, and clean up the usage. Namely, - spec->input_mux becomes a single element, private_imux[] is removed - spec->adc_nids becomes an array by itself, and private_adc_nids[] gets removed, too
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 56 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 36 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 111b82c..77045e9 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -128,7 +128,6 @@ struct alc_spec { /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; - const struct snd_kcontrol_new *cap_mixer; /* capture mixer */ unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
char stream_name_analog[32]; /* analog PCM stream */ @@ -152,8 +151,7 @@ struct alc_spec {
/* capture */ unsigned int num_adc_nids; - const hda_nid_t *adc_nids; - const hda_nid_t *capsrc_nids; + hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t dig_in_nid; /* digital-in NID; optional */ hda_nid_t mixer_nid; /* analog-mixer NID */
@@ -164,7 +162,7 @@ struct alc_spec {
/* capture source */ unsigned int num_mux_defs; - const struct hda_input_mux *input_mux; + struct hda_input_mux input_mux; unsigned int cur_mux[3]; hda_nid_t ext_mic_pin; hda_nid_t dock_mic_pin; @@ -184,10 +182,7 @@ struct alc_spec { struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; struct snd_array kctls; - struct hda_input_mux private_imux[3]; hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_adc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t private_capsrc_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ @@ -291,7 +286,7 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux[0], uinfo); + return snd_hda_input_mux_info(&spec->input_mux, uinfo); }
static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, @@ -374,8 +369,8 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, const struct hda_input_mux *imux; struct nid_path *path;
- imux = spec->input_mux; - if (!imux || !imux->num_items) + imux = &spec->input_mux; + if (!imux->num_items) return 0;
if (idx >= imux->num_items) @@ -632,8 +627,6 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
if (!spec->auto_mic || !spec->auto_mic_valid_imux) return; - if (snd_BUG_ON(!spec->adc_nids)) - return; if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return;
@@ -998,7 +991,7 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux;
- imux = spec->input_mux; + imux = &spec->input_mux; spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, spec->imux_pins, imux->num_items); spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, @@ -1441,7 +1434,7 @@ static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, struct nid_path *path; int i, adc_idx, err = 0;
- imux = spec->input_mux; + imux = &spec->input_mux; adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); codec->cached_write = 1; @@ -1488,7 +1481,7 @@ static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct nid_path *path; hda_nid_t nid; int i, dir, parm; @@ -1650,11 +1643,6 @@ static int __alc_build_controls(struct hda_codec *codec) if (err < 0) return err; } - if (spec->cap_mixer) { - err = snd_hda_add_new_ctls(codec, spec->cap_mixer); - if (err < 0) - return err; - } if (spec->multiout.dig_out_nid) { err = snd_hda_create_dig_out_ctls(codec, spec->multiout.dig_out_nid, @@ -2014,7 +2002,7 @@ static int alc_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = snd_pcm_2_1_chmaps; } - if (spec->adc_nids) { + if (spec->num_adc_nids) { p = spec->stream_analog_capture; if (!p) { if (spec->dyn_adc_switch) @@ -2074,8 +2062,7 @@ static int alc_build_pcms(struct hda_codec *codec) * model, configure a second analog capture-only PCM. */ have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic && - (!spec->input_mux || spec->input_mux->num_items > 1); + !spec->dyn_adc_switch && !spec->auto_mic; /* Additional Analaog capture for index #2 */ if (spec->alt_dac_nid || have_multi_adcs) { codec->num_pcms = 3; @@ -2442,8 +2429,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; hda_nid_t nid; - hda_nid_t *adc_nids = spec->private_adc_nids; - int max_nums = ARRAY_SIZE(spec->private_adc_nids); + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); int i, nums = 0;
nid = codec->start_nid; @@ -2457,7 +2444,6 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) if (++nums >= max_nums) break; } - spec->adc_nids = spec->private_adc_nids; spec->num_adc_nids = nums; return nums; } @@ -2468,8 +2454,8 @@ static int alc_auto_fill_adc_nids(struct hda_codec *codec) static int check_dyn_adc_switch(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; - hda_nid_t adc_nids[ARRAY_SIZE(spec->private_adc_nids)]; + struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; int i, n, nums; hda_nid_t pin, adc;
@@ -2489,7 +2475,7 @@ static int check_dyn_adc_switch(struct hda_codec *codec) if (!nums) { if (spec->shared_mic_hp) { spec->shared_mic_hp = 0; - spec->private_imux[0].num_items = 1; + imux->num_items = 1; goto again; }
@@ -2508,12 +2494,11 @@ static int check_dyn_adc_switch(struct hda_codec *codec) snd_printdd("realtek: enabling ADC switching\n"); spec->dyn_adc_switch = 1; } else if (nums != spec->num_adc_nids) { - memcpy(spec->private_adc_nids, adc_nids, - nums * sizeof(hda_nid_t)); + memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); spec->num_adc_nids = nums; }
- if (spec->input_mux->num_items == 1 || spec->shared_mic_hp) { + if (imux->num_items == 1 || spec->shared_mic_hp) { snd_printdd("realtek: reducing to a single ADC\n"); spec->num_adc_nids = 1; /* reduce to a single ADC */ } @@ -2592,7 +2577,7 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) static int create_capture_mixers(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct snd_kcontrol_new *knew; int i, n, nums;
@@ -2654,7 +2639,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) struct alc_spec *spec = codec->spec; const struct auto_pin_cfg *cfg = &spec->autocfg; hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; int num_adcs; int i, c, err, type_idx = 0; const char *prev_label = NULL; @@ -2718,7 +2703,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) } }
- spec->input_mux = imux; return 0; }
@@ -4187,7 +4171,7 @@ static void alc_auto_init_multi_io(struct hda_codec *codec) static void alc_auto_init_input_src(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->private_imux[0]; + struct hda_input_mux *imux = &spec->input_mux; struct nid_path *path; int i, c, nums;
The amps will be initialized via activate_path(), thus it's superfluous to set in alc_auto_init_analog_input().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 77045e9..b1222cc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2755,13 +2755,8 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) { + if (alc_is_input_pin(codec, nid)) alc_set_input_pin(codec, nid, cfg->inputs[i].type); - if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) - snd_hda_codec_write(codec, nid, 0, - AC_VERB_SET_AMP_GAIN_MUTE, - AMP_OUT_MUTE); - }
/* mute loopback inputs */ if (spec->mixer_nid) {
Make the function more generic for both input and output directions, and returns the assigned path pointer. The argument order is changed to follow the standard (from, to) way.
Now this new function is used for analog input and loopback path parser codes, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b1222cc..a8febe4 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1342,8 +1342,12 @@ static void alc_auto_set_output_and_unmute(struct hda_codec *codec, hda_nid_t dac); static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, bool is_digital); -static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac); +static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); +static struct nid_path *add_new_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix);
/* * Digital I/O handling @@ -1371,7 +1375,7 @@ static void alc_auto_init_digital(struct hda_codec *codec) static void alc_auto_parse_digital(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i, err, nums; + int i, nums; hda_nid_t dig_nid;
/* support multiple SPDIFs; the secondary is set up as a slave */ @@ -1381,6 +1385,8 @@ static void alc_auto_parse_digital(struct hda_codec *codec) dig_nid = alc_auto_look_for_dac(codec, pin, true); if (!dig_nid) continue; + if (!add_new_nid_path(codec, dig_nid, pin, 2)) + continue; if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -1390,7 +1396,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec) break; spec->slave_dig_outs[nums - 1] = dig_nid; } - add_new_out_path(codec, pin, dig_nid); nums++; }
@@ -1404,8 +1409,6 @@ static void alc_auto_parse_digital(struct hda_codec *codec) continue; if (!(wcaps & AC_WCAP_CONN_LIST)) continue; - err = get_connection_index(codec, dig_nid, - spec->autocfg.dig_in_pin); if (err >= 0) { spec->dig_in_nid = dig_nid; break; @@ -2343,10 +2346,6 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, return channel_name[ch]; }
-static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path); - #ifdef CONFIG_PM /* add the powersave loopback-list entry */ static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) @@ -2380,11 +2379,8 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */
- path = snd_array_new(&spec->paths); + path = add_new_nid_path(codec, pin, mix_nid, 2); if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, mix_nid, 2, path)) return -EINVAL;
idx = path->idx[path->depth - 1]; @@ -2937,21 +2933,25 @@ static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, struct nid_path *path);
-static bool add_new_out_path(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static struct nid_path *add_new_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix) { struct alc_spec *spec = codec->spec; struct nid_path *path;
+ if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) + return NULL; + path = snd_array_new(&spec->paths); if (!path) - return false; + return NULL; memset(path, 0, sizeof(*path)); - if (parse_nid_path(codec, dac, pin, 0, path)) - return true; + if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + return path; /* push back */ spec->paths.used--; - return false; + return NULL; }
/* get the path between the given NIDs; @@ -3093,7 +3093,7 @@ static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - if (!add_new_out_path(codec, pin, dac)) + if (!add_new_nid_path(codec, dac, pin, 0)) dac = dacs[i] = 0; if (dac) badness += assign_out_path_ctls(codec, pin, dac); @@ -3118,7 +3118,7 @@ static bool alc_map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - if (add_new_out_path(codec, pins[i], dac)) { + if (add_new_nid_path(codec, dac, pins[i], 0)) { dacs[i] = dac; found = true; } @@ -4015,7 +4015,7 @@ static int alc_auto_fill_multi_ios(struct hda_codec *codec, badness++; continue; } - if (!add_new_out_path(codec, nid, dac)) { + if (!add_new_nid_path(codec, dac, nid, 0)) { badness++; continue; }
This was the last forgotten path. Now it's parsed via the same path parser.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a8febe4..60cc1b5 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1402,14 +1402,16 @@ static void alc_auto_parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { dig_nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + struct nid_path *path; unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; if (!(wcaps & AC_WCAP_DIGITAL)) continue; - if (!(wcaps & AC_WCAP_CONN_LIST)) - continue; - if (err >= 0) { + path = add_new_nid_path(codec, spec->autocfg.dig_in_pin, + dig_nid, 2); + if (path) { + path->active = true; spec->dig_in_nid = dig_nid; break; }
Add a new field to indicate the possible pin NID for alternative vref setup for the shared hp/mic. Although 0x18 is valid for all Realtek codecs, it'll be different on other vendor's codecs.
Also, drop the sanity check in update_shared_mic_hp() since the reference pin is set explicitly in the caller side.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 60cc1b5..5c5197b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -187,6 +187,7 @@ struct alc_spec { unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; + hda_nid_t shared_mic_vref_pin;
/* DAC list */ int num_all_dacs; @@ -343,15 +344,11 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
/* This pin does not have vref caps - let's enable vref on pin 0x18 instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ) { - const hda_nid_t vref_pin = 0x18; - /* Sanity check pin 0x18 */ - if (get_wcaps_type(get_wcaps(codec, vref_pin)) == AC_WID_PIN && - get_defcfg_connect(snd_hda_codec_get_pincfg(codec, vref_pin)) == AC_JACK_PORT_NONE) { - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); - } + if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { + const hda_nid_t vref_pin = spec->shared_mic_vref_pin; + unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); + if (vref_val != AC_PINCTL_VREF_HIZ) + snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); }
val = set_as_mic ? val | PIN_IN : PIN_HP; @@ -5642,6 +5639,7 @@ static int patch_alc262(struct hda_codec *codec) return err;
spec = codec->spec; + spec->shared_mic_vref_pin = 0x18;
#if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -6425,6 +6423,7 @@ static int patch_alc269(struct hda_codec *codec) return err;
spec = codec->spec; + spec->shared_mic_vref_pin = 0x18;
alc_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups);
This flag is superfluous now and it's always as same as spec->auto_mic. Let's drop.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5c5197b..d55db33 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -209,7 +209,6 @@ struct alc_spec { unsigned int line_jack_present:1; unsigned int master_mute:1; unsigned int auto_mic:1; - unsigned int auto_mic_valid_imux:1; /* valid imux for auto-mic */ unsigned int automute_speaker:1; /* automute speaker outputs */ unsigned int automute_lo:1; /* automute LO outputs */ unsigned int detect_hp:1; /* Headphone detection enabled */ @@ -622,7 +621,7 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) struct alc_spec *spec = codec->spec; hda_nid_t *pins = spec->imux_pins;
- if (!spec->auto_mic || !spec->auto_mic_valid_imux) + if (!spec->auto_mic) return; if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) return; @@ -1004,8 +1003,6 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, ALC_MIC_EVENT, alc_mic_automute); - - spec->auto_mic_valid_imux = 1; return true; }
Also arranged alc_spec definitions to optimize bit fields. Use a bit field for spec->need_dac_fix, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d55db33..f5696ab 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -73,8 +73,6 @@ struct alc_multi_io { unsigned int ctl_in; /* cached input-pin control value */ };
-#define MAX_VOL_NIDS 0x40 - /* make compatible with old code */ #define alc_apply_pincfgs snd_hda_apply_pincfgs #define alc_apply_fixup snd_hda_apply_fixup @@ -161,7 +159,6 @@ struct alc_spec { unsigned int cur_adc_format;
/* capture source */ - unsigned int num_mux_defs; struct hda_input_mux input_mux; unsigned int cur_mux[3]; hda_nid_t ext_mic_pin; @@ -171,7 +168,6 @@ struct alc_spec { /* channel model */ const struct hda_channel_mode *channel_mode; int num_channel_mode; - int need_dac_fix; int const_channel_count; /* min. channel count (for speakers) */ int ext_channel_count; /* current channel count for multi-io */
@@ -218,19 +214,15 @@ struct alc_spec { unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */
/* other flags */ + unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ unsigned int no_analog :1; /* digital I/O only */ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int single_input_src:1; - unsigned int vol_in_capsrc:1; /* use capsrc volume (ADC has no vol) */ - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
- /* auto-mute control */ - int automute_mode; - hda_nid_t automute_mixer_nid[AUTO_CFG_MAX_OUTS]; + unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
int init_amp; int codec_variant; /* flag for other variants */ @@ -612,9 +604,6 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack call_update_outputs(codec); }
-#define get_connection_index(codec, mux, nid) \ - snd_hda_get_conn_index(codec, mux, nid, 0) - /* standard mic auto-switch helper */ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { @@ -1619,8 +1608,6 @@ static const char * const alc_slave_pfxs[] = { * build control elements */
-#define NID_MAPPING (-1) - static void alc_free_kctls(struct hda_codec *codec);
#ifdef CONFIG_SND_HDA_INPUT_BEEP
... so that the fixup just needs to set the hook function in FIXUP_ACT_PROBE. This will make easier to port for other codecs, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f5696ab..579a35e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1687,6 +1687,8 @@ static int __alc_build_controls(struct hda_codec *codec) true, &spec->vmaster_mute.sw_kctl); if (err < 0) return err; + if (spec->vmaster_mute.hook) + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); }
alc_free_kctls(codec); /* no longer needed */ @@ -1745,6 +1747,9 @@ static int alc_init(struct hda_codec *codec) snd_hda_gen_apply_verbs(codec); alc_auto_init_std(codec);
+ if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
hda_call_check_power_status(codec, 0x01); @@ -6019,15 +6024,8 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - switch (action) { - case ALC_FIXUP_ACT_BUILD: + if (action == ALC_FIXUP_ACT_PROBE) spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - break; - } }
/* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -6042,15 +6040,8 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - switch (action) { - case ALC_FIXUP_ACT_BUILD: + if (action == ALC_FIXUP_ACT_PROBE) spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - /* fallthru */ - case ALC_FIXUP_ACT_INIT: - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - break; - } }
static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
There are a few more cases where we can assign "Master" mixer element safely, e.g. when a single DAC is used in the whole output paths.
Also, when vmaster hook is present, avoid "Master" but assign "PCM" instead. Otherwise vmaster hook won't work properly.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 579a35e..c980489 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2306,7 +2306,14 @@ static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, *index = 0; if (cfg->line_outs == 1 && !spec->multi_ios && !cfg->hp_outs && !cfg->speaker_outs && can_be_master) - return "Master"; + return spec->vmaster_mute.hook ? "PCM" : "Master"; + + /* if there is really a single DAC used in the whole output paths, + * use it master (or "PCM" if a vmaster hook is present) + */ + if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && + !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) + return spec->vmaster_mute.hook ? "PCM" : "Master";
switch (cfg->line_out_type) { case AUTO_PIN_SPEAKER_OUT:
Merge a few functions that have been split due to historical reasons to single functions. Splitting too much (and placing too far away) actually worsens the readability.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c980489..7f4ad78 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -653,14 +653,6 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res) snd_hda_jack_unsol_event(codec, res >> 2); }
-/* call init functions of standard auto-mute helpers */ -static void alc_inithook(struct hda_codec *codec) -{ - alc_hp_automute(codec, NULL); - alc_line_automute(codec, NULL); - alc_mic_automute(codec, NULL); -} - /* additional initialization for ALC888 variants */ static void alc888_coef_init(struct hda_codec *codec) { @@ -1619,7 +1611,7 @@ static const struct snd_kcontrol_new alc_beep_mixer[] = { }; #endif
-static int __alc_build_controls(struct hda_codec *codec) +static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; int i, err; @@ -1693,13 +1685,6 @@ static int __alc_build_controls(struct hda_codec *codec)
alc_free_kctls(codec); /* no longer needed */
- return 0; -} - -static int alc_build_jacks(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->shared_mic_hp) { int err; int nid = spec->autocfg.inputs[1].pin; @@ -1711,18 +1696,10 @@ static int alc_build_jacks(struct hda_codec *codec) return err; }
- return snd_hda_jack_add_kctls(codec, &spec->autocfg); -} - -static int alc_build_controls(struct hda_codec *codec) -{ - int err = __alc_build_controls(codec); + err = snd_hda_jack_add_kctls(codec, &spec->autocfg); if (err < 0) return err;
- err = alc_build_jacks(codec); - if (err < 0) - return err; alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); return 0; } @@ -4244,7 +4221,10 @@ static void alc_auto_init_std(struct hda_codec *codec) alc_auto_init_analog_input(codec); alc_auto_init_input_src(codec); alc_auto_init_digital(codec); - alc_inithook(codec); + /* call init functions of standard auto-mute helpers */ + alc_hp_automute(codec, NULL); + alc_line_automute(codec, NULL); + alc_mic_automute(codec, NULL); }
/*
This prevents stupid typos.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7f4ad78..4253b9a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -862,7 +862,10 @@ alc_kcontrol_new(struct alc_spec *spec, const char *name, if (!knew) return NULL; *knew = *temp; - knew->name = kstrdup(name, GFP_KERNEL); + if (name) + knew->name = kstrdup(name, GFP_KERNEL); + else if (knew->name) + knew->name = kstrdup(knew->name, GFP_KERNEL); if (!knew->name) return NULL; return knew; @@ -872,7 +875,7 @@ static int alc_add_automute_mode_enum(struct hda_codec *codec) { struct alc_spec *spec = codec->spec;
- if (!alc_kcontrol_new(spec, "Auto-Mute Mode", &alc_automute_mode_enum)) + if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum)) return -ENOMEM; return 0; } @@ -1556,6 +1559,7 @@ static int alc_inv_dmic_sw_put(struct snd_kcontrol *kcontrol,
static const struct snd_kcontrol_new alc_inv_dmic_sw = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Inverted Internal Mic Capture Switch", .info = snd_ctl_boolean_mono_info, .get = alc_inv_dmic_sw_get, .put = alc_inv_dmic_sw_put, @@ -1565,8 +1569,7 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec;
- if (!alc_kcontrol_new(spec, "Inverted Internal Mic Capture Switch", - &alc_inv_dmic_sw)) + if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw)) return -ENOMEM; spec->inv_dmic_fixup = 1; spec->inv_dmic_muted = 0; @@ -2555,7 +2558,7 @@ static int create_capture_mixers(struct hda_codec *codec) nums = spec->num_adc_nids;
if (!spec->auto_mic && imux->num_items > 1) { - knew = alc_kcontrol_new(spec, "Input Source", &cap_src_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; @@ -2579,8 +2582,7 @@ static int create_capture_mixers(struct hda_codec *codec) }
if (vol) { - knew = alc_kcontrol_new(spec, "Capture Volume", - &cap_vol_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); if (!knew) return -ENOMEM; knew->index = n; @@ -2588,8 +2590,7 @@ static int create_capture_mixers(struct hda_codec *codec) knew->subdevice = HDA_SUBDEV_AMP_FLAG; } if (sw) { - knew = alc_kcontrol_new(spec, "Capture Switch", - &cap_sw_temp); + knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); if (!knew) return -ENOMEM; knew->index = n; @@ -4106,8 +4107,7 @@ static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) struct alc_spec *spec = codec->spec;
if (spec->multi_ios > 0) { - if (!alc_kcontrol_new(spec, "Channel Mode", - &alc_auto_channel_mode_enum)) + if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum)) return -ENOMEM; } return 0;
So far we create only "Capture Volume" and "Capture Switch" controls for binding all possible amps, but we'd prefer creating individual capture volume and switch controls per input in some cases (e.g. conexant parser does it).
Add a new flag, spec->multi_cap_vol, to follow that policy.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 149 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 18 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4253b9a..2295389 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -221,6 +221,7 @@ struct alc_spec { unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
@@ -2474,6 +2475,10 @@ static int check_dyn_adc_switch(struct hda_codec *codec) spec->num_adc_nids = 1; /* reduce to a single ADC */ }
+ /* single index for individual volumes ctls */ + if (!spec->dyn_adc_switch && spec->multi_cap_vol) + spec->num_adc_nids = 1; + return 0; }
@@ -2545,12 +2550,122 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) return 0; }
+static int add_single_cap_ctl(struct hda_codec *codec, const char *label, + int idx, bool is_switch, unsigned int ctl) +{ + struct alc_spec *spec = codec->spec; + char tmpname[44]; + int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; + const char *sfx = is_switch ? "Switch" : "Volume"; + + if (!ctl) + return 0; + + if (label) + snprintf(tmpname, sizeof(tmpname), + "%s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Capture %s", sfx); + return add_control(spec, type, tmpname, idx, ctl); +} + +/* create single (and simple) capture volume and switch controls */ +static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + int err; + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl); + if (err < 0) + return err; + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl); + if (err < 0) + return err; + return 0; +} + +/* create bound capture volume and switch controls */ +static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + struct alc_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (vol_ctl) { + knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = vol_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw_ctl) { + knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = sw_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + return 0; +} + +/* return the vol ctl when used first in the imux list */ +static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) +{ + struct alc_spec *spec = codec->spec; + struct nid_path *path; + unsigned int ctl; + int i; + + path = get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, 0, idx)); + if (!path) + return 0; + ctl = path->ctls[type]; + if (!ctl) + return 0; + for (i = 0; i < idx - 1; i++) { + path = get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, 0, i)); + if (path && path->ctls[type] == ctl) + return 0; + } + return ctl; +} + +/* create individual capture volume and switch controls per input */ +static int create_multi_cap_vol_ctl(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type, type_idx = 0; + const char *prev_label = NULL; + + for (i = 0; i < imux->num_items; i++) { + const char *label; + label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + for (type = 0; type < 2; type++) { + err = add_single_cap_ctl(codec, label, type_idx, type, + get_first_cap_ctl(codec, i, type)); + if (err < 0) + return err; + } + } + return 0; +} + static int create_capture_mixers(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct hda_input_mux *imux = &spec->input_mux; - struct snd_kcontrol_new *knew; - int i, n, nums; + int i, n, nums, err;
if (spec->dyn_adc_switch) nums = 1; @@ -2558,6 +2673,7 @@ static int create_capture_mixers(struct hda_codec *codec) nums = spec->num_adc_nids;
if (!spec->auto_mic && imux->num_items > 1) { + struct snd_kcontrol_new *knew; knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; @@ -2565,6 +2681,7 @@ static int create_capture_mixers(struct hda_codec *codec) }
for (n = 0; n < nums; n++) { + bool multi = false; int vol, sw;
vol = sw = 0; @@ -2577,26 +2694,22 @@ static int create_capture_mixers(struct hda_codec *codec) parse_capvol_in_path(codec, path); if (!vol) vol = path->ctls[NID_PATH_VOL_CTL]; + else if (vol != path->ctls[NID_PATH_VOL_CTL]) + multi = true; if (!sw) sw = path->ctls[NID_PATH_MUTE_CTL]; + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + multi = true; }
- if (vol) { - knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = n; - knew->private_value = vol; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw) { - knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = n; - knew->private_value = sw; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } + if (!multi) + err = create_single_cap_vol_ctl(codec, n, vol, sw); + else if (!spec->multi_cap_vol) + err = create_bind_cap_vol_ctl(codec, n, vol, sw); + else + err = create_multi_cap_vol_ctl(codec); + if (err < 0) + return err; }
return 0;
To make the parser more generic, a few codes to handle the inverted stereo dmic in a way Conexant parser does is added in this patch.
The caller should set spec->inv_dmic_split flag appropriately.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2295389..558bd6d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -184,6 +184,7 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin; + int inv_dmic_split_idx; /* used internally for inv_dmic_split */
/* DAC list */ int num_all_dacs; @@ -222,6 +223,7 @@ struct alc_spec { unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ + unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
@@ -2550,6 +2552,8 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) return 0; }
+static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs); + static int add_single_cap_ctl(struct hda_codec *codec, const char *label, int idx, bool is_switch, unsigned int ctl) { @@ -2557,17 +2561,37 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, char tmpname[44]; int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; + unsigned int chs; + int err;
if (!ctl) return 0;
+ if (idx == spec->inv_dmic_split_idx) + chs = 1; + else + chs = 3; + if (label) snprintf(tmpname, sizeof(tmpname), "%s Capture %s", label, sfx); else snprintf(tmpname, sizeof(tmpname), "Capture %s", sfx); - return add_control(spec, type, tmpname, idx, ctl); + err = add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, chs)); + if (err < 0 || chs == 3) + return err; + + /* Make independent right kcontrol */ + if (label) + snprintf(tmpname, sizeof(tmpname), + "Inverted %s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Inverted Capture %s", sfx); + return add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, 2)); }
/* create single (and simple) capture volume and switch controls */ @@ -2730,6 +2754,7 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) if (num_adcs < 0) return 0;
+ spec->inv_dmic_split_idx = -1; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -2783,6 +2808,14 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) imux_added = true; } } + + if (spec->inv_dmic_split) { + if (cfg->inputs[i].type == AUTO_PIN_MIC) { + unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); + if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) + spec->inv_dmic_split_idx = i; + } + } }
return 0;
Since the fixup code is used commonly, it's worth to move it to the common place, struct hda_codec, instead of keeping in hda_gen_spec.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_auto_parser.c | 39 ++++++++++----------- sound/pci/hda/hda_auto_parser.h | 78 ----------------------------------------- sound/pci/hda/hda_codec.c | 2 ++ sound/pci/hda/hda_codec.h | 8 +++++ sound/pci/hda/hda_local.h | 53 ++++++++++++++++++++++++++++ sound/pci/hda/patch_cirrus.c | 8 +---- sound/pci/hda/patch_conexant.c | 6 +--- sound/pci/hda/patch_realtek.c | 10 ++---- sound/pci/hda/patch_via.c | 4 --- 9 files changed, 86 insertions(+), 122 deletions(-)
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 7da883a..d460688 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -622,28 +622,27 @@ int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
-int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list) +int snd_hda_add_verbs(struct hda_codec *codec, + const struct hda_verb *list) { const struct hda_verb **v; - v = snd_array_new(&spec->verbs); + v = snd_array_new(&codec->verbs); if (!v) return -ENOMEM; *v = list; return 0; } -EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs); +EXPORT_SYMBOL_HDA(snd_hda_add_verbs);
-void snd_hda_gen_apply_verbs(struct hda_codec *codec) +void snd_hda_apply_verbs(struct hda_codec *codec) { - struct hda_gen_spec *spec = codec->spec; int i; - for (i = 0; i < spec->verbs.used; i++) { - struct hda_verb **v = snd_array_elem(&spec->verbs, i); + for (i = 0; i < codec->verbs.used; i++) { + struct hda_verb **v = snd_array_elem(&codec->verbs, i); snd_hda_sequence_write(codec, *v); } } -EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs); +EXPORT_SYMBOL_HDA(snd_hda_apply_verbs);
void snd_hda_apply_pincfgs(struct hda_codec *codec, const struct hda_pintbl *cfg) @@ -655,18 +654,17 @@ EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
void snd_hda_apply_fixup(struct hda_codec *codec, int action) { - struct hda_gen_spec *spec = codec->spec; - int id = spec->fixup_id; + int id = codec->fixup_id; #ifdef CONFIG_SND_DEBUG_VERBOSE - const char *modelname = spec->fixup_name; + const char *modelname = codec->fixup_name; #endif int depth = 0;
- if (!spec->fixup_list) + if (!codec->fixup_list) return;
while (id >= 0) { - const struct hda_fixup *fix = spec->fixup_list + id; + const struct hda_fixup *fix = codec->fixup_list + id;
switch (fix->type) { case HDA_FIXUP_PINS: @@ -683,7 +681,7 @@ void snd_hda_apply_fixup(struct hda_codec *codec, int action) snd_printdd(KERN_INFO SFX "%s: Apply fix-verbs for %s\n", codec->chip_name, modelname); - snd_hda_gen_add_verbs(codec->spec, fix->v.verbs); + snd_hda_add_verbs(codec, fix->v.verbs); break; case HDA_FIXUP_FUNC: if (!fix->v.func) @@ -713,15 +711,14 @@ void snd_hda_pick_fixup(struct hda_codec *codec, const struct snd_pci_quirk *quirk, const struct hda_fixup *fixlist) { - struct hda_gen_spec *spec = codec->spec; const struct snd_pci_quirk *q; int id = -1; const char *name = NULL;
/* when model=nofixup is given, don't pick up any fixups */ if (codec->modelname && !strcmp(codec->modelname, "nofixup")) { - spec->fixup_list = NULL; - spec->fixup_id = -1; + codec->fixup_list = NULL; + codec->fixup_id = -1; return; }
@@ -759,10 +756,10 @@ void snd_hda_pick_fixup(struct hda_codec *codec, } }
- spec->fixup_id = id; + codec->fixup_id = id; if (id >= 0) { - spec->fixup_list = fixlist; - spec->fixup_name = name; + codec->fixup_list = fixlist; + codec->fixup_name = name; } } EXPORT_SYMBOL_HDA(snd_hda_pick_fixup); diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index 632ad0a..ff11074 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -89,82 +89,4 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, #define snd_hda_parse_pin_def_config(codec, cfg, ignore) \ snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
-/* - */ - -struct hda_gen_spec { - /* fix-up list */ - int fixup_id; - const struct hda_fixup *fixup_list; - const char *fixup_name; - - /* additional init verbs */ - struct snd_array verbs; -}; - - -/* - * Fix-up pin default configurations and add default verbs - */ - -struct hda_pintbl { - hda_nid_t nid; - u32 val; -}; - -struct hda_model_fixup { - const int id; - const char *name; -}; - -struct hda_fixup { - int type; - bool chained; - int chain_id; - union { - const struct hda_pintbl *pins; - const struct hda_verb *verbs; - void (*func)(struct hda_codec *codec, - const struct hda_fixup *fix, - int action); - } v; -}; - -/* fixup types */ -enum { - HDA_FIXUP_INVALID, - HDA_FIXUP_PINS, - HDA_FIXUP_VERBS, - HDA_FIXUP_FUNC, -}; - -/* fixup action definitions */ -enum { - HDA_FIXUP_ACT_PRE_PROBE, - HDA_FIXUP_ACT_PROBE, - HDA_FIXUP_ACT_INIT, - HDA_FIXUP_ACT_BUILD, -}; - -int snd_hda_gen_add_verbs(struct hda_gen_spec *spec, - const struct hda_verb *list); -void snd_hda_gen_apply_verbs(struct hda_codec *codec); -void snd_hda_apply_pincfgs(struct hda_codec *codec, - const struct hda_pintbl *cfg); -void snd_hda_apply_fixup(struct hda_codec *codec, int action); -void snd_hda_pick_fixup(struct hda_codec *codec, - const struct hda_model_fixup *models, - const struct snd_pci_quirk *quirk, - const struct hda_fixup *fixlist); - -static inline void snd_hda_gen_init(struct hda_gen_spec *spec) -{ - snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8); -} - -static inline void snd_hda_gen_free(struct hda_gen_spec *spec) -{ - snd_array_free(&spec->verbs); -} - #endif /* __SOUND_HDA_AUTO_PARSER_H */ diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 7383746..12b88a2 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1253,6 +1253,7 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); + snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
#ifdef CONFIG_PM @@ -2407,6 +2408,7 @@ int snd_hda_codec_reset(struct hda_codec *codec) snd_array_free(&codec->driver_pins); snd_array_free(&codec->cvt_setups); snd_array_free(&codec->spdif_out); + snd_array_free(&codec->verbs); codec->num_pcms = 0; codec->pcm_info = NULL; codec->preset = NULL; diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index cab39b2..a1cb28f 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -896,6 +896,14 @@ struct hda_codec { /* jack detection */ struct snd_array jacks; #endif + + /* fix-up list */ + int fixup_id; + const struct hda_fixup *fixup_list; + const char *fixup_name; + + /* additional init verbs */ + struct snd_array verbs; };
/* direction */ diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index ff56da8..de12dcc 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -386,6 +386,59 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, const struct snd_kcontrol_new *knew);
/* + * Fix-up pin default configurations and add default verbs + */ + +struct hda_pintbl { + hda_nid_t nid; + u32 val; +}; + +struct hda_model_fixup { + const int id; + const char *name; +}; + +struct hda_fixup { + int type; + bool chained; + int chain_id; + union { + const struct hda_pintbl *pins; + const struct hda_verb *verbs; + void (*func)(struct hda_codec *codec, + const struct hda_fixup *fix, + int action); + } v; +}; + +/* fixup types */ +enum { + HDA_FIXUP_INVALID, + HDA_FIXUP_PINS, + HDA_FIXUP_VERBS, + HDA_FIXUP_FUNC, +}; + +/* fixup action definitions */ +enum { + HDA_FIXUP_ACT_PRE_PROBE, + HDA_FIXUP_ACT_PROBE, + HDA_FIXUP_ACT_INIT, + HDA_FIXUP_ACT_BUILD, +}; + +int snd_hda_add_verbs(struct hda_codec *codec, const struct hda_verb *list); +void snd_hda_apply_verbs(struct hda_codec *codec); +void snd_hda_apply_pincfgs(struct hda_codec *codec, + const struct hda_pintbl *cfg); +void snd_hda_apply_fixup(struct hda_codec *codec, int action); +void snd_hda_pick_fixup(struct hda_codec *codec, + const struct hda_model_fixup *models, + const struct snd_pci_quirk *quirk, + const struct hda_fixup *fixlist); + +/* * unsolicited event handler */
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c index a2537b2..7b0b8c3 100644 --- a/sound/pci/hda/patch_cirrus.c +++ b/sound/pci/hda/patch_cirrus.c @@ -34,8 +34,6 @@ */
struct cs_spec { - struct hda_gen_spec gen; - struct auto_pin_cfg autocfg; struct hda_multi_out multiout; struct snd_kcontrol *vmaster_sw; @@ -1201,7 +1199,7 @@ static int cs_init(struct hda_codec *codec)
snd_hda_sequence_write(codec, cs_coef_init_verbs);
- snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec);
if (spec->gpio_mask) { snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_MASK, @@ -1252,7 +1250,6 @@ static void cs_free(struct hda_codec *codec) struct cs_spec *spec = codec->spec; kfree(spec->capture_bind[0]); kfree(spec->capture_bind[1]); - snd_hda_gen_free(&spec->gen); kfree(codec->spec); }
@@ -1443,7 +1440,6 @@ static int patch_cs420x(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen);
spec->vendor_nid = CS420X_VENDOR_NID;
@@ -1981,7 +1977,6 @@ static int patch_cs4210(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen);
spec->vendor_nid = CS4210_VENDOR_NID;
@@ -2021,7 +2016,6 @@ static int patch_cs4213(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen);
spec->vendor_nid = CS4213_VENDOR_NID;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index 60890bf..727e678 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -67,8 +67,6 @@ struct imux_info { };
struct conexant_spec { - struct hda_gen_spec gen; - const struct snd_kcontrol_new *mixers[5]; int num_mixers; hda_nid_t vmaster_nid; @@ -451,7 +449,6 @@ static int conexant_init(struct hda_codec *codec) static void conexant_free(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_hda_gen_free(&spec->gen); snd_hda_detach_beep_device(codec); kfree(spec); } @@ -4045,7 +4042,7 @@ static void cx_auto_init_digital(struct hda_codec *codec) static int cx_auto_init(struct hda_codec *codec) { struct conexant_spec *spec = codec->spec; - snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec); cx_auto_init_output(codec); cx_auto_init_input(codec); cx_auto_init_digital(codec); @@ -4549,7 +4546,6 @@ static int patch_conexant_auto(struct hda_codec *codec) if (!spec) return -ENOMEM; codec->spec = spec; - snd_hda_gen_init(&spec->gen);
switch (codec->vendor_id) { case 0x14f15045: diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 558bd6d..1beb797 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -121,8 +121,6 @@ struct nid_path { };
struct alc_spec { - struct hda_gen_spec gen; - /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; @@ -1727,7 +1725,7 @@ static int alc_init(struct hda_codec *codec) alc_fix_pll(codec); alc_auto_init_amp(codec, spec->init_amp);
- snd_hda_gen_apply_verbs(codec); + snd_hda_apply_verbs(codec); alc_auto_init_std(codec);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) @@ -2117,7 +2115,6 @@ static void alc_free(struct hda_codec *codec) alc_free_kctls(codec); alc_free_bind_ctls(codec); snd_array_free(&spec->paths); - snd_hda_gen_free(&spec->gen); kfree(spec); snd_hda_detach_beep_device(codec); } @@ -4525,7 +4522,6 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) codec->spec = spec; codec->single_adc_amp = 1; spec->mixer_nid = mixer_nid; - snd_hda_gen_init(&spec->gen); snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); @@ -5001,7 +4997,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, alc_hp_automute); - snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs); + snd_hda_add_verbs(codec, alc_gpio1_init_verbs); } }
@@ -5878,7 +5874,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec) if (err > 0) { if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); - snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs); + snd_hda_add_verbs(codec, alc268_beep_init_verbs); } } return err; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 09bb649..b224b3d 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -120,8 +120,6 @@ enum { };
struct via_spec { - struct hda_gen_spec gen; - /* codec parameterization */ const struct snd_kcontrol_new *mixers[6]; unsigned int num_mixers; @@ -252,7 +250,6 @@ static struct via_spec * via_new_spec(struct hda_codec *codec) /* VT1708BCE & VT1708S are almost same */ if (spec->codec_type == VT1708BCE) spec->codec_type = VT1708S; - snd_hda_gen_init(&spec->gen); return spec; }
@@ -1657,7 +1654,6 @@ static void via_free(struct hda_codec *codec) vt1708_stop_hp_work(spec); kfree(spec->bind_cap_vol); kfree(spec->bind_cap_sw); - snd_hda_gen_free(&spec->gen); kfree(spec); }
The previous commit passed an utterly wrong value for checking the split inv dmic pin. This patch fixes it and also tries to remove inv_dmic_split_idx field.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 58 +++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1beb797..fecd89a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -182,7 +182,6 @@ struct alc_spec { int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin; - int inv_dmic_split_idx; /* used internally for inv_dmic_split */
/* DAC list */ int num_all_dacs; @@ -2552,23 +2551,19 @@ static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs);
static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl) + int idx, bool is_switch, unsigned int ctl, + bool inv_dmic) { struct alc_spec *spec = codec->spec; char tmpname[44]; int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs; + unsigned int chs = inv_dmic ? 1 : 3; int err;
if (!ctl) return 0;
- if (idx == spec->inv_dmic_split_idx) - chs = 1; - else - chs = 3; - if (label) snprintf(tmpname, sizeof(tmpname), "%s Capture %s", label, sfx); @@ -2591,15 +2586,36 @@ static int add_single_cap_ctl(struct hda_codec *codec, const char *label, amp_val_replace_channels(ctl, 2)); }
+static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct alc_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int val; + int i; + + if (!spec->inv_dmic_split) + return false; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return false; + val = snd_hda_codec_get_pincfg(codec, nid); + return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; + } + return false; +} + /* create single (and simple) capture volume and switch controls */ static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) + unsigned int vol_ctl, unsigned int sw_ctl, + bool inv_dmic) { int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl); + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); if (err < 0) return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl); + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); if (err < 0) return err; return 0; @@ -2665,16 +2681,19 @@ static int create_multi_cap_vol_ctl(struct hda_codec *codec)
for (i = 0; i < imux->num_items; i++) { const char *label; + bool inv_dmic; label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); if (prev_label && !strcmp(label, prev_label)) type_idx++; else type_idx = 0; prev_label = label; + inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]);
for (type = 0; type < 2; type++) { err = add_single_cap_ctl(codec, label, type_idx, type, - get_first_cap_ctl(codec, i, type)); + get_first_cap_ctl(codec, i, type), + inv_dmic); if (err < 0) return err; } @@ -2703,6 +2722,7 @@ static int create_capture_mixers(struct hda_codec *codec)
for (n = 0; n < nums; n++) { bool multi = false; + bool inv_dmic = false; int vol, sw;
vol = sw = 0; @@ -2721,10 +2741,13 @@ static int create_capture_mixers(struct hda_codec *codec) sw = path->ctls[NID_PATH_MUTE_CTL]; else if (sw != path->ctls[NID_PATH_MUTE_CTL]) multi = true; + if (is_inv_dmic_pin(codec, spec->imux_pins[i])) + inv_dmic = true; }
if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw); + err = create_single_cap_vol_ctl(codec, n, vol, sw, + inv_dmic); else if (!spec->multi_cap_vol) err = create_bind_cap_vol_ctl(codec, n, vol, sw); else @@ -2751,7 +2774,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) if (num_adcs < 0) return 0;
- spec->inv_dmic_split_idx = -1; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; @@ -2805,14 +2827,6 @@ static int alc_auto_create_input_ctls(struct hda_codec *codec) imux_added = true; } } - - if (spec->inv_dmic_split) { - if (cfg->inputs[i].type == AUTO_PIN_MIC) { - unsigned int def_conf = snd_hda_codec_get_pincfg(codec, pin); - if (snd_hda_get_input_pin_attr(def_conf) == INPUT_PIN_ATTR_INT) - spec->inv_dmic_split_idx = i; - } - } }
return 0;
Put INPUT_PIN_ATTR_FRONT after INPUT_PIN_ATTR_REAR, and define INPUT_PIN_ATTR_LAST to point to the last element.
This is a preliminary work for cleaning up Realtek auto-mic parser. In the auto-mic implementation, the front panel is preferred over the rear panel. By arranging the attr definitions like in this commit, we can simply use sort() for figuring out the priority order.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_auto_parser.c | 2 +- sound/pci/hda/hda_auto_parser.h | 3 ++- sound/pci/hda/patch_via.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index d460688..44c81d3 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -363,7 +363,7 @@ static const char *hda_get_input_pin_label(struct hda_codec *codec, { unsigned int def_conf; static const char * const mic_names[] = { - "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic", + "Internal Mic", "Dock Mic", "Mic", "Rear Mic", "Front Mic" }; int attr;
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h index ff11074..f748071 100644 --- a/sound/pci/hda/hda_auto_parser.h +++ b/sound/pci/hda/hda_auto_parser.h @@ -51,8 +51,9 @@ enum { INPUT_PIN_ATTR_INT, /* internal mic/line-in */ INPUT_PIN_ATTR_DOCK, /* docking mic/line-in */ INPUT_PIN_ATTR_NORMAL, /* mic/line-in jack */ - INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ INPUT_PIN_ATTR_REAR, /* mic/line-in jack in rear */ + INPUT_PIN_ATTR_FRONT, /* mic/line-in jack in front */ + INPUT_PIN_ATTR_LAST = INPUT_PIN_ATTR_FRONT, };
int snd_hda_get_input_pin_attr(unsigned int def_conf); diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index b224b3d..d3c852a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -1913,7 +1913,7 @@ static void mangle_smart51(struct hda_codec *codec) int i, j, nums, attr; int pins[AUTO_CFG_MAX_INS];
- for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { + for (attr = INPUT_PIN_ATTR_LAST; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { nums = 0; for (i = 0; i < cfg->num_inputs; i++) { unsigned int def;
This patch extends the capability of the auto-mic feature. Instead of limiting the automatic input-source selection only to the mics (internal, external and dock mics), allow it for generic inputs, e.g. switching between the rear line-in and the front mic.
The logic is to check the attribute and location of input pins, and enable the automatic selection feature only if all such pins are in different locations (e.g. internal, front, rear, etc) and line-in or mic pins. That is, if multiple input pins are assigned to a single location, the feature isn't enabled because we don't know the priority.
(You may wonder why this restriction doesn't exist for the headphone automute. The reason is that the output case is different from the input: the input source is an exclusive selection while the output can be multiplexed.)
Note that, for avoiding regressions, the line-in auto switching feature isn't activated as default. It has to be set explicitly via spec->line_in_auto_switch flag in a fixup code.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 143 +++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 65 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fecd89a..2aaecdc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -28,6 +28,7 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/module.h> +#include <linux/sort.h> #include <sound/core.h> #include <sound/jack.h> #include "hda_codec.h" @@ -90,6 +91,13 @@ struct alc_multi_io { #define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
+#define MAX_AUTO_MIC_PINS 3 + +struct alc_automic_entry { + hda_nid_t pin; /* pin */ + int idx; /* imux index, -1 = invalid */ + unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ +};
#define MAX_NID_PATH_DEPTH 5
@@ -159,9 +167,6 @@ struct alc_spec { /* capture source */ struct hda_input_mux input_mux; unsigned int cur_mux[3]; - hda_nid_t ext_mic_pin; - hda_nid_t dock_mic_pin; - hda_nid_t int_mic_pin;
/* channel model */ const struct hda_channel_mode *channel_mode; @@ -179,7 +184,6 @@ struct alc_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - int int_mic_idx, ext_mic_idx, dock_mic_idx; /* for auto-mic */ hda_nid_t inv_dmic_pin; hda_nid_t shared_mic_vref_pin;
@@ -190,6 +194,10 @@ struct alc_spec { /* path list */ struct snd_array paths;
+ /* auto-mic stuff */ + int am_num_entries; + struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS]; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -210,6 +218,7 @@ struct alc_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
/* other flags */ unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ @@ -608,20 +617,18 @@ static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct alc_spec *spec = codec->spec; - hda_nid_t *pins = spec->imux_pins; + int i;
if (!spec->auto_mic) return; - if (snd_BUG_ON(spec->int_mic_idx < 0 || spec->ext_mic_idx < 0)) - return;
- if (snd_hda_jack_detect(codec, pins[spec->ext_mic_idx])) - alc_mux_select(codec, 0, spec->ext_mic_idx, false); - else if (spec->dock_mic_idx >= 0 && - snd_hda_jack_detect(codec, pins[spec->dock_mic_idx])) - alc_mux_select(codec, 0, spec->dock_mic_idx, false); - else - alc_mux_select(codec, 0, spec->int_mic_idx, false); + for (i = spec->am_num_entries - 1; i > 0; i--) { + if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { + alc_mux_select(codec, 0, spec->am_entry[i].idx, false); + return; + } + } + alc_mux_select(codec, 0, spec->am_entry[0].idx, false); }
/* update the master volume per volume-knob's unsol event */ @@ -970,26 +977,33 @@ static bool alc_auto_mic_check_imux(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; + int i;
imux = &spec->input_mux; - spec->ext_mic_idx = find_idx_in_nid_list(spec->ext_mic_pin, - spec->imux_pins, imux->num_items); - spec->int_mic_idx = find_idx_in_nid_list(spec->int_mic_pin, - spec->imux_pins, imux->num_items); - spec->dock_mic_idx = find_idx_in_nid_list(spec->dock_mic_pin, - spec->imux_pins, imux->num_items); - if (spec->ext_mic_idx < 0 || spec->int_mic_idx < 0) - return false; /* no corresponding imux */ - - snd_hda_jack_detect_enable_callback(codec, spec->ext_mic_pin, - ALC_MIC_EVENT, alc_mic_automute); - if (spec->dock_mic_pin) - snd_hda_jack_detect_enable_callback(codec, spec->dock_mic_pin, + for (i = 0; i < spec->am_num_entries; i++) { + spec->am_entry[i].idx = + find_idx_in_nid_list(spec->am_entry[i].pin, + spec->imux_pins, imux->num_items); + if (spec->am_entry[i].idx < 0) + return false; /* no corresponding imux */ + } + + /* we don't need the jack detection for the first pin */ + for (i = 1; i < spec->am_num_entries; i++) + snd_hda_jack_detect_enable_callback(codec, + spec->am_entry[i].pin, ALC_MIC_EVENT, alc_mic_automute); return true; }
+static int compare_attr(const void *ap, const void *bp) +{ + const struct alc_automic_entry *a = ap; + const struct alc_automic_entry *b = bp; + return (int)(a->attr - b->attr); +} + /* * Check the availability of auto-mic switch; * Set up if really supported @@ -998,66 +1012,63 @@ static int alc_init_auto_mic(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t fixed, ext, dock; - int i; + unsigned int types; + int i, num_pins;
- spec->ext_mic_idx = spec->int_mic_idx = spec->dock_mic_idx = -1; - - fixed = ext = dock = 0; + types = 0; + num_pins = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; - unsigned int defcfg; - defcfg = snd_hda_codec_get_pincfg(codec, nid); - switch (snd_hda_get_input_pin_attr(defcfg)) { + unsigned int attr; + attr = snd_hda_codec_get_pincfg(codec, nid); + attr = snd_hda_get_input_pin_attr(attr); + if (types & (1 << attr)) + return 0; /* already occupied */ + switch (attr) { case INPUT_PIN_ATTR_INT: - if (fixed) - return 0; /* already occupied */ if (cfg->inputs[i].type != AUTO_PIN_MIC) return 0; /* invalid type */ - fixed = nid; break; case INPUT_PIN_ATTR_UNUSED: return 0; /* invalid entry */ - case INPUT_PIN_ATTR_DOCK: - if (dock) - return 0; /* already occupied */ - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - dock = nid; - break; default: - if (ext) - return 0; /* already occupied */ - if (cfg->inputs[i].type != AUTO_PIN_MIC) + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) return 0; /* invalid type */ - ext = nid; + if (!spec->line_in_auto_switch && + cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* only mic is allowed */ + if (!is_jack_detectable(codec, nid)) + return 0; /* no unsol support */ break; } + if (num_pins >= MAX_AUTO_MIC_PINS) + return 0; + types |= (1 << attr); + spec->am_entry[num_pins].pin = nid; + spec->am_entry[num_pins].attr = attr; + num_pins++; } - if (!ext && dock) { - ext = dock; - dock = 0; - } - if (!ext || !fixed) + + if (num_pins < 2) return 0; - if (!is_jack_detectable(codec, ext)) - return 0; /* no unsol support */ - if (dock && !is_jack_detectable(codec, dock)) - return 0; /* no unsol support */
- /* check imux indices */ - spec->ext_mic_pin = ext; - spec->int_mic_pin = fixed; - spec->dock_mic_pin = dock; + spec->am_num_entries = num_pins; + /* sort the am_entry in the order of attr so that the pin with a + * higher attr will be selected when the jack is plugged. + */ + sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), + compare_attr, NULL);
if (!alc_auto_mic_check_imux(codec)) return 0;
spec->auto_mic = 1; spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->int_mic_idx; + spec->cur_mux[0] = spec->am_entry[0].idx; snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - ext, fixed, dock); + spec->am_entry[0].pin, + spec->am_entry[1].pin, + spec->am_entry[2].pin);
return 0; } @@ -6193,8 +6204,10 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, { struct alc_spec *spec = codec->spec;
+ if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0])) + return; if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, spec->ext_mic_pin, + snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin, spec->autocfg.hp_pins[0]); }
This patch extends the capability of the auto-mic feature. Instead of limiting the automatic input-source selection only to the mics (internal, external and dock mics), allow it for generic inputs, e.g. switching between the rear line-in and the front mic.
The logic is to check the attribute and location of input pins, and enable the automatic selection feature only if all such pins are in different locations (e.g. internal, front, rear, etc) and line-in or mic pins. That is, if multiple input pins are assigned to a single location, the feature isn't enabled because we don't know the priority.
(You may wonder why this restriction doesn't exist for the headphone automute. The reason is that the output case is different from the input: the input source is an exclusive selection while the output can be multiplexed.)
Note that, for avoiding regressions, the line-in auto switching feature isn't activated as default. It has to be set explicitly via spec->line_in_auto_switch flag in a fixup code.
Is this feature automatically disabled when rear Mic or line in Jack is retasked as output ?
Does it mean that multi channel capture will not be implemented since it is conflict with both dynamic adc switching and auto Mic selection ?
At Thu, 10 Jan 2013 08:41:13 +0800, Raymond Yau wrote:
This patch extends the capability of the auto-mic feature. Instead of limiting the automatic input-source selection only to the mics (internal, external and dock mics), allow it for generic inputs, e.g. switching between the rear line-in and the front mic.
The logic is to check the attribute and location of input pins, and enable the automatic selection feature only if all such pins are in different locations (e.g. internal, front, rear, etc) and line-in or mic pins. That is, if multiple input pins are assigned to a single location, the feature isn't enabled because we don't know the priority.
(You may wonder why this restriction doesn't exist for the headphone automute. The reason is that the output case is different from the input: the input source is an exclusive selection while the output can be multiplexed.)
Note that, for avoiding regressions, the line-in auto switching feature isn't activated as default. It has to be set explicitly via spec->line_in_auto_switch flag in a fixup code.
Is this feature automatically disabled when rear Mic or line in Jack is retasked as output ?
Does it mean that multi channel capture will not be implemented since it is conflict with both dynamic adc switching and auto Mic selection ?
The recent jack detection codes (at least the code in test/hda-gen-parser branch) checks the direction of the pinctl. If a multi-io pin is configured as output, the mic autoswitch will ignore this pin. Vice versa for the auto-mute and headphone-as-mic case.
Takashi
Hello! where is the trace_hda_send_cmd function source code?I can not find it in the path of alsa-kernel\pci\hda\hda_code.c and linux kenerl. Thank you!
/* * Compose a 32bit command word to be sent to the HD-audio controller */ static inline unsigned int make_codec_cmd(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm) { u32 val; if ((codec->addr & ~0xf) || (direct & ~1) || (nid & ~0x7f) || (verb & ~0xfff) || (parm & ~0xffff)) { printk(KERN_ERR "hda-codec: out of range cmd %x:%x:%x:%x:%x\n", codec->addr, direct, nid, verb, parm); return ~0; } val = (u32)codec->addr << 28; val |= (u32)direct << 27; val |= (u32)nid << 20; val |= verb << 8; val |= parm; return val; } /* * Send and receive a verb */ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, unsigned int *res) { struct hda_bus *bus = codec->bus; int err; if (cmd == ~0) return -1; if (res) *res = -1; again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); trace_hda_send_cmd(codec, cmd); err = bus->ops.command(bus, cmd); if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); } mutex_unlock(&bus->cmd_mutex); snd_hda_power_down(codec); if (res && *res == -1 && bus->rirb_error) { if (bus->response_reset) { snd_printd("hda_codec: resetting BUS due to " "fatal communication error\n"); trace_hda_bus_reset(bus); bus->ops.bus_reset(bus); } goto again; } /* clear reset-flag when the communication gets recovered */ if (!err) bus->response_reset = 0; return err; }
At 2013-01-10 23:08:52,"Takashi Iwai" tiwai@suse.de wrote:
At Thu, 10 Jan 2013 08:41:13 +0800, Raymond Yau wrote:
This patch extends the capability of the auto-mic feature. Instead of limiting the automatic input-source selection only to the mics (internal, external and dock mics), allow it for generic inputs, e.g. switching between the rear line-in and the front mic.
The logic is to check the attribute and location of input pins, and enable the automatic selection feature only if all such pins are in different locations (e.g. internal, front, rear, etc) and line-in or mic pins. That is, if multiple input pins are assigned to a single location, the feature isn't enabled because we don't know the priority.
(You may wonder why this restriction doesn't exist for the headphone automute. The reason is that the output case is different from the input: the input source is an exclusive selection while the output can be multiplexed.)
Note that, for avoiding regressions, the line-in auto switching feature isn't activated as default. It has to be set explicitly via spec->line_in_auto_switch flag in a fixup code.
Is this feature automatically disabled when rear Mic or line in Jack is retasked as output ?
Does it mean that multi channel capture will not be implemented since it is conflict with both dynamic adc switching and auto Mic selection ?
The recent jack detection codes (at least the code in test/hda-gen-parser branch) checks the direction of the pinctl. If a multi-io pin is configured as output, the mic autoswitch will ignore this pin. Vice versa for the auto-mute and headphone-as-mic case.
Takashi
The argument "force" is always false in the recent code.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2aaecdc..2240ab6 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -360,7 +360,7 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic)
/* select the given imux item; either unmute exclusively or select the route */ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx, bool force) + unsigned int idx) { struct alc_spec *spec = codec->spec; const struct hda_input_mux *imux; @@ -372,7 +372,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
if (idx >= imux->num_items) idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx && !force) + if (spec->cur_mux[adc_idx] == idx) return 0;
path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], @@ -407,7 +407,7 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); return alc_mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0], false); + ucontrol->value.enumerated.item[0]); }
/* @@ -624,11 +624,11 @@ static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack)
for (i = spec->am_num_entries - 1; i > 0; i--) { if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { - alc_mux_select(codec, 0, spec->am_entry[i].idx, false); + alc_mux_select(codec, 0, spec->am_entry[i].idx); return; } } - alc_mux_select(codec, 0, spec->am_entry[0].idx, false); + alc_mux_select(codec, 0, spec->am_entry[0].idx); }
/* update the master volume per volume-knob's unsol event */
Finally the whole generic parser code in Realtek driver is moved into hda_generic.c so that it can be used for generic codec driver. The old dumb generic driver is replaced. Yay.
The future plan is to adapt this generic parser for other codecs, i.e. the codec driver calls the exported functions in generic driver but adds some codec-specific fixes and setups.
As of this commit, the complete driver code is still duplicated in Realtek codec driver. The big code reduction will come from now on.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4130 ++++++++++++++++++++++++++++++++++--------- sound/pci/hda/hda_generic.h | 199 +++ 2 files changed, 3518 insertions(+), 811 deletions(-) create mode 100644 sound/pci/hda/hda_generic.h
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b81d3d0..2d19b91 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -23,1063 +23,3571 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/sort.h> #include <sound/core.h> +#include <sound/jack.h> #include "hda_codec.h" #include "hda_local.h" +#include "hda_auto_parser.h" +#include "hda_jack.h" +#include "hda_generic.h"
-/* widget node for parsing */ -struct hda_gnode { - hda_nid_t nid; /* NID of this widget */ - unsigned short nconns; /* number of input connections */ - hda_nid_t *conn_list; - hda_nid_t slist[2]; /* temporay list */ - unsigned int wid_caps; /* widget capabilities */ - unsigned char type; /* widget type */ - unsigned char pin_ctl; /* pin controls */ - unsigned char checked; /* the flag indicates that the node is already parsed */ - unsigned int pin_caps; /* pin widget capabilities */ - unsigned int def_cfg; /* default configuration */ - unsigned int amp_out_caps; /* AMP out capabilities */ - unsigned int amp_in_caps; /* AMP in capabilities */ - struct list_head list; -}; - -/* patch-specific record */ - -#define MAX_PCM_VOLS 2 -struct pcm_vol { - struct hda_gnode *node; /* Node for PCM volume */ - unsigned int index; /* connection of PCM volume */ -};
-struct hda_gspec { - struct hda_gnode *dac_node[2]; /* DAC node */ - struct hda_gnode *out_pin_node[2]; /* Output pin (Line-Out) node */ - struct pcm_vol pcm_vol[MAX_PCM_VOLS]; /* PCM volumes */ - unsigned int pcm_vol_nodes; /* number of PCM volumes */ +/* initialize hda_gen_spec struct */ +int snd_hda_gen_spec_init(struct hda_gen_spec *spec) +{ + snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); + snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); + snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
- struct hda_gnode *adc_node; /* ADC node */ - struct hda_gnode *cap_vol_node; /* Node for capture volume */ - unsigned int cur_cap_src; /* current capture source */ - struct hda_input_mux input_mux; +static struct snd_kcontrol_new * +add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp) +{ + struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); + if (!knew) + return NULL; + *knew = *temp; + if (name) + knew->name = kstrdup(name, GFP_KERNEL); + else if (knew->name) + knew->name = kstrdup(knew->name, GFP_KERNEL); + if (!knew->name) + return NULL; + return knew; +}
- unsigned int def_amp_in_caps; - unsigned int def_amp_out_caps; +static void free_kctls(struct hda_gen_spec *spec) +{ + if (spec->kctls.list) { + struct snd_kcontrol_new *kctl = spec->kctls.list; + int i; + for (i = 0; i < spec->kctls.used; i++) + kfree(kctl[i].name); + } + snd_array_free(&spec->kctls); +}
- struct hda_pcm pcm_rec; /* PCM information */ +static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, + unsigned int nums, + struct hda_ctl_ops *ops) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_bind_ctls **ctlp, *ctl; + ctlp = snd_array_new(&spec->bind_ctls); + if (!ctlp) + return NULL; + ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); + *ctlp = ctl; + if (ctl) + ctl->ops = ops; + return ctl; +}
- struct list_head nid_list; /* list of widgets */ +static void free_bind_ctls(struct hda_gen_spec *spec) +{ + if (spec->bind_ctls.list) { + struct hda_bind_ctls **ctl = spec->bind_ctls.list; + int i; + for (i = 0; i < spec->bind_ctls.used; i++) + kfree(ctl[i]); + } + snd_array_free(&spec->bind_ctls); +}
-#ifdef CONFIG_PM -#define MAX_LOOPBACK_AMPS 7 - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[MAX_LOOPBACK_AMPS + 1]; -#endif -}; +void snd_hda_gen_spec_free(struct hda_gen_spec *spec) +{ + if (!spec) + return; + free_kctls(spec); + free_bind_ctls(spec); + snd_array_free(&spec->paths); +} +EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free);
/* - * retrieve the default device type from the default config value + * parsing paths */ -#define defcfg_type(node) (((node)->def_cfg & AC_DEFCFG_DEVICE) >> \ - AC_DEFCFG_DEVICE_SHIFT) -#define defcfg_location(node) (((node)->def_cfg & AC_DEFCFG_LOCATION) >> \ - AC_DEFCFG_LOCATION_SHIFT) -#define defcfg_port_conn(node) (((node)->def_cfg & AC_DEFCFG_PORT_CONN) >> \ - AC_DEFCFG_PORT_CONN_SHIFT)
-/* - * destructor +/* get the path between the given NIDs; + * passing 0 to either @pin or @dac behaves as a wildcard */ -static void snd_hda_generic_free(struct hda_codec *codec) +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node, *n; + struct hda_gen_spec *spec = codec->spec; + int i;
- if (! spec) - return; - /* free all widgets */ - list_for_each_entry_safe(node, n, &spec->nid_list, list) { - if (node->conn_list != node->slist) - kfree(node->conn_list); - kfree(node); + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if (path->depth <= 0) + continue; + if ((!from_nid || path->path[0] == from_nid) && + (!to_nid || path->path[path->depth - 1] == to_nid)) + return path; } - kfree(spec); + return NULL; } +EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
- -/* - * add a new widget node and read its attributes - */ -static int add_new_node(struct hda_codec *codec, struct hda_gspec *spec, hda_nid_t nid) +/* check whether the given DAC is already found in any existing paths */ +static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { - struct hda_gnode *node; - int nconns; - hda_nid_t conn_list[HDA_MAX_CONNECTIONS]; + struct hda_gen_spec *spec = codec->spec; + int i;
- node = kzalloc(sizeof(*node), GFP_KERNEL); - if (node == NULL) - return -ENOMEM; - node->nid = nid; - node->wid_caps = get_wcaps(codec, nid); - node->type = get_wcaps_type(node->wid_caps); - if (node->wid_caps & AC_WCAP_CONN_LIST) { - nconns = snd_hda_get_connections(codec, nid, conn_list, - HDA_MAX_CONNECTIONS); - if (nconns < 0) { - kfree(node); - return nconns; - } - } else { - nconns = 0; - } - if (nconns <= ARRAY_SIZE(node->slist)) - node->conn_list = node->slist; - else { - node->conn_list = kmalloc(sizeof(hda_nid_t) * nconns, - GFP_KERNEL); - if (! node->conn_list) { - snd_printk(KERN_ERR "hda-generic: cannot malloc\n"); - kfree(node); - return -ENOMEM; - } + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if (path->path[0] == nid) + return true; } - memcpy(node->conn_list, conn_list, nconns * sizeof(hda_nid_t)); - node->nconns = nconns; + return false; +}
- if (node->type == AC_WID_PIN) { - node->pin_caps = snd_hda_query_pin_caps(codec, node->nid); - node->pin_ctl = snd_hda_codec_read(codec, node->nid, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - node->def_cfg = snd_hda_codec_get_pincfg(codec, node->nid); - } +/* check whether the given two widgets can be connected */ +static bool is_reachable_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + if (!from_nid || !to_nid) + return false; + return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; +}
- if (node->wid_caps & AC_WCAP_OUT_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_out_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_OUT_CAP); - if (! node->amp_out_caps) - node->amp_out_caps = spec->def_amp_out_caps; - } - if (node->wid_caps & AC_WCAP_IN_AMP) { - if (node->wid_caps & AC_WCAP_AMP_OVRD) - node->amp_in_caps = snd_hda_param_read(codec, node->nid, AC_PAR_AMP_IN_CAP); - if (! node->amp_in_caps) - node->amp_in_caps = spec->def_amp_in_caps; +/* nid, dir and idx */ +#define AMP_VAL_COMPARE_MASK (0xffff | (1U << 18) | (0x0f << 19)) + +/* check whether the given ctl is already assigned in any path elements */ +static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + val &= AMP_VAL_COMPARE_MASK; + for (i = 0; i < spec->paths.used; i++) { + struct nid_path *path = snd_array_elem(&spec->paths, i); + if ((path->ctls[type] & AMP_VAL_COMPARE_MASK) == val) + return true; } - list_add_tail(&node->list, &spec->nid_list); - return 0; + return false; }
-/* - * build the AFG subtree - */ -static int build_afg_tree(struct hda_codec *codec) +/* check whether a control with the given (nid, dir, idx) was assigned */ +static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, + int dir, int idx) { - struct hda_gspec *spec = codec->spec; - int i, nodes, err; - hda_nid_t nid; - - if (snd_BUG_ON(!spec)) - return -EINVAL; - - spec->def_amp_out_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_OUT_CAP); - spec->def_amp_in_caps = snd_hda_param_read(codec, codec->afg, AC_PAR_AMP_IN_CAP); + unsigned int val = HDA_COMPOSE_AMP_VAL(nid, 3, idx, dir); + return is_ctl_used(codec, val, NID_PATH_VOL_CTL) || + is_ctl_used(codec, val, NID_PATH_MUTE_CTL); +}
- nodes = snd_hda_get_sub_nodes(codec, codec->afg, &nid); - if (! nid || nodes < 0) { - printk(KERN_ERR "Invalid AFG subtree\n"); - return -EINVAL; +/* called recursively */ +static bool __parse_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix, struct nid_path *path, int depth) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t conn[16]; + int i, nums; + + if (to_nid == spec->mixer_nid) { + if (!with_aa_mix) + return false; + with_aa_mix = 2; /* mark aa-mix is included */ }
- /* parse all nodes belonging to the AFG */ - for (i = 0; i < nodes; i++, nid++) { - if ((err = add_new_node(codec, spec, nid)) < 0) - return err; + nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); + for (i = 0; i < nums; i++) { + if (conn[i] != from_nid) { + /* special case: when from_nid is 0, + * try to find an empty DAC + */ + if (from_nid || + get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || + is_dac_already_used(codec, conn[i])) + continue; + } + /* aa-mix is requested but not included? */ + if (!(spec->mixer_nid && with_aa_mix == 1)) + goto found; } - - return 0; + if (depth >= MAX_NID_PATH_DEPTH) + return false; + for (i = 0; i < nums; i++) { + unsigned int type; + type = get_wcaps_type(get_wcaps(codec, conn[i])); + if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || + type == AC_WID_PIN) + continue; + if (__parse_nid_path(codec, from_nid, conn[i], + with_aa_mix, path, depth + 1)) + goto found; + } + return false; + + found: + path->path[path->depth] = conn[i]; + path->idx[path->depth + 1] = i; + if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) + path->multi[path->depth + 1] = 1; + path->depth++; + return true; }
- -/* - * look for the node record for the given NID +/* parse the widget path from the given nid to the target nid; + * when @from_nid is 0, try to find an empty DAC; + * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. + * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. + * when @with_aa_mix is 2, no special handling about spec->mixer_nid. */ -/* FIXME: should avoid the braindead linear search */ -static struct hda_gnode *hda_get_node(struct hda_gspec *spec, hda_nid_t nid) +bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path) { - struct hda_gnode *node; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->nid == nid) - return node; + if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + path->path[path->depth] = to_nid; + path->depth++; +#if 0 + snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", + path->depth, path->path[0], path->path[1], + path->path[2], path->path[3], path->path[4]); +#endif + return true; } - return NULL; + return false; } +EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path);
/* - * unmute (and set max vol) the output amplifier + * parse the path between the given NIDs and add to the path list. + * if no valid path is found, return NULL */ -static int unmute_output(struct hda_codec *codec, struct hda_gnode *node) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE OUT: NID=0x%x\n", node->nid); - val = (node->amp_out_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_out_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_OUTPUT, 0, 0xff, val); - return 0; +struct nid_path * +snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + + if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) + return NULL; + + path = snd_array_new(&spec->paths); + if (!path) + return NULL; + memset(path, 0, sizeof(*path)); + if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + return path; + /* push back */ + spec->paths.used--; + return NULL; } +EXPORT_SYMBOL_HDA(snd_hda_add_new_path);
-/* - * unmute (and set max vol) the input amplifier - */ -static int unmute_input(struct hda_codec *codec, struct hda_gnode *node, unsigned int index) -{ - unsigned int val, ofs; - snd_printdd("UNMUTE IN: NID=0x%x IDX=0x%x\n", node->nid, index); - val = (node->amp_in_caps & AC_AMPCAP_NUM_STEPS) >> AC_AMPCAP_NUM_STEPS_SHIFT; - ofs = (node->amp_in_caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - if (val >= ofs) - val -= ofs; - snd_hda_codec_amp_stereo(codec, node->nid, HDA_INPUT, index, 0xff, val); +/* look for an empty DAC slot */ +static hda_nid_t look_for_dac(struct hda_codec *codec, hda_nid_t pin, + bool is_digital) +{ + struct hda_gen_spec *spec = codec->spec; + bool cap_digital; + int i; + + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || is_dac_already_used(codec, nid)) + continue; + cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); + if (is_digital != cap_digital) + continue; + if (is_reachable_path(codec, nid, pin)) + return nid; + } return 0; }
-/* - * select the input connection of the given node. - */ -static int select_input_connection(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index) +/* replace the channels in the composed amp value with the given number */ +static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) { - snd_printdd("CONNECT: NID=0x%x IDX=0x%x\n", node->nid, index); - return snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_CONNECT_SEL, index); + val &= ~(0x3U << 16); + val |= chs << 16; + return val; }
-/* - * clear checked flag of each node in the node list - */ -static void clear_check_flags(struct hda_gspec *spec) +/* check whether the widget has the given amp capability for the direction */ +static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, + int dir, unsigned int bits) { - struct hda_gnode *node; + if (!nid) + return false; + if (get_wcaps(codec, nid) & (1 << (dir + 1))) + if (query_amp_caps(codec, nid, dir) & bits) + return true; + return false; +} + +#define nid_has_mute(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) +#define nid_has_volume(codec, nid, dir) \ + check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) + +/* look for a widget suitable for assigning a mute switch in the path */ +static hda_nid_t look_for_out_mute_nid(struct hda_codec *codec, + struct nid_path *path) +{ + int i; + + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; + if (i != path->depth - 1 && i != 0 && + nid_has_mute(codec, path->path[i], HDA_INPUT)) + return path->path[i]; + } + return 0; +} + +/* look for a widget suitable for assigning a volume ctl in the path */ +static hda_nid_t look_for_out_vol_nid(struct hda_codec *codec, + struct nid_path *path) +{ + int i;
- list_for_each_entry(node, &spec->nid_list, list) { - node->checked = 0; + for (i = path->depth - 1; i >= 0; i--) { + if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) + return path->path[i]; } + return 0; }
/* - * parse the output path recursively until reach to an audio output widget - * - * returns 0 if not found, 1 if found, or a negative error code. + * path activation / deactivation */ -static int parse_output_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int dac_idx) + +/* can have the amp-in capability? */ +static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) { - int i, err; - struct hda_gnode *child; + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_IN_AMP)) + return false; + if (type == AC_WID_PIN && idx > 0) /* only for input pins */ + return false; + return true; +}
- if (node->checked) - return 0; +/* can have the amp-out capability? */ +static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) +{ + hda_nid_t nid = path->path[idx]; + unsigned int caps = get_wcaps(codec, nid); + unsigned int type = get_wcaps_type(caps); + + if (!(caps & AC_WCAP_OUT_AMP)) + return false; + if (type == AC_WID_PIN && !idx) /* only for output pins */ + return false; + return true; +}
- node->checked = 1; - if (node->type == AC_WID_AUD_OUT) { - if (node->wid_caps & AC_WCAP_DIGITAL) { - snd_printdd("Skip Digital OUT node %x\n", node->nid); - return 0; - } - snd_printdd("AUD_OUT found %x\n", node->nid); - if (spec->dac_node[dac_idx]) { - /* already DAC node is assigned, just unmute & connect */ - return node == spec->dac_node[dac_idx]; - } - spec->dac_node[dac_idx] = node; - if ((node->wid_caps & AC_WCAP_OUT_AMP) && - spec->pcm_vol_nodes < MAX_PCM_VOLS) { - spec->pcm_vol[spec->pcm_vol_nodes].node = node; - spec->pcm_vol[spec->pcm_vol_nodes].index = 0; - spec->pcm_vol_nodes++; - } - return 1; /* found */ - } +/* check whether the given (nid,dir,idx) is active */ +static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, + unsigned int idx, unsigned int dir) +{ + struct hda_gen_spec *spec = codec->spec; + int i, n;
- for (i = 0; i < node->nconns; i++) { - child = hda_get_node(spec, node->conn_list[i]); - if (! child) + for (n = 0; n < spec->paths.used; n++) { + struct nid_path *path = snd_array_elem(&spec->paths, n); + if (!path->active) continue; - err = parse_output_path(codec, spec, child, dac_idx); - if (err < 0) - return err; - else if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - if (spec->dac_node[dac_idx] && - spec->pcm_vol_nodes < MAX_PCM_VOLS && - !(spec->dac_node[dac_idx]->wid_caps & - AC_WCAP_OUT_AMP)) { - if ((node->wid_caps & AC_WCAP_IN_AMP) || - (node->wid_caps & AC_WCAP_OUT_AMP)) { - int n = spec->pcm_vol_nodes; - spec->pcm_vol[n].node = node; - spec->pcm_vol[n].index = i; - spec->pcm_vol_nodes++; - } + for (i = 0; i < path->depth; i++) { + if (path->path[i] == nid) { + if (dir == HDA_OUTPUT || path->idx[i] == idx) + return true; + break; } - return 1; } } - return 0; + return false; }
-/* - * Look for the output PIN widget with the given jack type - * and parse the output path to that PIN. - * - * Returns the PIN node when the path to DAC is established. - */ -static struct hda_gnode *parse_output_jack(struct hda_codec *codec, - struct hda_gspec *spec, - int jack_type) +/* get the default amp value for the target state */ +static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, + int dir, bool enable) { - struct hda_gnode *node; - int err; - - list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* output capable? */ - if (! (node->pin_caps & AC_PINCAP_OUT)) - continue; - if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - continue; /* unconnected */ - if (jack_type >= 0) { - if (jack_type != defcfg_type(node)) - continue; - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - } else { - /* output as default? */ - if (! (node->pin_ctl & AC_PINCTL_OUT_EN)) - continue; - } - clear_check_flags(spec); - err = parse_output_path(codec, spec, node, 0); - if (err < 0) - return NULL; - if (! err && spec->out_pin_node[0]) { - err = parse_output_path(codec, spec, node, 1); - if (err < 0) - return NULL; - } - if (err > 0) { - /* unmute the PIN output */ - unmute_output(codec, node); - /* set PIN-Out enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, - AC_PINCTL_OUT_EN | - ((node->pin_caps & AC_PINCAP_HP_DRV) ? - AC_PINCTL_HP_EN : 0)); - return node; - } + unsigned int caps; + unsigned int val = 0; + + caps = query_amp_caps(codec, nid, dir); + if (caps & AC_AMPCAP_NUM_STEPS) { + /* set to 0dB */ + if (enable) + val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; } - return NULL; + if (caps & AC_AMPCAP_MUTE) { + if (!enable) + val |= HDA_AMP_MUTE; + } + return val; }
+/* initialize the amp value (only at the first time) */ +static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) +{ + int val = get_amp_val_to_activate(codec, nid, dir, false); + snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); +}
-/* - * parse outputs - */ -static int parse_output(struct hda_codec *codec) +static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, + int idx, bool enable) +{ + int val; + if (is_ctl_associated(codec, nid, dir, idx) || + is_active_nid(codec, nid, dir, idx)) + return; + val = get_amp_val_to_activate(codec, nid, dir, enable); + snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); +} + +static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, + int i, bool enable) +{ + hda_nid_t nid = path->path[i]; + init_amp(codec, nid, HDA_OUTPUT, 0); + activate_amp(codec, nid, HDA_OUTPUT, 0, enable); +} + +static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, + int i, bool enable, bool add_aamix) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; + struct hda_gen_spec *spec = codec->spec; + hda_nid_t conn[16]; + int n, nums, idx; + int type; + hda_nid_t nid = path->path[i]; + + nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + type = get_wcaps_type(get_wcaps(codec, nid)); + if (type == AC_WID_PIN || + (type == AC_WID_AUD_IN && codec->single_adc_amp)) { + nums = 1; + idx = 0; + } else + idx = path->idx[i]; + + for (n = 0; n < nums; n++) + init_amp(codec, nid, HDA_INPUT, n); + + if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) + return;
- /* - * Look for the output PIN widget + /* here is a little bit tricky in comparison with activate_amp_out(); + * when aa-mixer is available, we need to enable the path as well */ - /* first, look for the line-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_LINE_OUT); - if (node) /* found, remember the PIN node */ - spec->out_pin_node[0] = node; - else { - /* if no line-out is found, try speaker out */ - node = parse_output_jack(codec, spec, AC_JACK_SPEAKER); - if (node) - spec->out_pin_node[0] = node; - } - /* look for the HP-out pin */ - node = parse_output_jack(codec, spec, AC_JACK_HP_OUT); - if (node) { - if (! spec->out_pin_node[0]) - spec->out_pin_node[0] = node; - else - spec->out_pin_node[1] = node; + for (n = 0; n < nums; n++) { + if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) + continue; + activate_amp(codec, nid, HDA_INPUT, n, enable); } +}
- if (! spec->out_pin_node[0]) { - /* no line-out or HP pins found, - * then choose for the first output pin - */ - spec->out_pin_node[0] = parse_output_jack(codec, spec, -1); - if (! spec->out_pin_node[0]) - snd_printd("hda_generic: no proper output path found\n"); +/* activate or deactivate the given path + * if @add_aamix is set, enable the input from aa-mix NID as well (if any) + */ +void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix) +{ + int i; + + if (!enable) + path->active = false; + + for (i = path->depth - 1; i >= 0; i--) { + if (enable && path->multi[i]) + snd_hda_codec_write_cache(codec, path->path[i], 0, + AC_VERB_SET_CONNECT_SEL, + path->idx[i]); + if (has_amp_in(codec, path, i)) + activate_amp_in(codec, path, i, enable, add_aamix); + if (has_amp_out(codec, path, i)) + activate_amp_out(codec, path, i, enable); }
- return 0; + if (enable) + path->active = true; } +EXPORT_SYMBOL_HDA(snd_hda_activate_path); +
/* - * input MUX + * Helper functions for creating mixer ctl elements */
-/* control callbacks */ -static int capture_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} +enum { + HDA_CTL_WIDGET_VOL, + HDA_CTL_WIDGET_MUTE, + HDA_CTL_BIND_MUTE, + HDA_CTL_BIND_VOL, + HDA_CTL_BIND_SW, +}; +static const struct snd_kcontrol_new control_templates[] = { + HDA_CODEC_VOLUME(NULL, 0, 0, 0), + HDA_CODEC_MUTE(NULL, 0, 0, 0), + HDA_BIND_MUTE(NULL, 0, 0, 0), + HDA_BIND_VOL(NULL, 0), + HDA_BIND_SW(NULL, 0), +};
-static int capture_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +/* add dynamic controls from template */ +static int add_control(struct hda_gen_spec *spec, int type, const char *name, + int cidx, unsigned long val) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; + struct snd_kcontrol_new *knew;
- ucontrol->value.enumerated.item[0] = spec->cur_cap_src; + knew = add_kctl(spec, name, &control_templates[type]); + if (!knew) + return -ENOMEM; + knew->index = cidx; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + knew->private_value = val; return 0; }
-static int capture_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int add_control_with_pfx(struct hda_gen_spec *spec, int type, + const char *pfx, const char *dir, + const char *sfx, int cidx, unsigned long val) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct hda_gspec *spec = codec->spec; - return snd_hda_input_mux_put(codec, &spec->input_mux, ucontrol, - spec->adc_node->nid, &spec->cur_cap_src); + char name[32]; + snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); + return add_control(spec, type, name, cidx, val); }
-/* - * return the string name of the given input PIN widget - */ -static const char *get_input_type(struct hda_gnode *node, unsigned int *pinctl) -{ - unsigned int location = defcfg_location(node); - switch (defcfg_type(node)) { - case AC_JACK_LINE_IN: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Line"; - return "Line"; - case AC_JACK_CD: -#if 0 - if (pinctl) - *pinctl |= AC_PINCTL_VREF_GRD; -#endif - return "CD"; - case AC_JACK_AUX: - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Aux"; - return "Aux"; - case AC_JACK_MIC_IN: - if (pinctl && - (node->pin_caps & - (AC_PINCAP_VREF_80 << AC_PINCAP_VREF_SHIFT))) - *pinctl |= AC_PINCTL_VREF_80; - if ((location & 0x0f) == AC_JACK_LOC_FRONT) - return "Front Mic"; - return "Mic"; - case AC_JACK_SPDIF_IN: - return "SPDIF"; - case AC_JACK_DIG_OTHER_IN: - return "Digital"; +#define add_pb_vol_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) +#define add_pb_sw_ctrl(spec, type, pfx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) +#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) +#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ + add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) + +static int add_vol_ctl(struct hda_codec *codec, const char *pfx, int cidx, + unsigned int chs, struct nid_path *path) +{ + unsigned int val; + if (!path) + return 0; + val = path->ctls[NID_PATH_VOL_CTL]; + if (!val) + return 0; + val = amp_val_replace_channels(val, chs); + return __add_pb_vol_ctrl(codec->spec, HDA_CTL_WIDGET_VOL, pfx, cidx, val); +} + +/* return the channel bits suitable for the given path->ctls[] */ +static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, + int type) +{ + int chs = 1; /* mono (left only) */ + if (path) { + hda_nid_t nid = get_amp_nid_(path->ctls[type]); + if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) + chs = 3; /* stereo */ } - return NULL; + return chs; }
-/* - * parse the nodes recursively until reach to the input PIN - * - * returns 0 if not found, 1 if found, or a negative error code. +static int add_stereo_vol(struct hda_codec *codec, const char *pfx, int cidx, + struct nid_path *path) +{ + int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); + return add_vol_ctl(codec, pfx, cidx, chs, path); +} + +/* create a mute-switch for the given mixer widget; + * if it has multiple sources (e.g. DAC and loopback), create a bind-mute */ -static int parse_adc_sub_nodes(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, int idx) +static int add_sw_ctl(struct hda_codec *codec, const char *pfx, int cidx, + unsigned int chs, struct nid_path *path) { - int i, err; - unsigned int pinctl; - const char *type; + unsigned int val; + int type = HDA_CTL_WIDGET_MUTE;
- if (node->checked) + if (!path) return 0; - - node->checked = 1; - if (node->type != AC_WID_PIN) { - for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child; - child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_adc_sub_nodes(codec, spec, child, idx); - if (err < 0) - return err; - if (err > 0) { - /* found one, - * select the path, unmute both input and output - */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } + val = path->ctls[NID_PATH_MUTE_CTL]; + if (!val) return 0; + val = amp_val_replace_channels(val, chs); + if (get_amp_direction_(val) == HDA_INPUT) { + hda_nid_t nid = get_amp_nid_(val); + int nums = snd_hda_get_num_conns(codec, nid); + if (nums > 1) { + type = HDA_CTL_BIND_MUTE; + val |= nums << 19; + } } + return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); +}
- /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; +static int add_stereo_sw(struct hda_codec *codec, const char *pfx, + int cidx, struct nid_path *path) +{ + int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); + return add_sw_ctl(codec, pfx, cidx, chs, path); +}
- if (defcfg_port_conn(node) == AC_JACK_PORT_NONE) - return 0; /* unconnected */ +static const char * const channel_name[4] = { + "Front", "Surround", "CLFE", "Side" +};
- if (node->wid_caps & AC_WCAP_DIGITAL) - return 0; /* skip SPDIF */ +/* give some appropriate ctl name prefix for the given line out channel */ +static const char *get_line_out_pfx(struct hda_gen_spec *spec, int ch, + bool can_be_master, int *index) +{ + struct auto_pin_cfg *cfg = &spec->autocfg;
- if (spec->input_mux.num_items >= HDA_MAX_NUM_INPUTS) { - snd_printk(KERN_ERR "hda_generic: Too many items for capture\n"); - return -EINVAL; - } + *index = 0; + if (cfg->line_outs == 1 && !spec->multi_ios && + !cfg->hp_outs && !cfg->speaker_outs && can_be_master) + return spec->vmaster_mute.hook ? "PCM" : "Master";
- pinctl = AC_PINCTL_IN_EN; - /* create a proper capture source label */ - type = get_input_type(node, &pinctl); - if (! type) { - /* input as default? */ - if (! (node->pin_ctl & AC_PINCTL_IN_EN)) - return 0; - type = "Input"; + /* if there is really a single DAC used in the whole output paths, + * use it master (or "PCM" if a vmaster hook is present) + */ + if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && + !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) + return spec->vmaster_mute.hook ? "PCM" : "Master"; + + switch (cfg->line_out_type) { + case AUTO_PIN_SPEAKER_OUT: + if (cfg->line_outs == 1) + return "Speaker"; + if (cfg->line_outs == 2) + return ch ? "Bass Speaker" : "Speaker"; + break; + case AUTO_PIN_HP_OUT: + /* for multi-io case, only the primary out */ + if (ch && spec->multi_ios) + break; + *index = ch; + return "Headphone"; + default: + if (cfg->line_outs == 1 && !spec->multi_ios) + return "PCM"; + break; + } + if (ch >= ARRAY_SIZE(channel_name)) { + snd_BUG(); + return "PCM"; } - snd_hda_add_imux_item(&spec->input_mux, type, idx, NULL); - - /* unmute the PIN external input */ - unmute_input(codec, node, 0); /* index = 0? */ - /* set PIN-In enable */ - snd_hda_codec_write_cache(codec, node->nid, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
- return 1; /* found */ + return channel_name[ch]; }
/* - * parse input + * Parse output paths */ -static int parse_input_path(struct hda_codec *codec, struct hda_gnode *adc_node) + +/* badness definition */ +enum { + /* No primary DAC is found for the main output */ + BAD_NO_PRIMARY_DAC = 0x10000, + /* No DAC is found for the extra output */ + BAD_NO_DAC = 0x4000, + /* No possible multi-ios */ + BAD_MULTI_IO = 0x103, + /* No individual DAC for extra output */ + BAD_NO_EXTRA_DAC = 0x102, + /* No individual DAC for extra surrounds */ + BAD_NO_EXTRA_SURR_DAC = 0x101, + /* Primary DAC shared with main surrounds */ + BAD_SHARED_SURROUND = 0x100, + /* Primary DAC shared with main CLFE */ + BAD_SHARED_CLFE = 0x10, + /* Primary DAC shared with extra surrounds */ + BAD_SHARED_EXTRA_SURROUND = 0x10, + /* Volume widget is shared */ + BAD_SHARED_VOL = 0x10, +}; + +/* look for widgets in the path between the given NIDs appropriate for + * volume and mute controls, and assign the values to ctls[]. + * + * When no appropriate widget is found in the path, the badness value + * is incremented depending on the situation. The function returns the + * total badness for both volume and mute controls. + */ +static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int i, err; + struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin); + hda_nid_t nid; + unsigned int val; + int badness = 0; + + if (!path) + return BAD_SHARED_VOL * 2; + nid = look_for_out_vol_nid(codec, path); + if (nid) { + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) + badness += BAD_SHARED_VOL; + else + path->ctls[NID_PATH_VOL_CTL] = val; + } else + badness += BAD_SHARED_VOL; + nid = look_for_out_mute_nid(codec, path); + if (nid) { + unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); + if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || + nid_has_mute(codec, nid, HDA_OUTPUT)) + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) + badness += BAD_SHARED_VOL; + else + path->ctls[NID_PATH_MUTE_CTL] = val; + } else + badness += BAD_SHARED_VOL; + return badness; +}
- snd_printdd("AUD_IN = %x\n", adc_node->nid); - clear_check_flags(spec); +struct badness_table { + int no_primary_dac; /* no primary DAC */ + int no_dac; /* no secondary DACs */ + int shared_primary; /* primary DAC is shared with main output */ + int shared_surr; /* secondary DAC shared with main or primary */ + int shared_clfe; /* third DAC shared with main or primary */ + int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ +};
- // awk added - fixed no recording due to muted widget - unmute_input(codec, adc_node, 0); - - /* - * check each connection of the ADC - * if it reaches to a proper input PIN, add the path as the - * input path. - */ - /* first, check the direct connections to PIN widgets */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type == AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; +static struct badness_table main_out_badness = { + .no_primary_dac = BAD_NO_PRIMARY_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_PRIMARY_DAC, + .shared_surr = BAD_SHARED_SURROUND, + .shared_clfe = BAD_SHARED_CLFE, + .shared_surr_main = BAD_SHARED_SURROUND, +}; + +static struct badness_table extra_out_badness = { + .no_primary_dac = BAD_NO_DAC, + .no_dac = BAD_NO_DAC, + .shared_primary = BAD_NO_EXTRA_DAC, + .shared_surr = BAD_SHARED_EXTRA_SURROUND, + .shared_clfe = BAD_SHARED_EXTRA_SURROUND, + .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, +}; + +/* try to assign DACs to pins and return the resultant badness */ +static int try_assign_dacs(struct hda_codec *codec, int num_outs, + const hda_nid_t *pins, hda_nid_t *dacs, + const struct badness_table *bad) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, j; + int badness = 0; + hda_nid_t dac; + + if (!num_outs) + return 0; + + for (i = 0; i < num_outs; i++) { + hda_nid_t pin = pins[i]; + if (!dacs[i]) + dacs[i] = look_for_dac(codec, pin, false); + if (!dacs[i] && !i) { + for (j = 1; j < num_outs; j++) { + if (is_reachable_path(codec, dacs[j], pin)) { + dacs[0] = dacs[j]; + dacs[j] = 0; + break; + } + } } - } - /* ... then check the rests, more complicated connections */ - for (i = 0; i < adc_node->nconns; i++) { - node = hda_get_node(spec, adc_node->conn_list[i]); - if (node && node->type != AC_WID_PIN) { - err = parse_adc_sub_nodes(codec, spec, node, i); - if (err < 0) - return err; + dac = dacs[i]; + if (!dac) { + if (is_reachable_path(codec, dacs[0], pin)) + dac = dacs[0]; + else if (cfg->line_outs > i && + is_reachable_path(codec, spec->private_dac_nids[i], pin)) + dac = spec->private_dac_nids[i]; + if (dac) { + if (!i) + badness += bad->shared_primary; + else if (i == 1) + badness += bad->shared_surr; + else + badness += bad->shared_clfe; + } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { + dac = spec->private_dac_nids[0]; + badness += bad->shared_surr_main; + } else if (!i) + badness += bad->no_primary_dac; + else + badness += bad->no_dac; } + if (!snd_hda_add_new_path(codec, dac, pin, 0)) + dac = dacs[i] = 0; + if (dac) + badness += assign_out_path_ctls(codec, pin, dac); }
- if (! spec->input_mux.num_items) - return 0; /* no input path found... */ - - snd_printdd("[Capture Source] NID=0x%x, #SRC=%d\n", adc_node->nid, spec->input_mux.num_items); - for (i = 0; i < spec->input_mux.num_items; i++) - snd_printdd(" [%s] IDX=0x%x\n", spec->input_mux.items[i].label, - spec->input_mux.items[i].index); - - spec->adc_node = adc_node; - return 1; + return badness; }
-/* - * parse input - */ -static int parse_input(struct hda_codec *codec) +/* return NID if the given pin has only a single connection to a certain DAC */ +static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t nid_found = 0;
- /* - * At first we look for an audio input widget. - * If it reaches to certain input PINs, we take it as the - * input path. - */ - list_for_each_entry(node, &spec->nid_list, list) { - if (node->wid_caps & AC_WCAP_DIGITAL) - continue; /* skip SPDIF */ - if (node->type == AC_WID_AUD_IN) { - err = parse_input_path(codec, node); - if (err < 0) - return err; - else if (err > 0) + for (i = 0; i < spec->num_all_dacs; i++) { + hda_nid_t nid = spec->all_dacs[i]; + if (!nid || is_dac_already_used(codec, nid)) + continue; + if (is_reachable_path(codec, nid, pin)) { + if (nid_found) return 0; + nid_found = nid; } } - snd_printd("hda_generic: no proper input path found\n"); - return 0; + return nid_found; }
-#ifdef CONFIG_PM -static void add_input_loopback(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) +/* check whether the given pin can be a multi-io pin */ +static bool can_be_multiio_pin(struct hda_codec *codec, + unsigned int location, hda_nid_t nid) { - struct hda_gspec *spec = codec->spec; - struct hda_amp_list *p; - - if (spec->num_loopbacks >= MAX_LOOPBACK_AMPS) { - snd_printk(KERN_ERR "hda_generic: Too many loopback ctls\n"); - return; - } - p = &spec->loopback_list[spec->num_loopbacks++]; - p->nid = nid; - p->dir = dir; - p->idx = idx; - spec->loopback.amplist = spec->loopback_list; + unsigned int defcfg, caps; + + defcfg = snd_hda_codec_get_pincfg(codec, nid); + if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) + return false; + if (location && get_defcfg_location(defcfg) != location) + return false; + caps = snd_hda_query_pin_caps(codec, nid); + if (!(caps & AC_PINCAP_OUT)) + return false; + return true; } -#else -#define add_input_loopback(codec,nid,dir,idx) -#endif
/* - * create mixer controls if possible + * multi-io helper + * + * When hardwired is set, try to fill ony hardwired pins, and returns + * zero if any pins are filled, non-zero if nothing found. + * When hardwired is off, try to fill possible input pins, and returns + * the badness value. */ -static int create_mixer(struct hda_codec *codec, struct hda_gnode *node, - unsigned int index, const char *type, - const char *dir_sfx, int is_loopback) +static int fill_multi_ios(struct hda_codec *codec, + hda_nid_t reference_pin, + bool hardwired, int offset) { - char name[32]; - int err; - int created = 0; - struct snd_kcontrol_new knew; - - if (type) - sprintf(name, "%s %s Switch", type, dir_sfx); - else - sprintf(name, "%s Switch", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, index, HDA_INPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_INPUT, index); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && - (node->amp_out_caps & AC_AMPCAP_MUTE)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_MUTE(name, node->nid, 0, HDA_OUTPUT); - if (is_loopback) - add_input_loopback(codec, node->nid, HDA_OUTPUT, 0); - snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int type, i, j, dacs, num_pins, old_pins; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int badness = 0; + + old_pins = spec->multi_ios; + if (old_pins >= 2) + goto end_fill; + + num_pins = 0; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } } + if (num_pins < 2) + goto end_fill;
- if (type) - sprintf(name, "%s %s Volume", type, dir_sfx); - else - sprintf(name, "%s Volume", dir_sfx); - if ((node->wid_caps & AC_WCAP_IN_AMP) && - (node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, index, HDA_INPUT); - snd_printdd("[%s] NID=0x%x, DIR=IN, IDX=0x%x\n", name, node->nid, index); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } else if ((node->wid_caps & AC_WCAP_OUT_AMP) && - (node->amp_out_caps & AC_AMPCAP_NUM_STEPS)) { - knew = (struct snd_kcontrol_new)HDA_CODEC_VOLUME(name, node->nid, 0, HDA_OUTPUT); - snd_printdd("[%s] NID=0x%x, DIR=OUT\n", name, node->nid); - err = snd_hda_ctl_add(codec, node->nid, - snd_ctl_new1(&knew, codec)); - if (err < 0) - return err; - created = 1; - } + dacs = spec->multiout.num_dacs; + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + hda_nid_t dac = 0;
- return created; -} + if (cfg->inputs[i].type != type) + continue; + if (!can_be_multiio_pin(codec, location, nid)) + continue; + for (j = 0; j < spec->multi_ios; j++) { + if (nid == spec->multi_io[j].pin) + break; + } + if (j < spec->multi_ios) + continue; + + if (offset && offset + spec->multi_ios < dacs) { + dac = spec->private_dac_nids[offset + spec->multi_ios]; + if (!is_reachable_path(codec, dac, nid)) + dac = 0; + } + if (hardwired) + dac = get_dac_if_single(codec, nid); + else if (!dac) + dac = look_for_dac(codec, nid, false); + if (!dac) { + badness++; + continue; + } + if (!snd_hda_add_new_path(codec, dac, nid, 0)) { + badness++; + continue; + } + spec->multi_io[spec->multi_ios].pin = nid; + spec->multi_io[spec->multi_ios].dac = dac; + spec->multi_ios++; + if (spec->multi_ios >= 2) + break; + } + } + end_fill: + if (badness) + badness = BAD_MULTI_IO; + if (old_pins == spec->multi_ios) { + if (hardwired) + return 1; /* nothing found */ + else + return badness; /* no badness if nothing found */ + } + if (!hardwired && spec->multi_ios < 2) { + /* cancel newly assigned paths */ + spec->paths.used -= spec->multi_ios - old_pins; + spec->multi_ios = old_pins; + return badness; + } + + /* assign volume and mute controls */ + for (i = old_pins; i < spec->multi_ios; i++) + badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, + spec->multi_io[i].dac); + + return badness; +} + +/* map DACs for all pins in the list if they are single connections */ +static bool map_singles(struct hda_codec *codec, int outs, + const hda_nid_t *pins, hda_nid_t *dacs) +{ + int i; + bool found = false; + for (i = 0; i < outs; i++) { + hda_nid_t dac; + if (dacs[i]) + continue; + dac = get_dac_if_single(codec, pins[i]); + if (!dac) + continue; + if (snd_hda_add_new_path(codec, dac, pins[i], 0)) { + dacs[i] = dac; + found = true; + } + } + return found; +} + +/* fill in the dac_nids table from the parsed pin configuration */ +static int fill_and_eval_dacs(struct hda_codec *codec, + bool fill_hardwired, + bool fill_mio_first) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err, badness; + + /* set num_dacs once to full for look_for_dac() */ + spec->multiout.num_dacs = cfg->line_outs; + spec->multiout.dac_nids = spec->private_dac_nids; + memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); + memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); + memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); + spec->multi_ios = 0; + snd_array_free(&spec->paths); + badness = 0; + + /* fill hard-wired DACs first */ + if (fill_hardwired) { + bool mapped; + do { + mapped = map_singles(codec, cfg->line_outs, + cfg->line_out_pins, + spec->private_dac_nids); + mapped |= map_singles(codec, cfg->hp_outs, + cfg->hp_pins, + spec->multiout.hp_out_nid); + mapped |= map_singles(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid); + if (fill_mio_first && cfg->line_outs == 1 && + cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + if (!err) + mapped = true; + } + } while (mapped); + } + + badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, + spec->private_dac_nids, + &main_out_badness); + + /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else { + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + spec->private_dac_nids[cfg->line_outs - 1] = 0; + } + } + + if (fill_mio_first && + cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + /* try to fill multi-io first */ + err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + /* we don't count badness at this stage yet */ + } + + if (cfg->line_out_type != AUTO_PIN_HP_OUT) { + err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, + spec->multiout.hp_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = try_assign_dacs(codec, cfg->speaker_outs, + cfg->speaker_pins, + spec->multiout.extra_out_nid, + &extra_out_badness); + if (err < 0) + return err; + badness += err; + } + if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { + err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + if (err < 0) + return err; + badness += err; + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + /* try multi-ios with HP + inputs */ + int offset = 0; + if (cfg->line_outs >= 3) + offset = 1; + err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset); + if (err < 0) + return err; + badness += err; + } + + if (spec->multi_ios == 2) { + for (i = 0; i < 2; i++) + spec->private_dac_nids[spec->multiout.num_dacs++] = + spec->multi_io[i].dac; + spec->ext_channel_count = 2; + } else if (spec->multi_ios) { + spec->multi_ios = 0; + badness += BAD_MULTI_IO; + } + + return badness; +} + +#define DEBUG_BADNESS + +#ifdef DEBUG_BADNESS +#define debug_badness snd_printdd +#else +#define debug_badness(...) +#endif + +static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *cfg) +{ + debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->line_out_pins[0], cfg->line_out_pins[1], + cfg->line_out_pins[2], cfg->line_out_pins[2], + spec->multiout.dac_nids[0], + spec->multiout.dac_nids[1], + spec->multiout.dac_nids[2], + spec->multiout.dac_nids[3]); + if (spec->multi_ios > 0) + debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", + spec->multi_ios, + spec->multi_io[0].pin, spec->multi_io[1].pin, + spec->multi_io[0].dac, spec->multi_io[1].dac); + debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->hp_pins[0], cfg->hp_pins[1], + cfg->hp_pins[2], cfg->hp_pins[2], + spec->multiout.hp_out_nid[0], + spec->multiout.hp_out_nid[1], + spec->multiout.hp_out_nid[2], + spec->multiout.hp_out_nid[3]); + debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", + cfg->speaker_pins[0], cfg->speaker_pins[1], + cfg->speaker_pins[2], cfg->speaker_pins[3], + spec->multiout.extra_out_nid[0], + spec->multiout.extra_out_nid[1], + spec->multiout.extra_out_nid[2], + spec->multiout.extra_out_nid[3]); +} + +/* find all available DACs of the codec */ +static void fill_all_dac_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t nid = codec->start_nid; + + spec->num_all_dacs = 0; + memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); + for (i = 0; i < codec->num_nodes; i++, nid++) { + if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) + continue; + if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { + snd_printk(KERN_ERR "hda: Too many DACs!\n"); + break; + } + spec->all_dacs[spec->num_all_dacs++] = nid; + } +} + +static int parse_output_paths(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *best_cfg; + int best_badness = INT_MAX; + int badness; + bool fill_hardwired = true, fill_mio_first = true; + bool best_wired = true, best_mio = true; + bool hp_spk_swapped = false; + + fill_all_dac_nids(codec); + + best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); + if (!best_cfg) + return -ENOMEM; + *best_cfg = *cfg; + + for (;;) { + badness = fill_and_eval_dacs(codec, fill_hardwired, + fill_mio_first); + if (badness < 0) { + kfree(best_cfg); + return badness; + } + debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", + cfg->line_out_type, fill_hardwired, fill_mio_first, + badness); + debug_show_configs(spec, cfg); + if (badness < best_badness) { + best_badness = badness; + *best_cfg = *cfg; + best_wired = fill_hardwired; + best_mio = fill_mio_first; + } + if (!badness) + break; + fill_mio_first = !fill_mio_first; + if (!fill_mio_first) + continue; + fill_hardwired = !fill_hardwired; + if (!fill_hardwired) + continue; + if (hp_spk_swapped) + break; + hp_spk_swapped = true; + if (cfg->speaker_outs > 0 && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + cfg->hp_outs = cfg->line_outs; + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->line_outs = cfg->speaker_outs; + memcpy(cfg->line_out_pins, cfg->speaker_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = 0; + memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); + cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; + fill_hardwired = true; + continue; + } + if (cfg->hp_outs > 0 && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + fill_hardwired = true; + continue; + } + break; + } + + if (badness) { + *cfg = *best_cfg; + fill_and_eval_dacs(codec, best_wired, best_mio); + } + debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", + cfg->line_out_type, best_wired, best_mio); + debug_show_configs(spec, cfg); + + if (cfg->line_out_pins[0]) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, + spec->multiout.dac_nids[0], + cfg->line_out_pins[0]); + if (path) + spec->vmaster_nid = look_for_out_vol_nid(codec, path); + } + + kfree(best_cfg); + return 0; +} + +/* add playback controls from the parsed DAC table */ +static int create_multi_out_ctls(struct hda_codec *codec, + const struct auto_pin_cfg *cfg) +{ + struct hda_gen_spec *spec = codec->spec; + int i, err, noutputs; + + noutputs = cfg->line_outs; + if (spec->multi_ios > 0 && cfg->line_outs < 3) + noutputs += spec->multi_ios; + + for (i = 0; i < noutputs; i++) { + const char *name; + int index; + hda_nid_t dac, pin; + struct nid_path *path; + + dac = spec->multiout.dac_nids[i]; + if (!dac) + continue; + if (i >= cfg->line_outs) { + pin = spec->multi_io[i - 1].pin; + index = 0; + name = channel_name[i]; + } else { + pin = cfg->line_out_pins[i]; + name = get_line_out_pfx(spec, i, true, &index); + } + + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + continue; + if (!name || !strcmp(name, "CLFE")) { + /* Center/LFE */ + err = add_vol_ctl(codec, "Center", 0, 1, path); + if (err < 0) + return err; + err = add_vol_ctl(codec, "LFE", 0, 2, path); + if (err < 0) + return err; + err = add_sw_ctl(codec, "Center", 0, 1, path); + if (err < 0) + return err; + err = add_sw_ctl(codec, "LFE", 0, 2, path); + if (err < 0) + return err; + } else { + err = add_stereo_vol(codec, name, index, path); + if (err < 0) + return err; + err = add_stereo_sw(codec, name, index, path); + if (err < 0) + return err; + } + } + return 0; +} + +static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, + hda_nid_t dac, const char *pfx, int cidx) +{ + struct nid_path *path; + int err; + + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + return 0; + /* bind volume control will be created in the case of dac = 0 */ + if (dac) { + err = add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; + } + err = add_stereo_sw(codec, pfx, cidx, path); + if (err < 0) + return err; + return 0; +} + +/* add playback controls for speaker and HP outputs */ +static int create_extra_outs(struct hda_codec *codec, int num_pins, + const hda_nid_t *pins, const hda_nid_t *dacs, + const char *pfx) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_bind_ctls *ctl; + char name[32]; + int i, n, err; + + if (!num_pins || !pins[0]) + return 0; + + if (num_pins == 1) { + hda_nid_t dac = *dacs; + if (!dac) + dac = spec->multiout.dac_nids[0]; + return create_extra_out(codec, *pins, dac, pfx, 0); + } + + for (i = 0; i < num_pins; i++) { + hda_nid_t dac; + if (dacs[num_pins - 1]) + dac = dacs[i]; /* with individual volumes */ + else + dac = 0; + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { + err = create_extra_out(codec, pins[i], dac, + "Bass Speaker", 0); + } else if (num_pins >= 3) { + snprintf(name, sizeof(name), "%s %s", + pfx, channel_name[i]); + err = create_extra_out(codec, pins[i], dac, name, 0); + } else { + err = create_extra_out(codec, pins[i], dac, pfx, i); + } + if (err < 0) + return err; + } + if (dacs[num_pins - 1]) + return 0; + + /* Let's create a bind-controls for volumes */ + ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); + if (!ctl) + return -ENOMEM; + n = 0; + for (i = 0; i < num_pins; i++) { + hda_nid_t vol; + struct nid_path *path; + if (!pins[i] || !dacs[i]) + continue; + path = snd_hda_get_nid_path(codec, dacs[i], pins[i]); + if (!path) + continue; + vol = look_for_out_vol_nid(codec, path); + if (vol) + ctl->values[n++] = + HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); + } + if (n) { + snprintf(name, sizeof(name), "%s Playback Volume", pfx); + err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl); + if (err < 0) + return err; + } + return 0; +} + +static int create_hp_out_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + return create_extra_outs(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins, + spec->multiout.hp_out_nid, + "Headphone"); +} + +static int create_speaker_out_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + return create_extra_outs(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + spec->multiout.extra_out_nid, + "Speaker"); +} + +/* + * channel mode enum control + */ + +static int ch_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = spec->multi_ios + 1; + if (uinfo->value.enumerated.item > spec->multi_ios) + uinfo->value.enumerated.item = spec->multi_ios; + sprintf(uinfo->value.enumerated.name, "%dch", + (uinfo->value.enumerated.item + 1) * 2); + return 0; +} + +static int ch_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + return 0; +} + +static int set_multi_io(struct hda_codec *codec, int idx, bool output) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid = spec->multi_io[idx].pin; + struct nid_path *path; + + path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid); + if (!path) + return -EINVAL; + + if (path->active == output) + return 0; + + if (output) { + snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); + snd_hda_activate_path(codec, path, true, true); + } else { + snd_hda_activate_path(codec, path, false, true); + snd_hda_set_pin_ctl_cache(codec, nid, + spec->multi_io[idx].ctl_in); + } + return 0; +} + +static int ch_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + int i, ch; + + ch = ucontrol->value.enumerated.item[0]; + if (ch < 0 || ch > spec->multi_ios) + return -EINVAL; + if (ch == (spec->ext_channel_count - 1) / 2) + return 0; + spec->ext_channel_count = (ch + 1) * 2; + for (i = 0; i < spec->multi_ios; i++) + set_multi_io(codec, i, i < ch); + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + if (spec->need_dac_fix) + spec->multiout.num_dacs = spec->multiout.max_channels / 2; + return 1; +} + +static const struct snd_kcontrol_new channel_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Channel Mode", + .info = ch_mode_info, + .get = ch_mode_get, + .put = ch_mode_put, +}; + +static int create_multi_channel_mode(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->multi_ios > 0) { + if (!add_kctl(spec, NULL, &channel_mode_enum)) + return -ENOMEM; + } + return 0; +} + +/* + * shared headphone/mic handling + */ + +static void call_update_outputs(struct hda_codec *codec); + +/* for shared I/O, change the pin-control accordingly */ +static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int val; + hda_nid_t pin = spec->autocfg.inputs[1].pin; + /* NOTE: this assumes that there are only two inputs, the + * first is the real internal mic and the second is HP/mic jack. + */ + + val = snd_hda_get_default_vref(codec, pin); + + /* This pin does not have vref caps - let's enable vref on pin 0x18 + instead, as suggested by Realtek */ + if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { + const hda_nid_t vref_pin = spec->shared_mic_vref_pin; + unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); + if (vref_val != AC_PINCTL_VREF_HIZ) + snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); + } + + val = set_as_mic ? val | PIN_IN : PIN_HP; + snd_hda_set_pin_ctl(codec, pin, val); + + spec->automute_speaker = !set_as_mic; + call_update_outputs(codec); +} + +/* create a shared input with the headphone out */ +static int create_shared_input(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int defcfg; + hda_nid_t nid; + + /* only one internal input pin? */ + if (cfg->num_inputs != 1) + return 0; + defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); + if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) + return 0; + + if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ + else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) + nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ + else + return 0; /* both not available */ + + if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) + return 0; /* no input */ + + cfg->inputs[1].pin = nid; + cfg->inputs[1].type = AUTO_PIN_MIC; + cfg->num_inputs = 2; + spec->shared_mic_hp = 1; + snd_printdd("hda-codec: Enable shared I/O jack on NID 0x%x\n", nid); + return 0; +} + + +/* + * Parse input paths + */ + +#ifdef CONFIG_PM +/* add the powersave loopback-list entry */ +static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) +{ + struct hda_amp_list *list; + + if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) + return; + list = spec->loopback_list + spec->num_loopbacks; + list->nid = mix; + list->dir = HDA_INPUT; + list->idx = idx; + spec->num_loopbacks++; + spec->loopback.amplist = spec->loopback_list; +} +#else +#define add_loopback_list(spec, mix, idx) /* NOP */ +#endif + +/* create input playback/capture controls for the given pin */ +static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, + const char *ctlname, int ctlidx, + hda_nid_t mix_nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int val; + int err, idx; + + if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && + !nid_has_mute(codec, mix_nid, HDA_INPUT)) + return 0; /* no need for analog loopback */ + + path = snd_hda_add_new_path(codec, pin, mix_nid, 2); + if (!path) + return -EINVAL; + + idx = path->idx[path->depth - 1]; + if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_vol_ctrl(spec, HDA_CTL_WIDGET_VOL, ctlname, ctlidx, val); + if (err < 0) + return err; + path->ctls[NID_PATH_VOL_CTL] = val; + } + + if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { + val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); + err = __add_pb_sw_ctrl(spec, HDA_CTL_WIDGET_MUTE, ctlname, ctlidx, val); + if (err < 0) + return err; + path->ctls[NID_PATH_MUTE_CTL] = val; + } + + path->active = true; + add_loopback_list(spec, mix_nid, idx); + return 0; +} + +static int is_input_pin(struct hda_codec *codec, hda_nid_t nid) +{ + unsigned int pincap = snd_hda_query_pin_caps(codec, nid); + return (pincap & AC_PINCAP_IN) != 0; +} + +/* Parse the codec tree and retrieve ADCs */ +static int fill_adc_nids(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid; + hda_nid_t *adc_nids = spec->adc_nids; + int max_nums = ARRAY_SIZE(spec->adc_nids); + int i, nums = 0; + + nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, nid++) { + unsigned int caps = get_wcaps(codec, nid); + int type = get_wcaps_type(caps); + + if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) + continue; + adc_nids[nums] = nid; + if (++nums >= max_nums) + break; + } + spec->num_adc_nids = nums; + return nums; +} + +/* filter out invalid adc_nids that don't give all active input pins; + * if needed, check whether dynamic ADC-switching is available + */ +static int check_dyn_adc_switch(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; + int i, n, nums; + hda_nid_t pin, adc; + + again: + nums = 0; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + if (!is_reachable_path(codec, pin, adc)) + break; + } + if (i >= imux->num_items) + adc_nids[nums++] = adc; + } + + if (!nums) { + if (spec->shared_mic_hp) { + spec->shared_mic_hp = 0; + imux->num_items = 1; + goto again; + } + + /* check whether ADC-switch is possible */ + for (i = 0; i < imux->num_items; i++) { + pin = spec->imux_pins[i]; + for (n = 0; n < spec->num_adc_nids; n++) { + adc = spec->adc_nids[n]; + if (is_reachable_path(codec, pin, adc)) { + spec->dyn_adc_idx[i] = n; + break; + } + } + } + + snd_printdd("hda-codec: enabling ADC switching\n"); + spec->dyn_adc_switch = 1; + } else if (nums != spec->num_adc_nids) { + memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); + spec->num_adc_nids = nums; + } + + if (imux->num_items == 1 || spec->shared_mic_hp) { + snd_printdd("hda-codec: reducing to a single ADC\n"); + spec->num_adc_nids = 1; /* reduce to a single ADC */ + } + + /* single index for individual volumes ctls */ + if (!spec->dyn_adc_switch && spec->multi_cap_vol) + spec->num_adc_nids = 1; + + return 0; +} + +/* + * create playback/capture controls for input pins + */ +static int create_input_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct auto_pin_cfg *cfg = &spec->autocfg; + hda_nid_t mixer = spec->mixer_nid; + struct hda_input_mux *imux = &spec->input_mux; + int num_adcs; + int i, c, err, type_idx = 0; + const char *prev_label = NULL; + + num_adcs = fill_adc_nids(codec); + if (num_adcs < 0) + return 0; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t pin; + const char *label; + bool imux_added; + + pin = cfg->inputs[i].pin; + if (!is_input_pin(codec, pin)) + continue; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + if (mixer) { + if (is_reachable_path(codec, pin, mixer)) { + err = new_analog_input(codec, pin, + label, type_idx, mixer); + if (err < 0) + return err; + } + } + + imux_added = false; + for (c = 0; c < num_adcs; c++) { + struct nid_path *path; + hda_nid_t adc = spec->adc_nids[c]; + + if (!is_reachable_path(codec, pin, adc)) + continue; + path = snd_array_new(&spec->paths); + if (!path) + return -ENOMEM; + memset(path, 0, sizeof(*path)); + if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) { + snd_printd(KERN_ERR + "invalid input path 0x%x -> 0x%x\n", + pin, adc); + spec->paths.used--; + continue; + } + + if (!imux_added) { + spec->imux_pins[imux->num_items] = pin; + snd_hda_add_imux_item(imux, label, + imux->num_items, NULL); + imux_added = true; + } + } + } + + return 0; +} + + +/* + * input source mux + */ + +/* get the ADC NID corresponding to the given index */ +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; +} + +static int mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx); + +static int mux_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + return snd_hda_input_mux_info(&spec->input_mux, uinfo); +} + +static int mux_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + + ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; + return 0; +} + +static int mux_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + return mux_select(codec, adc_idx, + ucontrol->value.enumerated.item[0]); +} + +/* + * capture volume and capture switch ctls + */ + +static const struct snd_kcontrol_new cap_src_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Input Source", + .info = mux_enum_info, + .get = mux_enum_get, + .put = mux_enum_put, +}; + +typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + +static int cap_put_caller(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + put_call_t func, int type) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + struct nid_path *path; + int i, adc_idx, err = 0; + + imux = &spec->input_mux; + adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); + mutex_lock(&codec->control_mutex); + codec->cached_write = 1; + for (i = 0; i < imux->num_items; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, adc_idx, i)); + if (!path->ctls[type]) + continue; + kcontrol->private_value = path->ctls[type]; + err = func(kcontrol, ucontrol); + if (err < 0) + goto error; + } + error: + codec->cached_write = 0; + mutex_unlock(&codec->control_mutex); + if (err >= 0 && spec->cap_sync_hook) + spec->cap_sync_hook(codec); + return err; +} + +/* capture volume ctl callbacks */ +#define cap_vol_info snd_hda_mixer_amp_volume_info +#define cap_vol_get snd_hda_mixer_amp_volume_get +#define cap_vol_tlv snd_hda_mixer_amp_tlv + +static int cap_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_volume_put, + NID_PATH_VOL_CTL); +} + +static const struct snd_kcontrol_new cap_vol_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), + .info = cap_vol_info, + .get = cap_vol_get, + .put = cap_vol_put, + .tlv = { .c = cap_vol_tlv }, +}; + +/* capture switch ctl callbacks */ +#define cap_sw_info snd_ctl_boolean_stereo_info +#define cap_sw_get snd_hda_mixer_amp_switch_get + +static int cap_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return cap_put_caller(kcontrol, ucontrol, + snd_hda_mixer_amp_switch_put, + NID_PATH_MUTE_CTL); +} + +static const struct snd_kcontrol_new cap_sw_temp = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Switch", + .info = cap_sw_info, + .get = cap_sw_get, + .put = cap_sw_put, +}; + +static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) +{ + hda_nid_t nid; + int i, depth; + + path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; + for (depth = 0; depth < 3; depth++) { + if (depth >= path->depth) + return -EINVAL; + i = path->depth - depth - 1; + nid = path->path[i]; + if (!path->ctls[NID_PATH_VOL_CTL]) { + if (nid_has_volume(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_volume(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_VOL_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + if (!path->ctls[NID_PATH_MUTE_CTL]) { + if (nid_has_mute(codec, nid, HDA_OUTPUT)) + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); + else if (nid_has_mute(codec, nid, HDA_INPUT)) { + int idx = path->idx[i]; + if (!depth && codec->single_adc_amp) + idx = 0; + path->ctls[NID_PATH_MUTE_CTL] = + HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); + } + } + } + return 0; +} + +static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int val; + int i; + + if (!spec->inv_dmic_split) + return false; + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].pin != nid) + continue; + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return false; + val = snd_hda_codec_get_pincfg(codec, nid); + return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; + } + return false; +} + +static int add_single_cap_ctl(struct hda_codec *codec, const char *label, + int idx, bool is_switch, unsigned int ctl, + bool inv_dmic) +{ + struct hda_gen_spec *spec = codec->spec; + char tmpname[44]; + int type = is_switch ? HDA_CTL_WIDGET_MUTE : HDA_CTL_WIDGET_VOL; + const char *sfx = is_switch ? "Switch" : "Volume"; + unsigned int chs = inv_dmic ? 1 : 3; + int err; + + if (!ctl) + return 0; + + if (label) + snprintf(tmpname, sizeof(tmpname), + "%s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Capture %s", sfx); + err = add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, chs)); + if (err < 0 || !inv_dmic) + return err; + + /* Make independent right kcontrol */ + if (label) + snprintf(tmpname, sizeof(tmpname), + "Inverted %s Capture %s", label, sfx); + else + snprintf(tmpname, sizeof(tmpname), + "Inverted Capture %s", sfx); + return add_control(spec, type, tmpname, idx, + amp_val_replace_channels(ctl, 2)); +} + +/* create single (and simple) capture volume and switch controls */ +static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl, + bool inv_dmic) +{ + int err; + err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); + if (err < 0) + return err; + err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); + if (err < 0) + return err; + return 0; +} + +/* create bound capture volume and switch controls */ +static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, + unsigned int vol_ctl, unsigned int sw_ctl) +{ + struct hda_gen_spec *spec = codec->spec; + struct snd_kcontrol_new *knew; + + if (vol_ctl) { + knew = add_kctl(spec, NULL, &cap_vol_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = vol_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + if (sw_ctl) { + knew = add_kctl(spec, NULL, &cap_sw_temp); + if (!knew) + return -ENOMEM; + knew->index = idx; + knew->private_value = sw_ctl; + knew->subdevice = HDA_SUBDEV_AMP_FLAG; + } + return 0; +} + +/* return the vol ctl when used first in the imux list */ +static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; + unsigned int ctl; + int i; + + path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, 0, idx)); + if (!path) + return 0; + ctl = path->ctls[type]; + if (!ctl) + return 0; + for (i = 0; i < idx - 1; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, 0, i)); + if (path && path->ctls[type] == ctl) + return 0; + } + return ctl; +} + +/* create individual capture volume and switch controls per input */ +static int create_multi_cap_vol_ctl(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, err, type, type_idx = 0; + const char *prev_label = NULL; + + for (i = 0; i < imux->num_items; i++) { + const char *label; + bool inv_dmic; + label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); + + for (type = 0; type < 2; type++) { + err = add_single_cap_ctl(codec, label, type_idx, type, + get_first_cap_ctl(codec, i, type), + inv_dmic); + if (err < 0) + return err; + } + } + return 0; +} + +static int create_capture_mixers(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + int i, n, nums, err; + + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + if (!spec->auto_mic && imux->num_items > 1) { + struct snd_kcontrol_new *knew; + knew = add_kctl(spec, NULL, &cap_src_temp); + if (!knew) + return -ENOMEM; + knew->count = nums; + } + + for (n = 0; n < nums; n++) { + bool multi = false; + bool inv_dmic = false; + int vol, sw; + + vol = sw = 0; + for (i = 0; i < imux->num_items; i++) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, n, i)); + if (!path) + continue; + parse_capvol_in_path(codec, path); + if (!vol) + vol = path->ctls[NID_PATH_VOL_CTL]; + else if (vol != path->ctls[NID_PATH_VOL_CTL]) + multi = true; + if (!sw) + sw = path->ctls[NID_PATH_MUTE_CTL]; + else if (sw != path->ctls[NID_PATH_MUTE_CTL]) + multi = true; + if (is_inv_dmic_pin(codec, spec->imux_pins[i])) + inv_dmic = true; + } + + if (!multi) + err = create_single_cap_vol_ctl(codec, n, vol, sw, + inv_dmic); + else if (!spec->multi_cap_vol) + err = create_bind_cap_vol_ctl(codec, n, vol, sw); + else + err = create_multi_cap_vol_ctl(codec); + if (err < 0) + return err; + } + + return 0; +} + +/* + * add mic boosts if needed + */ +static int parse_mic_boost(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i, err; + int type_idx = 0; + hda_nid_t nid; + const char *prev_label = NULL; + + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type > AUTO_PIN_MIC) + break; + nid = cfg->inputs[i].pin; + if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { + const char *label; + char boost_label[32]; + struct nid_path *path; + unsigned int val; + + label = hda_get_autocfg_input_label(codec, cfg, i); + if (spec->shared_mic_hp && !strcmp(label, "Misc")) + label = "Headphone Mic"; + if (prev_label && !strcmp(label, prev_label)) + type_idx++; + else + type_idx = 0; + prev_label = label; + + snprintf(boost_label, sizeof(boost_label), + "%s Boost Volume", label); + val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); + err = add_control(spec, HDA_CTL_WIDGET_VOL, + boost_label, type_idx, val); + if (err < 0) + return err; + + path = snd_hda_get_nid_path(codec, nid, 0); + if (path) + path->ctls[NID_PATH_BOOST_CTL] = val; + } + } + return 0; +} + +/* + * parse digital I/Os and set up NIDs in BIOS auto-parse mode + */ +static void parse_digital(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i, nums; + hda_nid_t dig_nid; + + /* support multiple SPDIFs; the secondary is set up as a slave */ + nums = 0; + for (i = 0; i < spec->autocfg.dig_outs; i++) { + hda_nid_t pin = spec->autocfg.dig_out_pins[i]; + dig_nid = look_for_dac(codec, pin, true); + if (!dig_nid) + continue; + if (!snd_hda_add_new_path(codec, dig_nid, pin, 2)) + continue; + if (!nums) { + spec->multiout.dig_out_nid = dig_nid; + spec->dig_out_type = spec->autocfg.dig_out_type[0]; + } else { + spec->multiout.slave_dig_outs = spec->slave_dig_outs; + if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) + break; + spec->slave_dig_outs[nums - 1] = dig_nid; + } + nums++; + } + + if (spec->autocfg.dig_in_pin) { + dig_nid = codec->start_nid; + for (i = 0; i < codec->num_nodes; i++, dig_nid++) { + struct nid_path *path; + unsigned int wcaps = get_wcaps(codec, dig_nid); + if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) + continue; + if (!(wcaps & AC_WCAP_DIGITAL)) + continue; + path = snd_hda_add_new_path(codec, + spec->autocfg.dig_in_pin, + dig_nid, 2); + if (path) { + path->active = true; + spec->dig_in_nid = dig_nid; + break; + } + } + } +} + + +/* + * input MUX handling + */ + +static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur); + +/* select the given imux item; either unmute exclusively or select the route */ +static int mux_select(struct hda_codec *codec, unsigned int adc_idx, + unsigned int idx) +{ + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + struct nid_path *path; + + imux = &spec->input_mux; + if (!imux->num_items) + return 0; + + if (idx >= imux->num_items) + idx = imux->num_items - 1; + if (spec->cur_mux[adc_idx] == idx) + return 0; + + path = snd_hda_get_nid_path(codec, + spec->imux_pins[spec->cur_mux[adc_idx]], + spec->adc_nids[adc_idx]); + if (!path) + return 0; + if (path->active) + snd_hda_activate_path(codec, path, false, false); + + spec->cur_mux[adc_idx] = idx; + + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); + + if (spec->dyn_adc_switch) + dyn_adc_pcm_resetup(codec, idx); + + path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], + get_adc_nid(codec, adc_idx, idx)); + if (!path) + return 0; + if (path->active) + return 0; + snd_hda_activate_path(codec, path, true, false); + if (spec->cap_sync_hook) + spec->cap_sync_hook(codec); + return 1; +} + + +/* + * Jack detections for HP auto-mute and mic-switch + */ + +/* check each pin in the given array; returns true if any of them is plugged */ +static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) +{ + int i, present = 0; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + if (!nid) + break; + present |= snd_hda_jack_detect(codec, nid); + } + return present; +} + +/* standard HP/line-out auto-mute helper */ +static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, + bool mute, bool hp_out) +{ + struct hda_gen_spec *spec = codec->spec; + unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); + int i; + + for (i = 0; i < num_pins; i++) { + hda_nid_t nid = pins[i]; + unsigned int val; + if (!nid) + break; + /* don't reset VREF value in case it's controlling + * the amp (see alc861_fixup_asus_amp_vref_0f()) + */ + if (spec->keep_vref_in_automute) { + val = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val &= ~PIN_HP; + } else + val = 0; + val |= pin_bits; + snd_hda_set_pin_ctl(codec, nid, val); + } +} + +/* Toggle outputs muting */ +static void update_outputs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int on; + + /* Control HP pins/amps depending on master_mute state; + * in general, HP pins/amps control should be enabled in all cases, + * but currently set only for master_mute, just to be safe + */ + if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ + do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins, spec->master_mute, true); + + if (!spec->automute_speaker) + on = 0; + else + on = spec->hp_jack_present | spec->line_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), + spec->autocfg.speaker_pins, on, false); + + /* toggle line-out mutes if needed, too */ + /* if LO is a copy of either HP or Speaker, don't need to handle it */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || + spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) + return; + if (!spec->automute_lo) + on = 0; + else + on = spec->hp_jack_present; + on |= spec->master_mute; + do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins, on, false); +} + +static void call_update_outputs(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->automute_hook) + spec->automute_hook(codec); + else + update_outputs(codec); +} + +/* standard HP-automute helper */ +static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + + spec->hp_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), + spec->autocfg.hp_pins); + if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) + return; + call_update_outputs(codec); +} + +/* standard line-out-automute helper */ +static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + return; + /* check LO jack only when it's different from HP */ + if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) + return; + + spec->line_jack_present = + detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), + spec->autocfg.line_out_pins); + if (!spec->automute_speaker || !spec->detect_lo) + return; + call_update_outputs(codec); +} + +/* standard mic auto-switch helper */ +static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + if (!spec->auto_mic) + return; + + for (i = spec->am_num_entries - 1; i > 0; i--) { + if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { + mux_select(codec, 0, spec->am_entry[i].idx); + return; + } + } + mux_select(codec, 0, spec->am_entry[0].idx); +} + +/* + * Auto-Mute mode mixer enum support + */ +static int automute_mode_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + static const char * const texts3[] = { + "Disabled", "Speaker Only", "Line Out+Speaker" + }; + + if (spec->automute_speaker_possible && spec->automute_lo_possible) + return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} + +static int automute_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int val = 0; + if (spec->automute_speaker) + val++; + if (spec->automute_lo) + val++; + + ucontrol->value.enumerated.item[0] = val; + return 0; +} + +static int automute_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + switch (ucontrol->value.enumerated.item[0]) { + case 0: + if (!spec->automute_speaker && !spec->automute_lo) + return 0; + spec->automute_speaker = 0; + spec->automute_lo = 0; + break; + case 1: + if (spec->automute_speaker_possible) { + if (!spec->automute_lo && spec->automute_speaker) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 0; + } else if (spec->automute_lo_possible) { + if (spec->automute_lo) + return 0; + spec->automute_lo = 1; + } else + return -EINVAL; + break; + case 2: + if (!spec->automute_lo_possible || !spec->automute_speaker_possible) + return -EINVAL; + if (spec->automute_speaker && spec->automute_lo) + return 0; + spec->automute_speaker = 1; + spec->automute_lo = 1; + break; + default: + return -EINVAL; + } + call_update_outputs(codec); + return 1; +} + +static const struct snd_kcontrol_new automute_mode_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Auto-Mute Mode", + .info = automute_mode_info, + .get = automute_mode_get, + .put = automute_mode_put, +}; + +static int add_automute_mode_enum(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!add_kctl(spec, NULL, &automute_mode_enum)) + return -ENOMEM; + return 0; +} + +/* + * Check the availability of HP/line-out auto-mute; + * Set up appropriately if really supported + */ +static int check_auto_mute_availability(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int present = 0; + int i, err; + + if (cfg->hp_pins[0]) + present++; + if (cfg->line_out_pins[0]) + present++; + if (cfg->speaker_pins[0]) + present++; + if (present < 2) /* need two different output types */ + return 0; + + if (!cfg->speaker_pins[0] && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->speaker_outs = cfg->line_outs; + } + + if (!cfg->hp_pins[0] && + cfg->line_out_type == AUTO_PIN_HP_OUT) { + memcpy(cfg->hp_pins, cfg->line_out_pins, + sizeof(cfg->hp_pins)); + cfg->hp_outs = cfg->line_outs; + } + + for (i = 0; i < cfg->hp_outs; i++) { + hda_nid_t nid = cfg->hp_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", + nid); + snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, + hp_automute); + spec->detect_hp = 1; + } + + if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { + if (cfg->speaker_outs) + for (i = 0; i < cfg->line_outs; i++) { + hda_nid_t nid = cfg->line_out_pins[i]; + if (!is_jack_detectable(codec, nid)) + continue; + snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); + snd_hda_jack_detect_enable_callback(codec, nid, + HDA_GEN_FRONT_EVENT, + line_automute); + spec->detect_lo = 1; + } + spec->automute_lo_possible = spec->detect_hp; + } + + spec->automute_speaker_possible = cfg->speaker_outs && + (spec->detect_hp || spec->detect_lo); + + spec->automute_lo = spec->automute_lo_possible; + spec->automute_speaker = spec->automute_speaker_possible; + + if (spec->automute_speaker_possible || spec->automute_lo_possible) { + /* create a control for automute mode */ + err = add_automute_mode_enum(codec); + if (err < 0) + return err; + } + return 0; +} + +/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* check whether all auto-mic pins are valid; setup indices if OK */ +static bool auto_mic_check_imux(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + const struct hda_input_mux *imux; + int i; + + imux = &spec->input_mux; + for (i = 0; i < spec->am_num_entries; i++) { + spec->am_entry[i].idx = + find_idx_in_nid_list(spec->am_entry[i].pin, + spec->imux_pins, imux->num_items); + if (spec->am_entry[i].idx < 0) + return false; /* no corresponding imux */ + } + + /* we don't need the jack detection for the first pin */ + for (i = 1; i < spec->am_num_entries; i++) + snd_hda_jack_detect_enable_callback(codec, + spec->am_entry[i].pin, + HDA_GEN_MIC_EVENT, + mic_autoswitch); + return true; +} + +static int compare_attr(const void *ap, const void *bp) +{ + const struct automic_entry *a = ap; + const struct automic_entry *b = bp; + return (int)(a->attr - b->attr); +}
/* - * check whether the controls with the given name and direction suffix already exist + * Check the availability of auto-mic switch; + * Set up if really supported */ -static int check_existing_control(struct hda_codec *codec, const char *type, const char *dir) -{ - struct snd_ctl_elem_id id; - memset(&id, 0, sizeof(id)); - sprintf(id.name, "%s %s Volume", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; - sprintf(id.name, "%s %s Switch", type, dir); - id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; - if (snd_ctl_find_id(codec->bus->card, &id)) - return 1; +static int check_auto_mic_availability(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int types; + int i, num_pins; + + types = 0; + num_pins = 0; + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + unsigned int attr; + attr = snd_hda_codec_get_pincfg(codec, nid); + attr = snd_hda_get_input_pin_attr(attr); + if (types & (1 << attr)) + return 0; /* already occupied */ + switch (attr) { + case INPUT_PIN_ATTR_INT: + if (cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* invalid type */ + break; + case INPUT_PIN_ATTR_UNUSED: + return 0; /* invalid entry */ + default: + if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) + return 0; /* invalid type */ + if (!spec->line_in_auto_switch && + cfg->inputs[i].type != AUTO_PIN_MIC) + return 0; /* only mic is allowed */ + if (!is_jack_detectable(codec, nid)) + return 0; /* no unsol support */ + break; + } + if (num_pins >= MAX_AUTO_MIC_PINS) + return 0; + types |= (1 << attr); + spec->am_entry[num_pins].pin = nid; + spec->am_entry[num_pins].attr = attr; + num_pins++; + } + + if (num_pins < 2) + return 0; + + spec->am_num_entries = num_pins; + /* sort the am_entry in the order of attr so that the pin with a + * higher attr will be selected when the jack is plugged. + */ + sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), + compare_attr, NULL); + + if (!auto_mic_check_imux(codec)) + return 0; + + spec->auto_mic = 1; + spec->num_adc_nids = 1; + spec->cur_mux[0] = spec->am_entry[0].idx; + snd_printdd("hda-codec: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", + spec->am_entry[0].pin, + spec->am_entry[1].pin, + spec->am_entry[2].pin); + return 0; }
-/* - * build output mixer controls - */ -static int create_output_mixers(struct hda_codec *codec, - const char * const *names) -{ - struct hda_gspec *spec = codec->spec; - int i, err;
- for (i = 0; i < spec->pcm_vol_nodes; i++) { - err = create_mixer(codec, spec->pcm_vol[i].node, - spec->pcm_vol[i].index, - names[i], "Playback", 0); +/* parse the BIOS configuration and set up the hda_gen_spec */ +/* return 1 if successful, 0 if the proper config is not found, + * or a negative error code + */ +int snd_hda_gen_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int err; + + err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, + spec->parse_flags); + if (err < 0) + return err; + if (!cfg->line_outs) { + if (cfg->dig_outs || cfg->dig_in_pin) { + spec->multiout.max_channels = 2; + spec->no_analog = 1; + goto dig_only; + } + return 0; /* can't find valid BIOS pin config */ + } + + if (!spec->no_primary_hp && + cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && + cfg->line_outs <= cfg->hp_outs) { + /* use HP as primary out */ + cfg->speaker_outs = cfg->line_outs; + memcpy(cfg->speaker_pins, cfg->line_out_pins, + sizeof(cfg->speaker_pins)); + cfg->line_outs = cfg->hp_outs; + memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); + cfg->hp_outs = 0; + memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); + cfg->line_out_type = AUTO_PIN_HP_OUT; + } + + err = parse_output_paths(codec); + if (err < 0) + return err; + err = create_multi_channel_mode(codec); + if (err < 0) + return err; + err = create_multi_out_ctls(codec, cfg); + if (err < 0) + return err; + err = create_hp_out_ctls(codec); + if (err < 0) + return err; + err = create_speaker_out_ctls(codec); + if (err < 0) + return err; + err = create_shared_input(codec); + if (err < 0) + return err; + err = create_input_ctls(codec); + if (err < 0) + return err; + + /* check the multiple speaker pins */ + if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + spec->const_channel_count = cfg->line_outs * 2; + else + spec->const_channel_count = cfg->speaker_outs * 2; + + if (spec->multi_ios > 0) + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count); + else + spec->multiout.max_channels = spec->multiout.num_dacs * 2; + + err = check_auto_mute_availability(codec); + if (err < 0) + return err; + + err = check_dyn_adc_switch(codec); + if (err < 0) + return err; + + if (!spec->shared_mic_hp) { + err = check_auto_mic_availability(codec); if (err < 0) return err; } - return 0; -}
-static int build_output_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - static const char * const types_speaker[] = { "Speaker", "Headphone" }; - static const char * const types_line[] = { "Front", "Headphone" }; + err = create_capture_mixers(codec); + if (err < 0) + return err;
- switch (spec->pcm_vol_nodes) { - case 1: - return create_mixer(codec, spec->pcm_vol[0].node, - spec->pcm_vol[0].index, - "Master", "Playback", 0); - case 2: - if (defcfg_type(spec->out_pin_node[0]) == AC_JACK_SPEAKER) - return create_output_mixers(codec, types_speaker); - else - return create_output_mixers(codec, types_line); - } - return 0; + err = parse_mic_boost(codec); + if (err < 0) + return err; + + dig_only: + parse_digital(codec); + + return 1; } +EXPORT_SYMBOL_HDA(snd_hda_gen_parse_auto_config);
-/* create capture volume/switch */ -static int build_input_controls(struct hda_codec *codec) -{ - struct hda_gspec *spec = codec->spec; - struct hda_gnode *adc_node = spec->adc_node; - int i, err; - static struct snd_kcontrol_new cap_sel = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = capture_source_info, - .get = capture_source_get, - .put = capture_source_put, - };
- if (! adc_node || ! spec->input_mux.num_items) - return 0; /* not found */ +/* + * Build control elements + */ + +/* slave controls for virtual master */ +static const char * const slave_pfxs[] = { + "Front", "Surround", "Center", "LFE", "Side", + "Headphone", "Speaker", "Mono", "Line Out", + "CLFE", "Bass Speaker", "PCM", + NULL, +}; + +int snd_hda_gen_build_controls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int err;
- spec->cur_cap_src = 0; - select_input_connection(codec, adc_node, - spec->input_mux.items[0].index); + err = snd_hda_add_new_ctls(codec, spec->kctls.list); + if (err < 0) + return err;
- /* create capture volume and switch controls if the ADC has an amp */ - /* do we have only a single item? */ - if (spec->input_mux.num_items == 1) { - err = create_mixer(codec, adc_node, - spec->input_mux.items[0].index, - NULL, "Capture", 0); + if (spec->multiout.dig_out_nid) { + err = snd_hda_create_dig_out_ctls(codec, + spec->multiout.dig_out_nid, + spec->multiout.dig_out_nid, + spec->pcm_rec[1].pcm_type); + if (err < 0) + return err; + if (!spec->no_analog) { + err = snd_hda_create_spdif_share_sw(codec, + &spec->multiout); + if (err < 0) + return err; + spec->multiout.share_spdif = 1; + } + } + if (spec->dig_in_nid) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); if (err < 0) return err; - return 0; }
- /* create input MUX if multiple sources are available */ - err = snd_hda_ctl_add(codec, spec->adc_node->nid, - snd_ctl_new1(&cap_sel, codec)); - if (err < 0) - return err; + /* if we have no master control, let's create it */ + if (!spec->no_analog && + !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { + unsigned int vmaster_tlv[4]; + snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, + HDA_OUTPUT, vmaster_tlv); + err = snd_hda_add_vmaster(codec, "Master Playback Volume", + vmaster_tlv, slave_pfxs, + "Playback Volume"); + if (err < 0) + return err; + } + if (!spec->no_analog && + !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { + err = __snd_hda_add_vmaster(codec, "Master Playback Switch", + NULL, slave_pfxs, + "Playback Switch", + true, &spec->vmaster_mute.sw_kctl); + if (err < 0) + return err; + if (spec->vmaster_mute.hook) + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + }
- /* no volume control? */ - if (! (adc_node->wid_caps & AC_WCAP_IN_AMP) || - ! (adc_node->amp_in_caps & AC_AMPCAP_NUM_STEPS)) - return 0; + free_kctls(spec); /* no longer needed */
- for (i = 0; i < spec->input_mux.num_items; i++) { - struct snd_kcontrol_new knew; - char name[32]; - sprintf(name, "%s Capture Volume", - spec->input_mux.items[i].label); - knew = (struct snd_kcontrol_new) - HDA_CODEC_VOLUME(name, adc_node->nid, - spec->input_mux.items[i].index, - HDA_INPUT); - err = snd_hda_ctl_add(codec, adc_node->nid, - snd_ctl_new1(&knew, codec)); + if (spec->shared_mic_hp) { + int err; + int nid = spec->autocfg.inputs[1].pin; + err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); + if (err < 0) + return err; + err = snd_hda_jack_detect_enable(codec, nid, 0); if (err < 0) return err; }
+ err = snd_hda_jack_add_kctls(codec, &spec->autocfg); + if (err < 0) + return err; + return 0; } +EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls);
/* - * parse the nodes recursively until reach to the output PIN. - * - * returns 0 - if not found, - * 1 - if found, but no mixer is created - * 2 - if found and mixer was already created, (just skip) - * a negative error code + * PCM definitions */ -static int parse_loopback_path(struct hda_codec *codec, struct hda_gspec *spec, - struct hda_gnode *node, struct hda_gnode *dest_node, - const char *type) -{ - int i, err;
- if (node->checked) - return 0; +/* + * Analog playback callbacks + */ +static int playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +}
- node->checked = 1; - if (node == dest_node) { - /* loopback connection found */ - return 1; - } +static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +}
- for (i = 0; i < node->nconns; i++) { - struct hda_gnode *child = hda_get_node(spec, node->conn_list[i]); - if (! child) - continue; - err = parse_loopback_path(codec, spec, child, dest_node, type); - if (err < 0) - return err; - else if (err >= 1) { - if (err == 1) { - err = create_mixer(codec, node, i, type, - "Playback", 1); - if (err < 0) - return err; - if (err > 0) - return 2; /* ok, created */ - /* not created, maybe in the lower path */ - err = 1; - } - /* connect and unmute */ - if (node->nconns > 1) - select_input_connection(codec, node, i); - unmute_input(codec, node, i); - unmute_output(codec, node); - return err; - } - } - return 0; +static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); }
/* - * parse the tree and build the loopback controls + * Digital out */ -static int build_loopback_controls(struct hda_codec *codec) +static int dig_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - struct hda_gspec *spec = codec->spec; - struct hda_gnode *node; - int err; - const char *type; + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_open(codec, &spec->multiout); +}
- if (! spec->out_pin_node[0]) - return 0; +static int dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, + stream_tag, format, substream); +}
- list_for_each_entry(node, &spec->nid_list, list) { - if (node->type != AC_WID_PIN) - continue; - /* input capable? */ - if (! (node->pin_caps & AC_PINCAP_IN)) - return 0; - type = get_input_type(node, NULL); - if (type) { - if (check_existing_control(codec, type, "Playback")) - continue; - clear_check_flags(spec); - err = parse_loopback_path(codec, spec, - spec->out_pin_node[0], - node, type); - if (err < 0) - return err; - if (! err) - continue; - } - } - return 0; +static int dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); +} + +static int dig_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_dig_close(codec, &spec->multiout); }
/* - * build mixer controls + * Analog capture */ -static int build_generic_controls(struct hda_codec *codec) +static int alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) { - int err; + struct hda_gen_spec *spec = codec->spec;
- if ((err = build_input_controls(codec)) < 0 || - (err = build_output_controls(codec)) < 0 || - (err = build_loopback_controls(codec)) < 0) - return err; + snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], + stream_tag, 0, format); + return 0; +} + +static int alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec;
+ snd_hda_codec_cleanup_stream(codec, + spec->adc_nids[substream->number + 1]); return 0; }
/* - * PCM */ -static struct hda_pcm_stream generic_pcm_playback = { +static const struct hda_pcm_stream pcm_analog_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 8, + /* NID is set in build_pcms */ + .ops = { + .open = playback_pcm_open, + .prepare = playback_pcm_prepare, + .cleanup = playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_analog_capture = { .substreams = 1, .channels_min = 2, .channels_max = 2, + /* NID is set in build_pcms */ };
-static int generic_pcm2_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) +static const struct hda_pcm_stream pcm_analog_alt_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ +}; + +static const struct hda_pcm_stream pcm_analog_alt_capture = { + .substreams = 2, /* can be overridden */ + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ + .ops = { + .prepare = alt_capture_pcm_prepare, + .cleanup = alt_capture_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_digital_playback = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ + .ops = { + .open = dig_playback_pcm_open, + .close = dig_playback_pcm_close, + .prepare = dig_playback_pcm_prepare, + .cleanup = dig_playback_pcm_cleanup + }, +}; + +static const struct hda_pcm_stream pcm_digital_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + /* NID is set in build_pcms */ +}; + +/* Used by build_pcms to flag that a PCM has no playback stream */ +static const struct hda_pcm_stream pcm_null_stream = { + .substreams = 0, + .channels_min = 0, + .channels_max = 0, +}; + +/* + * dynamic changing ADC PCM streams + */ +static bool dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) { - struct hda_gspec *spec = codec->spec; + struct hda_gen_spec *spec = codec->spec; + hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; + + if (spec->cur_adc && spec->cur_adc != new_adc) { + /* stream is running, let's swap the current ADC */ + __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + return true; + } + return false; +}
- snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); - snd_hda_codec_setup_stream(codec, spec->dac_node[1]->nid, - stream_tag, 0, format); +/* analog capture with dynamic dual-adc changes */ +static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; + spec->cur_adc_stream_tag = stream_tag; + spec->cur_adc_format = format; + snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); return 0; }
-static int generic_pcm2_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) +static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) { - struct hda_gspec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, hinfo->nid); - snd_hda_codec_cleanup_stream(codec, spec->dac_node[1]->nid); + struct hda_gen_spec *spec = codec->spec; + snd_hda_codec_cleanup_stream(codec, spec->cur_adc); + spec->cur_adc = 0; return 0; }
-static int build_generic_pcms(struct hda_codec *codec) +static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = dyn_adc_capture_pcm_prepare, + .cleanup = dyn_adc_capture_pcm_cleanup + }, +}; + +/* build PCM streams based on the parsed results */ +int snd_hda_gen_build_pcms(struct hda_codec *codec) { - struct hda_gspec *spec = codec->spec; - struct hda_pcm *info = &spec->pcm_rec; + struct hda_gen_spec *spec = codec->spec; + struct hda_pcm *info = spec->pcm_rec; + const struct hda_pcm_stream *p; + bool have_multi_adcs; + int i; + + codec->num_pcms = 1; + codec->pcm_info = info; + + if (spec->no_analog) + goto skip_analog; + + snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), + "%s Analog", codec->chip_name); + info->name = spec->stream_name_analog; + + if (spec->multiout.num_dacs > 0) { + p = spec->stream_analog_playback; + if (!p) + p = &pcm_analog_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = + spec->multiout.max_channels; + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && + spec->autocfg.line_outs == 2) + info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = + snd_pcm_2_1_chmaps; + } + if (spec->num_adc_nids) { + p = spec->stream_analog_capture; + if (!p) { + if (spec->dyn_adc_switch) + p = &dyn_adc_pcm_analog_capture; + else + p = &pcm_analog_capture; + } + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; + } + + if (spec->channel_mode) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; + for (i = 0; i < spec->num_channel_mode; i++) { + if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; + } + } + } + + skip_analog: + /* SPDIF for stream index #1 */ + if (spec->multiout.dig_out_nid || spec->dig_in_nid) { + snprintf(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + "%s Digital", codec->chip_name); + codec->num_pcms = 2; + codec->slave_dig_outs = spec->multiout.slave_dig_outs; + info = spec->pcm_rec + 1; + info->name = spec->stream_name_digital; + if (spec->dig_out_type) + info->pcm_type = spec->dig_out_type; + else + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->multiout.dig_out_nid) { + p = spec->stream_digital_playback; + if (!p) + p = &pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; + } + if (spec->dig_in_nid) { + p = spec->stream_digital_capture; + if (!p) + p = &pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; + } + }
- if (! spec->dac_node[0] && ! spec->adc_node) { - snd_printd("hda_generic: no PCM found\n"); + if (spec->no_analog) return 0; + + /* If the use of more than one ADC is requested for the current + * model, configure a second analog capture-only PCM. + */ + have_multi_adcs = (spec->num_adc_nids > 1) && + !spec->dyn_adc_switch && !spec->auto_mic; + /* Additional Analaog capture for index #2 */ + if (spec->alt_dac_nid || have_multi_adcs) { + codec->num_pcms = 3; + info = spec->pcm_rec + 2; + info->name = spec->stream_name_analog; + if (spec->alt_dac_nid) { + p = spec->stream_analog_alt_playback; + if (!p) + p = &pcm_analog_alt_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = + spec->alt_dac_nid; + } else { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; + } + if (have_multi_adcs) { + p = spec->stream_analog_alt_capture; + if (!p) + p = &pcm_analog_alt_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = + spec->adc_nids[1]; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = + spec->num_adc_nids - 1; + } else { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + pcm_null_stream; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; + } }
- codec->num_pcms = 1; - codec->pcm_info = info; + return 0; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); + + +/* + * Standard auto-parser initializations + */ + +/* configure the path from the given dac to the pin as the proper output */ +static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, + int pin_type, hda_nid_t dac) +{ + struct nid_path *path; + + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); + path = snd_hda_get_nid_path(codec, dac, pin); + if (!path) + return; + if (path->active) + return; + snd_hda_activate_path(codec, path, true, true); +} + +/* initialize primary output paths */ +static void init_multi_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int pin_type; + int i; + + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + pin_type = PIN_HP; + else + pin_type = PIN_OUT; + + for (i = 0; i <= HDA_SIDE; i++) { + hda_nid_t nid = spec->autocfg.line_out_pins[i]; + if (nid) + set_output_and_unmute(codec, nid, pin_type, + spec->multiout.dac_nids[i]); + + } +} + +/* initialize hp and speaker paths */ +static void init_extra_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t pin, dac; + + for (i = 0; i < spec->autocfg.hp_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) + break; + pin = spec->autocfg.hp_pins[i]; + if (!pin) + break; + dac = spec->multiout.hp_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.hp_out_nid[0]) + dac = spec->multiout.hp_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + set_output_and_unmute(codec, pin, PIN_HP, dac); + } + for (i = 0; i < spec->autocfg.speaker_outs; i++) { + if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) + break; + pin = spec->autocfg.speaker_pins[i]; + if (!pin) + break; + dac = spec->multiout.extra_out_nid[i]; + if (!dac) { + if (i > 0 && spec->multiout.extra_out_nid[0]) + dac = spec->multiout.extra_out_nid[0]; + else + dac = spec->multiout.dac_nids[0]; + } + set_output_and_unmute(codec, pin, PIN_OUT, dac); + } +} + +/* initialize multi-io paths */ +static void init_multi_io(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + + for (i = 0; i < spec->multi_ios; i++) { + hda_nid_t pin = spec->multi_io[i].pin; + struct nid_path *path; + path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin); + if (!path) + continue; + if (!spec->multi_io[i].ctl_in) + spec->multi_io[i].ctl_in = + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_activate_path(codec, path, path->active, true); + } +} + +/* set up the input pin config, depending on the given auto-pin type */ +static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, + int auto_pin_type) +{ + unsigned int val = PIN_IN; + if (auto_pin_type == AUTO_PIN_MIC) + val |= snd_hda_get_default_vref(codec, nid); + snd_hda_set_pin_ctl(codec, nid, val); +} + +/* set up input pins and loopback paths */ +static void init_analog_input(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + int i; + + for (i = 0; i < cfg->num_inputs; i++) { + hda_nid_t nid = cfg->inputs[i].pin; + if (is_input_pin(codec, nid)) + set_input_pin(codec, nid, cfg->inputs[i].type); + + /* init loopback inputs */ + if (spec->mixer_nid) { + struct nid_path *path; + path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid); + if (path) + snd_hda_activate_path(codec, path, + path->active, false); + } + } +} + +/* initialize ADC paths */ +static void init_input_src(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + struct hda_input_mux *imux = &spec->input_mux; + struct nid_path *path; + int i, c, nums;
- info->name = "HDA Generic"; - if (spec->dac_node[0]) { - info->stream[0] = generic_pcm_playback; - info->stream[0].nid = spec->dac_node[0]->nid; - if (spec->dac_node[1]) { - info->stream[0].ops.prepare = generic_pcm2_prepare; - info->stream[0].ops.cleanup = generic_pcm2_cleanup; + if (spec->dyn_adc_switch) + nums = 1; + else + nums = spec->num_adc_nids; + + for (c = 0; c < nums; c++) { + for (i = 0; i < imux->num_items; i++) { + path = snd_hda_get_nid_path(codec, spec->imux_pins[i], + get_adc_nid(codec, c, i)); + if (path) { + bool active = path->active; + if (i == spec->cur_mux[c]) + active = true; + snd_hda_activate_path(codec, path, active, false); + } } } - if (spec->adc_node) { - info->stream[1] = generic_pcm_playback; - info->stream[1].nid = spec->adc_node->nid; + + if (spec->shared_mic_hp) + update_shared_mic_hp(codec, spec->cur_mux[0]); + + if (spec->cap_sync_hook) + spec->cap_sync_hook(codec); +} + +/* set right pin controls for digital I/O */ +static void init_digital(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + int i; + hda_nid_t pin; + + for (i = 0; i < spec->autocfg.dig_outs; i++) { + pin = spec->autocfg.dig_out_pins[i]; + if (!pin) + continue; + set_output_and_unmute(codec, pin, PIN_OUT, 0); } + pin = spec->autocfg.dig_in_pin; + if (pin) + snd_hda_set_pin_ctl(codec, pin, PIN_IN); +} + +int snd_hda_gen_init(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->init_hook) + spec->init_hook(codec); + + snd_hda_apply_verbs(codec); + + init_multi_out(codec); + init_extra_out(codec); + init_multi_io(codec); + init_analog_input(codec); + init_input_src(codec); + init_digital(codec);
+ /* call init functions of standard auto-mute helpers */ + hp_automute(codec, NULL); + line_automute(codec, NULL); + mic_autoswitch(codec, NULL); + + if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) + snd_hda_sync_vmaster_hook(&spec->vmaster_mute); + + hda_call_check_power_status(codec, 0x01); return 0; } +EXPORT_SYMBOL(snd_hda_gen_init); + + +/* + * the generic codec support + */
#ifdef CONFIG_PM static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - struct hda_gspec *spec = codec->spec; + struct hda_gen_spec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } #endif
+static void generic_free(struct hda_codec *codec) +{ + snd_hda_gen_spec_free(codec->spec); + kfree(codec->spec); + codec->spec = NULL; +}
-/* - */ -static struct hda_codec_ops generic_patch_ops = { - .build_controls = build_generic_controls, - .build_pcms = build_generic_pcms, - .free = snd_hda_generic_free, +static const struct hda_codec_ops generic_patch_ops = { + .build_controls = snd_hda_gen_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = snd_hda_gen_init, + .free = generic_free, + .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .check_power_status = generic_check_power_status, #endif };
-/* - * the generic parser - */ int snd_hda_parse_generic_codec(struct hda_codec *codec) { - struct hda_gspec *spec; + struct hda_gen_spec *spec; int err;
- if(!codec->afg) - return 0; - spec = kzalloc(sizeof(*spec), GFP_KERNEL); - if (spec == NULL) { - printk(KERN_ERR "hda_generic: can't allocate spec\n"); + if (!spec) return -ENOMEM; - } + snd_hda_gen_spec_init(spec); codec->spec = spec; - INIT_LIST_HEAD(&spec->nid_list); - - if ((err = build_afg_tree(codec)) < 0) - goto error;
- if ((err = parse_input(codec)) < 0 || - (err = parse_output(codec)) < 0) + err = snd_hda_gen_parse_auto_config(codec, NULL); + if (err < 0) goto error;
codec->patch_ops = generic_patch_ops; - return 0;
- error: - snd_hda_generic_free(codec); +error: + generic_free(codec); return err; } EXPORT_SYMBOL(snd_hda_parse_generic_codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h new file mode 100644 index 0000000..a9f4f63 --- /dev/null +++ b/sound/pci/hda/hda_generic.h @@ -0,0 +1,199 @@ +/* + * Generic BIOS auto-parser helper functions for HD-audio + * + * Copyright (c) 2012 Takashi Iwai tiwai@suse.de + * + * This driver is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __SOUND_HDA_GENERIC_H +#define __SOUND_HDA_GENERIC_H + +/* unsol event tags */ +enum { + HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, + HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT +}; + +/* table entry for multi-io paths */ +struct hda_multi_io { + hda_nid_t pin; /* multi-io widget pin NID */ + hda_nid_t dac; /* DAC to be connected */ + unsigned int ctl_in; /* cached input-pin control value */ +}; + +/* Widget connection path + * + * For output, stored in the order of DAC -> ... -> pin, + * for input, pin -> ... -> ADC. + * + * idx[i] contains the source index number to select on of the widget path[i]; + * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget + * multi[] indicates whether it's a selector widget with multi-connectors + * (i.e. the connection selection is mandatory) + * vol_ctl and mute_ctl contains the NIDs for the assigned mixers + */ + +#define MAX_NID_PATH_DEPTH 5 + +enum { + NID_PATH_VOL_CTL, + NID_PATH_MUTE_CTL, + NID_PATH_BOOST_CTL, + NID_PATH_NUM_CTLS +}; + +struct nid_path { + int depth; + hda_nid_t path[MAX_NID_PATH_DEPTH]; + unsigned char idx[MAX_NID_PATH_DEPTH]; + unsigned char multi[MAX_NID_PATH_DEPTH]; + unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ + bool active; +}; + +/* mic/line-in auto switching entry */ + +#define MAX_AUTO_MIC_PINS 3 + +struct automic_entry { + hda_nid_t pin; /* pin */ + int idx; /* imux index, -1 = invalid */ + unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ +}; + +struct hda_gen_spec { + char stream_name_analog[32]; /* analog PCM stream */ + const struct hda_pcm_stream *stream_analog_playback; + const struct hda_pcm_stream *stream_analog_capture; + const struct hda_pcm_stream *stream_analog_alt_playback; + const struct hda_pcm_stream *stream_analog_alt_capture; + + char stream_name_digital[32]; /* digital PCM stream */ + const struct hda_pcm_stream *stream_digital_playback; + const struct hda_pcm_stream *stream_digital_capture; + + /* playback */ + struct hda_multi_out multiout; /* playback set-up + * max_channels, dacs must be set + * dig_out_nid and hp_nid are optional + */ + hda_nid_t alt_dac_nid; + hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ + int dig_out_type; + + /* capture */ + unsigned int num_adc_nids; + hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t dig_in_nid; /* digital-in NID; optional */ + hda_nid_t mixer_nid; /* analog-mixer NID */ + + /* capture setup for dynamic dual-adc switch */ + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + + /* capture source */ + struct hda_input_mux input_mux; + unsigned int cur_mux[3]; + + /* channel model */ + const struct hda_channel_mode *channel_mode; + int num_channel_mode; + int const_channel_count; /* min. channel count (for speakers) */ + int ext_channel_count; /* current channel count for multi-io */ + + /* PCM information */ + struct hda_pcm pcm_rec[3]; /* used in build_pcms() */ + + /* dynamic controls, init_verbs and input_mux */ + struct auto_pin_cfg autocfg; + struct snd_array kctls; + hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; + hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; + unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; + hda_nid_t shared_mic_vref_pin; + + /* DAC list */ + int num_all_dacs; + hda_nid_t all_dacs[16]; + + /* path list */ + struct snd_array paths; + + /* auto-mic stuff */ + int am_num_entries; + struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; + + /* for pin sensing */ + unsigned int hp_jack_present:1; + unsigned int line_jack_present:1; + unsigned int master_mute:1; + unsigned int auto_mic:1; + unsigned int automute_speaker:1; /* automute speaker outputs */ + unsigned int automute_lo:1; /* automute LO outputs */ + unsigned int detect_hp:1; /* Headphone detection enabled */ + unsigned int detect_lo:1; /* Line-out detection enabled */ + unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ + unsigned int automute_lo_possible:1; /* there are line outs and HP */ + unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + + /* other flags */ + unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ + unsigned int no_analog:1; /* digital I/O only */ + unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ + unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ + unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ + unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ + unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ + + unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ + + /* for virtual master */ + hda_nid_t vmaster_nid; + struct hda_vmaster_mute_hook vmaster_mute; +#ifdef CONFIG_PM + struct hda_loopback_check loopback; + int num_loopbacks; + struct hda_amp_list loopback_list[8]; +#endif + + /* multi-io */ + int multi_ios; + struct hda_multi_io multi_io[4]; + + /* bind volumes */ + struct snd_array bind_ctls; + + /* hooks */ + void (*init_hook)(struct hda_codec *codec); + void (*automute_hook)(struct hda_codec *codec); + void (*cap_sync_hook)(struct hda_codec *codec); +}; + +int snd_hda_gen_spec_init(struct hda_gen_spec *spec); +void snd_hda_gen_spec_free(struct hda_gen_spec *spec); + +int snd_hda_gen_init(struct hda_codec *codec); + +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid); +bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix, + struct nid_path *path); +struct nid_path * +snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, + hda_nid_t to_nid, int with_aa_mix); +void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, + bool enable, bool add_aamix); + +int snd_hda_gen_parse_auto_config(struct hda_codec *codec, + const hda_nid_t *ignore_nids); +int snd_hda_gen_build_controls(struct hda_codec *codec); +int snd_hda_gen_build_pcms(struct hda_codec *codec); + +#endif /* __SOUND_HDA_GENERIC_H */
Enable EAPD in output path initializations automatically unless the new flag spec->own_eapd_ctl is set.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 7 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2d19b91..31c5677 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3333,6 +3333,7 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac) { + struct hda_gen_spec *spec = codec->spec; struct nid_path *path;
snd_hda_set_pin_ctl_cache(codec, pin, pin_type); @@ -3342,6 +3343,11 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, if (path->active) return; snd_hda_activate_path(codec, path, true, true); + + if (!spec->own_eapd_ctl && + (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, 0x02); }
/* initialize primary output paths */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a9f4f63..9c00bd5 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -150,6 +150,7 @@ struct hda_gen_spec { unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ + unsigned int own_eapd_ctl:1; /* set EAPD by own function */
unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
It may be used in other codec drivers, so let it free.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 19 ++++++++++--------- sound/pci/hda/hda_generic.h | 4 ++++ 2 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 31c5677..49e968c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -43,9 +43,9 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init);
-static struct snd_kcontrol_new * -add_kctl(struct hda_gen_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) +struct snd_kcontrol_new * +snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp) { struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); if (!knew) @@ -59,6 +59,7 @@ add_kctl(struct hda_gen_spec *spec, const char *name, return NULL; return knew; } +EXPORT_SYMBOL_HDA(snd_hda_gen_add_kctl);
static void free_kctls(struct hda_gen_spec *spec) { @@ -548,7 +549,7 @@ static int add_control(struct hda_gen_spec *spec, int type, const char *name, { struct snd_kcontrol_new *knew;
- knew = add_kctl(spec, name, &control_templates[type]); + knew = snd_hda_gen_add_kctl(spec, name, &control_templates[type]); if (!knew) return -ENOMEM; knew->index = cidx; @@ -1527,7 +1528,7 @@ static int create_multi_channel_mode(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec;
if (spec->multi_ios > 0) { - if (!add_kctl(spec, NULL, &channel_mode_enum)) + if (!snd_hda_gen_add_kctl(spec, NULL, &channel_mode_enum)) return -ENOMEM; } return 0; @@ -2086,7 +2087,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, struct snd_kcontrol_new *knew;
if (vol_ctl) { - knew = add_kctl(spec, NULL, &cap_vol_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_vol_temp); if (!knew) return -ENOMEM; knew->index = idx; @@ -2094,7 +2095,7 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, knew->subdevice = HDA_SUBDEV_AMP_FLAG; } if (sw_ctl) { - knew = add_kctl(spec, NULL, &cap_sw_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_sw_temp); if (!knew) return -ENOMEM; knew->index = idx; @@ -2171,7 +2172,7 @@ static int create_capture_mixers(struct hda_codec *codec)
if (!spec->auto_mic && imux->num_items > 1) { struct snd_kcontrol_new *knew; - knew = add_kctl(spec, NULL, &cap_src_temp); + knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums; @@ -2592,7 +2593,7 @@ static int add_automute_mode_enum(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec;
- if (!add_kctl(spec, NULL, &automute_mode_enum)) + if (!snd_hda_gen_add_kctl(spec, NULL, &automute_mode_enum)) return -ENOMEM; return 0; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 9c00bd5..d71e86d 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -192,6 +192,10 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix);
+struct snd_kcontrol_new * +snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, + const struct snd_kcontrol_new *temp); + int snd_hda_gen_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ignore_nids); int snd_hda_gen_build_controls(struct hda_codec *codec);
In some cases, we want to manipulate the auto_pin_cfg table before passing to snd_hda_gen_parse_auto_config() (e.g. Realtek SSID check code fiddles with the headphone pin). Also passing ignore_pins just for snd_hda_parse_pin_defcfg() isn't good.
In this patch, snd_hda_gen_parse_auto_config() is changed to receive the auto_pin_cfg table to be parsed. The passed auto_pin_cfg table must have been initialized (typically by calling snd_hda_gen_parse_auto_config()) beforehand by the caller.
Also together with this change, spec->parse_flags is also removed. Since this was referred only at the place calling snd_hda_parse_pin_defcfg(), no longer needed to be kept in spec.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 24 +++++++++++++++--------- sound/pci/hda/hda_generic.h | 4 +--- 2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 49e968c..e512cab 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2785,21 +2785,23 @@ static int check_auto_mic_availability(struct hda_codec *codec) }
-/* parse the BIOS configuration and set up the hda_gen_spec */ -/* return 1 if successful, 0 if the proper config is not found, +/* + * Parse the given BIOS configuration and set up the hda_gen_spec + * + * return 1 if successful, 0 if the proper config is not found, * or a negative error code */ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids) + struct auto_pin_cfg *cfg) { struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; int err;
- err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, - spec->parse_flags); - if (err < 0) - return err; + if (cfg != &spec->autocfg) { + spec->autocfg = *cfg; + cfg = &spec->autocfg; + } + if (!cfg->line_outs) { if (cfg->dig_outs || cfg->dig_in_pin) { spec->multiout.max_channels = 2; @@ -3586,7 +3588,11 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) snd_hda_gen_spec_init(spec); codec->spec = spec;
- err = snd_hda_gen_parse_auto_config(codec, NULL); + err = snd_hda_parse_pin_defcfg(codec, &spec->autocfg, NULL, 0); + if (err < 0) + return err; + + err = snd_hda_gen_parse_auto_config(codec, &spec->autocfg); if (err < 0) goto error;
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d71e86d..1a3b404 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -152,8 +152,6 @@ struct hda_gen_spec { unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */
- unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */ - /* for virtual master */ hda_nid_t vmaster_nid; struct hda_vmaster_mute_hook vmaster_mute; @@ -197,7 +195,7 @@ snd_hda_gen_add_kctl(struct hda_gen_spec *spec, const char *name, const struct snd_kcontrol_new *temp);
int snd_hda_gen_parse_auto_config(struct hda_codec *codec, - const hda_nid_t *ignore_nids); + struct auto_pin_cfg *cfg); int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_pcms(struct hda_codec *codec);
When no controls are assigned in the parser (e.g. no analog path), spec->kctls.list is still NULL. We need to check it before passing to snd_hda_add_new_ctls().
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index e512cab..364ec06 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2906,9 +2906,11 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) struct hda_gen_spec *spec = codec->spec; int err;
- err = snd_hda_add_new_ctls(codec, spec->kctls.list); - if (err < 0) - return err; + if (spec->kctls.used) { + err = snd_hda_add_new_ctls(codec, spec->kctls.list); + if (err < 0) + return err; + }
if (spec->multiout.dig_out_nid) { err = snd_hda_create_dig_out_ctls(codec,
These handlers are supposed to be called externally from the codec drivers once when they need to handle own jack events.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 26 +++++++++++++++----------- sound/pci/hda/hda_generic.h | 9 +++++++++ 2 files changed, 24 insertions(+), 11 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 364ec06..6914d70 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2414,7 +2414,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, }
/* Toggle outputs muting */ -static void update_outputs(struct hda_codec *codec) +void snd_hda_gen_update_outputs(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; int on; @@ -2448,6 +2448,7 @@ static void update_outputs(struct hda_codec *codec) do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), spec->autocfg.line_out_pins, on, false); } +EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
static void call_update_outputs(struct hda_codec *codec) { @@ -2455,11 +2456,11 @@ static void call_update_outputs(struct hda_codec *codec) if (spec->automute_hook) spec->automute_hook(codec); else - update_outputs(codec); + snd_hda_gen_update_outputs(codec); }
/* standard HP-automute helper */ -static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec;
@@ -2470,9 +2471,10 @@ static void hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) return; call_update_outputs(codec); } +EXPORT_SYMBOL_HDA(snd_hda_gen_hp_automute);
/* standard line-out-automute helper */ -static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec;
@@ -2489,9 +2491,10 @@ static void line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) return; call_update_outputs(codec); } +EXPORT_SYMBOL_HDA(snd_hda_gen_line_automute);
/* standard mic auto-switch helper */ -static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) { struct hda_gen_spec *spec = codec->spec; int i; @@ -2507,6 +2510,7 @@ static void mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack) } mux_select(codec, 0, spec->am_entry[0].idx); } +EXPORT_SYMBOL_HDA(snd_hda_gen_mic_autoswitch);
/* * Auto-Mute mode mixer enum support @@ -2639,7 +2643,7 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, - hp_automute); + snd_hda_gen_hp_automute); spec->detect_hp = 1; }
@@ -2652,7 +2656,7 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_FRONT_EVENT, - line_automute); + snd_hda_gen_line_automute); spec->detect_lo = 1; } spec->automute_lo_possible = spec->detect_hp; @@ -2704,7 +2708,7 @@ static bool auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, HDA_GEN_MIC_EVENT, - mic_autoswitch); + snd_hda_gen_mic_autoswitch); return true; }
@@ -3536,9 +3540,9 @@ int snd_hda_gen_init(struct hda_codec *codec) init_digital(codec);
/* call init functions of standard auto-mute helpers */ - hp_automute(codec, NULL); - line_automute(codec, NULL); - mic_autoswitch(codec, NULL); + snd_hda_gen_hp_automute(codec, NULL); + snd_hda_gen_line_automute(codec, NULL); + snd_hda_gen_mic_autoswitch(codec, NULL);
if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1a3b404..417ab65 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -199,4 +199,13 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, int snd_hda_gen_build_controls(struct hda_codec *codec); int snd_hda_gen_build_pcms(struct hda_codec *codec);
+/* standard jack event callbacks */ +void snd_hda_gen_hp_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_line_automute(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, + struct hda_jack_tbl *jack); +void snd_hda_gen_update_outputs(struct hda_codec *codec); + #endif /* __SOUND_HDA_GENERIC_H */
The next migration step is to use the common code in generic driver for Realtek driver. This is no drastic change and there should be no real functional changes, as the generic parser code comes from Realtek driver originally.
As Realtek driver requires the generic parser code, it needs a reverse-selection of CONFIG_SND_HDA_GENERIC kconfig.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/Kconfig | 1 + sound/pci/hda/patch_realtek.c | 4102 +++-------------------------------------- 2 files changed, 298 insertions(+), 3805 deletions(-)
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig index 6eeb889..ebec1b7 100644 --- a/sound/pci/hda/Kconfig +++ b/sound/pci/hda/Kconfig @@ -86,6 +86,7 @@ config SND_HDA_PATCH_LOADER config SND_HDA_CODEC_REALTEK bool "Build Realtek HD-audio codec support" default y + select SND_HDA_GENERIC help Say Y here to include Realtek HD-audio codec support in snd-hda-intel driver, such as ALC880. diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 2240ab6..17da84d 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -36,12 +36,10 @@ #include "hda_auto_parser.h" #include "hda_beep.h" #include "hda_jack.h" +#include "hda_generic.h"
/* unsol event tags */ -#define ALC_FRONT_EVENT 0x01 -#define ALC_DCVOL_EVENT 0x02 -#define ALC_HP_EVENT 0x04 -#define ALC_MIC_EVENT 0x08 +#define ALC_DCVOL_EVENT 0x08
/* for GPIO Poll */ #define GPIO_MASK 0x03 @@ -68,12 +66,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ };
-struct alc_multi_io { - hda_nid_t pin; /* multi-io widget pin NID */ - hda_nid_t dac; /* DAC to be connected */ - unsigned int ctl_in; /* cached input-pin control value */ -}; - /* make compatible with old code */ #define alc_apply_pincfgs snd_hda_apply_pincfgs #define alc_apply_fixup snd_hda_apply_fixup @@ -91,112 +83,21 @@ struct alc_multi_io { #define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT #define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD
-#define MAX_AUTO_MIC_PINS 3 - -struct alc_automic_entry { - hda_nid_t pin; /* pin */ - int idx; /* imux index, -1 = invalid */ - unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ -}; - -#define MAX_NID_PATH_DEPTH 5 - -enum { - NID_PATH_VOL_CTL, - NID_PATH_MUTE_CTL, - NID_PATH_BOOST_CTL, - NID_PATH_NUM_CTLS -}; - -/* Widget connection path - * - * For output, stored in the order of DAC -> ... -> pin, - * for input, pin -> ... -> ADC. - * - * idx[i] contains the source index number to select on of the widget path[i]; - * e.g. idx[1] is the index of the DAC (path[0]) selected by path[1] widget - * multi[] indicates whether it's a selector widget with multi-connectors - * (i.e. the connection selection is mandatory) - * vol_ctl and mute_ctl contains the NIDs for the assigned mixers - */ -struct nid_path { - int depth; - hda_nid_t path[MAX_NID_PATH_DEPTH]; - unsigned char idx[MAX_NID_PATH_DEPTH]; - unsigned char multi[MAX_NID_PATH_DEPTH]; - unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ - bool active; -}; - struct alc_spec { + struct hda_gen_spec gen; /* must be at head */ + /* codec parameterization */ const struct snd_kcontrol_new *mixers[5]; /* mixer arrays */ unsigned int num_mixers; unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */
- char stream_name_analog[32]; /* analog PCM stream */ - const struct hda_pcm_stream *stream_analog_playback; - const struct hda_pcm_stream *stream_analog_capture; - const struct hda_pcm_stream *stream_analog_alt_playback; - const struct hda_pcm_stream *stream_analog_alt_capture; - - char stream_name_digital[32]; /* digital PCM stream */ - const struct hda_pcm_stream *stream_digital_playback; - const struct hda_pcm_stream *stream_digital_capture; - - /* playback */ - struct hda_multi_out multiout; /* playback set-up - * max_channels, dacs must be set - * dig_out_nid and hp_nid are optional - */ - hda_nid_t alt_dac_nid; - hda_nid_t slave_dig_outs[3]; /* optional - for auto-parsing */ - int dig_out_type; - - /* capture */ - unsigned int num_adc_nids; - hda_nid_t adc_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t dig_in_nid; /* digital-in NID; optional */ - hda_nid_t mixer_nid; /* analog-mixer NID */ - - /* capture setup for dynamic dual-adc switch */ - hda_nid_t cur_adc; - unsigned int cur_adc_stream_tag; - unsigned int cur_adc_format; - - /* capture source */ - struct hda_input_mux input_mux; - unsigned int cur_mux[3]; - - /* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; - int const_channel_count; /* min. channel count (for speakers) */ - int ext_channel_count; /* current channel count for multi-io */ - - /* PCM information */ - struct hda_pcm pcm_rec[3]; /* used in alc_build_pcms() */ - - /* dynamic controls, init_verbs and input_mux */ - struct auto_pin_cfg autocfg; struct alc_customize_define cdefine; - struct snd_array kctls; - hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; - hda_nid_t imux_pins[HDA_MAX_NUM_INPUTS]; - unsigned int dyn_adc_idx[HDA_MAX_NUM_INPUTS]; - hda_nid_t inv_dmic_pin; - hda_nid_t shared_mic_vref_pin; + unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
- /* DAC list */ - int num_all_dacs; - hda_nid_t all_dacs[16]; - - /* path list */ - struct snd_array paths; - - /* auto-mic stuff */ - int am_num_entries; - struct alc_automic_entry am_entry[MAX_AUTO_MIC_PINS]; + /* inverted dmic fix */ + unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ + unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ + hda_nid_t inv_dmic_pin;
/* hooks */ void (*init_hook)(struct hda_codec *codec); @@ -204,224 +105,16 @@ struct alc_spec { void (*power_hook)(struct hda_codec *codec); #endif void (*shutup)(struct hda_codec *codec); - void (*automute_hook)(struct hda_codec *codec); - - /* for pin sensing */ - unsigned int hp_jack_present:1; - unsigned int line_jack_present:1; - unsigned int master_mute:1; - unsigned int auto_mic:1; - unsigned int automute_speaker:1; /* automute speaker outputs */ - unsigned int automute_lo:1; /* automute LO outputs */ - unsigned int detect_hp:1; /* Headphone detection enabled */ - unsigned int detect_lo:1; /* Line-out detection enabled */ - unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ - unsigned int automute_lo_possible:1; /* there are line outs and HP */ - unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ - unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ - - /* other flags */ - unsigned int need_dac_fix:1; /* need to limit DACs for multi channels */ - unsigned int no_analog :1; /* digital I/O only */ - unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ - unsigned int shared_mic_hp:1; /* HP/Mic-in sharing */ - unsigned int inv_dmic_fixup:1; /* has inverted digital-mic workaround */ - unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ - unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ - unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ - unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ - - unsigned int parse_flags; /* passed to snd_hda_parse_pin_defcfg() */
int init_amp; int codec_variant; /* flag for other variants */
- /* for virtual master */ - hda_nid_t vmaster_nid; - struct hda_vmaster_mute_hook vmaster_mute; -#ifdef CONFIG_PM - struct hda_loopback_check loopback; - int num_loopbacks; - struct hda_amp_list loopback_list[8]; -#endif - /* for PLL fix */ hda_nid_t pll_nid; unsigned int pll_coef_idx, pll_coef_bit; unsigned int coef0; - - /* multi-io */ - int multi_ios; - struct alc_multi_io multi_io[4]; - - /* bind volumes */ - struct snd_array bind_ctls; };
-static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, - int dir, unsigned int bits) -{ - if (!nid) - return false; - if (get_wcaps(codec, nid) & (1 << (dir + 1))) - if (query_amp_caps(codec, nid, dir) & bits) - return true; - return false; -} - -#define nid_has_mute(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) -#define nid_has_volume(codec, nid, dir) \ - check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS) - -static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix); - -/* - * input MUX handling - */ -static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - return snd_hda_input_mux_info(&spec->input_mux, uinfo); -} - -static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - - ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; - return 0; -} - -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) -{ - struct alc_spec *spec = codec->spec; - if (spec->dyn_adc_switch) - adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; -} - -static bool alc_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t new_adc = spec->adc_nids[spec->dyn_adc_idx[cur]]; - - if (spec->cur_adc && spec->cur_adc != new_adc) { - /* stream is running, let's swap the current ADC */ - __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); - spec->cur_adc = new_adc; - snd_hda_codec_setup_stream(codec, new_adc, - spec->cur_adc_stream_tag, 0, - spec->cur_adc_format); - return true; - } - return false; -} - -static void call_update_outputs(struct hda_codec *codec); -static void alc_inv_dmic_sync(struct hda_codec *codec, bool force); -static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx); - -/* for shared I/O, change the pin-control accordingly */ -static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) -{ - struct alc_spec *spec = codec->spec; - unsigned int val; - hda_nid_t pin = spec->autocfg.inputs[1].pin; - /* NOTE: this assumes that there are only two inputs, the - * first is the real internal mic and the second is HP/mic jack. - */ - - val = snd_hda_get_default_vref(codec, pin); - - /* This pin does not have vref caps - let's enable vref on pin 0x18 - instead, as suggested by Realtek */ - if (val == AC_PINCTL_VREF_HIZ && spec->shared_mic_vref_pin) { - const hda_nid_t vref_pin = spec->shared_mic_vref_pin; - unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); - if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); - } - - val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl(codec, pin, val); - - spec->automute_speaker = !set_as_mic; - call_update_outputs(codec); -} - -/* select the given imux item; either unmute exclusively or select the route */ -static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx, - unsigned int idx) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - - imux = &spec->input_mux; - if (!imux->num_items) - return 0; - - if (idx >= imux->num_items) - idx = imux->num_items - 1; - if (spec->cur_mux[adc_idx] == idx) - return 0; - - path = get_nid_path(codec, spec->imux_pins[spec->cur_mux[adc_idx]], - spec->adc_nids[adc_idx]); - if (!path) - return 0; - if (path->active) - activate_path(codec, path, false, false); - - spec->cur_mux[adc_idx] = idx; - - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[adc_idx]); - - if (spec->dyn_adc_switch) - alc_dyn_adc_pcm_resetup(codec, idx); - - path = get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, adc_idx, idx)); - if (!path) - return 0; - if (path->active) - return 0; - activate_path(codec, path, true, false); - alc_inv_dmic_sync(codec, true); - return 1; -} - -static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - return alc_mux_select(codec, adc_idx, - ucontrol->value.enumerated.item[0]); -} - -/* - * set up the input pin config (depending on the given auto-pin type) - */ -static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid, - int auto_pin_type) -{ - unsigned int val = PIN_IN; - if (auto_pin_type == AUTO_PIN_MIC) - val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); -} - /* * Append the given mixer and verb elements for the later use * The mixer array is referred in build_controls(), and init_verbs are @@ -491,146 +184,6 @@ static void alc_fix_pll_init(struct hda_codec *codec, hda_nid_t nid, alc_fix_pll(codec); }
-/* - * Jack detections for HP auto-mute and mic-switch - */ - -/* check each pin in the given array; returns true if any of them is plugged */ -static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) -{ - int i, present = 0; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - if (!nid) - break; - present |= snd_hda_jack_detect(codec, nid); - } - return present; -} - -/* standard HP/line-out auto-mute helper */ -static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, - bool mute, bool hp_out) -{ - struct alc_spec *spec = codec->spec; - unsigned int pin_bits = mute ? 0 : (hp_out ? PIN_HP : PIN_OUT); - int i; - - for (i = 0; i < num_pins; i++) { - hda_nid_t nid = pins[i]; - unsigned int val; - if (!nid) - break; - /* don't reset VREF value in case it's controlling - * the amp (see alc861_fixup_asus_amp_vref_0f()) - */ - if (spec->keep_vref_in_automute) { - val = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - val &= ~PIN_HP; - } else - val = 0; - val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); - } -} - -/* Toggle outputs muting */ -static void update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int on; - - /* Control HP pins/amps depending on master_mute state; - * in general, HP pins/amps control should be enabled in all cases, - * but currently set only for master_mute, just to be safe - */ - if (!spec->shared_mic_hp) /* don't change HP-pin when shared with mic */ - do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins, spec->master_mute, true); - - if (!spec->automute_speaker) - on = 0; - else - on = spec->hp_jack_present | spec->line_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), - spec->autocfg.speaker_pins, on, false); - - /* toggle line-out mutes if needed, too */ - /* if LO is a copy of either HP or Speaker, don't need to handle it */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0] || - spec->autocfg.line_out_pins[0] == spec->autocfg.speaker_pins[0]) - return; - if (!spec->automute_lo) - on = 0; - else - on = spec->hp_jack_present; - on |= spec->master_mute; - do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins, on, false); -} - -static void call_update_outputs(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->automute_hook) - spec->automute_hook(codec); - else - update_outputs(codec); -} - -/* standard HP-automute helper */ -static void alc_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - - spec->hp_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.hp_pins), - spec->autocfg.hp_pins); - if (!spec->detect_hp || (!spec->automute_speaker && !spec->automute_lo)) - return; - call_update_outputs(codec); -} - -/* standard line-out-automute helper */ -static void alc_line_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - return; - /* check LO jack only when it's different from HP */ - if (spec->autocfg.line_out_pins[0] == spec->autocfg.hp_pins[0]) - return; - - spec->line_jack_present = - detect_jacks(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), - spec->autocfg.line_out_pins); - if (!spec->automute_speaker || !spec->detect_lo) - return; - call_update_outputs(codec); -} - -/* standard mic auto-switch helper */ -static void alc_mic_automute(struct hda_codec *codec, struct hda_jack_tbl *jack) -{ - struct alc_spec *spec = codec->spec; - int i; - - if (!spec->auto_mic) - return; - - for (i = spec->am_num_entries - 1; i > 0; i--) { - if (snd_hda_jack_detect(codec, spec->am_entry[i].pin)) { - alc_mux_select(codec, 0, spec->am_entry[i].idx); - return; - } - } - alc_mux_select(codec, 0, spec->am_entry[0].idx); -} - /* update the master volume per volume-knob's unsol event */ static void alc_update_knob_master(struct hda_codec *codec, struct hda_jack_tbl *jack) { @@ -780,356 +333,64 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type) } }
+ /* - * Auto-Mute mode mixer enum support + * Realtek SSID verification */ -static int alc_automute_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - static const char * const texts3[] = { - "Disabled", "Speaker Only", "Line Out+Speaker" - }; - - if (spec->automute_speaker_possible && spec->automute_lo_possible) - return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); - return snd_hda_enum_bool_helper_info(kcontrol, uinfo); -} - -static int alc_automute_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - unsigned int val = 0; - if (spec->automute_speaker) - val++; - if (spec->automute_lo) - val++;
- ucontrol->value.enumerated.item[0] = val; - return 0; -} +/* Could be any non-zero and even value. When used as fixup, tells + * the driver to ignore any present sku defines. + */ +#define ALC_FIXUP_SKU_IGNORE (2)
-static int alc_automute_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_fixup_sku_ignore(struct hda_codec *codec, + const struct hda_fixup *fix, int action) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - - switch (ucontrol->value.enumerated.item[0]) { - case 0: - if (!spec->automute_speaker && !spec->automute_lo) - return 0; - spec->automute_speaker = 0; - spec->automute_lo = 0; - break; - case 1: - if (spec->automute_speaker_possible) { - if (!spec->automute_lo && spec->automute_speaker) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 0; - } else if (spec->automute_lo_possible) { - if (spec->automute_lo) - return 0; - spec->automute_lo = 1; - } else - return -EINVAL; - break; - case 2: - if (!spec->automute_lo_possible || !spec->automute_speaker_possible) - return -EINVAL; - if (spec->automute_speaker && spec->automute_lo) - return 0; - spec->automute_speaker = 1; - spec->automute_lo = 1; - break; - default: - return -EINVAL; + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->cdefine.fixup = 1; + spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; } - call_update_outputs(codec); - return 1; -} - -static const struct snd_kcontrol_new alc_automute_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Auto-Mute Mode", - .info = alc_automute_mode_info, - .get = alc_automute_mode_get, - .put = alc_automute_mode_put, -}; - -static struct snd_kcontrol_new * -alc_kcontrol_new(struct alc_spec *spec, const char *name, - const struct snd_kcontrol_new *temp) -{ - struct snd_kcontrol_new *knew = snd_array_new(&spec->kctls); - if (!knew) - return NULL; - *knew = *temp; - if (name) - knew->name = kstrdup(name, GFP_KERNEL); - else if (knew->name) - knew->name = kstrdup(knew->name, GFP_KERNEL); - if (!knew->name) - return NULL; - return knew; }
-static int alc_add_automute_mode_enum(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!alc_kcontrol_new(spec, NULL, &alc_automute_mode_enum)) - return -ENOMEM; - return 0; -} - -/* - * Check the availability of HP/line-out auto-mute; - * Set up appropriately if really supported - */ -static int alc_init_automute(struct hda_codec *codec) +static int alc_auto_parse_customize_define(struct hda_codec *codec) { + unsigned int ass, tmp, i; + unsigned nid = 0; struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int present = 0; - int i, err;
- if (cfg->hp_pins[0]) - present++; - if (cfg->line_out_pins[0]) - present++; - if (cfg->speaker_pins[0]) - present++; - if (present < 2) /* need two different output types */ - return 0; + spec->cdefine.enable_pcbeep = 1; /* assume always enabled */
- if (!cfg->speaker_pins[0] && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = cfg->line_outs; + if (spec->cdefine.fixup) { + ass = spec->cdefine.sku_cfg; + if (ass == ALC_FIXUP_SKU_IGNORE) + return -1; + goto do_sku; }
- if (!cfg->hp_pins[0] && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = cfg->line_outs; - } + ass = codec->subsystem_id & 0xffff; + if (ass != codec->bus->pci->subsystem_device && (ass & 1)) + goto do_sku;
- for (i = 0; i < cfg->hp_outs; i++) { - hda_nid_t nid = cfg->hp_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable HP auto-muting on NID 0x%x\n", - nid); - snd_hda_jack_detect_enable_callback(codec, nid, ALC_HP_EVENT, - alc_hp_automute); - spec->detect_hp = 1; - } + nid = 0x1d; + if (codec->vendor_id == 0x10ec0260) + nid = 0x17; + ass = snd_hda_codec_get_pincfg(codec, nid);
- if (cfg->line_out_type == AUTO_PIN_LINE_OUT && cfg->line_outs) { - if (cfg->speaker_outs) - for (i = 0; i < cfg->line_outs; i++) { - hda_nid_t nid = cfg->line_out_pins[i]; - if (!is_jack_detectable(codec, nid)) - continue; - snd_printdd("realtek: Enable Line-Out " - "auto-muting on NID 0x%x\n", nid); - snd_hda_jack_detect_enable_callback(codec, nid, ALC_FRONT_EVENT, - alc_line_automute); - spec->detect_lo = 1; - } - spec->automute_lo_possible = spec->detect_hp; + if (!(ass & 1)) { + printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", + codec->chip_name, ass); + return -1; }
- spec->automute_speaker_possible = cfg->speaker_outs && - (spec->detect_hp || spec->detect_lo); - - spec->automute_lo = spec->automute_lo_possible; - spec->automute_speaker = spec->automute_speaker_possible; - - if (spec->automute_speaker_possible || spec->automute_lo_possible) { - /* create a control for automute mode */ - err = alc_add_automute_mode_enum(codec); - if (err < 0) - return err; + /* check sum */ + tmp = 0; + for (i = 1; i < 16; i++) { + if ((ass >> i) & 1) + tmp++; } - return 0; -} - -/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - -/* check whether all auto-mic pins are valid; setup indices if OK */ -static bool alc_auto_mic_check_imux(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - int i; - - imux = &spec->input_mux; - for (i = 0; i < spec->am_num_entries; i++) { - spec->am_entry[i].idx = - find_idx_in_nid_list(spec->am_entry[i].pin, - spec->imux_pins, imux->num_items); - if (spec->am_entry[i].idx < 0) - return false; /* no corresponding imux */ - } - - /* we don't need the jack detection for the first pin */ - for (i = 1; i < spec->am_num_entries; i++) - snd_hda_jack_detect_enable_callback(codec, - spec->am_entry[i].pin, - ALC_MIC_EVENT, - alc_mic_automute); - return true; -} - -static int compare_attr(const void *ap, const void *bp) -{ - const struct alc_automic_entry *a = ap; - const struct alc_automic_entry *b = bp; - return (int)(a->attr - b->attr); -} - -/* - * Check the availability of auto-mic switch; - * Set up if really supported - */ -static int alc_init_auto_mic(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int types; - int i, num_pins; - - types = 0; - num_pins = 0; - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - unsigned int attr; - attr = snd_hda_codec_get_pincfg(codec, nid); - attr = snd_hda_get_input_pin_attr(attr); - if (types & (1 << attr)) - return 0; /* already occupied */ - switch (attr) { - case INPUT_PIN_ATTR_INT: - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* invalid type */ - break; - case INPUT_PIN_ATTR_UNUSED: - return 0; /* invalid entry */ - default: - if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) - return 0; /* invalid type */ - if (!spec->line_in_auto_switch && - cfg->inputs[i].type != AUTO_PIN_MIC) - return 0; /* only mic is allowed */ - if (!is_jack_detectable(codec, nid)) - return 0; /* no unsol support */ - break; - } - if (num_pins >= MAX_AUTO_MIC_PINS) - return 0; - types |= (1 << attr); - spec->am_entry[num_pins].pin = nid; - spec->am_entry[num_pins].attr = attr; - num_pins++; - } - - if (num_pins < 2) - return 0; - - spec->am_num_entries = num_pins; - /* sort the am_entry in the order of attr so that the pin with a - * higher attr will be selected when the jack is plugged. - */ - sort(spec->am_entry, num_pins, sizeof(spec->am_entry[0]), - compare_attr, NULL); - - if (!alc_auto_mic_check_imux(codec)) - return 0; - - spec->auto_mic = 1; - spec->num_adc_nids = 1; - spec->cur_mux[0] = spec->am_entry[0].idx; - snd_printdd("realtek: Enable auto-mic switch on NID 0x%x/0x%x/0x%x\n", - spec->am_entry[0].pin, - spec->am_entry[1].pin, - spec->am_entry[2].pin); - - return 0; -} - -/* - * Realtek SSID verification - */ - -/* Could be any non-zero and even value. When used as fixup, tells - * the driver to ignore any present sku defines. - */ -#define ALC_FIXUP_SKU_IGNORE (2) - -static void alc_fixup_sku_ignore(struct hda_codec *codec, - const struct hda_fixup *fix, int action) -{ - struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PRE_PROBE) { - spec->cdefine.fixup = 1; - spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE; - } -} - -static int alc_auto_parse_customize_define(struct hda_codec *codec) -{ - unsigned int ass, tmp, i; - unsigned nid = 0; - struct alc_spec *spec = codec->spec; - - spec->cdefine.enable_pcbeep = 1; /* assume always enabled */ - - if (spec->cdefine.fixup) { - ass = spec->cdefine.sku_cfg; - if (ass == ALC_FIXUP_SKU_IGNORE) - return -1; - goto do_sku; - } - - ass = codec->subsystem_id & 0xffff; - if (ass != codec->bus->pci->subsystem_device && (ass & 1)) - goto do_sku; - - nid = 0x1d; - if (codec->vendor_id == 0x10ec0260) - nid = 0x17; - ass = snd_hda_codec_get_pincfg(codec, nid); - - if (!(ass & 1)) { - printk(KERN_INFO "hda_codec: %s: SKU not ready 0x%08x\n", - codec->chip_name, ass); - return -1; - } - - /* check sum */ - tmp = 0; - for (i = 1; i < 16; i++) { - if ((ass >> i) & 1) - tmp++; - } - if (((ass >> 16) & 0xf) != tmp) - return -1; + if (((ass >> 16) & 0xf) != tmp) + return -1;
spec->cdefine.port_connectivity = ass >> 30; spec->cdefine.enable_pcbeep = (ass & 0x100000) >> 20; @@ -1157,6 +418,15 @@ do_sku: return 0; }
+/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} /* return true if the given NID is found in the list */ static bool found_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) { @@ -1259,9 +529,9 @@ do_sku: * 15 : 1 --> enable the function "Mute internal speaker * when the external headphone out jack is plugged" */ - if (!spec->autocfg.hp_pins[0] && - !(spec->autocfg.line_out_pins[0] && - spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)) { + if (!spec->gen.autocfg.hp_pins[0] && + !(spec->gen.autocfg.line_out_pins[0] && + spec->gen.autocfg.line_out_type == AUTO_PIN_HP_OUT)) { hda_nid_t nid; tmp = (ass >> 11) & 0x3; /* HP to chassis */ if (tmp == 0) @@ -1274,10 +544,10 @@ do_sku: nid = porti; else return 1; - if (found_in_nid_list(nid, spec->autocfg.line_out_pins, - spec->autocfg.line_outs)) + if (found_in_nid_list(nid, spec->gen.autocfg.line_out_pins, + spec->gen.autocfg.line_outs)) return 1; - spec->autocfg.hp_pins[0] = nid; + spec->gen.autocfg.hp_pins[0] = nid; } return 1; } @@ -1326,170 +596,35 @@ static unsigned int alc_get_coef0(struct hda_codec *codec) return spec->coef0; }
-static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac); -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital); -static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path); -static struct nid_path *add_new_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix); - -/* - * Digital I/O handling - */ - -/* set right pin controls for digital I/O */ -static void alc_auto_init_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin; - - for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - if (!pin) - continue; - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, 0); - } - pin = spec->autocfg.dig_in_pin; - if (pin) - snd_hda_set_pin_ctl(codec, pin, PIN_IN); -} - -/* parse digital I/Os and set up NIDs in BIOS auto-parse mode */ -static void alc_auto_parse_digital(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i, nums; - hda_nid_t dig_nid; - - /* support multiple SPDIFs; the secondary is set up as a slave */ - nums = 0; - for (i = 0; i < spec->autocfg.dig_outs; i++) { - hda_nid_t pin = spec->autocfg.dig_out_pins[i]; - dig_nid = alc_auto_look_for_dac(codec, pin, true); - if (!dig_nid) - continue; - if (!add_new_nid_path(codec, dig_nid, pin, 2)) - continue; - if (!nums) { - spec->multiout.dig_out_nid = dig_nid; - spec->dig_out_type = spec->autocfg.dig_out_type[0]; - } else { - spec->multiout.slave_dig_outs = spec->slave_dig_outs; - if (nums >= ARRAY_SIZE(spec->slave_dig_outs) - 1) - break; - spec->slave_dig_outs[nums - 1] = dig_nid; - } - nums++; - } - - if (spec->autocfg.dig_in_pin) { - dig_nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - struct nid_path *path; - unsigned int wcaps = get_wcaps(codec, dig_nid); - if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) - continue; - if (!(wcaps & AC_WCAP_DIGITAL)) - continue; - path = add_new_nid_path(codec, spec->autocfg.dig_in_pin, - dig_nid, 2); - if (path) { - path->active = true; - spec->dig_in_nid = dig_nid; - break; - } - } - } -} - /* - * capture mixer elements */ -#define alc_cap_vol_info snd_hda_mixer_amp_volume_info -#define alc_cap_vol_get snd_hda_mixer_amp_volume_get -#define alc_cap_vol_tlv snd_hda_mixer_amp_tlv - -typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol); - -static int alc_cap_put_caller(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol, - put_call_t func, int type) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; - const struct hda_input_mux *imux; - struct nid_path *path; - int i, adc_idx, err = 0; - - imux = &spec->input_mux; - adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); - mutex_lock(&codec->control_mutex); - codec->cached_write = 1; - for (i = 0; i < imux->num_items; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, adc_idx, i)); - if (!path->ctls[type]) - continue; - kcontrol->private_value = path->ctls[type]; - err = func(kcontrol, ucontrol); - if (err < 0) - goto error; - } - error: - codec->cached_write = 0; - mutex_unlock(&codec->control_mutex); - snd_hda_codec_resume_amp(codec); - if (err >= 0 && type == NID_PATH_MUTE_CTL && - spec->inv_dmic_fixup && spec->inv_dmic_muted) - alc_inv_dmic_sync_adc(codec, adc_idx); - return err; -} - -static int alc_cap_vol_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return alc_cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_volume_put, - NID_PATH_VOL_CTL); -} - -/* capture mixer elements */ -#define alc_cap_sw_info snd_ctl_boolean_stereo_info -#define alc_cap_sw_get snd_hda_mixer_amp_switch_get
-static int alc_cap_sw_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) { - return alc_cap_put_caller(kcontrol, ucontrol, - snd_hda_mixer_amp_switch_put, - NID_PATH_MUTE_CTL); + struct hda_gen_spec *spec = codec->spec; + if (spec->dyn_adc_switch) + adc_idx = spec->dyn_adc_idx[imux_idx]; + return spec->adc_nids[adc_idx]; }
static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) { struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; + struct hda_input_mux *imux = &spec->gen.input_mux; struct nid_path *path; hda_nid_t nid; int i, dir, parm; unsigned int val;
for (i = 0; i < imux->num_items; i++) { - if (spec->imux_pins[i] == spec->inv_dmic_pin) + if (spec->gen.imux_pins[i] == spec->inv_dmic_pin) break; } if (i >= imux->num_items) return;
- path = get_nid_path(codec, spec->inv_dmic_pin, - get_adc_nid(codec, adc_idx, i)); + path = snd_hda_get_nid_path(codec, spec->inv_dmic_pin, + get_adc_nid(codec, adc_idx, i)); val = path->ctls[NID_PATH_MUTE_CTL]; if (!val) return; @@ -1531,12 +666,12 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) return; if (!spec->inv_dmic_muted && !force) return; - nums = spec->dyn_adc_switch ? 1 : spec->num_adc_nids; + nums = spec->gen.dyn_adc_switch ? 1 : spec->gen.num_adc_nids; for (src = 0; src < nums; src++) { bool dmic_fixup = false;
if (spec->inv_dmic_muted && - spec->imux_pins[spec->cur_mux[src]] == spec->inv_dmic_pin) + spec->gen.imux_pins[spec->gen.cur_mux[src]] == spec->inv_dmic_pin) dmic_fixup = true; if (!dmic_fixup && !force) continue; @@ -1544,6 +679,11 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) } }
+static void alc_inv_dmic_hook(struct hda_codec *codec) +{ + alc_inv_dmic_sync(codec, false); +} + static int alc_inv_dmic_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -1580,11 +720,12 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) { struct alc_spec *spec = codec->spec;
- if (!alc_kcontrol_new(spec, NULL, &alc_inv_dmic_sw)) + if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &alc_inv_dmic_sw)) return -ENOMEM; spec->inv_dmic_fixup = 1; spec->inv_dmic_muted = 0; spec->inv_dmic_pin = nid; + spec->gen.cap_sync_hook = alc_inv_dmic_hook; return 0; }
@@ -1594,2807 +735,208 @@ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, { if (action == ALC_FIXUP_ACT_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); -} - -/* - * virtual master controls - */ - -/* - * slave controls for virtual master - */ -static const char * const alc_slave_pfxs[] = { - "Front", "Surround", "Center", "LFE", "Side", - "Headphone", "Speaker", "Mono", "Line Out", - "CLFE", "Bass Speaker", "PCM", - NULL, -}; - -/* - * build control elements - */ - -static void alc_free_kctls(struct hda_codec *codec); - -#ifdef CONFIG_SND_HDA_INPUT_BEEP -/* additional beep mixers; the actual parameters are overwritten at build */ -static const struct snd_kcontrol_new alc_beep_mixer[] = { - HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), - HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), - { } /* end */ -}; -#endif - -static int alc_build_controls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i, err; - - for (i = 0; i < spec->num_mixers; i++) { - err = snd_hda_add_new_ctls(codec, spec->mixers[i]); - if (err < 0) - return err; - } - if (spec->multiout.dig_out_nid) { - err = snd_hda_create_dig_out_ctls(codec, - spec->multiout.dig_out_nid, - spec->multiout.dig_out_nid, - spec->pcm_rec[1].pcm_type); - if (err < 0) - return err; - if (!spec->no_analog) { - err = snd_hda_create_spdif_share_sw(codec, - &spec->multiout); - if (err < 0) - return err; - spec->multiout.share_spdif = 1; - } - } - if (spec->dig_in_nid) { - err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); - if (err < 0) - return err; - } - -#ifdef CONFIG_SND_HDA_INPUT_BEEP - /* create beep controls if needed */ - if (spec->beep_amp) { - const struct snd_kcontrol_new *knew; - for (knew = alc_beep_mixer; knew->name; knew++) { - struct snd_kcontrol *kctl; - kctl = snd_ctl_new1(knew, codec); - if (!kctl) - return -ENOMEM; - kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, 0, kctl); - if (err < 0) - return err; - } - } -#endif - - /* if we have no master control, let's create it */ - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { - unsigned int vmaster_tlv[4]; - snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid, - HDA_OUTPUT, vmaster_tlv); - err = snd_hda_add_vmaster(codec, "Master Playback Volume", - vmaster_tlv, alc_slave_pfxs, - "Playback Volume"); - if (err < 0) - return err; - } - if (!spec->no_analog && - !snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { - err = __snd_hda_add_vmaster(codec, "Master Playback Switch", - NULL, alc_slave_pfxs, - "Playback Switch", - true, &spec->vmaster_mute.sw_kctl); - if (err < 0) - return err; - if (spec->vmaster_mute.hook) - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); - } - - alc_free_kctls(codec); /* no longer needed */ - - if (spec->shared_mic_hp) { - int err; - int nid = spec->autocfg.inputs[1].pin; - err = snd_hda_jack_add_kctl(codec, nid, "Headphone Mic", 0); - if (err < 0) - return err; - err = snd_hda_jack_detect_enable(codec, nid, 0); - if (err < 0) - return err; - } - - err = snd_hda_jack_add_kctls(codec, &spec->autocfg); - if (err < 0) - return err; - - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); - return 0; -} - - -/* - * Common callbacks - */ - -static void alc_auto_init_std(struct hda_codec *codec); - -static int alc_init(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->init_hook) - spec->init_hook(codec); - - alc_fix_pll(codec); - alc_auto_init_amp(codec, spec->init_amp); - - snd_hda_apply_verbs(codec); - alc_auto_init_std(codec); - - if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) - snd_hda_sync_vmaster_hook(&spec->vmaster_mute); - - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); - - hda_call_check_power_status(codec, 0x01); - return 0; -} - -#ifdef CONFIG_PM -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); -} -#endif - -/* - * Analog playback callbacks - */ -static int alc_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, - hinfo); -} - -static int alc_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); -} - -/* - * Digital out - */ -static int alc_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_open(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, - stream_tag, format, substream); -} - -static int alc_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); -} - -static int alc_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_multi_out_dig_close(codec, &spec->multiout); -} - -/* - * Analog capture - */ -static int alc_alt_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number + 1], - stream_tag, 0, format); - return 0; -} - -static int alc_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - - snd_hda_codec_cleanup_stream(codec, - spec->adc_nids[substream->number + 1]); - return 0; -} - -/* analog capture with dynamic dual-adc changes */ -static int dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - unsigned int stream_tag, - unsigned int format, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - spec->cur_adc = spec->adc_nids[spec->dyn_adc_idx[spec->cur_mux[0]]]; - spec->cur_adc_stream_tag = stream_tag; - spec->cur_adc_format = format; - snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); - return 0; -} - -static int dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, - struct hda_codec *codec, - struct snd_pcm_substream *substream) -{ - struct alc_spec *spec = codec->spec; - snd_hda_codec_cleanup_stream(codec, spec->cur_adc); - spec->cur_adc = 0; - return 0; -} - -static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - .nid = 0, /* fill later */ - .ops = { - .prepare = dyn_adc_capture_pcm_prepare, - .cleanup = dyn_adc_capture_pcm_cleanup - }, -}; - -/* - */ -static const struct hda_pcm_stream alc_pcm_analog_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 8, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_analog_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -static const struct hda_pcm_stream alc_pcm_analog_alt_capture = { - .substreams = 2, /* can be overridden */ - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .prepare = alc_alt_capture_pcm_prepare, - .cleanup = alc_alt_capture_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_playback = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ - .ops = { - .open = alc_dig_playback_pcm_open, - .close = alc_dig_playback_pcm_close, - .prepare = alc_dig_playback_pcm_prepare, - .cleanup = alc_dig_playback_pcm_cleanup - }, -}; - -static const struct hda_pcm_stream alc_pcm_digital_capture = { - .substreams = 1, - .channels_min = 2, - .channels_max = 2, - /* NID is set in alc_build_pcms */ -}; - -/* Used by alc_build_pcms to flag that a PCM has no playback stream */ -static const struct hda_pcm_stream alc_pcm_null_stream = { - .substreams = 0, - .channels_min = 0, - .channels_max = 0, -}; - -static int alc_build_pcms(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_pcm *info = spec->pcm_rec; - const struct hda_pcm_stream *p; - bool have_multi_adcs; - int i; - - codec->num_pcms = 1; - codec->pcm_info = info; - - if (spec->no_analog) - goto skip_analog; - - snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); - info->name = spec->stream_name_analog; - - if (spec->multiout.num_dacs > 0) { - p = spec->stream_analog_playback; - if (!p) - p = &alc_pcm_analog_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0]; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = - spec->multiout.max_channels; - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT && - spec->autocfg.line_outs == 2) - info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap = - snd_pcm_2_1_chmaps; - } - if (spec->num_adc_nids) { - p = spec->stream_analog_capture; - if (!p) { - if (spec->dyn_adc_switch) - p = &dyn_adc_pcm_analog_capture; - else - p = &alc_pcm_analog_capture; - } - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; - } - - if (spec->channel_mode) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; - for (i = 0; i < spec->num_channel_mode; i++) { - if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; - } - } - } - - skip_analog: - /* SPDIF for stream index #1 */ - if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); - codec->num_pcms = 2; - codec->slave_dig_outs = spec->multiout.slave_dig_outs; - info = spec->pcm_rec + 1; - info->name = spec->stream_name_digital; - if (spec->dig_out_type) - info->pcm_type = spec->dig_out_type; - else - info->pcm_type = HDA_PCM_TYPE_SPDIF; - if (spec->multiout.dig_out_nid) { - p = spec->stream_digital_playback; - if (!p) - p = &alc_pcm_digital_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid; - } - if (spec->dig_in_nid) { - p = spec->stream_digital_capture; - if (!p) - p = &alc_pcm_digital_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid; - } - /* FIXME: do we need this for all Realtek codec models? */ - codec->spdif_status_reset = 1; - } - - if (spec->no_analog) - return 0; - - /* If the use of more than one ADC is requested for the current - * model, configure a second analog capture-only PCM. - */ - have_multi_adcs = (spec->num_adc_nids > 1) && - !spec->dyn_adc_switch && !spec->auto_mic; - /* Additional Analaog capture for index #2 */ - if (spec->alt_dac_nid || have_multi_adcs) { - codec->num_pcms = 3; - info = spec->pcm_rec + 2; - info->name = spec->stream_name_analog; - if (spec->alt_dac_nid) { - p = spec->stream_analog_alt_playback; - if (!p) - p = &alc_pcm_analog_alt_playback; - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *p; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = - spec->alt_dac_nid; - } else { - info->stream[SNDRV_PCM_STREAM_PLAYBACK] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 0; - } - if (have_multi_adcs) { - p = spec->stream_analog_alt_capture; - if (!p) - p = &alc_pcm_analog_alt_capture; - info->stream[SNDRV_PCM_STREAM_CAPTURE] = *p; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = - spec->adc_nids[1]; - info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = - spec->num_adc_nids - 1; - } else { - info->stream[SNDRV_PCM_STREAM_CAPTURE] = - alc_pcm_null_stream; - info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 0; - } - } - - return 0; -} - -static inline void alc_shutup(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec && spec->shutup) - spec->shutup(codec); - snd_hda_shutup_pins(codec); -} - -static void alc_free_kctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (spec->kctls.list) { - struct snd_kcontrol_new *kctl = spec->kctls.list; - int i; - for (i = 0; i < spec->kctls.used; i++) - kfree(kctl[i].name); - } - snd_array_free(&spec->kctls); -} - -static void alc_free_bind_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - if (spec->bind_ctls.list) { - struct hda_bind_ctls **ctl = spec->bind_ctls.list; - int i; - for (i = 0; i < spec->bind_ctls.used; i++) - kfree(ctl[i]); - } - snd_array_free(&spec->bind_ctls); -} - -static void alc_free(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - - if (!spec) - return; - - alc_free_kctls(codec); - alc_free_bind_ctls(codec); - snd_array_free(&spec->paths); - kfree(spec); - snd_hda_detach_beep_device(codec); -} - -#ifdef CONFIG_PM -static void alc_power_eapd(struct hda_codec *codec) -{ - alc_auto_setup_eapd(codec, false); -} - -static int alc_suspend(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - alc_shutup(codec); - if (spec && spec->power_hook) - spec->power_hook(codec); - return 0; -} -#endif - -#ifdef CONFIG_PM -static int alc_resume(struct hda_codec *codec) -{ - msleep(150); /* to avoid pop noise */ - codec->patch_ops.init(codec); - snd_hda_codec_resume_amp(codec); - snd_hda_codec_resume_cache(codec); - alc_inv_dmic_sync(codec, true); - hda_call_check_power_status(codec, 0x01); - return 0; -} -#endif - -/* - */ -static const struct hda_codec_ops alc_patch_ops = { - .build_controls = alc_build_controls, - .build_pcms = alc_build_pcms, - .init = alc_init, - .free = alc_free, - .unsol_event = snd_hda_jack_unsol_event, -#ifdef CONFIG_PM - .resume = alc_resume, -#endif -#ifdef CONFIG_PM - .suspend = alc_suspend, - .check_power_status = alc_check_power_status, -#endif - .reboot_notify = alc_shutup, -}; - - -/* replace the codec chip_name with the given string */ -static int alc_codec_rename(struct hda_codec *codec, const char *name) -{ - kfree(codec->chip_name); - codec->chip_name = kstrdup(name, GFP_KERNEL); - if (!codec->chip_name) { - alc_free(codec); - return -ENOMEM; - } - return 0; -} - -/* - * Rename codecs appropriately from COEF value - */ -struct alc_codec_rename_table { - unsigned int vendor_id; - unsigned short coef_mask; - unsigned short coef_bits; - const char *name; -}; - -static struct alc_codec_rename_table rename_tbl[] = { - { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, - { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, - { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, - { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, - { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, - { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, - { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, - { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, - { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, - { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, - { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, - { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, - { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, - { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, - { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, - { } /* terminator */ -}; - -static int alc_codec_rename_from_preset(struct hda_codec *codec) -{ - const struct alc_codec_rename_table *p; - - for (p = rename_tbl; p->vendor_id; p++) { - if (p->vendor_id != codec->vendor_id) - continue; - if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) - return alc_codec_rename(codec, p->name); - } - return 0; -} - -/* - * Automatic parse of I/O pins from the BIOS configuration - */ - -enum { - ALC_CTL_WIDGET_VOL, - ALC_CTL_WIDGET_MUTE, - ALC_CTL_BIND_MUTE, - ALC_CTL_BIND_VOL, - ALC_CTL_BIND_SW, -}; -static const struct snd_kcontrol_new alc_control_templates[] = { - HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), - HDA_BIND_MUTE(NULL, 0, 0, 0), - HDA_BIND_VOL(NULL, 0), - HDA_BIND_SW(NULL, 0), -}; - -/* add dynamic controls */ -static int add_control(struct alc_spec *spec, int type, const char *name, - int cidx, unsigned long val) -{ - struct snd_kcontrol_new *knew; - - knew = alc_kcontrol_new(spec, name, &alc_control_templates[type]); - if (!knew) - return -ENOMEM; - knew->index = cidx; - if (get_amp_nid_(val)) - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - knew->private_value = val; - return 0; -} - -static int add_control_with_pfx(struct alc_spec *spec, int type, - const char *pfx, const char *dir, - const char *sfx, int cidx, unsigned long val) -{ - char name[32]; - snprintf(name, sizeof(name), "%s %s %s", pfx, dir, sfx); - return add_control(spec, type, name, cidx, val); -} - -#define add_pb_vol_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", 0, val) -#define add_pb_sw_ctrl(spec, type, pfx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", 0, val) -#define __add_pb_vol_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Volume", cidx, val) -#define __add_pb_sw_ctrl(spec, type, pfx, cidx, val) \ - add_control_with_pfx(spec, type, pfx, "Playback", "Switch", cidx, val) - -static const char * const channel_name[4] = { - "Front", "Surround", "CLFE", "Side" -}; - -static const char *alc_get_line_out_pfx(struct alc_spec *spec, int ch, - bool can_be_master, int *index) -{ - struct auto_pin_cfg *cfg = &spec->autocfg; - - *index = 0; - if (cfg->line_outs == 1 && !spec->multi_ios && - !cfg->hp_outs && !cfg->speaker_outs && can_be_master) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - /* if there is really a single DAC used in the whole output paths, - * use it master (or "PCM" if a vmaster hook is present) - */ - if (spec->multiout.num_dacs == 1 && !spec->mixer_nid && - !spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0]) - return spec->vmaster_mute.hook ? "PCM" : "Master"; - - switch (cfg->line_out_type) { - case AUTO_PIN_SPEAKER_OUT: - if (cfg->line_outs == 1) - return "Speaker"; - if (cfg->line_outs == 2) - return ch ? "Bass Speaker" : "Speaker"; - break; - case AUTO_PIN_HP_OUT: - /* for multi-io case, only the primary out */ - if (ch && spec->multi_ios) - break; - *index = ch; - return "Headphone"; - default: - if (cfg->line_outs == 1 && !spec->multi_ios) - return "PCM"; - break; - } - if (ch >= ARRAY_SIZE(channel_name)) { - snd_BUG(); - return "PCM"; - } - - return channel_name[ch]; -} - -#ifdef CONFIG_PM -/* add the powersave loopback-list entry */ -static void add_loopback_list(struct alc_spec *spec, hda_nid_t mix, int idx) -{ - struct hda_amp_list *list; - - if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) - return; - list = spec->loopback_list + spec->num_loopbacks; - list->nid = mix; - list->dir = HDA_INPUT; - list->idx = idx; - spec->num_loopbacks++; - spec->loopback.amplist = spec->loopback_list; -} -#else -#define add_loopback_list(spec, mix, idx) /* NOP */ -#endif - -/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, - const char *ctlname, int ctlidx, - hda_nid_t mix_nid) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - unsigned int val; - int err, idx; - - if (!nid_has_volume(codec, mix_nid, HDA_INPUT) && - !nid_has_mute(codec, mix_nid, HDA_INPUT)) - return 0; /* no need for analog loopback */ - - path = add_new_nid_path(codec, pin, mix_nid, 2); - if (!path) - return -EINVAL; - - idx = path->idx[path->depth - 1]; - if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_vol_ctrl(spec, ALC_CTL_WIDGET_VOL, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_VOL_CTL] = val; - } - - if (nid_has_mute(codec, mix_nid, HDA_INPUT)) { - val = HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT); - err = __add_pb_sw_ctrl(spec, ALC_CTL_WIDGET_MUTE, ctlname, ctlidx, val); - if (err < 0) - return err; - path->ctls[NID_PATH_MUTE_CTL] = val; - } - - path->active = true; - add_loopback_list(spec, mix_nid, idx); - return 0; -} - -static int alc_is_input_pin(struct hda_codec *codec, hda_nid_t nid) -{ - unsigned int pincap = snd_hda_query_pin_caps(codec, nid); - return (pincap & AC_PINCAP_IN) != 0; -} - -/* check whether the given two widgets can be connected */ -static bool is_reachable_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) -{ - if (!from_nid || !to_nid) - return false; - return snd_hda_get_conn_index(codec, to_nid, from_nid, true) >= 0; -} - -/* Parse the codec tree and retrieve ADCs */ -static int alc_auto_fill_adc_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t nid; - hda_nid_t *adc_nids = spec->adc_nids; - int max_nums = ARRAY_SIZE(spec->adc_nids); - int i, nums = 0; - - nid = codec->start_nid; - for (i = 0; i < codec->num_nodes; i++, nid++) { - unsigned int caps = get_wcaps(codec, nid); - int type = get_wcaps_type(caps); - - if (type != AC_WID_AUD_IN || (caps & AC_WCAP_DIGITAL)) - continue; - adc_nids[nums] = nid; - if (++nums >= max_nums) - break; - } - spec->num_adc_nids = nums; - return nums; -} - -/* filter out invalid adc_nids that don't give all active input pins; - * if needed, check whether dynamic ADC-switching is available - */ -static int check_dyn_adc_switch(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - hda_nid_t adc_nids[ARRAY_SIZE(spec->adc_nids)]; - int i, n, nums; - hda_nid_t pin, adc; - - again: - nums = 0; - for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - if (!is_reachable_path(codec, pin, adc)) - break; - } - if (i >= imux->num_items) - adc_nids[nums++] = adc; - } - - if (!nums) { - if (spec->shared_mic_hp) { - spec->shared_mic_hp = 0; - imux->num_items = 1; - goto again; - } - - /* check whether ADC-switch is possible */ - for (i = 0; i < imux->num_items; i++) { - pin = spec->imux_pins[i]; - for (n = 0; n < spec->num_adc_nids; n++) { - adc = spec->adc_nids[n]; - if (is_reachable_path(codec, pin, adc)) { - spec->dyn_adc_idx[i] = n; - break; - } - } - } - - snd_printdd("realtek: enabling ADC switching\n"); - spec->dyn_adc_switch = 1; - } else if (nums != spec->num_adc_nids) { - memcpy(spec->adc_nids, adc_nids, nums * sizeof(hda_nid_t)); - spec->num_adc_nids = nums; - } - - if (imux->num_items == 1 || spec->shared_mic_hp) { - snd_printdd("realtek: reducing to a single ADC\n"); - spec->num_adc_nids = 1; /* reduce to a single ADC */ - } - - /* single index for individual volumes ctls */ - if (!spec->dyn_adc_switch && spec->multi_cap_vol) - spec->num_adc_nids = 1; - - return 0; -} - -/* templates for capture controls */ -static const struct snd_kcontrol_new cap_src_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Input Source", - .info = alc_mux_enum_info, - .get = alc_mux_enum_get, - .put = alc_mux_enum_put, -}; - -static const struct snd_kcontrol_new cap_vol_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE | - SNDRV_CTL_ELEM_ACCESS_TLV_READ | - SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK), - .info = alc_cap_vol_info, - .get = alc_cap_vol_get, - .put = alc_cap_vol_put, - .tlv = { .c = alc_cap_vol_tlv }, -}; - -static const struct snd_kcontrol_new cap_sw_temp = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Switch", - .info = alc_cap_sw_info, - .get = alc_cap_sw_get, - .put = alc_cap_sw_put, -}; - -static int parse_capvol_in_path(struct hda_codec *codec, struct nid_path *path) -{ - hda_nid_t nid; - int i, depth; - - path->ctls[NID_PATH_VOL_CTL] = path->ctls[NID_PATH_MUTE_CTL] = 0; - for (depth = 0; depth < 3; depth++) { - if (depth >= path->depth) - return -EINVAL; - i = path->depth - depth - 1; - nid = path->path[i]; - if (!path->ctls[NID_PATH_VOL_CTL]) { - if (nid_has_volume(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_volume(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_VOL_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - if (!path->ctls[NID_PATH_MUTE_CTL]) { - if (nid_has_mute(codec, nid, HDA_OUTPUT)) - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else if (nid_has_mute(codec, nid, HDA_INPUT)) { - int idx = path->idx[i]; - if (!depth && codec->single_adc_amp) - idx = 0; - path->ctls[NID_PATH_MUTE_CTL] = - HDA_COMPOSE_AMP_VAL(nid, 3, idx, HDA_INPUT); - } - } - } - return 0; -} - -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs); - -static int add_single_cap_ctl(struct hda_codec *codec, const char *label, - int idx, bool is_switch, unsigned int ctl, - bool inv_dmic) -{ - struct alc_spec *spec = codec->spec; - char tmpname[44]; - int type = is_switch ? ALC_CTL_WIDGET_MUTE : ALC_CTL_WIDGET_VOL; - const char *sfx = is_switch ? "Switch" : "Volume"; - unsigned int chs = inv_dmic ? 1 : 3; - int err; - - if (!ctl) - return 0; - - if (label) - snprintf(tmpname, sizeof(tmpname), - "%s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Capture %s", sfx); - err = add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, chs)); - if (err < 0 || chs == 3) - return err; - - /* Make independent right kcontrol */ - if (label) - snprintf(tmpname, sizeof(tmpname), - "Inverted %s Capture %s", label, sfx); - else - snprintf(tmpname, sizeof(tmpname), - "Inverted Capture %s", sfx); - return add_control(spec, type, tmpname, idx, - amp_val_replace_channels(ctl, 2)); -} - -static bool is_inv_dmic_pin(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int val; - int i; - - if (!spec->inv_dmic_split) - return false; - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].pin != nid) - continue; - if (cfg->inputs[i].type != AUTO_PIN_MIC) - return false; - val = snd_hda_codec_get_pincfg(codec, nid); - return snd_hda_get_input_pin_attr(val) == INPUT_PIN_ATTR_INT; - } - return false; -} - -/* create single (and simple) capture volume and switch controls */ -static int create_single_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl, - bool inv_dmic) -{ - int err; - err = add_single_cap_ctl(codec, NULL, idx, false, vol_ctl, inv_dmic); - if (err < 0) - return err; - err = add_single_cap_ctl(codec, NULL, idx, true, sw_ctl, inv_dmic); - if (err < 0) - return err; - return 0; -} - -/* create bound capture volume and switch controls */ -static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, - unsigned int vol_ctl, unsigned int sw_ctl) -{ - struct alc_spec *spec = codec->spec; - struct snd_kcontrol_new *knew; - - if (vol_ctl) { - knew = alc_kcontrol_new(spec, NULL, &cap_vol_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = vol_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - if (sw_ctl) { - knew = alc_kcontrol_new(spec, NULL, &cap_sw_temp); - if (!knew) - return -ENOMEM; - knew->index = idx; - knew->private_value = sw_ctl; - knew->subdevice = HDA_SUBDEV_AMP_FLAG; - } - return 0; -} - -/* return the vol ctl when used first in the imux list */ -static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - unsigned int ctl; - int i; - - path = get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, 0, idx)); - if (!path) - return 0; - ctl = path->ctls[type]; - if (!ctl) - return 0; - for (i = 0; i < idx - 1; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, 0, i)); - if (path && path->ctls[type] == ctl) - return 0; - } - return ctl; -} - -/* create individual capture volume and switch controls per input */ -static int create_multi_cap_vol_ctl(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, err, type, type_idx = 0; - const char *prev_label = NULL; - - for (i = 0; i < imux->num_items; i++) { - const char *label; - bool inv_dmic; - label = hda_get_autocfg_input_label(codec, &spec->autocfg, i); - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - inv_dmic = is_inv_dmic_pin(codec, spec->imux_pins[i]); - - for (type = 0; type < 2; type++) { - err = add_single_cap_ctl(codec, label, type_idx, type, - get_first_cap_ctl(codec, i, type), - inv_dmic); - if (err < 0) - return err; - } - } - return 0; -} - -static int create_capture_mixers(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - int i, n, nums, err; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - if (!spec->auto_mic && imux->num_items > 1) { - struct snd_kcontrol_new *knew; - knew = alc_kcontrol_new(spec, NULL, &cap_src_temp); - if (!knew) - return -ENOMEM; - knew->count = nums; - } - - for (n = 0; n < nums; n++) { - bool multi = false; - bool inv_dmic = false; - int vol, sw; - - vol = sw = 0; - for (i = 0; i < imux->num_items; i++) { - struct nid_path *path; - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, n, i)); - if (!path) - continue; - parse_capvol_in_path(codec, path); - if (!vol) - vol = path->ctls[NID_PATH_VOL_CTL]; - else if (vol != path->ctls[NID_PATH_VOL_CTL]) - multi = true; - if (!sw) - sw = path->ctls[NID_PATH_MUTE_CTL]; - else if (sw != path->ctls[NID_PATH_MUTE_CTL]) - multi = true; - if (is_inv_dmic_pin(codec, spec->imux_pins[i])) - inv_dmic = true; - } - - if (!multi) - err = create_single_cap_vol_ctl(codec, n, vol, sw, - inv_dmic); - else if (!spec->multi_cap_vol) - err = create_bind_cap_vol_ctl(codec, n, vol, sw); - else - err = create_multi_cap_vol_ctl(codec); - if (err < 0) - return err; - } - - return 0; -} - -/* create playback/capture controls for input pins */ -static int alc_auto_create_input_ctls(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - const struct auto_pin_cfg *cfg = &spec->autocfg; - hda_nid_t mixer = spec->mixer_nid; - struct hda_input_mux *imux = &spec->input_mux; - int num_adcs; - int i, c, err, type_idx = 0; - const char *prev_label = NULL; - - num_adcs = alc_auto_fill_adc_nids(codec); - if (num_adcs < 0) - return 0; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t pin; - const char *label; - bool imux_added; - - pin = cfg->inputs[i].pin; - if (!alc_is_input_pin(codec, pin)) - continue; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - if (mixer) { - if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, pin, - label, type_idx, mixer); - if (err < 0) - return err; - } - } - - imux_added = false; - for (c = 0; c < num_adcs; c++) { - struct nid_path *path; - hda_nid_t adc = spec->adc_nids[c]; - - if (!is_reachable_path(codec, pin, adc)) - continue; - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!parse_nid_path(codec, pin, adc, 2, path)) { - snd_printd(KERN_ERR - "invalid input path 0x%x -> 0x%x\n", - pin, adc); - spec->paths.used--; - continue; - } - - if (!imux_added) { - spec->imux_pins[imux->num_items] = pin; - snd_hda_add_imux_item(imux, label, - imux->num_items, NULL); - imux_added = true; - } - } - } - - return 0; -} - -/* create a shared input with the headphone out */ -static int alc_auto_create_shared_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - unsigned int defcfg; - hda_nid_t nid; - - /* only one internal input pin? */ - if (cfg->num_inputs != 1) - return 0; - defcfg = snd_hda_codec_get_pincfg(codec, cfg->inputs[0].pin); - if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) - return 0; - - if (cfg->hp_outs == 1 && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - nid = cfg->hp_pins[0]; /* OK, we have a single HP-out */ - else if (cfg->line_outs == 1 && cfg->line_out_type == AUTO_PIN_HP_OUT) - nid = cfg->line_out_pins[0]; /* OK, we have a single line-out */ - else - return 0; /* both not available */ - - if (!(snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_IN)) - return 0; /* no input */ - - cfg->inputs[1].pin = nid; - cfg->inputs[1].type = AUTO_PIN_MIC; - cfg->num_inputs = 2; - spec->shared_mic_hp = 1; - snd_printdd("realtek: Enable shared I/O jack on NID 0x%x\n", nid); - return 0; -} - -static int get_pin_type(int line_out_type) -{ - if (line_out_type == AUTO_PIN_HP_OUT) - return PIN_HP; - else - return PIN_OUT; -} - -static void alc_auto_init_analog_input(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i; - - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - if (alc_is_input_pin(codec, nid)) - alc_set_input_pin(codec, nid, cfg->inputs[i].type); - - /* mute loopback inputs */ - if (spec->mixer_nid) { - struct nid_path *path; - path = get_nid_path(codec, nid, spec->mixer_nid); - if (path) - activate_path(codec, path, path->active, false); - } - } -} - -static bool alc_is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->path[0] == nid) - return true; - } - return false; -} - -/* look for an empty DAC slot */ -static hda_nid_t alc_auto_look_for_dac(struct hda_codec *codec, hda_nid_t pin, - bool is_digital) -{ - struct alc_spec *spec = codec->spec; - bool cap_digital; - int i; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || alc_is_dac_already_used(codec, nid)) - continue; - cap_digital = !!(get_wcaps(codec, nid) & AC_WCAP_DIGITAL); - if (is_digital != cap_digital) - continue; - if (is_reachable_path(codec, nid, pin)) - return nid; - } - return 0; -} - -/* called recursively */ -static bool __parse_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix, struct nid_path *path, int depth) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t conn[16]; - int i, nums; - - if (to_nid == spec->mixer_nid) { - if (!with_aa_mix) - return false; - with_aa_mix = 2; /* mark aa-mix is included */ - } - - nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); - for (i = 0; i < nums; i++) { - if (conn[i] != from_nid) { - /* special case: when from_nid is 0, - * try to find an empty DAC - */ - if (from_nid || - get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT || - alc_is_dac_already_used(codec, conn[i])) - continue; - } - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) - goto found; - } - if (depth >= MAX_NID_PATH_DEPTH) - return false; - for (i = 0; i < nums; i++) { - unsigned int type; - type = get_wcaps_type(get_wcaps(codec, conn[i])); - if (type == AC_WID_AUD_OUT || type == AC_WID_AUD_IN || - type == AC_WID_PIN) - continue; - if (__parse_nid_path(codec, from_nid, conn[i], - with_aa_mix, path, depth + 1)) - goto found; - } - return false; - - found: - path->path[path->depth] = conn[i]; - path->idx[path->depth + 1] = i; - if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) - path->multi[path->depth + 1] = 1; - path->depth++; - return true; -} - -/* parse the widget path from the given nid to the target nid; - * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. - * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. - * when @with_aa_mix is 2, no special handling about spec->mixer_nid. - */ -static bool parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, - struct nid_path *path) -{ - if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { - path->path[path->depth] = to_nid; - path->depth++; -#if 0 - snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); -#endif - return true; - } - return false; -} - -static hda_nid_t get_dac_if_single(struct hda_codec *codec, hda_nid_t pin) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t nid_found = 0; - - for (i = 0; i < spec->num_all_dacs; i++) { - hda_nid_t nid = spec->all_dacs[i]; - if (!nid || alc_is_dac_already_used(codec, nid)) - continue; - if (is_reachable_path(codec, nid, pin)) { - if (nid_found) - return 0; - nid_found = nid; - } - } - return nid_found; -} - -static bool is_ctl_used(struct hda_codec *codec, unsigned int val, int type) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->ctls[type] == val) - return true; - } - return false; -} - -/* badness definition */ -enum { - /* No primary DAC is found for the main output */ - BAD_NO_PRIMARY_DAC = 0x10000, - /* No DAC is found for the extra output */ - BAD_NO_DAC = 0x4000, - /* No possible multi-ios */ - BAD_MULTI_IO = 0x103, - /* No individual DAC for extra output */ - BAD_NO_EXTRA_DAC = 0x102, - /* No individual DAC for extra surrounds */ - BAD_NO_EXTRA_SURR_DAC = 0x101, - /* Primary DAC shared with main surrounds */ - BAD_SHARED_SURROUND = 0x100, - /* Primary DAC shared with main CLFE */ - BAD_SHARED_CLFE = 0x10, - /* Primary DAC shared with extra surrounds */ - BAD_SHARED_EXTRA_SURROUND = 0x10, - /* Volume widget is shared */ - BAD_SHARED_VOL = 0x10, -}; - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path); -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path); - -static struct nid_path *add_new_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix) -{ - struct alc_spec *spec = codec->spec; - struct nid_path *path; - - if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) - return NULL; - - path = snd_array_new(&spec->paths); - if (!path) - return NULL; - memset(path, 0, sizeof(*path)); - if (parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) - return path; - /* push back */ - spec->paths.used--; - return NULL; -} - -/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard - */ -static struct nid_path * -get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) -{ - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *path = snd_array_elem(&spec->paths, i); - if (path->depth <= 0) - continue; - if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) - return path; - } - return NULL; -} - -/* look for widgets in the path between the given NIDs appropriate for - * volume and mute controls, and assign the values to ctls[]. - * - * When no appropriate widget is found in the path, the badness value - * is incremented depending on the situation. The function returns the - * total badness for both volume and mute controls. - */ -static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) -{ - struct nid_path *path = get_nid_path(codec, dac, pin); - hda_nid_t nid; - unsigned int val; - int badness = 0; - - if (!path) - return BAD_SHARED_VOL * 2; - nid = alc_look_for_out_vol_nid(codec, path); - if (nid) { - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - if (is_ctl_used(codec, val, NID_PATH_VOL_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_VOL_CTL] = val; - } else - badness += BAD_SHARED_VOL; - nid = alc_look_for_out_mute_nid(codec, path); - if (nid) { - unsigned int wid_type = get_wcaps_type(get_wcaps(codec, nid)); - if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT || - nid_has_mute(codec, nid, HDA_OUTPUT)) - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); - else - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - if (is_ctl_used(codec, val, NID_PATH_MUTE_CTL)) - badness += BAD_SHARED_VOL; - else - path->ctls[NID_PATH_MUTE_CTL] = val; - } else - badness += BAD_SHARED_VOL; - return badness; -} - -struct badness_table { - int no_primary_dac; /* no primary DAC */ - int no_dac; /* no secondary DACs */ - int shared_primary; /* primary DAC is shared with main output */ - int shared_surr; /* secondary DAC shared with main or primary */ - int shared_clfe; /* third DAC shared with main or primary */ - int shared_surr_main; /* secondary DAC sahred with main/DAC0 */ -}; - -static struct badness_table main_out_badness = { - .no_primary_dac = BAD_NO_PRIMARY_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_PRIMARY_DAC, - .shared_surr = BAD_SHARED_SURROUND, - .shared_clfe = BAD_SHARED_CLFE, - .shared_surr_main = BAD_SHARED_SURROUND, -}; - -static struct badness_table extra_out_badness = { - .no_primary_dac = BAD_NO_DAC, - .no_dac = BAD_NO_DAC, - .shared_primary = BAD_NO_EXTRA_DAC, - .shared_surr = BAD_SHARED_EXTRA_SURROUND, - .shared_clfe = BAD_SHARED_EXTRA_SURROUND, - .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, -}; - -/* try to assign DACs to pins and return the resultant badness */ -static int alc_auto_fill_dacs(struct hda_codec *codec, int num_outs, - const hda_nid_t *pins, hda_nid_t *dacs, - const struct badness_table *bad) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, j; - int badness = 0; - hda_nid_t dac; - - if (!num_outs) - return 0; - - for (i = 0; i < num_outs; i++) { - hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = alc_auto_look_for_dac(codec, pin, false); - if (!dacs[i] && !i) { - for (j = 1; j < num_outs; j++) { - if (is_reachable_path(codec, dacs[j], pin)) { - dacs[0] = dacs[j]; - dacs[j] = 0; - break; - } - } - } - dac = dacs[i]; - if (!dac) { - if (is_reachable_path(codec, dacs[0], pin)) - dac = dacs[0]; - else if (cfg->line_outs > i && - is_reachable_path(codec, spec->private_dac_nids[i], pin)) - dac = spec->private_dac_nids[i]; - if (dac) { - if (!i) - badness += bad->shared_primary; - else if (i == 1) - badness += bad->shared_surr; - else - badness += bad->shared_clfe; - } else if (is_reachable_path(codec, spec->private_dac_nids[0], pin)) { - dac = spec->private_dac_nids[0]; - badness += bad->shared_surr_main; - } else if (!i) - badness += bad->no_primary_dac; - else - badness += bad->no_dac; - } - if (!add_new_nid_path(codec, dac, pin, 0)) - dac = dacs[i] = 0; - if (dac) - badness += assign_out_path_ctls(codec, pin, dac); - } - - return badness; -} - -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset); - -static bool alc_map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) -{ - int i; - bool found = false; - for (i = 0; i < outs; i++) { - hda_nid_t dac; - if (dacs[i]) - continue; - dac = get_dac_if_single(codec, pins[i]); - if (!dac) - continue; - if (add_new_nid_path(codec, dac, pins[i], 0)) { - dacs[i] = dac; - found = true; - } - } - return found; -} - -/* fill in the dac_nids table from the parsed pin configuration */ -static int fill_and_eval_dacs(struct hda_codec *codec, - bool fill_hardwired, - bool fill_mio_first) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err, badness; - - /* set num_dacs once to full for alc_auto_look_for_dac() */ - spec->multiout.num_dacs = cfg->line_outs; - spec->multiout.dac_nids = spec->private_dac_nids; - memset(spec->private_dac_nids, 0, sizeof(spec->private_dac_nids)); - memset(spec->multiout.hp_out_nid, 0, sizeof(spec->multiout.hp_out_nid)); - memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); - spec->multi_ios = 0; - snd_array_free(&spec->paths); - badness = 0; - - /* fill hard-wired DACs first */ - if (fill_hardwired) { - bool mapped; - do { - mapped = alc_map_singles(codec, cfg->line_outs, - cfg->line_out_pins, - spec->private_dac_nids); - mapped |= alc_map_singles(codec, cfg->hp_outs, - cfg->hp_pins, - spec->multiout.hp_out_nid); - mapped |= alc_map_singles(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid); - if (fill_mio_first && cfg->line_outs == 1 && - cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); - if (!err) - mapped = true; - } - } while (mapped); - } - - badness += alc_auto_fill_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, - &main_out_badness); - - /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - - if (fill_mio_first && - cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - /* try to fill multi-io first */ - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - /* we don't count badness at this stage yet */ - } - - if (cfg->line_out_type != AUTO_PIN_HP_OUT) { - err = alc_auto_fill_dacs(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_dacs(codec, cfg->speaker_outs, - cfg->speaker_pins, - spec->multiout.extra_out_nid, - &extra_out_badness); - if (err < 0) - return err; - badness += err; - } - if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = alc_auto_fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = alc_auto_fill_multi_ios(codec, cfg->hp_pins[0], false, - offset); - if (err < 0) - return err; - badness += err; - } - - if (spec->multi_ios == 2) { - for (i = 0; i < 2; i++) - spec->private_dac_nids[spec->multiout.num_dacs++] = - spec->multi_io[i].dac; - spec->ext_channel_count = 2; - } else if (spec->multi_ios) { - spec->multi_ios = 0; - badness += BAD_MULTI_IO; - } - - return badness; -} - -#define DEBUG_BADNESS - -#ifdef DEBUG_BADNESS -#define debug_badness snd_printdd -#else -#define debug_badness(...) -#endif - -static void debug_show_configs(struct alc_spec *spec, struct auto_pin_cfg *cfg) -{ - debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], - spec->multiout.dac_nids[0], - spec->multiout.dac_nids[1], - spec->multiout.dac_nids[2], - spec->multiout.dac_nids[3]); - if (spec->multi_ios > 0) - debug_badness("multi_ios(%d) = %x/%x : %x/%x\n", - spec->multi_ios, - spec->multi_io[0].pin, spec->multi_io[1].pin, - spec->multi_io[0].dac, spec->multi_io[1].dac); - debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[2], - spec->multiout.hp_out_nid[0], - spec->multiout.hp_out_nid[1], - spec->multiout.hp_out_nid[2], - spec->multiout.hp_out_nid[3]); - debug_badness("spk_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", - cfg->speaker_pins[0], cfg->speaker_pins[1], - cfg->speaker_pins[2], cfg->speaker_pins[3], - spec->multiout.extra_out_nid[0], - spec->multiout.extra_out_nid[1], - spec->multiout.extra_out_nid[2], - spec->multiout.extra_out_nid[3]); -} - -/* find all available DACs of the codec */ -static void alc_fill_all_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int i; - hda_nid_t nid = codec->start_nid; - - spec->num_all_dacs = 0; - memset(spec->all_dacs, 0, sizeof(spec->all_dacs)); - for (i = 0; i < codec->num_nodes; i++, nid++) { - if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_OUT) - continue; - if (spec->num_all_dacs >= ARRAY_SIZE(spec->all_dacs)) { - snd_printk(KERN_ERR "hda: Too many DACs!\n"); - break; - } - spec->all_dacs[spec->num_all_dacs++] = nid; - } -} - -static int alc_auto_fill_dac_nids(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - struct auto_pin_cfg *best_cfg; - int best_badness = INT_MAX; - int badness; - bool fill_hardwired = true, fill_mio_first = true; - bool best_wired = true, best_mio = true; - bool hp_spk_swapped = false; - - alc_fill_all_nids(codec); - - best_cfg = kmalloc(sizeof(*best_cfg), GFP_KERNEL); - if (!best_cfg) - return -ENOMEM; - *best_cfg = *cfg; - - for (;;) { - badness = fill_and_eval_dacs(codec, fill_hardwired, - fill_mio_first); - if (badness < 0) { - kfree(best_cfg); - return badness; - } - debug_badness("==> lo_type=%d, wired=%d, mio=%d, badness=0x%x\n", - cfg->line_out_type, fill_hardwired, fill_mio_first, - badness); - debug_show_configs(spec, cfg); - if (badness < best_badness) { - best_badness = badness; - *best_cfg = *cfg; - best_wired = fill_hardwired; - best_mio = fill_mio_first; - } - if (!badness) - break; - fill_mio_first = !fill_mio_first; - if (!fill_mio_first) - continue; - fill_hardwired = !fill_hardwired; - if (!fill_hardwired) - continue; - if (hp_spk_swapped) - break; - hp_spk_swapped = true; - if (cfg->speaker_outs > 0 && - cfg->line_out_type == AUTO_PIN_HP_OUT) { - cfg->hp_outs = cfg->line_outs; - memcpy(cfg->hp_pins, cfg->line_out_pins, - sizeof(cfg->hp_pins)); - cfg->line_outs = cfg->speaker_outs; - memcpy(cfg->line_out_pins, cfg->speaker_pins, - sizeof(cfg->speaker_pins)); - cfg->speaker_outs = 0; - memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins)); - cfg->line_out_type = AUTO_PIN_SPEAKER_OUT; - fill_hardwired = true; - continue; - } - if (cfg->hp_outs > 0 && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, - sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - fill_hardwired = true; - continue; - } - break; - } - - if (badness) { - *cfg = *best_cfg; - fill_and_eval_dacs(codec, best_wired, best_mio); - } - debug_badness("==> Best config: lo_type=%d, wired=%d, mio=%d\n", - cfg->line_out_type, best_wired, best_mio); - debug_show_configs(spec, cfg); - - if (cfg->line_out_pins[0]) { - struct nid_path *path = get_nid_path(codec, - spec->multiout.dac_nids[0], - cfg->line_out_pins[0]); - if (path) - spec->vmaster_nid = alc_look_for_out_vol_nid(codec, path); - } - - kfree(best_cfg); - return 0; -} - -/* replace the channels in the composed amp value with the given number */ -static unsigned int amp_val_replace_channels(unsigned int val, unsigned int chs) -{ - val &= ~(0x3U << 16); - val |= chs << 16; - return val; -} - -static int alc_auto_add_vol_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - unsigned int chs, - struct nid_path *path) -{ - unsigned int val; - if (!path) - return 0; - val = path->ctls[NID_PATH_VOL_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - return __add_pb_vol_ctrl(codec->spec, ALC_CTL_WIDGET_VOL, pfx, cidx, val); -} - -/* return the channel bits suitable for the given path->ctls[] */ -static int get_default_ch_nums(struct hda_codec *codec, struct nid_path *path, - int type) -{ - int chs = 1; /* mono (left only) */ - if (path) { - hda_nid_t nid = get_amp_nid_(path->ctls[type]); - if (nid && (get_wcaps(codec, nid) & AC_WCAP_STEREO)) - chs = 3; /* stereo */ - } - return chs; -} - -static int alc_auto_add_stereo_vol(struct hda_codec *codec, - const char *pfx, int cidx, - struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_VOL_CTL); - return alc_auto_add_vol_ctl(codec, pfx, cidx, chs, path); -} - -/* create a mute-switch for the given mixer widget; - * if it has multiple sources (e.g. DAC and loopback), create a bind-mute - */ -static int alc_auto_add_sw_ctl(struct hda_codec *codec, - const char *pfx, int cidx, - unsigned int chs, - struct nid_path *path) -{ - unsigned int val; - int type = ALC_CTL_WIDGET_MUTE; - - if (!path) - return 0; - val = path->ctls[NID_PATH_MUTE_CTL]; - if (!val) - return 0; - val = amp_val_replace_channels(val, chs); - if (get_amp_direction_(val) == HDA_INPUT) { - hda_nid_t nid = get_amp_nid_(val); - int nums = snd_hda_get_num_conns(codec, nid); - if (nums > 1) { - type = ALC_CTL_BIND_MUTE; - val |= nums << 19; - } - } - return __add_pb_sw_ctrl(codec->spec, type, pfx, cidx, val); -} - -static int alc_auto_add_stereo_sw(struct hda_codec *codec, const char *pfx, - int cidx, struct nid_path *path) -{ - int chs = get_default_ch_nums(codec, path, NID_PATH_MUTE_CTL); - return alc_auto_add_sw_ctl(codec, pfx, cidx, chs, path); -} - -static hda_nid_t alc_look_for_out_mute_nid(struct hda_codec *codec, - struct nid_path *path) -{ - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_mute(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - if (i != path->depth - 1 && i != 0 && - nid_has_mute(codec, path->path[i], HDA_INPUT)) - return path->path[i]; - } - return 0; -} - -static hda_nid_t alc_look_for_out_vol_nid(struct hda_codec *codec, - struct nid_path *path) -{ - int i; - - for (i = path->depth - 1; i >= 0; i--) { - if (nid_has_volume(codec, path->path[i], HDA_OUTPUT)) - return path->path[i]; - } - return 0; -} - -/* add playback controls from the parsed DAC table */ -static int alc_auto_create_multi_out_ctls(struct hda_codec *codec, - const struct auto_pin_cfg *cfg) -{ - struct alc_spec *spec = codec->spec; - int i, err, noutputs; - - noutputs = cfg->line_outs; - if (spec->multi_ios > 0 && cfg->line_outs < 3) - noutputs += spec->multi_ios; - - for (i = 0; i < noutputs; i++) { - const char *name; - int index; - hda_nid_t dac, pin; - struct nid_path *path; - - dac = spec->multiout.dac_nids[i]; - if (!dac) - continue; - if (i >= cfg->line_outs) { - pin = spec->multi_io[i - 1].pin; - index = 0; - name = channel_name[i]; - } else { - pin = cfg->line_out_pins[i]; - name = alc_get_line_out_pfx(spec, i, true, &index); - } - - path = get_nid_path(codec, dac, pin); - if (!path) - continue; - if (!name || !strcmp(name, "CLFE")) { - /* Center/LFE */ - err = alc_auto_add_vol_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = alc_auto_add_vol_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "Center", 0, 1, path); - if (err < 0) - return err; - err = alc_auto_add_sw_ctl(codec, "LFE", 0, 2, path); - if (err < 0) - return err; - } else { - err = alc_auto_add_stereo_vol(codec, name, index, path); - if (err < 0) - return err; - err = alc_auto_add_stereo_sw(codec, name, index, path); - if (err < 0) - return err; - } - } - return 0; -} - -static int alc_auto_create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx, - int cidx) -{ - struct nid_path *path; - int err; - - path = get_nid_path(codec, dac, pin); - if (!path) - return 0; - /* bind volume control will be created in the case of dac = 0 */ - if (dac) { - err = alc_auto_add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - } - err = alc_auto_add_stereo_sw(codec, pfx, cidx, path); - if (err < 0) - return err; - return 0; -} - -static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, - unsigned int nums, - struct hda_ctl_ops *ops) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls **ctlp, *ctl; - ctlp = snd_array_new(&spec->bind_ctls); - if (!ctlp) - return NULL; - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); - *ctlp = ctl; - if (ctl) - ctl->ops = ops; - return ctl; -} - -/* add playback controls for speaker and HP outputs */ -static int alc_auto_create_extra_outs(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, - const hda_nid_t *dacs, - const char *pfx) -{ - struct alc_spec *spec = codec->spec; - struct hda_bind_ctls *ctl; - char name[32]; - int i, n, err; - - if (!num_pins || !pins[0]) - return 0; - - if (num_pins == 1) { - hda_nid_t dac = *dacs; - if (!dac) - dac = spec->multiout.dac_nids[0]; - return alc_auto_create_extra_out(codec, *pins, dac, pfx, 0); - } - - for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = alc_auto_create_extra_out(codec, pins[i], dac, - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", - pfx, channel_name[i]); - err = alc_auto_create_extra_out(codec, pins[i], dac, - name, 0); - } else { - err = alc_auto_create_extra_out(codec, pins[i], dac, - pfx, i); - } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); - if (!ctl) - return -ENOMEM; - n = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t vol; - struct nid_path *path; - if (!pins[i] || !dacs[i]) - continue; - path = get_nid_path(codec, dacs[i], pins[i]); - if (!path) - continue; - vol = alc_look_for_out_vol_nid(codec, path); - if (vol) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, ALC_CTL_BIND_VOL, name, 0, (long)ctl); - if (err < 0) - return err; - } - return 0; -} - -static int alc_auto_create_hp_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, - "Headphone"); -} - -static int alc_auto_create_speaker_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - return alc_auto_create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, - "Speaker"); -} - -/* check whether a control with the given (nid, dir, idx) was assigned */ -static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx) -{ - struct alc_spec *spec = codec->spec; - int i, type; - - for (i = 0; i < spec->paths.used; i++) { - struct nid_path *p = snd_array_elem(&spec->paths, i); - if (p->depth <= 0) - continue; - for (type = 0; type < NID_PATH_NUM_CTLS; type++) { - unsigned int val = p->ctls[type]; - if (get_amp_nid_(val) == nid && - get_amp_direction_(val) == dir && - get_amp_index_(val) == idx) - return true; - } - } - return false; -} - -/* can have the amp-in capability? */ -static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_IN_AMP)) - return false; - if (type == AC_WID_PIN && idx > 0) /* only for input pins */ - return false; - return true; -} - -/* can have the amp-out capability? */ -static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx) -{ - hda_nid_t nid = path->path[idx]; - unsigned int caps = get_wcaps(codec, nid); - unsigned int type = get_wcaps_type(caps); - - if (!(caps & AC_WCAP_OUT_AMP)) - return false; - if (type == AC_WID_PIN && !idx) /* only for output pins */ - return false; - return true; -} - -/* check whether the given (nid,dir,idx) is active */ -static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, - unsigned int idx, unsigned int dir) -{ - struct alc_spec *spec = codec->spec; - int i, n; - - for (n = 0; n < spec->paths.used; n++) { - struct nid_path *path = snd_array_elem(&spec->paths, n); - if (!path->active) - continue; - for (i = 0; i < path->depth; i++) { - if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || path->idx[i] == idx) - return true; - break; - } - } - } - return false; -} - -/* get the default amp value for the target state */ -static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, - int dir, bool enable) -{ - unsigned int caps; - unsigned int val = 0; - - caps = query_amp_caps(codec, nid, dir); - if (caps & AC_AMPCAP_NUM_STEPS) { - /* set to 0dB */ - if (enable) - val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; - } - if (caps & AC_AMPCAP_MUTE) { - if (!enable) - val |= HDA_AMP_MUTE; - } - return val; -} - -/* initialize the amp value (only at the first time) */ -static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx) -{ - int val = get_amp_val_to_activate(codec, nid, dir, false); - snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val); -} - -static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, - int idx, bool enable) -{ - int val; - if (is_ctl_associated(codec, nid, dir, idx) || - is_active_nid(codec, nid, dir, idx)) - return; - val = get_amp_val_to_activate(codec, nid, dir, enable); - snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val); -} - -static void activate_amp_out(struct hda_codec *codec, struct nid_path *path, - int i, bool enable) -{ - hda_nid_t nid = path->path[i]; - init_amp(codec, nid, HDA_OUTPUT, 0); - activate_amp(codec, nid, HDA_OUTPUT, 0, enable); -} - -static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, - int i, bool enable, bool add_aamix) -{ - struct alc_spec *spec = codec->spec; - hda_nid_t conn[16]; - int n, nums, idx; - int type; - hda_nid_t nid = path->path[i]; - - nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); - type = get_wcaps_type(get_wcaps(codec, nid)); - if (type == AC_WID_PIN || - (type == AC_WID_AUD_IN && codec->single_adc_amp)) { - nums = 1; - idx = 0; - } else - idx = path->idx[i]; - - for (n = 0; n < nums; n++) - init_amp(codec, nid, HDA_INPUT, n); - - if (is_ctl_associated(codec, nid, HDA_INPUT, idx)) - return; - - /* here is a little bit tricky in comparison with activate_amp_out(); - * when aa-mixer is available, we need to enable the path as well - */ - for (n = 0; n < nums; n++) { - if (n != idx && (!add_aamix || conn[n] != spec->mixer_nid)) - continue; - activate_amp(codec, nid, HDA_INPUT, n, enable); - } -} - -static void activate_path(struct hda_codec *codec, struct nid_path *path, - bool enable, bool add_aamix) -{ - int i; - - if (!enable) - path->active = false; - - for (i = path->depth - 1; i >= 0; i--) { - if (enable && path->multi[i]) - snd_hda_codec_write_cache(codec, path->path[i], 0, - AC_VERB_SET_CONNECT_SEL, - path->idx[i]); - if (has_amp_in(codec, path, i)) - activate_amp_in(codec, path, i, enable, add_aamix); - if (has_amp_out(codec, path, i)) - activate_amp_out(codec, path, i, enable); - } - - if (enable) - path->active = true; -} - -/* configure the path from the given dac to the pin as the proper output */ -static void alc_auto_set_output_and_unmute(struct hda_codec *codec, - hda_nid_t pin, int pin_type, - hda_nid_t dac) -{ - struct nid_path *path; - - snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = get_nid_path(codec, dac, pin); - if (!path) - return; - if (path->active) - return; - activate_path(codec, path, true, true); -} - -static void alc_auto_init_multi_out(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - int pin_type = get_pin_type(spec->autocfg.line_out_type); - int i; +}
- for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - alc_auto_set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]);
- } -} +#ifdef CONFIG_SND_HDA_INPUT_BEEP +/* additional beep mixers; the actual parameters are overwritten at build */ +static const struct snd_kcontrol_new alc_beep_mixer[] = { + HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_INPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_INPUT), + { } /* end */ +}; +#endif
-static void alc_auto_init_extra_out(struct hda_codec *codec) +static int alc_build_controls(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - int i; - hda_nid_t pin, dac; + int i, err;
- for (i = 0; i < spec->autocfg.hp_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - break; - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac); + err = snd_hda_gen_build_controls(codec); + if (err < 0) + return err; + + for (i = 0; i < spec->num_mixers; i++) { + err = snd_hda_add_new_ctls(codec, spec->mixers[i]); + if (err < 0) + return err; } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - break; - pin = spec->autocfg.speaker_pins[i]; - if (!pin) - break; - dac = spec->multiout.extra_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; + +#ifdef CONFIG_SND_HDA_INPUT_BEEP + /* create beep controls if needed */ + if (spec->beep_amp) { + const struct snd_kcontrol_new *knew; + for (knew = alc_beep_mixer; knew->name; knew++) { + struct snd_kcontrol *kctl; + kctl = snd_ctl_new1(knew, codec); + if (!kctl) + return -ENOMEM; + kctl->private_value = spec->beep_amp; + err = snd_hda_ctl_add(codec, 0, kctl); + if (err < 0) + return err; } - alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac); } -} +#endif
-/* check whether the given pin can be a multi-io pin */ -static bool can_be_multiio_pin(struct hda_codec *codec, - unsigned int location, hda_nid_t nid) -{ - unsigned int defcfg, caps; - - defcfg = snd_hda_codec_get_pincfg(codec, nid); - if (get_defcfg_connect(defcfg) != AC_JACK_PORT_COMPLEX) - return false; - if (location && get_defcfg_location(defcfg) != location) - return false; - caps = snd_hda_query_pin_caps(codec, nid); - if (!(caps & AC_PINCAP_OUT)) - return false; - return true; + alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + return 0; }
+ /* - * multi-io helper - * - * When hardwired is set, try to fill ony hardwired pins, and returns - * zero if any pins are filled, non-zero if nothing found. - * When hardwired is off, try to fill possible input pins, and returns - * the badness value. + * Common callbacks */ -static int alc_auto_fill_multi_ios(struct hda_codec *codec, - hda_nid_t reference_pin, - bool hardwired, int offset) + +static int alc_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, num_pins, old_pins; - unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); - unsigned int location = get_defcfg_location(defcfg); - int badness = 0; - - old_pins = spec->multi_ios; - if (old_pins >= 2) - goto end_fill; - - num_pins = 0; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } - if (num_pins < 2) - goto end_fill; - - dacs = spec->multiout.num_dacs; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - hda_nid_t nid = cfg->inputs[i].pin; - hda_nid_t dac = 0; - - if (cfg->inputs[i].type != type) - continue; - if (!can_be_multiio_pin(codec, location, nid)) - continue; - for (j = 0; j < spec->multi_ios; j++) { - if (nid == spec->multi_io[j].pin) - break; - } - if (j < spec->multi_ios) - continue; - - if (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!is_reachable_path(codec, dac, nid)) - dac = 0; - } - if (hardwired) - dac = get_dac_if_single(codec, nid); - else if (!dac) - dac = alc_auto_look_for_dac(codec, nid, false); - if (!dac) { - badness++; - continue; - } - if (!add_new_nid_path(codec, dac, nid, 0)) { - badness++; - continue; - } - spec->multi_io[spec->multi_ios].pin = nid; - spec->multi_io[spec->multi_ios].dac = dac; - spec->multi_ios++; - if (spec->multi_ios >= 2) - break; - } - } - end_fill: - if (badness) - badness = BAD_MULTI_IO; - if (old_pins == spec->multi_ios) { - if (hardwired) - return 1; /* nothing found */ - else - return badness; /* no badness if nothing found */ - } - if (!hardwired && spec->multi_ios < 2) { - /* cancel newly assigned paths */ - spec->paths.used -= spec->multi_ios - old_pins; - spec->multi_ios = old_pins; - return badness; - }
- /* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) - badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, - spec->multi_io[i].dac); + if (spec->init_hook) + spec->init_hook(codec);
- return badness; -} + alc_fix_pll(codec); + alc_auto_init_amp(codec, spec->init_amp);
-static int alc_auto_ch_mode_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); - struct alc_spec *spec = codec->spec; + snd_hda_gen_init(codec); + + alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; - uinfo->count = 1; - uinfo->value.enumerated.items = spec->multi_ios + 1; - if (uinfo->value.enumerated.item > spec->multi_ios) - uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); return 0; }
-static int alc_auto_ch_mode_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +#ifdef CONFIG_PM +static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; - return 0; + return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); } +#endif
-static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output) +static inline void alc_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - hda_nid_t nid = spec->multi_io[idx].pin; - struct nid_path *path; - - path = get_nid_path(codec, spec->multi_io[idx].dac, nid); - if (!path) - return -EINVAL; - - if (path->active == output) - return 0;
- if (output) { - snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); - activate_path(codec, path, true, true); - } else { - activate_path(codec, path, false, true); - snd_hda_set_pin_ctl_cache(codec, nid, - spec->multi_io[idx].ctl_in); - } - return 0; + if (spec && spec->shutup) + spec->shutup(codec); + snd_hda_shutup_pins(codec); }
-static int alc_auto_ch_mode_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static void alc_free(struct hda_codec *codec) { - struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct alc_spec *spec = codec->spec; - int i, ch;
- ch = ucontrol->value.enumerated.item[0]; - if (ch < 0 || ch > spec->multi_ios) - return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) - return 0; - spec->ext_channel_count = (ch + 1) * 2; - for (i = 0; i < spec->multi_ios; i++) - alc_set_multi_io(codec, i, i < ch); - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - if (spec->need_dac_fix) - spec->multiout.num_dacs = spec->multiout.max_channels / 2; - return 1; + if (!spec) + return; + + snd_hda_gen_spec_free(&spec->gen); + snd_hda_detach_beep_device(codec); + kfree(spec); }
-static const struct snd_kcontrol_new alc_auto_channel_mode_enum = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Channel Mode", - .info = alc_auto_ch_mode_info, - .get = alc_auto_ch_mode_get, - .put = alc_auto_ch_mode_put, -}; +#ifdef CONFIG_PM +static void alc_power_eapd(struct hda_codec *codec) +{ + alc_auto_setup_eapd(codec, false); +}
-static int alc_auto_add_multi_channel_mode(struct hda_codec *codec) +static int alc_suspend(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - - if (spec->multi_ios > 0) { - if (!alc_kcontrol_new(spec, NULL, &alc_auto_channel_mode_enum)) - return -ENOMEM; - } + alc_shutup(codec); + if (spec && spec->power_hook) + spec->power_hook(codec); return 0; } +#endif
-static void alc_auto_init_multi_io(struct hda_codec *codec) +#ifdef CONFIG_PM +static int alc_resume(struct hda_codec *codec) { - struct alc_spec *spec = codec->spec; - int i; - - for (i = 0; i < spec->multi_ios; i++) { - hda_nid_t pin = spec->multi_io[i].pin; - struct nid_path *path; - path = get_nid_path(codec, spec->multi_io[i].dac, pin); - if (!path) - continue; - if (!spec->multi_io[i].ctl_in) - spec->multi_io[i].ctl_in = - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - activate_path(codec, path, path->active, true); - } + msleep(150); /* to avoid pop noise */ + codec->patch_ops.init(codec); + snd_hda_codec_resume_amp(codec); + snd_hda_codec_resume_cache(codec); + alc_inv_dmic_sync(codec, true); + hda_call_check_power_status(codec, 0x01); + return 0; } +#endif
/* - * initialize ADC paths */ -static void alc_auto_init_input_src(struct hda_codec *codec) -{ - struct alc_spec *spec = codec->spec; - struct hda_input_mux *imux = &spec->input_mux; - struct nid_path *path; - int i, c, nums; - - if (spec->dyn_adc_switch) - nums = 1; - else - nums = spec->num_adc_nids; - - for (c = 0; c < nums; c++) { - for (i = 0; i < imux->num_items; i++) { - path = get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, c, i)); - if (path) { - bool active = path->active; - if (i == spec->cur_mux[c]) - active = true; - activate_path(codec, path, active, false); - } - } - } +static const struct hda_codec_ops alc_patch_ops = { + .build_controls = alc_build_controls, + .build_pcms = snd_hda_gen_build_pcms, + .init = alc_init, + .free = alc_free, + .unsol_event = snd_hda_jack_unsol_event, +#ifdef CONFIG_PM + .resume = alc_resume, +#endif +#ifdef CONFIG_PM + .suspend = alc_suspend, + .check_power_status = alc_check_power_status, +#endif + .reboot_notify = alc_shutup, +};
- alc_inv_dmic_sync(codec, true); - if (spec->shared_mic_hp) - update_shared_mic_hp(codec, spec->cur_mux[0]); -}
-/* add mic boosts if needed */ -static int alc_auto_add_mic_boost(struct hda_codec *codec) +/* replace the codec chip_name with the given string */ +static int alc_codec_rename(struct hda_codec *codec, const char *name) { - struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; - int i, err; - int type_idx = 0; - hda_nid_t nid; - const char *prev_label = NULL; - - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type > AUTO_PIN_MIC) - break; - nid = cfg->inputs[i].pin; - if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { - const char *label; - char boost_label[32]; - struct nid_path *path; - unsigned int val; - - label = hda_get_autocfg_input_label(codec, cfg, i); - if (spec->shared_mic_hp && !strcmp(label, "Misc")) - label = "Headphone Mic"; - if (prev_label && !strcmp(label, prev_label)) - type_idx++; - else - type_idx = 0; - prev_label = label; - - snprintf(boost_label, sizeof(boost_label), - "%s Boost Volume", label); - val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT); - err = add_control(spec, ALC_CTL_WIDGET_VOL, - boost_label, type_idx, val); - if (err < 0) - return err; - - path = get_nid_path(codec, nid, 0); - if (path) - path->ctls[NID_PATH_BOOST_CTL] = val; - } + kfree(codec->chip_name); + codec->chip_name = kstrdup(name, GFP_KERNEL); + if (!codec->chip_name) { + alc_free(codec); + return -ENOMEM; } return 0; }
/* - * standard auto-parser initializations + * Rename codecs appropriately from COEF value */ -static void alc_auto_init_std(struct hda_codec *codec) +struct alc_codec_rename_table { + unsigned int vendor_id; + unsigned short coef_mask; + unsigned short coef_bits; + const char *name; +}; + +static struct alc_codec_rename_table rename_tbl[] = { + { 0x10ec0269, 0xfff0, 0x3010, "ALC277" }, + { 0x10ec0269, 0xf0f0, 0x2010, "ALC259" }, + { 0x10ec0269, 0xf0f0, 0x3010, "ALC258" }, + { 0x10ec0269, 0x00f0, 0x0010, "ALC269VB" }, + { 0x10ec0269, 0xffff, 0xa023, "ALC259" }, + { 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, + { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, + { 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" }, + { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, + { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, + { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, + { 0x10ec0899, 0x2000, 0x2000, "ALC899" }, + { 0x10ec0892, 0xffff, 0x8020, "ALC661" }, + { 0x10ec0892, 0xffff, 0x8011, "ALC661" }, + { 0x10ec0892, 0xffff, 0x4011, "ALC656" }, + { } /* terminator */ +}; + +static int alc_codec_rename_from_preset(struct hda_codec *codec) { - alc_auto_init_multi_out(codec); - alc_auto_init_extra_out(codec); - alc_auto_init_multi_io(codec); - alc_auto_init_analog_input(codec); - alc_auto_init_input_src(codec); - alc_auto_init_digital(codec); - /* call init functions of standard auto-mute helpers */ - alc_hp_automute(codec, NULL); - alc_line_automute(codec, NULL); - alc_mic_automute(codec, NULL); + const struct alc_codec_rename_table *p; + + for (p = rename_tbl; p->vendor_id; p++) { + if (p->vendor_id != codec->vendor_id) + continue; + if ((alc_get_coef0(codec) & p->coef_mask) == p->coef_bits) + return alc_codec_rename(codec, p->name); + } + return 0; }
+ /* * Digital-beep handlers */ @@ -4436,102 +978,20 @@ static int alc_parse_auto_config(struct hda_codec *codec, const hda_nid_t *ssid_nids) { struct alc_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; + struct auto_pin_cfg *cfg = &spec->gen.autocfg; int err;
err = snd_hda_parse_pin_defcfg(codec, cfg, ignore_nids, spec->parse_flags); if (err < 0) return err; - if (!cfg->line_outs) { - if (cfg->dig_outs || cfg->dig_in_pin) { - spec->multiout.max_channels = 2; - spec->no_analog = 1; - goto dig_only; - } - return 0; /* can't find valid BIOS pin config */ - } - - if (!spec->no_primary_hp && - cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && - cfg->line_outs <= cfg->hp_outs) { - /* use HP as primary out */ - cfg->speaker_outs = cfg->line_outs; - memcpy(cfg->speaker_pins, cfg->line_out_pins, - sizeof(cfg->speaker_pins)); - cfg->line_outs = cfg->hp_outs; - memcpy(cfg->line_out_pins, cfg->hp_pins, sizeof(cfg->hp_pins)); - cfg->hp_outs = 0; - memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins)); - cfg->line_out_type = AUTO_PIN_HP_OUT; - } - - err = alc_auto_fill_dac_nids(codec); - if (err < 0) - return err; - err = alc_auto_add_multi_channel_mode(codec); - if (err < 0) - return err; - err = alc_auto_create_multi_out_ctls(codec, cfg); - if (err < 0) - return err; - err = alc_auto_create_hp_out(codec); - if (err < 0) - return err; - err = alc_auto_create_speaker_out(codec); - if (err < 0) - return err; - err = alc_auto_create_shared_input(codec); - if (err < 0) - return err; - err = alc_auto_create_input_ctls(codec); - if (err < 0) - return err; - - /* check the multiple speaker pins */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = cfg->line_outs * 2; - else - spec->const_channel_count = cfg->speaker_outs * 2; - - if (spec->multi_ios > 0) - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - else - spec->multiout.max_channels = spec->multiout.num_dacs * 2; - - dig_only: - alc_auto_parse_digital(codec);
if (ssid_nids) alc_ssid_check(codec, ssid_nids);
- if (!spec->no_analog) { - err = alc_init_automute(codec); - if (err < 0) - return err; - - err = check_dyn_adc_switch(codec); - if (err < 0) - return err; - - if (!spec->shared_mic_hp) { - err = alc_init_auto_mic(codec); - if (err < 0) - return err; - } - - err = create_capture_mixers(codec); - if (err < 0) - return err; - - err = alc_auto_add_mic_boost(codec); - if (err < 0) - return err; - } - - if (spec->kctls.list) - add_mixer(spec, spec->kctls.list); + err = snd_hda_gen_parse_auto_config(codec, cfg); + if (err < 0) + return err;
return 1; } @@ -4545,11 +1005,12 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) if (!spec) return -ENOMEM; codec->spec = spec; + snd_hda_gen_spec_init(&spec->gen); + spec->gen.mixer_nid = mixer_nid; + spec->gen.own_eapd_ctl = 1; codec->single_adc_amp = 1; - spec->mixer_nid = mixer_nid; - snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); - snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + /* FIXME: do we need this for all Realtek codec models? */ + codec->spdif_status_reset = 1;
err = alc_codec_rename_from_preset(codec); if (err < 0) { @@ -4945,7 +1406,7 @@ static int patch_alc880(struct hda_codec *codec) return err;
spec = codec->spec; - spec->need_dac_fix = 1; + spec->gen.need_dac_fix = 1;
alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); @@ -4956,7 +1417,7 @@ static int patch_alc880(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5005,7 +1466,7 @@ static void alc260_gpio1_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, - spec->hp_jack_present); + spec->gen.hp_jack_present); }
static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, @@ -5016,12 +1477,12 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ - spec->automute_hook = alc260_gpio1_automute; - spec->detect_hp = 1; - spec->automute_speaker = 1; - spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ - snd_hda_jack_detect_enable_callback(codec, 0x0f, ALC_HP_EVENT, - alc_hp_automute); + spec->gen.automute_hook = alc260_gpio1_automute; + spec->gen.detect_hp = 1; + spec->gen.automute_speaker = 1; + spec->gen.autocfg.hp_pins[0] = 0x0f; /* copy it for automute */ + snd_hda_jack_detect_enable_callback(codec, 0x0f, HDA_GEN_HP_EVENT, + snd_hda_gen_hp_automute); snd_hda_add_verbs(codec, alc_gpio1_init_verbs); } } @@ -5147,7 +1608,7 @@ static int patch_alc260(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5304,7 +1765,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); val |= AC_PINCTL_VREF_80; snd_hda_set_pin_ctl(codec, nids[i], val); - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; break; } } @@ -5326,7 +1787,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, nids[i], val); } - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; }
/* Don't take HP output as primary @@ -5337,7 +1798,7 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PRE_PROBE) - spec->no_primary_hp = 1; + spec->gen.no_primary_hp = 1; }
static const struct alc_fixup alc882_fixups[] = { @@ -5656,7 +2117,7 @@ static int patch_alc882(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5782,7 +2243,7 @@ static int patch_alc262(struct hda_codec *codec) return err;
spec = codec->spec; - spec->shared_mic_vref_pin = 0x18; + spec->gen.shared_mic_vref_pin = 0x18;
#if 0 /* pshou 07/11/05 set a zero PCM sample to DAC when FIFO is @@ -5809,7 +2270,7 @@ static int patch_alc262(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -5897,7 +2358,8 @@ static int alc268_parse_auto_config(struct hda_codec *codec) struct alc_spec *spec = codec->spec; int err = alc_parse_auto_config(codec, NULL, alc268_ssids); if (err > 0) { - if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) { + if (!spec->gen.no_analog && + spec->gen.autocfg.speaker_pins[0] != 0x1d) { add_mixer(spec, alc268_beep_mixer); snd_hda_add_verbs(codec, alc268_beep_init_verbs); } @@ -5963,6 +2425,35 @@ static int patch_alc268(struct hda_codec *codec) /* * ALC269 */ + +static int playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + hinfo); +} + +static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); +} + +static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); +} + static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .substreams = 1, .channels_min = 2, @@ -5970,9 +2461,9 @@ static const struct hda_pcm_stream alc269_44k_pcm_analog_playback = { .rates = SNDRV_PCM_RATE_44100, /* fixed rate */ /* NID is set in alc_build_pcms */ .ops = { - .open = alc_playback_pcm_open, - .prepare = alc_playback_pcm_prepare, - .cleanup = alc_playback_pcm_cleanup + .open = playback_pcm_open, + .prepare = playback_pcm_prepare, + .cleanup = playback_pcm_cleanup }, };
@@ -6121,8 +2612,8 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, /* Due to a hardware problem on Lenovo Ideadpad, we need to * fix the sample rate of analog I/O to 44.1kHz */ - spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; - spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; + spec->gen.stream_analog_playback = &alc269_44k_pcm_analog_playback; + spec->gen.stream_analog_capture = &alc269_44k_pcm_analog_capture; }
static void alc269_fixup_stereo_dmic(struct hda_codec *codec, @@ -6143,7 +2634,7 @@ static void alc269_fixup_stereo_dmic(struct hda_codec *codec,
static void alc269_quanta_automute(struct hda_codec *codec) { - update_outputs(codec); + snd_hda_gen_update_outputs(codec);
snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_COEF_INDEX, 0x0c); @@ -6162,7 +2653,7 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, struct alc_spec *spec = codec->spec; if (action != ALC_FIXUP_ACT_PROBE) return; - spec->automute_hook = alc269_quanta_automute; + spec->gen.automute_hook = alc269_quanta_automute; }
/* update mute-LED according to the speaker mute state via mic1 VREF pin */ @@ -6179,7 +2670,7 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PROBE) - spec->vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; }
/* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -6195,7 +2686,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, { struct alc_spec *spec = codec->spec; if (action == ALC_FIXUP_ACT_PROBE) - spec->vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; }
static void alc271_hp_gate_mic_jack(struct hda_codec *codec, @@ -6204,11 +2695,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, { struct alc_spec *spec = codec->spec;
- if (snd_BUG_ON(!spec->am_entry[1].pin || !spec->autocfg.hp_pins[0])) + if (snd_BUG_ON(!spec->gen.am_entry[1].pin || + !spec->gen.autocfg.hp_pins[0])) return; if (action == ALC_FIXUP_ACT_PROBE) - snd_hda_jack_set_gating_jack(codec, spec->am_entry[1].pin, - spec->autocfg.hp_pins[0]); + snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, + spec->gen.autocfg.hp_pins[0]); }
enum { @@ -6554,7 +3046,7 @@ static int patch_alc269(struct hda_codec *codec) return err;
spec = codec->spec; - spec->shared_mic_vref_pin = 0x18; + spec->gen.shared_mic_vref_pin = 0x18;
alc_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); @@ -6594,7 +3086,7 @@ static int patch_alc269(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error; @@ -6650,7 +3142,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, 0x0f, val); - spec->keep_vref_in_automute = 1; + spec->gen.keep_vref_in_automute = 1; }
/* suppress the jack-detection */ @@ -6717,7 +3209,7 @@ static int patch_alc861(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -6812,7 +3304,7 @@ static int patch_alc861vd(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog) { + if (!spec->gen.no_analog) { err = snd_hda_attach_beep_device(codec, 0x23); if (err < 0) goto error; @@ -7211,7 +3703,7 @@ static int patch_alc662(struct hda_codec *codec) if (err < 0) goto error;
- if (!spec->no_analog && has_cdefine_beep(codec)) { + if (!spec->gen.no_analog && has_cdefine_beep(codec)) { err = snd_hda_attach_beep_device(codec, 0x1); if (err < 0) goto error;
In general we prefer "Capture Source" to "Input Source". The latter was chosen in many places just because "Capture Source" label doesn't work well with the current alsa-lib mixer abstraction when multiple instances are present. But when we know that there is a single input-source element, we can safely choose "Capture Source" label.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6914d70..f8d1d03 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2172,7 +2172,9 @@ static int create_capture_mixers(struct hda_codec *codec)
if (!spec->auto_mic && imux->num_items > 1) { struct snd_kcontrol_new *knew; - knew = snd_hda_gen_add_kctl(spec, NULL, &cap_src_temp); + const char *name; + name = nums > 1 ? "Input Source" : "Capture Source"; + knew = snd_hda_gen_add_kctl(spec, name, &cap_src_temp); if (!knew) return -ENOMEM; knew->count = nums;
The commit [2e9bf24: ALSA: hda_codec: Check for invalid zero connections] trims the whole connection list when an invalid value is reported by the hardware. But some codecs (at least AD1986A) may give a zero NID in the middle of the connection list, so dropping the whole list isn't good for such cases.
In this patch, as a workaround, allow a single zero NID in the read connection list. If it hits zero twice, it's handled as an error, so that we can avoid "too many connections" errors.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 12b88a2..650498b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -424,6 +424,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, unsigned int shift, num_elems, mask; unsigned int wcaps; hda_nid_t prev_nid; + int null_count = 0;
if (snd_BUG_ON(!conn_list || max_conns <= 0)) return -EINVAL; @@ -474,7 +475,7 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, } range_val = !!(parm & (1 << (shift-1))); /* ranges */ val = parm & mask; - if (val == 0) { + if (val == 0 && null_count++) { /* no second chance */ snd_printk(KERN_WARNING "hda_codec: " "invalid CONNECT_LIST verb %x[%i]:%x\n", nid, i, parm);
When verbs or amps are actually written to the hardware, we can clear dirty flag so that the later snd_hda_codec_resume_*() calls can skip these verbs / amps.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 650498b..44b525f 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1848,6 +1848,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, bool init_only) { struct hda_amp_info *info; + unsigned int cache_only;
if (snd_BUG_ON(mask & ~0xff)) mask &= 0xff; @@ -1865,10 +1866,9 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, return 0; } info->vol[ch] = val; - if (codec->cached_write) - info->head.dirty = 1; + cache_only = info->head.dirty = codec->cached_write; mutex_unlock(&codec->hash_mutex); - if (!codec->cached_write) + if (!cache_only) put_vol_mute(codec, info, nid, ch, direction, idx, val); return 1; } @@ -3450,8 +3450,10 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, int err; struct hda_cache_head *c; u32 key; + unsigned int cache_only;
- if (!codec->cached_write) { + cache_only = codec->cached_write; + if (!cache_only) { err = snd_hda_codec_write(codec, nid, direct, verb, parm); if (err < 0) return err; @@ -3465,8 +3467,7 @@ int snd_hda_codec_write_cache(struct hda_codec *codec, hda_nid_t nid, c = get_alloc_hash(&codec->cmd_cache, key); if (c) c->val = parm; - if (codec->cached_write) - c->dirty = 1; + c->dirty = cache_only; mutex_unlock(&codec->bus->cmd_mutex); return 0; }
These functions are supposed to be called at finishing the cached sequential writes, so clear the flag properly for lazy developers who often forget details.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 44b525f..df4abbe 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1955,6 +1955,7 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) int i;
mutex_lock(&codec->hash_mutex); + codec->cached_write = 0; for (i = 0; i < codec->amp_cache.buf.used; i++) { struct hda_amp_info *buffer; u32 key; @@ -3519,6 +3520,7 @@ void snd_hda_codec_resume_cache(struct hda_codec *codec) int i;
mutex_lock(&codec->hash_mutex); + codec->cached_write = 0; for (i = 0; i < codec->cmd_cache.buf.used; i++) { struct hda_cache_head *buffer; u32 key;
Add an overflow check of CORB in HD-audio controller and codec drivers so that flood of sequential writes would work properly. In the controller side, add a check of CORB read-pointer to make returning -EAGAIN when it's full. Meanwhile in the codec side, when -EAGAIN error is received, it retries the write after flushing the pending verbs (calling get_response() essentially does it).
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 10 ++++++++-- sound/pci/hda/hda_intel.c | 11 +++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index df4abbe..f362ef7 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -222,8 +222,14 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd, again: snd_hda_power_up(codec); mutex_lock(&bus->cmd_mutex); - trace_hda_send_cmd(codec, cmd); - err = bus->ops.command(bus, cmd); + for (;;) { + trace_hda_send_cmd(codec, cmd); + err = bus->ops.command(bus, cmd); + if (err != -EAGAIN) + break; + /* process pending verbs */ + bus->ops.get_response(bus, codec->addr); + } if (!err && res) { *res = bus->ops.get_response(bus, codec->addr); trace_hda_get_response(codec, *res); diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index cca8727..d0f42d1 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -794,7 +794,7 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) { struct azx *chip = bus->private_data; unsigned int addr = azx_command_addr(val); - unsigned int wp; + unsigned int wp, rp;
spin_lock_irq(&chip->reg_lock);
@@ -803,11 +803,18 @@ static int azx_corb_send_cmd(struct hda_bus *bus, u32 val) if (wp == 0xffff) { /* something wrong, controller likely turned to D3 */ spin_unlock_irq(&chip->reg_lock); - return -1; + return -EIO; } wp++; wp %= ICH6_MAX_CORB_ENTRIES;
+ rp = azx_readw(chip, CORBRP); + if (wp == rp) { + /* oops, it's full */ + spin_unlock_irq(&chip->reg_lock); + return -EAGAIN; + } + chip->rirb.cmds[addr]++; chip->corb.buf[wp] = cpu_to_le32(val); azx_writel(chip, CORBWP, wp);
The inverted dmic fix overwrites the right channel amp value, but it would work only when the amp values have been already actually written. Put snd_hda_codec_resume_amp() before the amp write for flushing caches.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/patch_realtek.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 17da84d..20e657b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -633,6 +633,9 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) parm = AC_AMP_SET_RIGHT | (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
+ /* flush all cached amps at first */ + snd_hda_codec_resume_amp(codec); + /* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); if (val & 0x80) /* if already muted, we don't need to touch */
It makes easier to understand although these are identical with snd_hda_codec_resume_*() functions.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.h | 4 ++++ sound/pci/hda/hda_local.h | 4 ++++ sound/pci/hda/patch_realtek.c | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index a1cb28f..2d9a51c 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -970,6 +970,10 @@ int snd_hda_codec_update_cache(struct hda_codec *codec, hda_nid_t nid, int direct, unsigned int verb, unsigned int parm); void snd_hda_codec_resume_cache(struct hda_codec *codec);
+/* it's alias but a bit clearer meaning */ +#define snd_hda_codec_flush_cmd_cache(codec) \ + snd_hda_codec_resume_cache(codec) + /* the struct for codec->pin_configs */ struct hda_pincfg { hda_nid_t nid; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index de12dcc..fec0e2d 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -139,6 +139,10 @@ int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, int dir, int idx, int mask, int val); void snd_hda_codec_resume_amp(struct hda_codec *codec);
+/* it's alias but a bit clearer meaning */ +#define snd_hda_codec_flush_amp_cache(codec) \ + snd_hda_codec_resume_amp(codec) + void snd_hda_set_vmaster_tlv(struct hda_codec *codec, hda_nid_t nid, int dir, unsigned int *tlv); struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 20e657b..0b32729 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -634,7 +634,7 @@ static void alc_inv_dmic_sync_adc(struct hda_codec *codec, int adc_idx) (dir == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT);
/* flush all cached amps at first */ - snd_hda_codec_resume_amp(codec); + snd_hda_codec_flush_amp_cache(codec);
/* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0);
The bound capture volume and switch controls use the cached amp updates, but it's missing the flushing at the end.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f8d1d03..3e818b6 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1919,6 +1919,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, error: codec->cached_write = 0; mutex_unlock(&codec->control_mutex); + snd_hda_codec_flush_amp_cache(codec); if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec); return err;
A bit of details won't hurt.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 3e818b6..f5eb57c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1877,10 +1877,6 @@ static int mux_enum_put(struct snd_kcontrol *kcontrol, ucontrol->value.enumerated.item[0]); }
-/* - * capture volume and capture switch ctls - */ - static const struct snd_kcontrol_new cap_src_temp = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Input Source", @@ -1889,9 +1885,14 @@ static const struct snd_kcontrol_new cap_src_temp = { .put = mux_enum_put, };
+/* + * capture volume and capture switch ctls + */ + typedef int (*put_call_t)(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+/* call the given amp update function for all amps in the imux list at once */ static int cap_put_caller(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol, put_call_t func, int type) @@ -1905,6 +1906,10 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, imux = &spec->input_mux; adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); mutex_lock(&codec->control_mutex); + /* we use the cache-only update at first since multiple input paths + * may shared the same amp; by updating only caches, the redundant + * writes to hardware can be reduced. + */ codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { path = snd_hda_get_nid_path(codec, spec->imux_pins[i], @@ -1919,7 +1924,7 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, error: codec->cached_write = 0; mutex_unlock(&codec->control_mutex); - snd_hda_codec_flush_amp_cache(codec); + snd_hda_codec_flush_amp_cache(codec); /* flush the updates */ if (err >= 0 && spec->cap_sync_hook) spec->cap_sync_hook(codec); return err;
This would reduce the number of actually executed verbs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f5eb57c..6fb454e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3540,6 +3540,8 @@ int snd_hda_gen_init(struct hda_codec *codec)
snd_hda_apply_verbs(codec);
+ codec->cached_write = 1; + init_multi_out(codec); init_extra_out(codec); init_multi_io(codec); @@ -3552,6 +3554,9 @@ int snd_hda_gen_init(struct hda_codec *codec) snd_hda_gen_line_automute(codec, NULL); snd_hda_gen_mic_autoswitch(codec, NULL);
+ snd_hda_codec_flush_amp_cache(codec); + snd_hda_codec_flush_cmd_cache(codec); + if (spec->vmaster_mute.sw_kctl && spec->vmaster_mute.hook) snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
The dirty entry has to be checked at the beginning in the loop, not in the inner loop for channels. This caused a regression that the right channel isn't properly written.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index f362ef7..48591ab 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1969,6 +1969,9 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) unsigned int idx, dir, ch;
buffer = snd_array_elem(&codec->amp_cache.buf, i); + if (!buffer->head.dirty) + continue; + buffer->head.dirty = 0; key = buffer->head.key; if (!key) continue; @@ -1978,9 +1981,6 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) for (ch = 0; ch < 2; ch++) { if (!(buffer->head.val & INFO_AMP_VOL(ch))) continue; - if (!buffer->head.dirty) - continue; - buffer->head.dirty = 0; mutex_unlock(&codec->hash_mutex); put_vol_mute(codec, buffer, nid, ch, dir, idx, buffer->vol[ch]);
The access to a cache array element could be invalid outside the mutex, so copy locally for the later references.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 48591ab..9eb73b0 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1807,7 +1807,7 @@ update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch, /* * write the current volume in info to the h/w */ -static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, +static void put_vol_mute(struct hda_codec *codec, unsigned int amp_caps, hda_nid_t nid, int ch, int direction, int index, int val) { @@ -1816,8 +1816,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, parm = ch ? AC_AMP_SET_RIGHT : AC_AMP_SET_LEFT; parm |= direction == HDA_OUTPUT ? AC_AMP_SET_OUTPUT : AC_AMP_SET_INPUT; parm |= index << AC_AMP_SET_INDEX_SHIFT; - if ((val & HDA_AMP_MUTE) && !(info->amp_caps & AC_AMPCAP_MUTE) && - (info->amp_caps & AC_AMPCAP_MIN_MUTE)) + if ((val & HDA_AMP_MUTE) && !(amp_caps & AC_AMPCAP_MUTE) && + (amp_caps & AC_AMPCAP_MIN_MUTE)) ; /* set the zero value as a fake mute */ else parm |= val; @@ -1854,6 +1854,7 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, bool init_only) { struct hda_amp_info *info; + unsigned int caps; unsigned int cache_only;
if (snd_BUG_ON(mask & ~0xff)) @@ -1873,9 +1874,10 @@ static int codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, } info->vol[ch] = val; cache_only = info->head.dirty = codec->cached_write; + caps = info->amp_caps; mutex_unlock(&codec->hash_mutex); if (!cache_only) - put_vol_mute(codec, info, nid, ch, direction, idx, val); + put_vol_mute(codec, caps, nid, ch, direction, idx, val); return 1; }
@@ -1967,23 +1969,25 @@ void snd_hda_codec_resume_amp(struct hda_codec *codec) u32 key; hda_nid_t nid; unsigned int idx, dir, ch; + struct hda_amp_info info;
buffer = snd_array_elem(&codec->amp_cache.buf, i); if (!buffer->head.dirty) continue; buffer->head.dirty = 0; - key = buffer->head.key; + info = *buffer; + key = info.head.key; if (!key) continue; nid = key & 0xff; idx = (key >> 16) & 0xff; dir = (key >> 24) & 0xff; for (ch = 0; ch < 2; ch++) { - if (!(buffer->head.val & INFO_AMP_VOL(ch))) + if (!(info.head.val & INFO_AMP_VOL(ch))) continue; mutex_unlock(&codec->hash_mutex); - put_vol_mute(codec, buffer, nid, ch, dir, idx, - buffer->vol[ch]); + put_vol_mute(codec, info.amp_caps, nid, ch, dir, idx, + info.vol[ch]); mutex_lock(&codec->hash_mutex); } }
Old codecs like AD1986A tend to have long paths as they were just made to be the way like AC97. The current max depth 5 can be too short for such devices.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_generic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 9eb73b0..380c9ed 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -591,7 +591,7 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, return i; if (!recursive) return -1; - if (recursive > 5) { + if (recursive > 10) { snd_printd("hda_codec: too deep connection for 0x%x\n", nid); return -1; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 417ab65..89ad877 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -37,7 +37,7 @@ struct hda_multi_io { * vol_ctl and mute_ctl contains the NIDs for the assigned mixers */
-#define MAX_NID_PATH_DEPTH 5 +#define MAX_NID_PATH_DEPTH 10
enum { NID_PATH_VOL_CTL,
... to distinguish from the invalid event type.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 89ad877..b598899 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -14,7 +14,7 @@
/* unsol event tags */ enum { - HDA_GEN_HP_EVENT, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, + HDA_GEN_HP_EVENT = 1, HDA_GEN_FRONT_EVENT, HDA_GEN_MIC_EVENT, HDA_GEN_LAST_EVENT = HDA_GEN_MIC_EVENT };
Add a flag to indicate whether the vmaster mute hook enum is exposed or not. Conexant codecs may want not to expose the control depending on the model.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 3 ++- sound/pci/hda/hda_generic.h | 1 + sound/pci/hda/patch_realtek.c | 8 ++++++-- 3 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 6fb454e..a5c4bc0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2966,7 +2966,8 @@ int snd_hda_gen_build_controls(struct hda_codec *codec) if (err < 0) return err; if (spec->vmaster_mute.hook) - snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, true); + snd_hda_add_vmaster_hook(codec, &spec->vmaster_mute, + spec->vmaster_mute_enum); }
free_kctls(spec); /* no longer needed */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index b598899..a406cd4 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -151,6 +151,7 @@ struct hda_gen_spec { unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ + unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */
/* for virtual master */ hda_nid_t vmaster_nid; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0b32729..1c343f2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2672,8 +2672,10 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + spec->gen.vmaster_mute_enum = 1; + } }
/* update mute-LED according to the speaker mute state via mic2 VREF pin */ @@ -2688,8 +2690,10 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct alc_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == ALC_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->gen.vmaster_mute_enum = 1; + } }
static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
For preliminary works to migrate the generic parser for Conexant codecs: the same function is ported to hda_generic.c. But now it looks through the jack detect table so that it can cover better.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a5c4bc0..b7b8d7e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3532,6 +3532,23 @@ static void init_digital(struct hda_codec *codec) snd_hda_set_pin_ctl(codec, pin, PIN_IN); }
+/* clear unsol-event tags on unused pins; Conexant codecs seem to leave + * invalid unsol tags by some reason + */ +static void clear_unsol_on_unused_pins(struct hda_codec *codec) +{ + int i; + + for (i = 0; i < codec->init_pins.used; i++) { + struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); + hda_nid_t nid = pin->nid; + if (is_jack_detectable(codec, nid) && + !snd_hda_jack_tbl_get(codec, nid)) + snd_hda_codec_update_cache(codec, nid, 0, + AC_VERB_SET_UNSOLICITED_ENABLE, 0); + } +} + int snd_hda_gen_init(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3550,6 +3567,8 @@ int snd_hda_gen_init(struct hda_codec *codec) init_input_src(codec); init_digital(codec);
+ clear_unsol_on_unused_pins(codec); + /* call init functions of standard auto-mute helpers */ snd_hda_gen_hp_automute(codec, NULL); snd_hda_gen_line_automute(codec, NULL);
Just a small clean up by splitting a function. No functional changes at all.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 49 ++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 25 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b7b8d7e..96c779b 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3389,45 +3389,44 @@ static void init_multi_out(struct hda_codec *codec) } }
-/* initialize hp and speaker paths */ -static void init_extra_out(struct hda_codec *codec) + +static void __init_extra_out(struct hda_codec *codec, int num_outs, + hda_nid_t *pins, hda_nid_t *dacs, int type) { struct hda_gen_spec *spec = codec->spec; int i; hda_nid_t pin, dac;
- for (i = 0; i < spec->autocfg.hp_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT) - break; - pin = spec->autocfg.hp_pins[i]; - if (!pin) - break; - dac = spec->multiout.hp_out_nid[i]; - if (!dac) { - if (i > 0 && spec->multiout.hp_out_nid[0]) - dac = spec->multiout.hp_out_nid[0]; - else - dac = spec->multiout.dac_nids[0]; - } - set_output_and_unmute(codec, pin, PIN_HP, dac); - } - for (i = 0; i < spec->autocfg.speaker_outs; i++) { - if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) - break; - pin = spec->autocfg.speaker_pins[i]; + for (i = 0; i < num_outs; i++) { + pin = pins[i]; if (!pin) break; - dac = spec->multiout.extra_out_nid[i]; + dac = dacs[i]; if (!dac) { - if (i > 0 && spec->multiout.extra_out_nid[0]) - dac = spec->multiout.extra_out_nid[0]; + if (i > 0 && dacs[0]) + dac = dacs[0]; else dac = spec->multiout.dac_nids[0]; } - set_output_and_unmute(codec, pin, PIN_OUT, dac); + set_output_and_unmute(codec, pin, type, dac); } }
+/* initialize hp and speaker paths */ +static void init_extra_out(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) + __init_extra_out(codec, spec->autocfg.hp_outs, + spec->autocfg.hp_pins, + spec->multiout.hp_out_nid, PIN_HP); + if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) + __init_extra_out(codec, spec->autocfg.speaker_outs, + spec->autocfg.speaker_pins, + spec->multiout.extra_out_nid, PIN_OUT); +} + /* initialize multi-io paths */ static void init_multi_io(struct hda_codec *codec) {
There were some old codes that look not stable enough, which was derived from the old Realtek code. The initialization for primary output in init_multi_out() needs to consider the case of shared DAC.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 96c779b..a133fcf 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3372,6 +3372,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + hda_nid_t nid, dac; int pin_type; int i;
@@ -3380,12 +3381,14 @@ static void init_multi_out(struct hda_codec *codec) else pin_type = PIN_OUT;
- for (i = 0; i <= HDA_SIDE; i++) { - hda_nid_t nid = spec->autocfg.line_out_pins[i]; - if (nid) - set_output_and_unmute(codec, nid, pin_type, - spec->multiout.dac_nids[i]); - + for (i = 0; i < spec->autocfg.line_outs; i++) { + nid = spec->autocfg.line_out_pins[i]; + if (nid) { + dac = spec->multiout.dac_nids[i]; + if (!dac) + dac = spec->multiout.dac_nids[0]; + set_output_and_unmute(codec, nid, pin_type, dac); + } } }
When spec->own_eapd_ctl isn't set, try to turn on/off EAPD on demand for each pin.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a133fcf..46b00e0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -523,6 +523,18 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, } EXPORT_SYMBOL_HDA(snd_hda_activate_path);
+/* turn on/off EAPD on the given pin */ +static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->own_eapd_ctl || + !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) + return; + snd_hda_codec_update_cache(codec, pin, 0, + AC_VERB_SET_EAPD_BTLENABLE, + enable ? 0x02 : 0x00); +} +
/* * Helper functions for creating mixer ctl elements @@ -1485,7 +1497,9 @@ static int set_multi_io(struct hda_codec *codec, int idx, bool output) if (output) { snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT); snd_hda_activate_path(codec, path, true, true); + set_pin_eapd(codec, nid, true); } else { + set_pin_eapd(codec, nid, false); snd_hda_activate_path(codec, path, false, true); snd_hda_set_pin_ctl_cache(codec, nid, spec->multi_io[idx].ctl_in); @@ -2418,6 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, val = 0; val |= pin_bits; snd_hda_set_pin_ctl(codec, nid, val); + set_pin_eapd(codec, nid, !mute); } }
@@ -3351,7 +3366,6 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, int pin_type, hda_nid_t dac) { - struct hda_gen_spec *spec = codec->spec; struct nid_path *path;
snd_hda_set_pin_ctl_cache(codec, pin, pin_type); @@ -3361,11 +3375,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, if (path->active) return; snd_hda_activate_path(codec, path, true, true); - - if (!spec->own_eapd_ctl && - (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) - snd_hda_codec_update_cache(codec, pin, 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x02); + set_pin_eapd(codec, pin, true); }
/* initialize primary output paths */
There is no reason to avoid snd_hda_set_pin_ctl_cache() there.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 46b00e0..f4b5043 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1572,11 +1572,12 @@ static void update_shared_mic_hp(struct hda_codec *codec, bool set_as_mic) const hda_nid_t vref_pin = spec->shared_mic_vref_pin; unsigned int vref_val = snd_hda_get_default_vref(codec, vref_pin); if (vref_val != AC_PINCTL_VREF_HIZ) - snd_hda_set_pin_ctl(codec, vref_pin, PIN_IN | (set_as_mic ? vref_val : 0)); + snd_hda_set_pin_ctl_cache(codec, vref_pin, + PIN_IN | (set_as_mic ? vref_val : 0)); }
val = set_as_mic ? val | PIN_IN : PIN_HP; - snd_hda_set_pin_ctl(codec, pin, val); + snd_hda_set_pin_ctl_cache(codec, pin, val);
spec->automute_speaker = !set_as_mic; call_update_outputs(codec); @@ -2431,7 +2432,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, } else val = 0; val |= pin_bits; - snd_hda_set_pin_ctl(codec, nid, val); + snd_hda_set_pin_ctl_cache(codec, nid, val); set_pin_eapd(codec, nid, !mute); } } @@ -3467,7 +3468,7 @@ static void set_input_pin(struct hda_codec *codec, hda_nid_t nid, unsigned int val = PIN_IN; if (auto_pin_type == AUTO_PIN_MIC) val |= snd_hda_get_default_vref(codec, nid); - snd_hda_set_pin_ctl(codec, nid, val); + snd_hda_set_pin_ctl_cache(codec, nid, val); }
/* set up input pins and loopback paths */ @@ -3541,7 +3542,7 @@ static void init_digital(struct hda_codec *codec) } pin = spec->autocfg.dig_in_pin; if (pin) - snd_hda_set_pin_ctl(codec, pin, PIN_IN); + snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); }
/* clear unsol-event tags on unused pins; Conexant codecs seem to leave
When a PCM name string is generated from the chip name, it might become strange like "CX20549 (Venice) Analog". In this patch, the parser tries to drop the invalid words like "(Venice)" in the PCM name string. Also, when the name string is given beforehand by the caller, respect it and use it as is.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f4b5043..d4cb9df 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -24,6 +24,8 @@ #include <linux/slab.h> #include <linux/export.h> #include <linux/sort.h> +#include <linux/ctype.h> +#include <linux/string.h> #include <sound/core.h> #include <sound/jack.h> #include "hda_codec.h" @@ -3230,6 +3232,25 @@ static const struct hda_pcm_stream dyn_adc_pcm_analog_capture = { }, };
+static void fill_pcm_stream_name(char *str, size_t len, const char *sfx, + const char *chip_name) +{ + char *p; + + if (*str) + return; + strlcpy(str, chip_name, len); + + /* drop non-alnum chars after a space */ + for (p = strchr(str, ' '); p; p = strchr(p + 1, ' ')) { + if (!isalnum(p[1])) { + *p = 0; + break; + } + } + strlcat(str, sfx, len); +} + /* build PCM streams based on the parsed results */ int snd_hda_gen_build_pcms(struct hda_codec *codec) { @@ -3245,8 +3266,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) if (spec->no_analog) goto skip_analog;
- snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), - "%s Analog", codec->chip_name); + fill_pcm_stream_name(spec->stream_name_analog, + sizeof(spec->stream_name_analog), + " Analog", codec->chip_name); info->name = spec->stream_name_analog;
if (spec->multiout.num_dacs > 0) { @@ -3286,9 +3308,9 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { - snprintf(spec->stream_name_digital, - sizeof(spec->stream_name_digital), - "%s Digital", codec->chip_name); + fill_pcm_stream_name(spec->stream_name_digital, + sizeof(spec->stream_name_digital), + " Digital", codec->chip_name); codec->num_pcms = 2; codec->slave_dig_outs = spec->multiout.slave_dig_outs; info = spec->pcm_rec + 1;
It's never used in the generic parser. It was there from the old Realtek code, which has been dropped quite ago, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 10 ---------- sound/pci/hda/hda_generic.h | 2 -- 2 files changed, 12 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d4cb9df..43acf3d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3258,7 +3258,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) struct hda_pcm *info = spec->pcm_rec; const struct hda_pcm_stream *p; bool have_multi_adcs; - int i;
codec->num_pcms = 1; codec->pcm_info = info; @@ -3296,15 +3295,6 @@ int snd_hda_gen_build_pcms(struct hda_codec *codec) info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; }
- if (spec->channel_mode) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 0; - for (i = 0; i < spec->num_channel_mode; i++) { - if (spec->channel_mode[i].channels > info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max) { - info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->channel_mode[i].channels; - } - } - } - skip_analog: /* SPDIF for stream index #1 */ if (spec->multiout.dig_out_nid || spec->dig_in_nid) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index a406cd4..6365140 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -101,8 +101,6 @@ struct hda_gen_spec { unsigned int cur_mux[3];
/* channel model */ - const struct hda_channel_mode *channel_mode; - int num_channel_mode; int const_channel_count; /* min. channel count (for speakers) */ int ext_channel_count; /* current channel count for multi-io */
Add a better debug print code to show the new assigned paths in generic parser. It appears only with CONFIG_SND_DEBUG_VERBOSE=y.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 46 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 43acf3d..8af5324 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -186,6 +186,21 @@ static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid, is_ctl_used(codec, val, NID_PATH_MUTE_CTL); }
+static void print_nid_path(const char *pfx, struct nid_path *path) +{ + char buf[40]; + int i; + + + buf[0] = 0; + for (i = 0; i < path->depth; i++) { + char tmp[4]; + sprintf(tmp, ":%02x", path->path[i]); + strlcat(buf, tmp, sizeof(buf)); + } + snd_printdd("%s path: depth=%d %s\n", pfx, path->depth, buf); +} + /* called recursively */ static bool __parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, @@ -252,11 +267,6 @@ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { path->path[path->depth] = to_nid; path->depth++; -#if 0 - snd_printdd("path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", - path->depth, path->path[0], path->path[1], - path->path[2], path->path[3], path->path[4]); -#endif return true; } return false; @@ -816,6 +826,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, return 0;
for (i = 0; i < num_outs; i++) { + struct nid_path *path; hda_nid_t pin = pins[i]; if (!dacs[i]) dacs[i] = look_for_dac(codec, pin, false); @@ -850,8 +861,11 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - if (!snd_hda_add_new_path(codec, dac, pin, 0)) + path = snd_hda_add_new_path(codec, dac, pin, 0); + if (!path) dac = dacs[i] = 0; + else + print_nid_path("output", path); if (dac) badness += assign_out_path_ctls(codec, pin, dac); } @@ -935,6 +949,7 @@ static int fill_multi_ios(struct hda_codec *codec, dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { + struct nid_path *path; hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0;
@@ -962,10 +977,12 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - if (!snd_hda_add_new_path(codec, dac, nid, 0)) { + path = snd_hda_add_new_path(codec, dac, nid, 0); + if (!path) { badness++; continue; } + print_nid_path("multiio", path); spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; spec->multi_ios++; @@ -1004,15 +1021,18 @@ static bool map_singles(struct hda_codec *codec, int outs, int i; bool found = false; for (i = 0; i < outs; i++) { + struct nid_path *path; hda_nid_t dac; if (dacs[i]) continue; dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - if (snd_hda_add_new_path(codec, dac, pins[i], 0)) { + path = snd_hda_add_new_path(codec, dac, pins[i], 0); + if (path) { dacs[i] = dac; found = true; + print_nid_path("output", path); } } return found; @@ -1268,6 +1288,7 @@ static int parse_output_paths(struct hda_codec *codec) }
if (badness) { + debug_badness("==> restoring best_cfg\n"); *cfg = *best_cfg; fill_and_eval_dacs(codec, best_wired, best_mio); } @@ -1659,6 +1680,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_add_new_path(codec, pin, mix_nid, 2); if (!path) return -EINVAL; + print_nid_path("loopback", path);
idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { @@ -1836,6 +1858,7 @@ static int create_input_ctls(struct hda_codec *codec) spec->paths.used--; continue; } + print_nid_path("input", path);
if (!imux_added) { spec->imux_pins[imux->num_items] = pin; @@ -2295,6 +2318,7 @@ static int parse_mic_boost(struct hda_codec *codec) static void parse_digital(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; + struct nid_path *path; int i, nums; hda_nid_t dig_nid;
@@ -2305,8 +2329,10 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - if (!snd_hda_add_new_path(codec, dig_nid, pin, 2)) + path = snd_hda_add_new_path(codec, dig_nid, pin, 2); + if (!path) continue; + print_nid_path("digout", path); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -2322,7 +2348,6 @@ static void parse_digital(struct hda_codec *codec) if (spec->autocfg.dig_in_pin) { dig_nid = codec->start_nid; for (i = 0; i < codec->num_nodes; i++, dig_nid++) { - struct nid_path *path; unsigned int wcaps = get_wcaps(codec, dig_nid); if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) continue; @@ -2332,6 +2357,7 @@ static void parse_digital(struct hda_codec *codec) spec->autocfg.dig_in_pin, dig_nid, 2); if (path) { + print_nid_path("digin", path); path->active = true; spec->dig_in_nid = dig_nid; break;
It never showed the 4th line out and headphone pins since quite ago. Oh well.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 8af5324..b341450 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1164,7 +1164,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c { debug_badness("multi_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->line_out_pins[0], cfg->line_out_pins[1], - cfg->line_out_pins[2], cfg->line_out_pins[2], + cfg->line_out_pins[2], cfg->line_out_pins[3], spec->multiout.dac_nids[0], spec->multiout.dac_nids[1], spec->multiout.dac_nids[2], @@ -1176,7 +1176,7 @@ static void debug_show_configs(struct hda_gen_spec *spec, struct auto_pin_cfg *c spec->multi_io[0].dac, spec->multi_io[1].dac); debug_badness("hp_outs = %x/%x/%x/%x : %x/%x/%x/%x\n", cfg->hp_pins[0], cfg->hp_pins[1], - cfg->hp_pins[2], cfg->hp_pins[2], + cfg->hp_pins[2], cfg->hp_pins[3], spec->multiout.hp_out_nid[0], spec->multiout.hp_out_nid[1], spec->multiout.hp_out_nid[2],
... instead of numbers.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 28 +++++++++++++++------------- sound/pci/hda/hda_generic.h | 7 +++++++ 2 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b341450..18b5fae 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -211,9 +211,9 @@ static bool __parse_nid_path(struct hda_codec *codec, int i, nums;
if (to_nid == spec->mixer_nid) { - if (!with_aa_mix) + if (with_aa_mix == HDA_PARSE_NO_AAMIX) return false; - with_aa_mix = 2; /* mark aa-mix is included */ + with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ }
nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); @@ -228,7 +228,7 @@ static bool __parse_nid_path(struct hda_codec *codec, continue; } /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == 1)) + if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX)) goto found; } if (depth >= MAX_NID_PATH_DEPTH) @@ -256,9 +256,11 @@ static bool __parse_nid_path(struct hda_codec *codec,
/* parse the widget path from the given nid to the target nid; * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is 0, paths with spec->mixer_nid are excluded. - * when @with_aa_mix is 1, paths without spec->mixer_nid are excluded. - * when @with_aa_mix is 2, no special handling about spec->mixer_nid. + * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are + * excluded, only the paths that don't go through the mixer will be chosen. + * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through + * spec->mixer_nid will be chosen. + * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget. */ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int with_aa_mix, @@ -861,7 +863,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - path = snd_hda_add_new_path(codec, dac, pin, 0); + path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); if (!path) dac = dacs[i] = 0; else @@ -977,7 +979,7 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - path = snd_hda_add_new_path(codec, dac, nid, 0); + path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX); if (!path) { badness++; continue; @@ -1028,7 +1030,7 @@ static bool map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - path = snd_hda_add_new_path(codec, dac, pins[i], 0); + path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); if (path) { dacs[i] = dac; found = true; @@ -1677,7 +1679,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */
- path = snd_hda_add_new_path(codec, pin, mix_nid, 2); + path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL); if (!path) return -EINVAL; print_nid_path("loopback", path); @@ -1851,7 +1853,7 @@ static int create_input_ctls(struct hda_codec *codec) if (!path) return -ENOMEM; memset(path, 0, sizeof(*path)); - if (!snd_hda_parse_nid_path(codec, pin, adc, 2, path)) { + if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", pin, adc); @@ -2329,7 +2331,7 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, 2); + path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL); if (!path) continue; print_nid_path("digout", path); @@ -2355,7 +2357,7 @@ static void parse_digital(struct hda_codec *codec) continue; path = snd_hda_add_new_path(codec, spec->autocfg.dig_in_pin, - dig_nid, 2); + dig_nid, HDA_PARSE_ALL); if (path) { print_nid_path("digin", path); path->active = true; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 6365140..85d138f 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -180,6 +180,13 @@ int snd_hda_gen_init(struct hda_codec *codec);
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); + +enum { + HDA_PARSE_NO_AAMIX, + HDA_PARSE_ONLY_AAMIX, + HDA_PARSE_ALL, +}; + bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, int with_aa_mix, struct nid_path *path);
Allow the path including the loopback mixer widget in the primary output channel as an alternative in the generic codec parser.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 18b5fae..f3c6ace 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -864,6 +864,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, badness += bad->no_dac; } path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); + if (!path && i > 0 && spec->mixer_nid) { + /* try with aamix */ + path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); + } if (!path) dac = dacs[i] = 0; else @@ -1020,6 +1024,7 @@ static int fill_multi_ios(struct hda_codec *codec, static bool map_singles(struct hda_codec *codec, int outs, const hda_nid_t *pins, hda_nid_t *dacs) { + struct hda_gen_spec *spec = codec->spec; int i; bool found = false; for (i = 0; i < outs; i++) { @@ -1031,6 +1036,8 @@ static bool map_singles(struct hda_codec *codec, int outs, if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); + if (!path && i > 0 && spec->mixer_nid) + path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); if (path) { dacs[i] = dac; found = true;
Similar like the implementation in patch_analog.c and patch_via.c, the generic parser can provide the independent HP PCM stream now. It's enabled when spec->indep_hp is set by the caller while parsing.
Currently no dynamic PCM switching as in patch_via.c is implemented yet. The control returns -EBUSY when the value is changed during PCM operations.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 128 +++++++++++++++++++++++++++++++++++++++++++- sound/pci/hda/hda_generic.h | 9 ++++ 2 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f3c6ace..cc47460 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -41,6 +41,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); + mutex_init(&spec->pcm_mutex); return 0; } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_init); @@ -1485,6 +1486,77 @@ static int create_speaker_out_ctls(struct hda_codec *codec) }
/* + * independent HP controls + */ + +static int indep_hp_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + return snd_hda_enum_bool_helper_info(kcontrol, uinfo); +} + +static int indep_hp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->indep_hp_enabled; + return 0; +} + +static int indep_hp_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int select = ucontrol->value.enumerated.item[0]; + + mutex_lock(&spec->pcm_mutex); + if (spec->active_streams) { + mutex_unlock(&spec->pcm_mutex); + return -EBUSY; + } + + if (spec->indep_hp_enabled != select) { + spec->indep_hp_enabled = select; + if (spec->indep_hp_enabled) + spec->multiout.hp_out_nid[0] = 0; + else + spec->multiout.hp_out_nid[0] = spec->alt_dac_nid; + return 1; + } + mutex_unlock(&spec->pcm_mutex); + return 0; +} + +static const struct snd_kcontrol_new indep_hp_ctl = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Independent HP", + .info = indep_hp_info, + .get = indep_hp_get, + .put = indep_hp_put, +}; + + +static int create_indep_hp_ctls(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!spec->indep_hp) + return 0; + if (!spec->multiout.hp_out_nid[0]) { + spec->indep_hp = 0; + return 0; + } + + spec->indep_hp_enabled = false; + spec->alt_dac_nid = spec->multiout.hp_out_nid[0]; + if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl)) + return -ENOMEM; + return 0; +} + +/* * channel mode enum control */
@@ -2905,6 +2977,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, err = create_speaker_out_ctls(codec); if (err < 0) return err; + err = create_indep_hp_ctls(codec); + if (err < 0) + return err; err = create_shared_input(codec); if (err < 0) return err; @@ -3057,8 +3132,16 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, + int err; + + mutex_lock(&spec->pcm_mutex); + err = snd_hda_multi_out_analog_open(codec, + &spec->multiout, substream, hinfo); + if (!err) + spec->active_streams |= 1 << STREAM_MULTI_OUT; + mutex_unlock(&spec->pcm_mutex); + return err; }
static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, @@ -3080,6 +3163,44 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); }
+static int playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + mutex_lock(&spec->pcm_mutex); + spec->active_streams &= ~(1 << STREAM_MULTI_OUT); + mutex_unlock(&spec->pcm_mutex); + return 0; +} + +static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + int err = 0; + + mutex_lock(&spec->pcm_mutex); + if (!spec->indep_hp_enabled) + err = -EBUSY; + else + spec->active_streams |= 1 << STREAM_INDEP_HP; + mutex_unlock(&spec->pcm_mutex); + return err; +} + +static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + struct hda_gen_spec *spec = codec->spec; + mutex_lock(&spec->pcm_mutex); + spec->active_streams &= ~(1 << STREAM_INDEP_HP); + mutex_unlock(&spec->pcm_mutex); + return 0; +} + /* * Digital out */ @@ -3154,6 +3275,7 @@ static const struct hda_pcm_stream pcm_analog_playback = { /* NID is set in build_pcms */ .ops = { .open = playback_pcm_open, + .close = playback_pcm_close, .prepare = playback_pcm_prepare, .cleanup = playback_pcm_cleanup }, @@ -3171,6 +3293,10 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = { .channels_min = 2, .channels_max = 2, /* NID is set in build_pcms */ + .ops = { + .open = alt_playback_pcm_open, + .close = alt_playback_pcm_close + }, };
static const struct hda_pcm_stream pcm_analog_alt_capture = { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 85d138f..5c1569c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -65,6 +65,9 @@ struct automic_entry { unsigned int attr; /* pin attribute (INPUT_PIN_ATTR_*) */ };
+/* active stream id */ +enum { STREAM_MULTI_OUT, STREAM_INDEP_HP }; + struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; @@ -76,6 +79,10 @@ struct hda_gen_spec { const struct hda_pcm_stream *stream_digital_playback; const struct hda_pcm_stream *stream_digital_capture;
+ /* PCM */ + unsigned int active_streams; + struct mutex pcm_mutex; + /* playback */ struct hda_multi_out multiout; /* playback set-up * max_channels, dacs must be set @@ -150,6 +157,8 @@ struct hda_gen_spec { unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int vmaster_mute_enum:1; /* add vmaster mute mode enum */ + unsigned int indep_hp:1; /* independent HP supported */ + unsigned int indep_hp_enabled:1; /* independent HP enabled */
/* for virtual master */ hda_nid_t vmaster_nid;
Add the new flag, codec->inv_eapd, indicating that the EAPD implementation is inverted.
There are always broken hardware in the world.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_generic.c | 2 ++ 2 files changed, 3 insertions(+)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 2d9a51c..369ffaf 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -866,6 +866,7 @@ struct hda_codec { unsigned int pins_shutup:1; /* pins are shut up */ unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ + unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index cc47460..18daa8f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -545,6 +545,8 @@ static void set_pin_eapd(struct hda_codec *codec, hda_nid_t pin, bool enable) if (spec->own_eapd_ctl || !(snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)) return; + if (codec->inv_eapd) + enable = !enable; snd_hda_codec_update_cache(codec, pin, 0, AC_VERB_SET_EAPD_BTLENABLE, enable ? 0x02 : 0x00);
Yet another broken hardware workaround: there are hardware indicating the inverted jack detection bit result.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.h | 1 + sound/pci/hda/hda_jack.c | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 369ffaf..9f241d1 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -867,6 +867,7 @@ struct hda_codec { unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */ unsigned int no_jack_detect:1; /* Machine has no jack-detection */ unsigned int inv_eapd:1; /* broken h/w: inverted EAPD control */ + unsigned int inv_jack_detect:1; /* broken h/w: inverted detection bit */ unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int epss:1; /* supporting EPSS? */ unsigned int cached_write:1; /* write only to caches */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6e9f57b..6479b65 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -39,6 +39,7 @@ EXPORT_SYMBOL_HDA(is_jack_detectable); static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) { u32 pincap; + u32 val;
if (!codec->no_trigger_sense) { pincap = snd_hda_query_pin_caps(codec, nid); @@ -46,8 +47,11 @@ static u32 read_pin_sense(struct hda_codec *codec, hda_nid_t nid) snd_hda_codec_read(codec, nid, 0, AC_VERB_SET_PIN_SENSE, 0); } - return snd_hda_codec_read(codec, nid, 0, + val = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_PIN_SENSE, 0); + if (codec->inv_jack_detect) + val ^= AC_PINSENSE_PRESENCE; + return val; }
/**
Manage the connection list cache using linked lists instead of snd_array, and revive snd_hda_get_conn_list() again, so that we don't have to keep the expanded values locally. This will reduce the stack usage by recursive call of snd_hda_get_conn_index() or parse_nid_path() of the generic parser.
The list management doesn't include any mutex protection, thus the caller needs to take care of race appropriately.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 164 ++++++++++++++++++++++++++------------------ sound/pci/hda/hda_codec.h | 4 +- sound/pci/hda/hda_generic.c | 8 +-- 3 files changed, 106 insertions(+), 70 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 380c9ed..cd318e4 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -334,20 +334,51 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid, } EXPORT_SYMBOL_HDA(snd_hda_get_sub_nodes);
+/* connection list element */ +struct hda_conn_list { + struct list_head list; + int len; + hda_nid_t nid; + hda_nid_t conns[0]; +}; + /* look up the cached results */ -static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid) +static struct hda_conn_list * +lookup_conn_list(struct hda_codec *codec, hda_nid_t nid) { - int i, len; - for (i = 0; i < array->used; ) { - hda_nid_t *p = snd_array_elem(array, i); - if (nid == *p) + struct hda_conn_list *p; + list_for_each_entry(p, &codec->conn_list, list) { + if (p->nid == nid) return p; - len = p[1]; - i += len + 2; } return NULL; }
+static int add_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, + const hda_nid_t *list) +{ + struct hda_conn_list *p; + + p = kmalloc(sizeof(*p) + len * sizeof(hda_nid_t), GFP_KERNEL); + if (!p) + return -ENOMEM; + p->len = len; + p->nid = nid; + memcpy(p->conns, list, len * sizeof(hda_nid_t)); + list_add(&p->list, &codec->conn_list); + return 0; +} + +static void remove_conn_list(struct hda_codec *codec) +{ + while (!list_empty(&codec->conn_list)) { + struct hda_conn_list *p; + p = list_first_entry(&codec->conn_list, typeof(*p), list); + list_del(&p->list); + kfree(p); + } +} + /* read the connection and add to the cache */ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) { @@ -361,6 +392,49 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) }
/** + * snd_hda_get_conn_list - get connection list + * @codec: the HDA codec + * @nid: NID to parse + * @len: number of connection list entries + * @listp: the pointer to store NID list + * + * Parses the connection list of the given widget and stores the pointer + * to the list of NIDs. + * + * Returns the number of connections, or a negative error code. + * + * Note that the returned pointer isn't protected against the list + * modification. If snd_hda_override_conn_list() might be called + * concurrently, protect with a mutex appropriately. + */ +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp) +{ + bool added = false; + + for (;;) { + int err; + const struct hda_conn_list *p; + + /* if the connection-list is already cached, read it */ + p = lookup_conn_list(codec, nid); + if (p) { + if (listp) + *listp = p->conns; + return p->len; + } + if (snd_BUG_ON(added)) + return -EINVAL; + + err = read_and_add_raw_conns(codec, nid); + if (err < 0) + return err; + added = true; + } +} +EXPORT_SYMBOL_HDA(snd_hda_get_conn_list); + +/** * snd_hda_get_connections - copy connection list * @codec: the HDA codec * @nid: NID to parse @@ -375,39 +449,20 @@ static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid) int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns) { - struct snd_array *array = &codec->conn_lists; - int len; - hda_nid_t *p; - bool added = false; + const hda_nid_t *list; + int len = snd_hda_get_conn_list(codec, nid, &list);
- again: - mutex_lock(&codec->hash_mutex); - len = -1; - /* if the connection-list is already cached, read it */ - p = lookup_conn_list(array, nid); - if (p) { - len = p[1]; - if (conn_list && len > max_conns) { + if (len > 0 && conn_list) { + if (len > max_conns) { snd_printk(KERN_ERR "hda_codec: " "Too many connections %d for NID 0x%x\n", len, nid); - mutex_unlock(&codec->hash_mutex); return -EINVAL; } - if (conn_list && len) - memcpy(conn_list, p + 2, len * sizeof(hda_nid_t)); + memcpy(conn_list, list, len * sizeof(hda_nid_t)); } - mutex_unlock(&codec->hash_mutex); - if (len >= 0) - return len; - if (snd_BUG_ON(added)) - return -EINVAL;
- len = read_and_add_raw_conns(codec, nid); - if (len < 0) - return len; - added = true; - goto again; + return len; } EXPORT_SYMBOL_HDA(snd_hda_get_connections);
@@ -519,15 +574,6 @@ int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, return conns; }
-static bool add_conn_list(struct snd_array *array, hda_nid_t nid) -{ - hda_nid_t *p = snd_array_new(array); - if (!p) - return false; - *p = nid; - return true; -} - /** * snd_hda_override_conn_list - add/modify the connection-list to cache * @codec: the HDA codec @@ -543,28 +589,15 @@ static bool add_conn_list(struct snd_array *array, hda_nid_t nid) int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len, const hda_nid_t *list) { - struct snd_array *array = &codec->conn_lists; - hda_nid_t *p; - int i, old_used; + struct hda_conn_list *p;
- mutex_lock(&codec->hash_mutex); - p = lookup_conn_list(array, nid); - if (p) - *p = -1; /* invalidate the old entry */ - - old_used = array->used; - if (!add_conn_list(array, nid) || !add_conn_list(array, len)) - goto error_add; - for (i = 0; i < len; i++) - if (!add_conn_list(array, list[i])) - goto error_add; - mutex_unlock(&codec->hash_mutex); - return 0; + p = lookup_conn_list(codec, nid); + if (p) { + list_del(&p->list); + kfree(p); + }
- error_add: - array->used = old_used; - mutex_unlock(&codec->hash_mutex); - return -ENOMEM; + return add_conn_list(codec, nid, len, list); } EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -582,10 +615,10 @@ EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, hda_nid_t nid, int recursive) { - hda_nid_t conn[HDA_MAX_NUM_INPUTS]; + const hda_nid_t *conn; int i, nums;
- nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, mux, &conn); for (i = 0; i < nums; i++) if (conn[i] == nid) return i; @@ -1186,8 +1219,8 @@ static void snd_hda_codec_free(struct hda_codec *codec) snd_array_free(&codec->mixers); snd_array_free(&codec->nids); snd_array_free(&codec->cvt_setups); - snd_array_free(&codec->conn_lists); snd_array_free(&codec->spdif_out); + remove_conn_list(codec); codec->bus->caddr_tbl[codec->addr] = NULL; if (codec->patch_ops.free) codec->patch_ops.free(codec); @@ -1257,10 +1290,11 @@ int snd_hda_codec_new(struct hda_bus *bus, snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16); snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8); - snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64); snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16); snd_array_init(&codec->jacktbl, sizeof(struct hda_jack_tbl), 16); snd_array_init(&codec->verbs, sizeof(struct hda_verb *), 8); + INIT_LIST_HEAD(&codec->conn_list); + INIT_DELAYED_WORK(&codec->jackpoll_work, hda_jackpoll_work);
#ifdef CONFIG_PM diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h index 9f241d1..93ec747 100644 --- a/sound/pci/hda/hda_codec.h +++ b/sound/pci/hda/hda_codec.h @@ -831,7 +831,7 @@ struct hda_codec { struct hda_cache_rec amp_cache; /* cache for amp access */ struct hda_cache_rec cmd_cache; /* cache for other commands */
- struct snd_array conn_lists; /* connection-list array */ + struct list_head conn_list; /* linked-list of connection-list */
struct mutex spdif_mutex; struct mutex control_mutex; @@ -944,6 +944,8 @@ snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) } int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, hda_nid_t *conn_list, int max_conns); +int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, + const hda_nid_t **listp); int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, const hda_nid_t *list); int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 18daa8f..f69f2ab 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -208,7 +208,7 @@ static bool __parse_nid_path(struct hda_codec *codec, int with_aa_mix, struct nid_path *path, int depth) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t conn[16]; + const hda_nid_t *conn; int i, nums;
if (to_nid == spec->mixer_nid) { @@ -217,7 +217,7 @@ static bool __parse_nid_path(struct hda_codec *codec, with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ }
- nums = snd_hda_get_connections(codec, to_nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, to_nid, &conn); for (i = 0; i < nums; i++) { if (conn[i] != from_nid) { /* special case: when from_nid is 0, @@ -481,12 +481,12 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path, int i, bool enable, bool add_aamix) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t conn[16]; + const hda_nid_t *conn; int n, nums, idx; int type; hda_nid_t nid = path->path[i];
- nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); + nums = snd_hda_get_conn_list(codec, nid, &conn); type = get_wcaps_type(get_wcaps(codec, nid)); if (type == AC_WID_PIN || (type == AC_WID_AUD_IN && codec->single_adc_amp)) {
... as a preliminary work for migrating patch_sigmatel.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 6 ++++++ sound/pci/hda/hda_generic.h | 8 ++++++++ 2 files changed, 14 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f69f2ab..c5a9b84 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2778,6 +2778,8 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable HP auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_HP_EVENT, + spec->hp_automute_hook ? + spec->hp_automute_hook : snd_hda_gen_hp_automute); spec->detect_hp = 1; } @@ -2791,6 +2793,8 @@ static int check_auto_mute_availability(struct hda_codec *codec) snd_printdd("hda-codec: Enable Line-Out auto-muting on NID 0x%x\n", nid); snd_hda_jack_detect_enable_callback(codec, nid, HDA_GEN_FRONT_EVENT, + spec->line_automute_hook ? + spec->line_automute_hook : snd_hda_gen_line_automute); spec->detect_lo = 1; } @@ -2843,6 +2847,8 @@ static bool auto_mic_check_imux(struct hda_codec *codec) snd_hda_jack_detect_enable_callback(codec, spec->am_entry[i].pin, HDA_GEN_MIC_EVENT, + spec->mic_autoswitch_hook ? + spec->mic_autoswitch_hook : snd_hda_gen_mic_autoswitch); return true; } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 5c1569c..1090a52 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -180,6 +180,14 @@ struct hda_gen_spec { void (*init_hook)(struct hda_codec *codec); void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec); + + /* automute / autoswitch hooks */ + void (*hp_automute_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); + void (*line_automute_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); + void (*mic_autoswitch_hook)(struct hda_codec *codec, + struct hda_jack_tbl *tbl); };
int snd_hda_gen_spec_init(struct hda_gen_spec *spec);
activate_amp() in the generic parser checks whether the given NID is included in any active paths and skips it if found. This was a workaround for avoiding disabling the widgets in the active paths when one path is disabled, thus it shouldn't be applied to the case for path activation. Due to this wrong check, some analog loopback paths haven't been initialized correctly.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c5a9b84..08eced0 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -463,7 +463,7 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir, { int val; if (is_ctl_associated(codec, nid, dir, idx) || - is_active_nid(codec, nid, dir, idx)) + (!enable && is_active_nid(codec, nid, dir, idx))) return; val = get_amp_val_to_activate(codec, nid, dir, enable); snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
Set path->active flag at the path creation time and let the paths initialized according to the current path->active state in set_output_and_unmute(). This allows to modify the active flag of some output paths dynamically, e.g. switching the front output route with or without aamix like patch_via.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 08eced0..5051350 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -873,8 +873,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, } if (!path) dac = dacs[i] = 0; - else + else { print_nid_path("output", path); + path->active = true; + } if (dac) badness += assign_out_path_ctls(codec, pin, dac); } @@ -1045,6 +1047,7 @@ static bool map_singles(struct hda_codec *codec, int outs, dacs[i] = dac; found = true; print_nid_path("output", path); + path->active = true; } } return found; @@ -2416,6 +2419,7 @@ static void parse_digital(struct hda_codec *codec) if (!path) continue; print_nid_path("digout", path); + path->active = true; if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -3554,10 +3558,8 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_get_nid_path(codec, dac, pin); if (!path) return; - if (path->active) - return; - snd_hda_activate_path(codec, path, true, true); - set_pin_eapd(codec, pin, true); + snd_hda_activate_path(codec, path, path->active, true); + set_pin_eapd(codec, pin, path->active); }
/* initialize primary output paths */
When the paths are created in map_singles(), we don't have to re-create new paths in try_assign_dacs(). Just evaluate the badness and skip to the next item.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 5051350..978a0ed 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -833,8 +833,13 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, for (i = 0; i < num_outs; i++) { struct nid_path *path; hda_nid_t pin = pins[i]; - if (!dacs[i]) - dacs[i] = look_for_dac(codec, pin, false); + + if (dacs[i]) { + badness += assign_out_path_ctls(codec, pin, dacs[i]); + continue; + } + + dacs[i] = look_for_dac(codec, pin, false); if (!dacs[i] && !i) { for (j = 1; j < num_outs; j++) { if (is_reachable_path(codec, dacs[j], pin)) {
If the requested path has been already added, return the existing path instance instead of adding a duplicated instance.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 31 ++++++++++++++++++++++++------- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 978a0ed..5516905 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -116,11 +116,9 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); * parsing paths */
-/* get the path between the given NIDs; - * passing 0 to either @pin or @dac behaves as a wildcard - */ -struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, - hda_nid_t from_nid, hda_nid_t to_nid) +static struct nid_path *get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid, + int with_aa_mix) { struct hda_gen_spec *spec = codec->spec; int i; @@ -130,11 +128,23 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, if (path->depth <= 0) continue; if ((!from_nid || path->path[0] == from_nid) && - (!to_nid || path->path[path->depth - 1] == to_nid)) - return path; + (!to_nid || path->path[path->depth - 1] == to_nid)) { + if (with_aa_mix == HDA_PARSE_ALL || + path->with_aa_mix == with_aa_mix) + return path; + } } return NULL; } + +/* get the path between the given NIDs; + * passing 0 to either @pin or @dac behaves as a wildcard + */ +struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, + hda_nid_t from_nid, hda_nid_t to_nid) +{ + return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL); +} EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
/* check whether the given DAC is already found in any existing paths */ @@ -248,6 +258,8 @@ static bool __parse_nid_path(struct hda_codec *codec,
found: path->path[path->depth] = conn[i]; + if (conn[i] == spec->mixer_nid) + path->with_aa_mix = true; path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; @@ -290,6 +302,11 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, if (from_nid && to_nid && !is_reachable_path(codec, from_nid, to_nid)) return NULL;
+ /* check whether the path has been already added */ + path = get_nid_path(codec, from_nid, to_nid, with_aa_mix); + if (path) + return path; + path = snd_array_new(&spec->paths); if (!path) return NULL; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1090a52..f1cae2e 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -53,6 +53,7 @@ struct nid_path { unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; + bool with_aa_mix; };
/* mic/line-in auto switching entry */
When speakers are chosen as the the primary output during evaluation, we did some tricks to assign the possible multi-io jacks with a certain offset value to multi_out dacs. This was a workaround for the case with multiple speakers like Acer Aspire. But this is quite ugly at the same time and the resultant code is hard to understand. More badly, it works wrongly for 2.1 speakers like Apple iMac91.
In this patch, instead of fiddling with the offset to multi_out dacs, simply add a certain badness number if headphone(s) + multi-ios are possible. This simplify the code a bit, and it's more robust.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 63 +++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 31 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 5516905..c928b38 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -943,6 +943,28 @@ static bool can_be_multiio_pin(struct hda_codec *codec, return true; }
+/* count the number of input pins that are capable to be multi-io */ +static int count_multiio_pins(struct hda_codec *codec, hda_nid_t reference_pin) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); + unsigned int location = get_defcfg_location(defcfg); + int type, i; + int num_pins = 0; + + for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { + for (i = 0; i < cfg->num_inputs; i++) { + if (cfg->inputs[i].type != type) + continue; + if (can_be_multiio_pin(codec, location, + cfg->inputs[i].pin)) + num_pins++; + } + } + return num_pins; +} + /* * multi-io helper * @@ -953,11 +975,11 @@ static bool can_be_multiio_pin(struct hda_codec *codec, */ static int fill_multi_ios(struct hda_codec *codec, hda_nid_t reference_pin, - bool hardwired, int offset) + bool hardwired) { struct hda_gen_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int type, i, j, dacs, num_pins, old_pins; + int type, i, j, num_pins, old_pins; unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int badness = 0; @@ -966,20 +988,10 @@ static int fill_multi_ios(struct hda_codec *codec, if (old_pins >= 2) goto end_fill;
- num_pins = 0; - for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { - for (i = 0; i < cfg->num_inputs; i++) { - if (cfg->inputs[i].type != type) - continue; - if (can_be_multiio_pin(codec, location, - cfg->inputs[i].pin)) - num_pins++; - } - } + num_pins = count_multiio_pins(codec, reference_pin); if (num_pins < 2) goto end_fill;
- dacs = spec->multiout.num_dacs; for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { struct nid_path *path; @@ -997,11 +1009,6 @@ static int fill_multi_ios(struct hda_codec *codec, if (j < spec->multi_ios) continue;
- if (offset && offset + spec->multi_ios < dacs) { - dac = spec->private_dac_nids[offset + spec->multi_ios]; - if (!is_reachable_path(codec, dac, nid)) - dac = 0; - } if (hardwired) dac = get_dac_if_single(codec, nid); else if (!dac) @@ -1109,7 +1116,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->multiout.extra_out_nid); if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], true, 0); + err = fill_multi_ios(codec, cfg->line_out_pins[0], true); if (!err) mapped = true; } @@ -1136,7 +1143,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ - err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); + err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; /* we don't count badness at this stage yet */ @@ -1160,22 +1167,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, badness += err; } if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { - err = fill_multi_ios(codec, cfg->line_out_pins[0], false, 0); - if (err < 0) - return err; - badness += err; - } - if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) { - /* try multi-ios with HP + inputs */ - int offset = 0; - if (cfg->line_outs >= 3) - offset = 1; - err = fill_multi_ios(codec, cfg->hp_pins[0], false, offset); + err = fill_multi_ios(codec, cfg->line_out_pins[0], false); if (err < 0) return err; badness += err; }
+ if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) + if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) + spec->multi_ios = 1; /* give badness */ + if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) spec->private_dac_nids[spec->multiout.num_dacs++] =
The multi-io pins are calculated with a blind assumption of cfg->line_outs = 1. This isn't always true.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index c928b38..75a519c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1368,7 +1368,7 @@ static int create_multi_out_ctls(struct hda_codec *codec, if (!dac) continue; if (i >= cfg->line_outs) { - pin = spec->multi_io[i - 1].pin; + pin = spec->multi_io[i - cfg->line_outs].pin; index = 0; name = channel_name[i]; } else {
Instead of search for the path with the certain route at each time, keep the path index for each output and loopback, and just use it when referred.
In this implementation, the path index number begins with one, not zero (although I've been writing in C over decades). It's just to make the check for uninitialized values easier.
So far, the input paths aren't handled with indices yet, but still picked up via snd_hda_get_nid_path() at each time.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 139 ++++++++++++++++++++++++++++---------------- sound/pci/hda/hda_generic.h | 9 +++ 2 files changed, 98 insertions(+), 50 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 75a519c..2296839 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -147,6 +147,33 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, } EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
+/* get the index number corresponding to the path instance; + * the index starts from 1, for easier checking the invalid value + */ +int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path) +{ + struct hda_gen_spec *spec = codec->spec; + struct nid_path *array = spec->paths.list; + ssize_t idx; + + if (!spec->paths.used) + return 0; + idx = path - array; + if (idx < 0 || idx >= spec->paths.used) + return 0; + return idx + 1; +} + +/* get the path instance corresponding to the given index number */ +struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + + if (idx <= 0 || idx > spec->paths.used) + return NULL; + return snd_array_elem(&spec->paths, idx - 1); +} + /* check whether the given DAC is already found in any existing paths */ static bool is_dac_already_used(struct hda_codec *codec, hda_nid_t nid) { @@ -836,6 +863,7 @@ static struct badness_table extra_out_badness = { /* try to assign DACs to pins and return the resultant badness */ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs, + int *path_idx, const struct badness_table *bad) { struct hda_gen_spec *spec = codec->spec; @@ -862,6 +890,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, if (is_reachable_path(codec, dacs[j], pin)) { dacs[0] = dacs[j]; dacs[j] = 0; + path_idx[j] = 0; break; } } @@ -898,6 +927,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else { print_nid_path("output", path); path->active = true; + path_idx[i] = snd_hda_get_path_idx(codec, path); } if (dac) badness += assign_out_path_ctls(codec, pin, dac); @@ -1025,6 +1055,8 @@ static int fill_multi_ios(struct hda_codec *codec, print_nid_path("multiio", path); spec->multi_io[spec->multi_ios].pin = nid; spec->multi_io[spec->multi_ios].dac = dac; + spec->out_paths[cfg->line_outs + spec->multi_ios] = + snd_hda_get_path_idx(codec, path); spec->multi_ios++; if (spec->multi_ios >= 2) break; @@ -1056,7 +1088,7 @@ static int fill_multi_ios(struct hda_codec *codec,
/* map DACs for all pins in the list if they are single connections */ static bool map_singles(struct hda_codec *codec, int outs, - const hda_nid_t *pins, hda_nid_t *dacs) + const hda_nid_t *pins, hda_nid_t *dacs, int *path_idx) { struct hda_gen_spec *spec = codec->spec; int i; @@ -1077,6 +1109,7 @@ static bool map_singles(struct hda_codec *codec, int outs, found = true; print_nid_path("output", path); path->active = true; + path_idx[i] = snd_hda_get_path_idx(codec, path); } } return found; @@ -1107,13 +1140,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, do { mapped = map_singles(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids); + spec->private_dac_nids, + spec->out_paths); mapped |= map_singles(codec, cfg->hp_outs, cfg->hp_pins, - spec->multiout.hp_out_nid); + spec->multiout.hp_out_nid, + spec->hp_paths); mapped |= map_singles(codec, cfg->speaker_outs, cfg->speaker_pins, - spec->multiout.extra_out_nid); + spec->multiout.extra_out_nid, + spec->speaker_paths); if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { err = fill_multi_ios(codec, cfg->line_out_pins[0], true); @@ -1124,7 +1160,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, }
badness += try_assign_dacs(codec, cfg->line_outs, cfg->line_out_pins, - spec->private_dac_nids, + spec->private_dac_nids, spec->out_paths, &main_out_badness);
/* re-count num_dacs and squash invalid entries */ @@ -1152,6 +1188,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (cfg->line_out_type != AUTO_PIN_HP_OUT) { err = try_assign_dacs(codec, cfg->hp_outs, cfg->hp_pins, spec->multiout.hp_out_nid, + spec->hp_paths, &extra_out_badness); if (err < 0) return err; @@ -1161,7 +1198,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec, err = try_assign_dacs(codec, cfg->speaker_outs, cfg->speaker_pins, spec->multiout.extra_out_nid, - &extra_out_badness); + spec->speaker_paths, + &extra_out_badness); if (err < 0) return err; badness += err; @@ -1336,9 +1374,7 @@ static int parse_output_paths(struct hda_codec *codec)
if (cfg->line_out_pins[0]) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, - spec->multiout.dac_nids[0], - cfg->line_out_pins[0]); + path = snd_hda_get_path_from_idx(codec, spec->out_paths[0]); if (path) spec->vmaster_nid = look_for_out_vol_nid(codec, path); } @@ -1361,22 +1397,20 @@ static int create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - hda_nid_t dac, pin; + hda_nid_t dac; struct nid_path *path;
dac = spec->multiout.dac_nids[i]; if (!dac) continue; if (i >= cfg->line_outs) { - pin = spec->multi_io[i - cfg->line_outs].pin; index = 0; name = channel_name[i]; } else { - pin = cfg->line_out_pins[i]; name = get_line_out_pfx(spec, i, true, &index); }
- path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, spec->out_paths[i]); if (!path) continue; if (!name || !strcmp(name, "CLFE")) { @@ -1406,12 +1440,13 @@ static int create_multi_out_ctls(struct hda_codec *codec, }
static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, const char *pfx, int cidx) + hda_nid_t dac, int path_idx, + const char *pfx, int cidx) { struct nid_path *path; int err;
- path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return 0; /* bind volume control will be created in the case of dac = 0 */ @@ -1429,7 +1464,7 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, /* add playback controls for speaker and HP outputs */ static int create_extra_outs(struct hda_codec *codec, int num_pins, const hda_nid_t *pins, const hda_nid_t *dacs, - const char *pfx) + const int *paths, const char *pfx) { struct hda_gen_spec *spec = codec->spec; struct hda_bind_ctls *ctl; @@ -1443,7 +1478,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, hda_nid_t dac = *dacs; if (!dac) dac = spec->multiout.dac_nids[0]; - return create_extra_out(codec, *pins, dac, pfx, 0); + return create_extra_out(codec, *pins, dac, paths[0], pfx, 0); }
for (i = 0; i < num_pins; i++) { @@ -1453,14 +1488,16 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, else dac = 0; if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = create_extra_out(codec, pins[i], dac, + err = create_extra_out(codec, pins[i], dac, paths[i], "Bass Speaker", 0); } else if (num_pins >= 3) { snprintf(name, sizeof(name), "%s %s", pfx, channel_name[i]); - err = create_extra_out(codec, pins[i], dac, name, 0); + err = create_extra_out(codec, pins[i], dac, paths[i], + name, 0); } else { - err = create_extra_out(codec, pins[i], dac, pfx, i); + err = create_extra_out(codec, pins[i], dac, paths[i], + pfx, i); } if (err < 0) return err; @@ -1478,7 +1515,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, struct nid_path *path; if (!pins[i] || !dacs[i]) continue; - path = snd_hda_get_nid_path(codec, dacs[i], pins[i]); + path = snd_hda_get_path_from_idx(codec, paths[i]); if (!path) continue; vol = look_for_out_vol_nid(codec, path); @@ -1501,6 +1538,7 @@ static int create_hp_out_ctls(struct hda_codec *codec) return create_extra_outs(codec, spec->autocfg.hp_outs, spec->autocfg.hp_pins, spec->multiout.hp_out_nid, + spec->hp_paths, "Headphone"); }
@@ -1510,6 +1548,7 @@ static int create_speaker_out_ctls(struct hda_codec *codec) return create_extra_outs(codec, spec->autocfg.speaker_outs, spec->autocfg.speaker_pins, spec->multiout.extra_out_nid, + spec->speaker_paths, "Speaker"); }
@@ -1613,13 +1652,21 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol, return 0; }
+static inline struct nid_path * +get_multiio_path(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + return snd_hda_get_path_from_idx(codec, + spec->out_paths[spec->autocfg.line_outs + idx]); +} + static int set_multi_io(struct hda_codec *codec, int idx, bool output) { struct hda_gen_spec *spec = codec->spec; hda_nid_t nid = spec->multi_io[idx].pin; struct nid_path *path;
- path = snd_hda_get_nid_path(codec, spec->multi_io[idx].dac, nid); + path = get_multiio_path(codec, idx); if (!path) return -EINVAL;
@@ -1773,8 +1820,8 @@ static void add_loopback_list(struct hda_gen_spec *spec, hda_nid_t mix, int idx) #endif
/* create input playback/capture controls for the given pin */ -static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, - const char *ctlname, int ctlidx, +static int new_analog_input(struct hda_codec *codec, int input_idx, + hda_nid_t pin, const char *ctlname, int ctlidx, hda_nid_t mix_nid) { struct hda_gen_spec *spec = codec->spec; @@ -1790,6 +1837,7 @@ static int new_analog_input(struct hda_codec *codec, hda_nid_t pin, if (!path) return -EINVAL; print_nid_path("loopback", path); + spec->loopback_paths[input_idx] = snd_hda_get_path_idx(codec, path);
idx = path->idx[path->depth - 1]; if (nid_has_volume(codec, mix_nid, HDA_INPUT)) { @@ -1942,7 +1990,7 @@ static int create_input_ctls(struct hda_codec *codec)
if (mixer) { if (is_reachable_path(codec, pin, mixer)) { - err = new_analog_input(codec, pin, + err = new_analog_input(codec, i, pin, label, type_idx, mixer); if (err < 0) return err; @@ -2443,6 +2491,7 @@ static void parse_digital(struct hda_codec *codec) continue; print_nid_path("digout", path); path->active = true; + spec->digout_paths[i] = snd_hda_get_path_idx(codec, path); if (!nums) { spec->multiout.dig_out_nid = dig_nid; spec->dig_out_type = spec->autocfg.dig_out_type[0]; @@ -3573,12 +3622,12 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms);
/* configure the path from the given dac to the pin as the proper output */ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, - int pin_type, hda_nid_t dac) + int pin_type, int path_idx) { struct nid_path *path;
snd_hda_set_pin_ctl_cache(codec, pin, pin_type); - path = snd_hda_get_nid_path(codec, dac, pin); + path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return; snd_hda_activate_path(codec, path, path->active, true); @@ -3589,7 +3638,7 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid, dac; + hda_nid_t nid; int pin_type; int i;
@@ -3600,35 +3649,24 @@ static void init_multi_out(struct hda_codec *codec)
for (i = 0; i < spec->autocfg.line_outs; i++) { nid = spec->autocfg.line_out_pins[i]; - if (nid) { - dac = spec->multiout.dac_nids[i]; - if (!dac) - dac = spec->multiout.dac_nids[0]; - set_output_and_unmute(codec, nid, pin_type, dac); - } + if (nid) + set_output_and_unmute(codec, nid, pin_type, + spec->out_paths[i]); } }
static void __init_extra_out(struct hda_codec *codec, int num_outs, - hda_nid_t *pins, hda_nid_t *dacs, int type) + hda_nid_t *pins, int *paths, int type) { - struct hda_gen_spec *spec = codec->spec; int i; - hda_nid_t pin, dac; + hda_nid_t pin;
for (i = 0; i < num_outs; i++) { pin = pins[i]; if (!pin) break; - dac = dacs[i]; - if (!dac) { - if (i > 0 && dacs[0]) - dac = dacs[0]; - else - dac = spec->multiout.dac_nids[0]; - } - set_output_and_unmute(codec, pin, type, dac); + set_output_and_unmute(codec, pin, type, paths[i]); } }
@@ -3640,11 +3678,11 @@ static void init_extra_out(struct hda_codec *codec) if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) __init_extra_out(codec, spec->autocfg.hp_outs, spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, PIN_HP); + spec->hp_paths, PIN_HP); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, PIN_OUT); + spec->speaker_paths, PIN_OUT); }
/* initialize multi-io paths */ @@ -3656,7 +3694,7 @@ static void init_multi_io(struct hda_codec *codec) for (i = 0; i < spec->multi_ios; i++) { hda_nid_t pin = spec->multi_io[i].pin; struct nid_path *path; - path = snd_hda_get_nid_path(codec, spec->multi_io[i].dac, pin); + path = get_multiio_path(codec, i); if (!path) continue; if (!spec->multi_io[i].ctl_in) @@ -3692,7 +3730,7 @@ static void init_analog_input(struct hda_codec *codec) /* init loopback inputs */ if (spec->mixer_nid) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, nid, spec->mixer_nid); + path = snd_hda_get_path_from_idx(codec, spec->loopback_paths[i]); if (path) snd_hda_activate_path(codec, path, path->active, false); @@ -3744,7 +3782,8 @@ static void init_digital(struct hda_codec *codec) pin = spec->autocfg.dig_out_pins[i]; if (!pin) continue; - set_output_and_unmute(codec, pin, PIN_OUT, 0); + set_output_and_unmute(codec, pin, PIN_OUT, + spec->digout_paths[i]); } pin = spec->autocfg.dig_in_pin; if (pin) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index f1cae2e..71d409f 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -130,6 +130,13 @@ struct hda_gen_spec { /* path list */ struct snd_array paths;
+ /* path indices */ + int out_paths[AUTO_CFG_MAX_OUTS]; + int hp_paths[AUTO_CFG_MAX_OUTS]; + int speaker_paths[AUTO_CFG_MAX_OUTS]; + int digout_paths[AUTO_CFG_MAX_OUTS]; + int loopback_paths[HDA_MAX_NUM_INPUTS]; + /* auto-mic stuff */ int am_num_entries; struct automic_entry am_entry[MAX_AUTO_MIC_PINS]; @@ -198,6 +205,8 @@ int snd_hda_gen_init(struct hda_codec *codec);
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); +int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); +struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx);
enum { HDA_PARSE_NO_AAMIX,
Call the path activation for the digital input pin properly, not only setting the pin control. Also add spec->digin_path for keeping the path index.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 8 +++++++- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2296839..65883c7 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -2519,6 +2519,7 @@ static void parse_digital(struct hda_codec *codec) print_nid_path("digin", path); path->active = true; spec->dig_in_nid = dig_nid; + spec->digin_path = snd_hda_get_path_idx(codec, path); break; } } @@ -3786,8 +3787,13 @@ static void init_digital(struct hda_codec *codec) spec->digout_paths[i]); } pin = spec->autocfg.dig_in_pin; - if (pin) + if (pin) { + struct nid_path *path; snd_hda_set_pin_ctl_cache(codec, pin, PIN_IN); + path = snd_hda_get_path_from_idx(codec, spec->digin_path); + if (path) + snd_hda_activate_path(codec, path, path->active, false); + } }
/* clear unsol-event tags on unused pins; Conexant codecs seem to leave diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 71d409f..ba8de12 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -136,6 +136,7 @@ struct hda_gen_spec { int speaker_paths[AUTO_CFG_MAX_OUTS]; int digout_paths[AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; + int digin_path;
/* auto-mic stuff */ int am_num_entries;
The output paths including aamix should be parsed only for the first output. The surround paths including aamix must be wrong, since it would mix all streams, i.e. all channels would be mixed into a single and multiplexed again.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 65883c7..774c092 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -918,7 +918,7 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, badness += bad->no_dac; } path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); - if (!path && i > 0 && spec->mixer_nid) { + if (!path && !i && spec->mixer_nid) { /* try with aamix */ path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); } @@ -1102,7 +1102,7 @@ static bool map_singles(struct hda_codec *codec, int outs, if (!dac) continue; path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); - if (!path && i > 0 && spec->mixer_nid) + if (!path && !i && spec->mixer_nid) path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); if (path) { dacs[i] = dac;
For codecs that have individual routes going through a loopback mixer (like VIA codecs), we need to provide an explicit switch to choose whether the output goes through mixer or directly from DAC.
This patch adds the check for such paths and creates "Loopback Mixing" enum control when available.
It won't influence on codecs like Realtek or others where the loopback mixer is connected independently from the primary output routes.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 106 ++++++++++++++++++++++++++++++++++++++++++++ sound/pci/hda/hda_generic.h | 4 ++ 2 files changed, 110 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 774c092..5b30f9f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1115,6 +1115,24 @@ static bool map_singles(struct hda_codec *codec, int outs, return found; }
+/* create a new path including aamix if available, and return its index */ +static int check_aamix_out_path(struct hda_codec *codec, int path_idx) +{ + struct nid_path *path; + + path = snd_hda_get_path_from_idx(codec, path_idx); + if (!path || !path->depth || path->with_aa_mix) + return 0; + path = snd_hda_add_new_path(codec, path->path[0], + path->path[path->depth - 1], + HDA_PARSE_ONLY_AAMIX); + if (!path) + return 0; + print_nid_path("output-aamix", path); + path->active = false; /* unused as default */ + return snd_hda_get_path_idx(codec, path); +} + /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired, @@ -1211,6 +1229,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec, badness += err; }
+ if (spec->mixer_nid) { + spec->aamix_out_paths[0] = + check_aamix_out_path(codec, spec->out_paths[0]); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + spec->aamix_out_paths[1] = + check_aamix_out_path(codec, spec->hp_paths[0]); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + spec->aamix_out_paths[2] = + check_aamix_out_path(codec, spec->speaker_paths[0]); + } + if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */ @@ -1728,6 +1757,80 @@ static int create_multi_channel_mode(struct hda_codec *codec) }
/* + * aamix loopback enable/disable switch + */ + +#define loopback_mixing_info indep_hp_info + +static int loopback_mixing_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + ucontrol->value.enumerated.item[0] = spec->aamix_mode; + return 0; +} + +static void update_aamix_paths(struct hda_codec *codec, bool do_mix, + int nomix_path_idx, int mix_path_idx) +{ + struct nid_path *nomix_path, *mix_path; + + nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); + mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); + if (!nomix_path || !mix_path) + return; + if (do_mix) { + snd_hda_activate_path(codec, nomix_path, false, true); + snd_hda_activate_path(codec, mix_path, true, true); + } else { + snd_hda_activate_path(codec, mix_path, false, true); + snd_hda_activate_path(codec, nomix_path, true, true); + } +} + +static int loopback_mixing_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + unsigned int val = ucontrol->value.enumerated.item[0]; + + if (val == spec->aamix_mode) + return 0; + spec->aamix_mode = val; + update_aamix_paths(codec, val, spec->out_paths[0], + spec->aamix_out_paths[0]); + update_aamix_paths(codec, val, spec->hp_paths[0], + spec->aamix_out_paths[1]); + update_aamix_paths(codec, val, spec->speaker_paths[0], + spec->aamix_out_paths[2]); + return 1; +} + +static const struct snd_kcontrol_new loopback_mixing_enum = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Loopback Mixing", + .info = loopback_mixing_info, + .get = loopback_mixing_get, + .put = loopback_mixing_put, +}; + +static int create_loopback_mixing_ctl(struct hda_codec *codec) +{ + struct hda_gen_spec *spec = codec->spec; + + if (!spec->mixer_nid) + return 0; + if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] || + spec->aamix_out_paths[2])) + return 0; + if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) + return -ENOMEM; + return 0; +} + +/* * shared headphone/mic handling */
@@ -3065,6 +3168,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, err = create_indep_hp_ctls(codec); if (err < 0) return err; + err = create_loopback_mixing_ctl(codec); + if (err < 0) + return err; err = create_shared_input(codec); if (err < 0) return err; diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index ba8de12..d4a8f6c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -134,6 +134,7 @@ struct hda_gen_spec { int out_paths[AUTO_CFG_MAX_OUTS]; int hp_paths[AUTO_CFG_MAX_OUTS]; int speaker_paths[AUTO_CFG_MAX_OUTS]; + int aamix_out_paths[3]; int digout_paths[AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; int digin_path; @@ -169,6 +170,9 @@ struct hda_gen_spec { unsigned int indep_hp:1; /* independent HP supported */ unsigned int indep_hp_enabled:1; /* independent HP enabled */
+ /* loopback mixing mode */ + bool aamix_mode; + /* for virtual master */ hda_nid_t vmaster_nid; struct hda_vmaster_mute_hook vmaster_mute;
... like "Speaker Surround Playback Switch". This fix had been already applied to patch_conexant.c but was forgotten in other places, then migrated to hda_generic.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 5b30f9f..2b33d50 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1497,7 +1497,7 @@ static int create_extra_outs(struct hda_codec *codec, int num_pins, { struct hda_gen_spec *spec = codec->spec; struct hda_bind_ctls *ctl; - char name[32]; + char name[44]; int i, n, err;
if (!num_pins || !pins[0]) @@ -2543,7 +2543,7 @@ static int parse_mic_boost(struct hda_codec *codec) nid = cfg->inputs[i].pin; if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) { const char *label; - char boost_label[32]; + char boost_label[44]; struct nid_path *path; unsigned int val;
When 5.1 or more multiple speakers with found but not enough DACs are available, it's better to bind such pins to the DACs of the primary outputs with the same channels rather than binding to the first DAC (i.e. the front channel). For the cases with two speaker pins, it's rather regarded as front + bass combination, thus it's more practical to still bind to the front, though.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 2b33d50..fa8302f 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -860,6 +860,27 @@ static struct badness_table extra_out_badness = { .shared_surr_main = BAD_NO_EXTRA_SURR_DAC, };
+/* get the DAC of the primary output corresponding to the given array index */ +static hda_nid_t get_primary_out(struct hda_codec *codec, int idx) +{ + struct hda_gen_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + + if (cfg->line_outs > idx) + return spec->private_dac_nids[idx]; + idx -= cfg->line_outs; + if (spec->multi_ios > idx) + return spec->multi_io[idx].dac; + return 0; +} + +/* return the DAC if it's reachable, otherwise zero */ +static inline hda_nid_t try_dac(struct hda_codec *codec, + hda_nid_t dac, hda_nid_t pin) +{ + return is_reachable_path(codec, dac, pin) ? dac : 0; +} + /* try to assign DACs to pins and return the resultant badness */ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const hda_nid_t *pins, hda_nid_t *dacs, @@ -867,7 +888,6 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, const struct badness_table *bad) { struct hda_gen_spec *spec = codec->spec; - struct auto_pin_cfg *cfg = &spec->autocfg; int i, j; int badness = 0; hda_nid_t dac; @@ -897,11 +917,12 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, } dac = dacs[i]; if (!dac) { - if (is_reachable_path(codec, dacs[0], pin)) - dac = dacs[0]; - else if (cfg->line_outs > i && - is_reachable_path(codec, spec->private_dac_nids[i], pin)) - dac = spec->private_dac_nids[i]; + if (num_outs > 2) + dac = try_dac(codec, get_primary_out(codec, i), pin); + if (!dac) + dac = try_dac(codec, dacs[0], pin); + if (!dac) + dac = try_dac(codec, get_primary_out(codec, i), pin); if (dac) { if (!i) badness += bad->shared_primary;
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index fa8302f..823fac5 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3250,6 +3250,9 @@ static const char * const slave_pfxs[] = { "Front", "Surround", "Center", "LFE", "Side", "Headphone", "Speaker", "Mono", "Line Out", "CLFE", "Bass Speaker", "PCM", + "Speaker Front", "Speaker Surround", "Speaker CLFE", "Speaker Side", + "Headphone Front", "Headphone Surround", "Headphone CLFE", + "Headphone Side", NULL, };
Just a minor refactoring.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 823fac5..dd65d2a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3751,16 +3751,18 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_pcms); * Standard auto-parser initializations */
-/* configure the path from the given dac to the pin as the proper output */ -static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, +/* configure the given path as a proper output */ +static void set_output_and_unmute(struct hda_codec *codec, int pin_type, int path_idx) { struct nid_path *path; + hda_nid_t pin;
- snd_hda_set_pin_ctl_cache(codec, pin, pin_type); path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path) + if (!path || !path->depth) return; + pin = path->path[path->depth - 1]; + snd_hda_set_pin_ctl_cache(codec, pin, pin_type); snd_hda_activate_path(codec, path, path->active, true); set_pin_eapd(codec, pin, path->active); } @@ -3769,7 +3771,6 @@ static void set_output_and_unmute(struct hda_codec *codec, hda_nid_t pin, static void init_multi_out(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; - hda_nid_t nid; int pin_type; int i;
@@ -3778,27 +3779,18 @@ static void init_multi_out(struct hda_codec *codec) else pin_type = PIN_OUT;
- for (i = 0; i < spec->autocfg.line_outs; i++) { - nid = spec->autocfg.line_out_pins[i]; - if (nid) - set_output_and_unmute(codec, nid, pin_type, - spec->out_paths[i]); - } + for (i = 0; i < spec->autocfg.line_outs; i++) + set_output_and_unmute(codec, pin_type, spec->out_paths[i]); }
static void __init_extra_out(struct hda_codec *codec, int num_outs, - hda_nid_t *pins, int *paths, int type) + int *paths, int type) { int i; - hda_nid_t pin;
- for (i = 0; i < num_outs; i++) { - pin = pins[i]; - if (!pin) - break; - set_output_and_unmute(codec, pin, type, paths[i]); - } + for (i = 0; i < num_outs; i++) + set_output_and_unmute(codec, type, paths[i]); }
/* initialize hp and speaker paths */ @@ -3808,11 +3800,9 @@ static void init_extra_out(struct hda_codec *codec)
if (spec->autocfg.line_out_type != AUTO_PIN_HP_OUT) __init_extra_out(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, spec->hp_paths, PIN_HP); if (spec->autocfg.line_out_type != AUTO_PIN_SPEAKER_OUT) __init_extra_out(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, spec->speaker_paths, PIN_OUT); }
@@ -3909,13 +3899,8 @@ static void init_digital(struct hda_codec *codec) int i; hda_nid_t pin;
- for (i = 0; i < spec->autocfg.dig_outs; i++) { - pin = spec->autocfg.dig_out_pins[i]; - if (!pin) - continue; - set_output_and_unmute(codec, pin, PIN_OUT, - spec->digout_paths[i]); - } + for (i = 0; i < spec->autocfg.dig_outs; i++) + set_output_and_unmute(codec, PIN_OUT, spec->digout_paths[i]); pin = spec->autocfg.dig_in_pin; if (pin) { struct nid_path *path;
The bind-volume workaround was introduced for simplifying the mixer abstraction in the case where one or more pins of multiple outputs lack of individual volume controls. This was essentially the case like Acer Aspire 5935, which has 5.1 speakers and 5.1 (multi-io) jacks although there are 5 DACs, so some of them must share a DAC.
However, the recent code rewrite changed the DAC assignment policy to share with the same channel instead of binding to the front, thus binding the volumes for all channels makes little sense now, rather it's confusing. So in this patch, the ugly workaround is finally dropped and simply create the volume control corresponding to the parsed path position.
For dual headphones or 2.1 speakers with a shared volume control, it's anyway bound to the same DAC if needed, so this change shouldn't bring any practical difference.
And, as a good bonus, we can cut off the whole code handling the bind volume elements.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 114 +++++++------------------------------------- sound/pci/hda/hda_generic.h | 3 -- 2 files changed, 17 insertions(+), 100 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index dd65d2a..15b742a 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -39,7 +39,6 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec) { snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32); - snd_array_init(&spec->bind_ctls, sizeof(struct hda_bind_ctls *), 8); snd_array_init(&spec->paths, sizeof(struct nid_path), 8); mutex_init(&spec->pcm_mutex); return 0; @@ -75,39 +74,11 @@ static void free_kctls(struct hda_gen_spec *spec) snd_array_free(&spec->kctls); }
-static struct hda_bind_ctls *new_bind_ctl(struct hda_codec *codec, - unsigned int nums, - struct hda_ctl_ops *ops) -{ - struct hda_gen_spec *spec = codec->spec; - struct hda_bind_ctls **ctlp, *ctl; - ctlp = snd_array_new(&spec->bind_ctls); - if (!ctlp) - return NULL; - ctl = kzalloc(sizeof(*ctl) + sizeof(long) * (nums + 1), GFP_KERNEL); - *ctlp = ctl; - if (ctl) - ctl->ops = ops; - return ctl; -} - -static void free_bind_ctls(struct hda_gen_spec *spec) -{ - if (spec->bind_ctls.list) { - struct hda_bind_ctls **ctl = spec->bind_ctls.list; - int i; - for (i = 0; i < spec->bind_ctls.used; i++) - kfree(ctl[i]); - } - snd_array_free(&spec->bind_ctls); -} - void snd_hda_gen_spec_free(struct hda_gen_spec *spec) { if (!spec) return; free_kctls(spec); - free_bind_ctls(spec); snd_array_free(&spec->paths); } EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); @@ -1489,8 +1460,7 @@ static int create_multi_out_ctls(struct hda_codec *codec, return 0; }
-static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac, int path_idx, +static int create_extra_out(struct hda_codec *codec, int path_idx, const char *pfx, int cidx) { struct nid_path *path; @@ -1499,12 +1469,9 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin, path = snd_hda_get_path_from_idx(codec, path_idx); if (!path) return 0; - /* bind volume control will be created in the case of dac = 0 */ - if (dac) { - err = add_stereo_vol(codec, pfx, cidx, path); - if (err < 0) - return err; - } + err = add_stereo_vol(codec, pfx, cidx, path); + if (err < 0) + return err; err = add_stereo_sw(codec, pfx, cidx, path); if (err < 0) return err; @@ -1513,69 +1480,26 @@ static int create_extra_out(struct hda_codec *codec, hda_nid_t pin,
/* add playback controls for speaker and HP outputs */ static int create_extra_outs(struct hda_codec *codec, int num_pins, - const hda_nid_t *pins, const hda_nid_t *dacs, const int *paths, const char *pfx) { - struct hda_gen_spec *spec = codec->spec; - struct hda_bind_ctls *ctl; - char name[44]; - int i, n, err; - - if (!num_pins || !pins[0]) - return 0; - - if (num_pins == 1) { - hda_nid_t dac = *dacs; - if (!dac) - dac = spec->multiout.dac_nids[0]; - return create_extra_out(codec, *pins, dac, paths[0], pfx, 0); - } + int i;
for (i = 0; i < num_pins; i++) { - hda_nid_t dac; - if (dacs[num_pins - 1]) - dac = dacs[i]; /* with individual volumes */ - else - dac = 0; - if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) { - err = create_extra_out(codec, pins[i], dac, paths[i], - "Bass Speaker", 0); - } else if (num_pins >= 3) { - snprintf(name, sizeof(name), "%s %s", + const char *name; + char tmp[44]; + int err, idx = 0; + + if (num_pins == 2 && i == 1 && !strcmp(pfx, "Speaker")) + name = "Bass Speaker"; + else if (num_pins >= 3) { + snprintf(tmp, sizeof(tmp), "%s %s", pfx, channel_name[i]); - err = create_extra_out(codec, pins[i], dac, paths[i], - name, 0); + name = tmp; } else { - err = create_extra_out(codec, pins[i], dac, paths[i], - pfx, i); + name = pfx; + idx = i; } - if (err < 0) - return err; - } - if (dacs[num_pins - 1]) - return 0; - - /* Let's create a bind-controls for volumes */ - ctl = new_bind_ctl(codec, num_pins, &snd_hda_bind_vol); - if (!ctl) - return -ENOMEM; - n = 0; - for (i = 0; i < num_pins; i++) { - hda_nid_t vol; - struct nid_path *path; - if (!pins[i] || !dacs[i]) - continue; - path = snd_hda_get_path_from_idx(codec, paths[i]); - if (!path) - continue; - vol = look_for_out_vol_nid(codec, path); - if (vol) - ctl->values[n++] = - HDA_COMPOSE_AMP_VAL(vol, 3, 0, HDA_OUTPUT); - } - if (n) { - snprintf(name, sizeof(name), "%s Playback Volume", pfx); - err = add_control(spec, HDA_CTL_BIND_VOL, name, 0, (long)ctl); + err = create_extra_out(codec, paths[i], name, idx); if (err < 0) return err; } @@ -1586,8 +1510,6 @@ static int create_hp_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.hp_outs, - spec->autocfg.hp_pins, - spec->multiout.hp_out_nid, spec->hp_paths, "Headphone"); } @@ -1596,8 +1518,6 @@ static int create_speaker_out_ctls(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; return create_extra_outs(codec, spec->autocfg.speaker_outs, - spec->autocfg.speaker_pins, - spec->multiout.extra_out_nid, spec->speaker_paths, "Speaker"); } diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index d4a8f6c..4c0d9ad 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -186,9 +186,6 @@ struct hda_gen_spec { int multi_ios; struct hda_multi_io multi_io[4];
- /* bind volumes */ - struct snd_array bind_ctls; - /* hooks */ void (*init_hook)(struct hda_codec *codec); void (*automute_hook)(struct hda_codec *codec);
The new hook which is called at each PCM playback ops. It can be used to control the codec-specific power-saving feature in each codec driver.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 65 +++++++++++++++++++++++++++++++++++++++++---- sound/pci/hda/hda_generic.h | 14 ++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 15b742a..a041032 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3259,6 +3259,16 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_build_controls); * PCM definitions */
+static void call_pcm_playback_hook(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action) +{ + struct hda_gen_spec *spec = codec->spec; + if (spec->pcm_playback_hook) + spec->pcm_playback_hook(hinfo, codec, substream, action); +} + /* * Analog playback callbacks */ @@ -3273,8 +3283,11 @@ static int playback_pcm_open(struct hda_pcm_stream *hinfo, err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, hinfo); - if (!err) + if (!err) { spec->active_streams |= 1 << STREAM_MULTI_OUT; + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_OPEN); + } mutex_unlock(&spec->pcm_mutex); return err; } @@ -3286,8 +3299,14 @@ static int playback_pcm_prepare(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, - stream_tag, format, substream); + int err; + + err = snd_hda_multi_out_analog_prepare(codec, &spec->multiout, + stream_tag, format, substream); + if (!err) + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return err; }
static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, @@ -3295,7 +3314,13 @@ static int playback_pcm_cleanup(struct hda_pcm_stream *hinfo, struct snd_pcm_substream *substream) { struct hda_gen_spec *spec = codec->spec; - return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + int err; + + err = snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); + if (!err) + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return err; }
static int playback_pcm_close(struct hda_pcm_stream *hinfo, @@ -3305,6 +3330,8 @@ static int playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_MULTI_OUT); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; } @@ -3321,6 +3348,8 @@ static int alt_playback_pcm_open(struct hda_pcm_stream *hinfo, err = -EBUSY; else spec->active_streams |= 1 << STREAM_INDEP_HP; + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_OPEN); mutex_unlock(&spec->pcm_mutex); return err; } @@ -3332,10 +3361,34 @@ static int alt_playback_pcm_close(struct hda_pcm_stream *hinfo, struct hda_gen_spec *spec = codec->spec; mutex_lock(&spec->pcm_mutex); spec->active_streams &= ~(1 << STREAM_INDEP_HP); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLOSE); mutex_unlock(&spec->pcm_mutex); return 0; }
+static int alt_playback_pcm_prepare(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + unsigned int stream_tag, + unsigned int format, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_setup_stream(codec, hinfo->nid, stream_tag, 0, format); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_PREPARE); + return 0; +} + +static int alt_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream) +{ + snd_hda_codec_cleanup_stream(codec, hinfo->nid); + call_pcm_playback_hook(hinfo, codec, substream, + HDA_GEN_PCM_ACT_CLEANUP); + return 0; +} + /* * Digital out */ @@ -3430,7 +3483,9 @@ static const struct hda_pcm_stream pcm_analog_alt_playback = { /* NID is set in build_pcms */ .ops = { .open = alt_playback_pcm_open, - .close = alt_playback_pcm_close + .close = alt_playback_pcm_close, + .prepare = alt_playback_pcm_prepare, + .cleanup = alt_playback_pcm_cleanup }, };
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 4c0d9ad..7e84c22 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -69,6 +69,14 @@ struct automic_entry { /* active stream id */ enum { STREAM_MULTI_OUT, STREAM_INDEP_HP };
+/* PCM hook action */ +enum { + HDA_GEN_PCM_ACT_OPEN, + HDA_GEN_PCM_ACT_PREPARE, + HDA_GEN_PCM_ACT_CLEANUP, + HDA_GEN_PCM_ACT_CLOSE, +}; + struct hda_gen_spec { char stream_name_analog[32]; /* analog PCM stream */ const struct hda_pcm_stream *stream_analog_playback; @@ -191,6 +199,12 @@ struct hda_gen_spec { void (*automute_hook)(struct hda_codec *codec); void (*cap_sync_hook)(struct hda_codec *codec);
+ /* PCM playback hook */ + void (*pcm_playback_hook)(struct hda_pcm_stream *hinfo, + struct hda_codec *codec, + struct snd_pcm_substream *substream, + int action); + /* automute / autoswitch hooks */ void (*hp_automute_hook)(struct hda_codec *codec, struct hda_jack_tbl *tbl);
Let is_jack_detectable() return true when the jack polling is enabled for the codec. VT1708 uses the polling explicitly so we need to allow it.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_jack.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 6479b65..1d035ef 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -29,7 +29,8 @@ bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid) if (get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) & AC_DEFCFG_MISC_NO_PRESENCE) return false; - if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP)) + if (!(get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) && + !codec->jackpoll_interval) return false; return true; }
Just to remove duplicated codes. Also fixed EXPORT_SYMBOL() in hda_generic.c.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 33 ++++++++++++++++++--------------- sound/pci/hda/hda_generic.h | 5 +++++ sound/pci/hda/patch_realtek.c | 12 +----------- 3 files changed, 24 insertions(+), 26 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index a041032..78f210d 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3937,36 +3937,39 @@ int snd_hda_gen_init(struct hda_codec *codec) hda_call_check_power_status(codec, 0x01); return 0; } -EXPORT_SYMBOL(snd_hda_gen_init); +EXPORT_SYMBOL_HDA(snd_hda_gen_init);
-/* - * the generic codec support - */ +void snd_hda_gen_free(struct hda_codec *codec) +{ + snd_hda_gen_spec_free(codec->spec); + kfree(codec->spec); + codec->spec = NULL; +} +EXPORT_SYMBOL_HDA(snd_hda_gen_free);
#ifdef CONFIG_PM -static int generic_check_power_status(struct hda_codec *codec, hda_nid_t nid) +int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec; return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); } +EXPORT_SYMBOL_HDA(snd_hda_gen_check_power_status); #endif
-static void generic_free(struct hda_codec *codec) -{ - snd_hda_gen_spec_free(codec->spec); - kfree(codec->spec); - codec->spec = NULL; -} + +/* + * the generic codec support + */
static const struct hda_codec_ops generic_patch_ops = { .build_controls = snd_hda_gen_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = snd_hda_gen_init, - .free = generic_free, + .free = snd_hda_gen_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM - .check_power_status = generic_check_power_status, + .check_power_status = snd_hda_gen_check_power_status, #endif };
@@ -3993,7 +3996,7 @@ int snd_hda_parse_generic_codec(struct hda_codec *codec) return 0;
error: - generic_free(codec); + snd_hda_gen_free(codec); return err; } -EXPORT_SYMBOL(snd_hda_parse_generic_codec); +EXPORT_SYMBOL_HDA(snd_hda_parse_generic_codec); diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 7e84c22..00a1eab 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -218,6 +218,7 @@ int snd_hda_gen_spec_init(struct hda_gen_spec *spec); void snd_hda_gen_spec_free(struct hda_gen_spec *spec);
int snd_hda_gen_init(struct hda_codec *codec); +void snd_hda_gen_free(struct hda_codec *codec);
struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); @@ -257,4 +258,8 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *jack); void snd_hda_gen_update_outputs(struct hda_codec *codec);
+#ifdef CONFIG_PM +int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid); +#endif + #endif /* __SOUND_HDA_GENERIC_H */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1c343f2..ac0937f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -808,14 +808,6 @@ static int alc_init(struct hda_codec *codec) return 0; }
-#ifdef CONFIG_PM -static int alc_check_power_status(struct hda_codec *codec, hda_nid_t nid) -{ - struct alc_spec *spec = codec->spec; - return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); -} -#endif - static inline void alc_shutup(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -876,10 +868,8 @@ static const struct hda_codec_ops alc_patch_ops = { .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM .resume = alc_resume, -#endif -#ifdef CONFIG_PM .suspend = alc_suspend, - .check_power_status = alc_check_power_status, + .check_power_status = snd_hda_gen_check_power_status, #endif .reboot_notify = alc_shutup, };
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 78f210d..68024ad 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -576,15 +576,11 @@ enum { HDA_CTL_WIDGET_VOL, HDA_CTL_WIDGET_MUTE, HDA_CTL_BIND_MUTE, - HDA_CTL_BIND_VOL, - HDA_CTL_BIND_SW, }; static const struct snd_kcontrol_new control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), HDA_CODEC_MUTE(NULL, 0, 0, 0), HDA_BIND_MUTE(NULL, 0, 0, 0), - HDA_BIND_VOL(NULL, 0), - HDA_BIND_SW(NULL, 0), };
/* add dynamic controls from template */
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 68024ad..7c76a00 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3899,6 +3899,10 @@ static void clear_unsol_on_unused_pins(struct hda_codec *codec) } }
+/* + * initialize the generic spec; + * this can be put as patch_ops.init function + */ int snd_hda_gen_init(struct hda_codec *codec) { struct hda_gen_spec *spec = codec->spec; @@ -3935,7 +3939,10 @@ int snd_hda_gen_init(struct hda_codec *codec) } EXPORT_SYMBOL_HDA(snd_hda_gen_init);
- +/* + * free the generic spec; + * this can be put as patch_ops.free function + */ void snd_hda_gen_free(struct hda_codec *codec) { snd_hda_gen_spec_free(codec->spec); @@ -3945,6 +3952,10 @@ void snd_hda_gen_free(struct hda_codec *codec) EXPORT_SYMBOL_HDA(snd_hda_gen_free);
#ifdef CONFIG_PM +/* + * check the loopback power save state; + * this can be put as patch_ops.check_power_status function + */ int snd_hda_gen_check_power_status(struct hda_codec *codec, hda_nid_t nid) { struct hda_gen_spec *spec = codec->spec;
The path indices must be reset at each evaluation of DAC assignment. Otherwise the badness value will be wrongly calculated and mixers may be inconsistently assigned.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 7c76a00..990a79e 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1138,6 +1138,16 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->multiout.extra_out_nid, 0, sizeof(spec->multiout.extra_out_nid)); spec->multi_ios = 0; snd_array_free(&spec->paths); + + /* clear path indices */ + memset(spec->out_paths, 0, sizeof(spec->out_paths)); + memset(spec->hp_paths, 0, sizeof(spec->hp_paths)); + memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); + memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); + memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); + memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); + memset(&spec->digin_path, 0, sizeof(spec->digin_path)); + badness = 0;
/* fill hard-wired DACs first */
Instead of looking through paths with the dac -> pin connection at each time, just pass the already parsed path index to assign_out_path_ctls(). This simplifies the code a bit.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 990a79e..27b5e02 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -757,23 +757,26 @@ enum { BAD_SHARED_VOL = 0x10, };
-/* look for widgets in the path between the given NIDs appropriate for +/* look for widgets in the given path which are appropriate for * volume and mute controls, and assign the values to ctls[]. * * When no appropriate widget is found in the path, the badness value * is incremented depending on the situation. The function returns the * total badness for both volume and mute controls. */ -static int assign_out_path_ctls(struct hda_codec *codec, hda_nid_t pin, - hda_nid_t dac) +static int assign_out_path_ctls(struct hda_codec *codec, struct nid_path *path) { - struct nid_path *path = snd_hda_get_nid_path(codec, dac, pin); hda_nid_t nid; unsigned int val; int badness = 0;
if (!path) return BAD_SHARED_VOL * 2; + + if (path->ctls[NID_PATH_VOL_CTL] || + path->ctls[NID_PATH_MUTE_CTL]) + return 0; /* already evaluated */ + nid = look_for_out_vol_nid(codec, path); if (nid) { val = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT); @@ -866,8 +869,9 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, struct nid_path *path; hda_nid_t pin = pins[i];
- if (dacs[i]) { - badness += assign_out_path_ctls(codec, pin, dacs[i]); + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (path) { + badness += assign_out_path_ctls(codec, path); continue; }
@@ -916,9 +920,8 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, print_nid_path("output", path); path->active = true; path_idx[i] = snd_hda_get_path_idx(codec, path); + badness += assign_out_path_ctls(codec, path); } - if (dac) - badness += assign_out_path_ctls(codec, pin, dac); }
return badness; @@ -1001,6 +1004,7 @@ static int fill_multi_ios(struct hda_codec *codec, unsigned int defcfg = snd_hda_codec_get_pincfg(codec, reference_pin); unsigned int location = get_defcfg_location(defcfg); int badness = 0; + struct nid_path *path;
old_pins = spec->multi_ios; if (old_pins >= 2) @@ -1012,7 +1016,6 @@ static int fill_multi_ios(struct hda_codec *codec,
for (type = AUTO_PIN_LINE_IN; type >= AUTO_PIN_MIC; type--) { for (i = 0; i < cfg->num_inputs; i++) { - struct nid_path *path; hda_nid_t nid = cfg->inputs[i].pin; hda_nid_t dac = 0;
@@ -1067,9 +1070,10 @@ static int fill_multi_ios(struct hda_codec *codec, }
/* assign volume and mute controls */ - for (i = old_pins; i < spec->multi_ios; i++) - badness += assign_out_path_ctls(codec, spec->multi_io[i].pin, - spec->multi_io[i].dac); + for (i = old_pins; i < spec->multi_ios; i++) { + path = snd_hda_get_path_from_idx(codec, spec->out_paths[cfg->line_outs + i]); + badness += assign_out_path_ctls(codec, path); + }
return badness; }
Remove useless code.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 27b5e02..d13e404 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1428,12 +1428,8 @@ static int create_multi_out_ctls(struct hda_codec *codec, for (i = 0; i < noutputs; i++) { const char *name; int index; - hda_nid_t dac; struct nid_path *path;
- dac = spec->multiout.dac_nids[i]; - if (!dac) - continue; if (i >= cfg->line_outs) { index = 0; name = channel_name[i];
We don't have to set up a stream that has been already set up previously.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index cd318e4..c8cc7ce 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1493,7 +1493,7 @@ void snd_hda_codec_setup_stream(struct hda_codec *codec, hda_nid_t nid, "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n", nid, stream_tag, channel_id, format); p = get_hda_cvt_setup(codec, nid); - if (!p) + if (!p || p->active) return;
if (codec->pcm_format_first) @@ -1540,7 +1540,7 @@ void __snd_hda_codec_cleanup_stream(struct hda_codec *codec, hda_nid_t nid,
snd_printdd("hda_codec_cleanup_stream: NID=0x%x\n", nid); p = get_hda_cvt_setup(codec, nid); - if (p) { + if (p && p->active) { /* here we just clear the active flag when do_now isn't set; * actual clean-ups will be done later in * purify_inactive_streams() called from snd_hda_codec_prpapre()
The multi-io channels can vary not only from 1 to 6 but also may vary from 6 to 8 or such. At the same time, there are more speaker pins available than the primary output pins. So, we need three variables to check: the minimum channel counts for primary outputs, the current channel counts for primary outputs, and the minimum channel counts for all outputs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 92 ++++++++++++++++++++++++++++++--------------- sound/pci/hda/hda_generic.h | 16 +++++++- 2 files changed, 76 insertions(+), 32 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index d13e404..193dd55 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1125,6 +1125,25 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) return snd_hda_get_path_idx(codec, path); }
+/* fill the empty entries in the dac array for speaker/hp with the + * shared dac pointed by the paths + */ +static void refill_shared_dacs(struct hda_codec *codec, int num_outs, + hda_nid_t *dacs, int *path_idx) +{ + struct nid_path *path; + int i; + + for (i = 0; i < num_outs; i++) { + if (dacs[i]) + continue; + path = snd_hda_get_path_from_idx(codec, path_idx[i]); + if (!path) + continue; + dacs[i] = path->path[0]; + } +} + /* fill in the dac_nids table from the parsed pin configuration */ static int fill_and_eval_dacs(struct hda_codec *codec, bool fill_hardwired, @@ -1183,19 +1202,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec, spec->private_dac_nids, spec->out_paths, &main_out_badness);
- /* re-count num_dacs and squash invalid entries */ - spec->multiout.num_dacs = 0; - for (i = 0; i < cfg->line_outs; i++) { - if (spec->private_dac_nids[i]) - spec->multiout.num_dacs++; - else { - memmove(spec->private_dac_nids + i, - spec->private_dac_nids + i + 1, - sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); - spec->private_dac_nids[cfg->line_outs - 1] = 0; - } - } - if (fill_mio_first && cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { /* try to fill multi-io first */ @@ -1246,16 +1252,41 @@ static int fill_and_eval_dacs(struct hda_codec *codec, if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) spec->multi_ios = 1; /* give badness */
+ /* re-count num_dacs and squash invalid entries */ + spec->multiout.num_dacs = 0; + for (i = 0; i < cfg->line_outs; i++) { + if (spec->private_dac_nids[i]) + spec->multiout.num_dacs++; + else { + memmove(spec->private_dac_nids + i, + spec->private_dac_nids + i + 1, + sizeof(hda_nid_t) * (cfg->line_outs - i - 1)); + spec->private_dac_nids[cfg->line_outs - 1] = 0; + } + } + + spec->ext_channel_count = spec->min_channel_count = + spec->multiout.num_dacs; + if (spec->multi_ios == 2) { for (i = 0; i < 2; i++) spec->private_dac_nids[spec->multiout.num_dacs++] = spec->multi_io[i].dac; - spec->ext_channel_count = 2; } else if (spec->multi_ios) { spec->multi_ios = 0; badness += BAD_MULTI_IO; }
+ /* re-fill the shared DAC for speaker / headphone */ + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + refill_shared_dacs(codec, cfg->hp_outs, + spec->multiout.hp_out_nid, + spec->hp_paths); + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + refill_shared_dacs(codec, cfg->speaker_outs, + spec->multiout.extra_out_nid, + spec->speaker_paths); + return badness; }
@@ -1608,14 +1639,15 @@ static int ch_mode_info(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; + int chs;
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; uinfo->value.enumerated.items = spec->multi_ios + 1; if (uinfo->value.enumerated.item > spec->multi_ios) uinfo->value.enumerated.item = spec->multi_ios; - sprintf(uinfo->value.enumerated.name, "%dch", - (uinfo->value.enumerated.item + 1) * 2); + chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count; + sprintf(uinfo->value.enumerated.name, "%dch", chs); return 0; }
@@ -1624,7 +1656,8 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_gen_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2; + ucontrol->value.enumerated.item[0] = + (spec->ext_channel_count - spec->min_channel_count) / 2; return 0; }
@@ -1672,9 +1705,9 @@ static int ch_mode_put(struct snd_kcontrol *kcontrol, ch = ucontrol->value.enumerated.item[0]; if (ch < 0 || ch > spec->multi_ios) return -EINVAL; - if (ch == (spec->ext_channel_count - 1) / 2) + if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2) return 0; - spec->ext_channel_count = (ch + 1) * 2; + spec->ext_channel_count = ch * 2 + spec->min_channel_count; for (i = 0; i < spec->multi_ios; i++) set_multi_io(codec, i, i < ch); spec->multiout.max_channels = max(spec->ext_channel_count, @@ -3125,17 +3158,16 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec, if (err < 0) return err;
- /* check the multiple speaker pins */ - if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) - spec->const_channel_count = cfg->line_outs * 2; - else - spec->const_channel_count = cfg->speaker_outs * 2; - - if (spec->multi_ios > 0) - spec->multiout.max_channels = max(spec->ext_channel_count, - spec->const_channel_count); - else - spec->multiout.max_channels = spec->multiout.num_dacs * 2; + spec->const_channel_count = spec->ext_channel_count; + /* check the multiple speaker and headphone pins */ + if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) + spec->const_channel_count = max(spec->const_channel_count, + cfg->speaker_outs * 2); + if (cfg->line_out_type != AUTO_PIN_HP_OUT) + spec->const_channel_count = max(spec->const_channel_count, + cfg->hp_outs * 2); + spec->multiout.max_channels = max(spec->ext_channel_count, + spec->const_channel_count);
err = check_auto_mute_availability(codec); if (err < 0) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 00a1eab..b65769c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -117,8 +117,20 @@ struct hda_gen_spec { unsigned int cur_mux[3];
/* channel model */ - int const_channel_count; /* min. channel count (for speakers) */ - int ext_channel_count; /* current channel count for multi-io */ + /* min_channel_count contains the minimum channel count for primary + * outputs. When multi_ios is set, the channels can be configured + * between min_channel_count and (min_channel_count + multi_ios * 2). + * + * ext_channel_count contains the current channel count of the primary + * out. This varies in the range above. + * + * Meanwhile, const_channel_count is the channel count for all outputs + * including headphone and speakers. It's a constant value, and the + * PCM is set up as max(ext_channel_count, const_channel_count). + */ + int min_channel_count; /* min. channel count for primary out */ + int ext_channel_count; /* current channel count for primary */ + int const_channel_count; /* channel count for all */
/* PCM information */ struct hda_pcm pcm_rec[3]; /* used in build_pcms() */
The multi-io channels can vary not only from 1 to 6 but also may vary from 6 to 8 or such. At the same time, there are more speaker pins available than the primary output pins. So, we need three variables to check: the minimum channel counts for primary outputs, the current channel counts for primary outputs, and the minimum channel counts for all outputs.
Under which condition will the driver create the 6 / 8 channel mode control ?
An unused DAC
desktop with 5 jacks at rear panel and retask Blue Line In jack as output Jack for side channel (AsRock 890GX Extreme3)
... like we did for output and loopback paths. It makes the code slightly easier.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 44 ++++++++++++++++++-------------------------- sound/pci/hda/hda_generic.h | 1 + 2 files changed, 19 insertions(+), 26 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 193dd55..f04a568 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -1168,6 +1168,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec, memset(spec->speaker_paths, 0, sizeof(spec->speaker_paths)); memset(spec->aamix_out_paths, 0, sizeof(spec->aamix_out_paths)); memset(spec->digout_paths, 0, sizeof(spec->digout_paths)); + memset(spec->input_paths, 0, sizeof(spec->input_paths)); memset(spec->loopback_paths, 0, sizeof(spec->loopback_paths)); memset(&spec->digin_path, 0, sizeof(spec->digin_path));
@@ -2056,6 +2057,7 @@ static int create_input_ctls(struct hda_codec *codec) for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t pin; const char *label; + int imux_idx; bool imux_added;
pin = cfg->inputs[i].pin; @@ -2081,24 +2083,23 @@ static int create_input_ctls(struct hda_codec *codec) }
imux_added = false; + imux_idx = imux->num_items; for (c = 0; c < num_adcs; c++) { struct nid_path *path; hda_nid_t adc = spec->adc_nids[c];
if (!is_reachable_path(codec, pin, adc)) continue; - path = snd_array_new(&spec->paths); - if (!path) - return -ENOMEM; - memset(path, 0, sizeof(*path)); - if (!snd_hda_parse_nid_path(codec, pin, adc, HDA_PARSE_ALL, path)) { + path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL); + if (!path) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", pin, adc); - spec->paths.used--; continue; } print_nid_path("input", path); + spec->input_paths[imux_idx][c] = + snd_hda_get_path_idx(codec, path);
if (!imux_added) { spec->imux_pins[imux->num_items] = pin; @@ -2117,13 +2118,13 @@ static int create_input_ctls(struct hda_codec *codec) * input source mux */
-/* get the ADC NID corresponding to the given index */ -static hda_nid_t get_adc_nid(struct hda_codec *codec, int adc_idx, int imux_idx) +/* get the input path specified by the given adc and imux indices */ +static struct nid_path *get_input_path(struct hda_codec *codec, int adc_idx, int imux_idx) { struct hda_gen_spec *spec = codec->spec; if (spec->dyn_adc_switch) adc_idx = spec->dyn_adc_idx[imux_idx]; - return spec->adc_nids[adc_idx]; + return snd_hda_get_path_from_idx(codec, spec->input_paths[imux_idx][adc_idx]); }
static int mux_select(struct hda_codec *codec, unsigned int adc_idx, @@ -2192,9 +2193,8 @@ static int cap_put_caller(struct snd_kcontrol *kcontrol, */ codec->cached_write = 1; for (i = 0; i < imux->num_items; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, adc_idx, i)); - if (!path->ctls[type]) + path = get_input_path(codec, adc_idx, i); + if (!path || !path->ctls[type]) continue; kcontrol->private_value = path->ctls[type]; err = func(kcontrol, ucontrol); @@ -2394,21 +2394,18 @@ static int create_bind_cap_vol_ctl(struct hda_codec *codec, int idx, /* return the vol ctl when used first in the imux list */ static unsigned int get_first_cap_ctl(struct hda_codec *codec, int idx, int type) { - struct hda_gen_spec *spec = codec->spec; struct nid_path *path; unsigned int ctl; int i;
- path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, 0, idx)); + path = get_input_path(codec, 0, idx); if (!path) return 0; ctl = path->ctls[type]; if (!ctl) return 0; for (i = 0; i < idx - 1; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, 0, i)); + path = get_input_path(codec, 0, i); if (path && path->ctls[type] == ctl) return 0; } @@ -2474,8 +2471,7 @@ static int create_capture_mixers(struct hda_codec *codec) vol = sw = 0; for (i = 0; i < imux->num_items; i++) { struct nid_path *path; - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, n, i)); + path = get_input_path(codec, n, i); if (!path) continue; parse_capvol_in_path(codec, path); @@ -2633,9 +2629,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, if (spec->cur_mux[adc_idx] == idx) return 0;
- path = snd_hda_get_nid_path(codec, - spec->imux_pins[spec->cur_mux[adc_idx]], - spec->adc_nids[adc_idx]); + path = get_input_path(codec, adc_idx, spec->cur_mux[adc_idx]); if (!path) return 0; if (path->active) @@ -2649,8 +2643,7 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx, if (spec->dyn_adc_switch) dyn_adc_pcm_resetup(codec, idx);
- path = snd_hda_get_nid_path(codec, spec->imux_pins[idx], - get_adc_nid(codec, adc_idx, idx)); + path = get_input_path(codec, adc_idx, idx); if (!path) return 0; if (path->active) @@ -3887,8 +3880,7 @@ static void init_input_src(struct hda_codec *codec)
for (c = 0; c < nums; c++) { for (i = 0; i < imux->num_items; i++) { - path = snd_hda_get_nid_path(codec, spec->imux_pins[i], - get_adc_nid(codec, c, i)); + path = get_input_path(codec, c, i); if (path) { bool active = path->active; if (i == spec->cur_mux[c]) diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index b65769c..1ad9127 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -156,6 +156,7 @@ struct hda_gen_spec { int speaker_paths[AUTO_CFG_MAX_OUTS]; int aamix_out_paths[3]; int digout_paths[AUTO_CFG_MAX_OUTS]; + int input_paths[HDA_MAX_NUM_INPUTS][AUTO_CFG_MAX_OUTS]; int loopback_paths[HDA_MAX_NUM_INPUTS]; int digin_path;
This commit modifies the definition of snd_hda_parse_nid_path() slightly, now with_aa_mix argument is changed to anchor_nid, so that it can handle any NID generically as an anchor point to include or exclude.
The with_aa_mix field in struct nid_path is removed again by this change.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 102 ++++++++++++++++++++++++-------------------- sound/pci/hda/hda_generic.h | 12 +----- 2 files changed, 57 insertions(+), 57 deletions(-)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index f04a568..ba88be2 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -87,9 +87,25 @@ EXPORT_SYMBOL_HDA(snd_hda_gen_spec_free); * parsing paths */
+/* return the position of NID in the list, or -1 if not found */ +static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) +{ + int i; + for (i = 0; i < nums; i++) + if (list[i] == nid) + return i; + return -1; +} + +/* return true if the given NID is contained in the path */ +static bool is_nid_contained(struct nid_path *path, hda_nid_t nid) +{ + return find_idx_in_nid_list(nid, path->path, path->depth) >= 0; +} + static struct nid_path *get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix) + int anchor_nid) { struct hda_gen_spec *spec = codec->spec; int i; @@ -100,8 +116,9 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, continue; if ((!from_nid || path->path[0] == from_nid) && (!to_nid || path->path[path->depth - 1] == to_nid)) { - if (with_aa_mix == HDA_PARSE_ALL || - path->with_aa_mix == with_aa_mix) + if (!anchor_nid || + (anchor_nid > 0 && is_nid_contained(path, anchor_nid)) || + (anchor_nid < 0 && !is_nid_contained(path, anchor_nid))) return path; } } @@ -114,7 +131,7 @@ static struct nid_path *get_nid_path(struct hda_codec *codec, struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid) { - return get_nid_path(codec, from_nid, to_nid, HDA_PARSE_ALL); + return get_nid_path(codec, from_nid, to_nid, 0); } EXPORT_SYMBOL_HDA(snd_hda_get_nid_path);
@@ -213,17 +230,16 @@ static void print_nid_path(const char *pfx, struct nid_path *path) /* called recursively */ static bool __parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid, - int with_aa_mix, struct nid_path *path, int depth) + int anchor_nid, struct nid_path *path, + int depth) { - struct hda_gen_spec *spec = codec->spec; const hda_nid_t *conn; int i, nums;
- if (to_nid == spec->mixer_nid) { - if (with_aa_mix == HDA_PARSE_NO_AAMIX) - return false; - with_aa_mix = HDA_PARSE_ALL; /* mark aa-mix is included */ - } + if (to_nid == anchor_nid) + anchor_nid = 0; /* anchor passed */ + else if (to_nid == (hda_nid_t)(-anchor_nid)) + return false; /* hit the exclusive nid */
nums = snd_hda_get_conn_list(codec, to_nid, &conn); for (i = 0; i < nums; i++) { @@ -236,8 +252,8 @@ static bool __parse_nid_path(struct hda_codec *codec, is_dac_already_used(codec, conn[i])) continue; } - /* aa-mix is requested but not included? */ - if (!(spec->mixer_nid && with_aa_mix == HDA_PARSE_ONLY_AAMIX)) + /* anchor is not requested or already passed? */ + if (anchor_nid <= 0) goto found; } if (depth >= MAX_NID_PATH_DEPTH) @@ -249,15 +265,13 @@ static bool __parse_nid_path(struct hda_codec *codec, type == AC_WID_PIN) continue; if (__parse_nid_path(codec, from_nid, conn[i], - with_aa_mix, path, depth + 1)) + anchor_nid, path, depth + 1)) goto found; } return false;
found: path->path[path->depth] = conn[i]; - if (conn[i] == spec->mixer_nid) - path->with_aa_mix = true; path->idx[path->depth + 1] = i; if (nums > 1 && get_wcaps_type(get_wcaps(codec, to_nid)) != AC_WID_AUD_MIX) path->multi[path->depth + 1] = 1; @@ -267,17 +281,17 @@ static bool __parse_nid_path(struct hda_codec *codec,
/* parse the widget path from the given nid to the target nid; * when @from_nid is 0, try to find an empty DAC; - * when @with_aa_mix is HDA_PARSE_NO_AAMIX, paths with spec->mixer_nid are - * excluded, only the paths that don't go through the mixer will be chosen. - * when @with_aa_mix is HDA_PARSE_ONLY_AAMIX, only the paths going through - * spec->mixer_nid will be chosen. - * when @with_aa_mix is HDA_PARSE_ALL, no special handling about mixer widget. + * when @anchor_nid is set to a positive value, only paths through the widget + * with the given value are evaluated. + * when @anchor_nid is set to a negative value, paths through the widget + * with the negative of given value are excluded, only other paths are chosen. + * when @anchor_nid is zero, no special handling about path selection. */ bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, + hda_nid_t to_nid, int anchor_nid, struct nid_path *path) { - if (__parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path, 1)) { + if (__parse_nid_path(codec, from_nid, to_nid, anchor_nid, path, 1)) { path->path[path->depth] = to_nid; path->depth++; return true; @@ -292,7 +306,7 @@ EXPORT_SYMBOL_HDA(snd_hda_parse_nid_path); */ struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix) + hda_nid_t to_nid, int anchor_nid) { struct hda_gen_spec *spec = codec->spec; struct nid_path *path; @@ -301,7 +315,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, return NULL;
/* check whether the path has been already added */ - path = get_nid_path(codec, from_nid, to_nid, with_aa_mix); + path = get_nid_path(codec, from_nid, to_nid, anchor_nid); if (path) return path;
@@ -309,7 +323,7 @@ snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, if (!path) return NULL; memset(path, 0, sizeof(*path)); - if (snd_hda_parse_nid_path(codec, from_nid, to_nid, with_aa_mix, path)) + if (snd_hda_parse_nid_path(codec, from_nid, to_nid, anchor_nid, path)) return path; /* push back */ spec->paths.used--; @@ -909,10 +923,10 @@ static int try_assign_dacs(struct hda_codec *codec, int num_outs, else badness += bad->no_dac; } - path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, pin, -spec->mixer_nid); if (!path && !i && spec->mixer_nid) { /* try with aamix */ - path = snd_hda_add_new_path(codec, dac, pin, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dac, pin, 0); } if (!path) dac = dacs[i] = 0; @@ -1038,7 +1052,8 @@ static int fill_multi_ios(struct hda_codec *codec, badness++; continue; } - path = snd_hda_add_new_path(codec, dac, nid, HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, nid, + -spec->mixer_nid); if (!path) { badness++; continue; @@ -1093,9 +1108,10 @@ static bool map_singles(struct hda_codec *codec, int outs, dac = get_dac_if_single(codec, pins[i]); if (!dac) continue; - path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_NO_AAMIX); + path = snd_hda_add_new_path(codec, dac, pins[i], + -spec->mixer_nid); if (!path && !i && spec->mixer_nid) - path = snd_hda_add_new_path(codec, dac, pins[i], HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dac, pins[i], 0); if (path) { dacs[i] = dac; found = true; @@ -1110,14 +1126,16 @@ static bool map_singles(struct hda_codec *codec, int outs, /* create a new path including aamix if available, and return its index */ static int check_aamix_out_path(struct hda_codec *codec, int path_idx) { + struct hda_gen_spec *spec = codec->spec; struct nid_path *path;
path = snd_hda_get_path_from_idx(codec, path_idx); - if (!path || !path->depth || path->with_aa_mix) + if (!path || !path->depth || + is_nid_contained(path, spec->mixer_nid)) return 0; path = snd_hda_add_new_path(codec, path->path[0], path->path[path->depth - 1], - HDA_PARSE_ONLY_AAMIX); + spec->mixer_nid); if (!path) return 0; print_nid_path("output-aamix", path); @@ -1917,7 +1935,7 @@ static int new_analog_input(struct hda_codec *codec, int input_idx, !nid_has_mute(codec, mix_nid, HDA_INPUT)) return 0; /* no need for analog loopback */
- path = snd_hda_add_new_path(codec, pin, mix_nid, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, pin, mix_nid, 0); if (!path) return -EINVAL; print_nid_path("loopback", path); @@ -2090,7 +2108,7 @@ static int create_input_ctls(struct hda_codec *codec)
if (!is_reachable_path(codec, pin, adc)) continue; - path = snd_hda_add_new_path(codec, pin, adc, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, pin, adc, 0); if (!path) { snd_printd(KERN_ERR "invalid input path 0x%x -> 0x%x\n", @@ -2565,7 +2583,7 @@ static void parse_digital(struct hda_codec *codec) dig_nid = look_for_dac(codec, pin, true); if (!dig_nid) continue; - path = snd_hda_add_new_path(codec, dig_nid, pin, HDA_PARSE_ALL); + path = snd_hda_add_new_path(codec, dig_nid, pin, 0); if (!path) continue; print_nid_path("digout", path); @@ -2593,7 +2611,7 @@ static void parse_digital(struct hda_codec *codec) continue; path = snd_hda_add_new_path(codec, spec->autocfg.dig_in_pin, - dig_nid, HDA_PARSE_ALL); + dig_nid, 0); if (path) { print_nid_path("digin", path); path->active = true; @@ -2969,16 +2987,6 @@ static int check_auto_mute_availability(struct hda_codec *codec) return 0; }
-/* return the position of NID in the list, or -1 if not found */ -static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums) -{ - int i; - for (i = 0; i < nums; i++) - if (list[i] == nid) - return i; - return -1; -} - /* check whether all auto-mic pins are valid; setup indices if OK */ static bool auto_mic_check_imux(struct hda_codec *codec) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 1ad9127..343195c 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -53,7 +53,6 @@ struct nid_path { unsigned char multi[MAX_NID_PATH_DEPTH]; unsigned int ctls[NID_PATH_NUM_CTLS]; /* NID_PATH_XXX_CTL */ bool active; - bool with_aa_mix; };
/* mic/line-in auto switching entry */ @@ -237,19 +236,12 @@ struct nid_path *snd_hda_get_nid_path(struct hda_codec *codec, hda_nid_t from_nid, hda_nid_t to_nid); int snd_hda_get_path_idx(struct hda_codec *codec, struct nid_path *path); struct nid_path *snd_hda_get_path_from_idx(struct hda_codec *codec, int idx); - -enum { - HDA_PARSE_NO_AAMIX, - HDA_PARSE_ONLY_AAMIX, - HDA_PARSE_ALL, -}; - bool snd_hda_parse_nid_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix, + hda_nid_t to_nid, int anchor_nid, struct nid_path *path); struct nid_path * snd_hda_add_new_path(struct hda_codec *codec, hda_nid_t from_nid, - hda_nid_t to_nid, int with_aa_mix); + hda_nid_t to_nid, int anchor_nid); void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, bool enable, bool add_aamix);
When the default config value shows the connection AC_JACK_PORT_BOTH, it's better to handle it as a speaker pin. This makes the behavior consistent in snd_hda_get_pin_label() and snd_hda_parse_pin_defcfg().
There are only few old machines showing this attribute, and all of them are actually fixed speaker pins, as far as I know.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_auto_parser.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 44c81d3..6a01c01 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -156,7 +156,8 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
/* workaround for buggy BIOS setups */ if (dev == AC_JACK_LINE_OUT) { - if (conn == AC_JACK_PORT_FIXED) + if (conn == AC_JACK_PORT_FIXED || + conn == AC_JACK_PORT_BOTH) dev = AC_JACK_SPEAKER; }
Add a new flag spec->suppress_mic_auto_switch for codecs that don't support unsol events properly like VT1708.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/pci/hda/hda_generic.c | 3 +++ sound/pci/hda/hda_generic.h | 1 + 2 files changed, 4 insertions(+)
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index ba88be2..2e02094 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -3032,6 +3032,9 @@ static int check_auto_mic_availability(struct hda_codec *codec) unsigned int types; int i, num_pins;
+ if (spec->suppress_auto_mic) + return 0; + types = 0; num_pins = 0; for (i = 0; i < cfg->num_inputs; i++) { diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 343195c..1763e33 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -175,6 +175,7 @@ struct hda_gen_spec { unsigned int automute_speaker_possible:1; /* there are speakers and either LO or HP */ unsigned int automute_lo_possible:1; /* there are line outs and HP */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ + unsigned int suppress_auto_mic:1; /* suppress input jack auto switch */ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */
/* other flags */
At Tue, 8 Jan 2013 12:37:53 +0100, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs. It's based on the Realtek codec parser, so I started hacking patch_realtek.c for more generalization and improvements. Then it's merged to the existing generic parser code, hda_generic.c, and let Realtek parser just uses this new parser code. In the end, patch_realtek.c contains only the device specific fixups and the call of generic parser functions.
The series contains a few improvements in the HD-audio core code, some of which aren't really dependent on the generic parser code. They are just there as I've developed this for a couple of weeks.
After this patchset, most of other codecs can be migrated as well. Currently analog, ca0110, cirrus, cmedia, conexant and via codecs have been already rewritten (i.e. the own parser codes dropped). patch_sigmatel.c is still not converted yet, but can be done soon later. I'm going to send the conversion patches for these codecs right after this series.
I don't expect to merge all conversions of codec parsers in 3.9 kernel, but at least, the conversion of Realtek and this generic parser code addition can be merged to 3.9. Maybe minor and easy ones like ca0110 and cmedia can be merged as well.
One thing I forgot to mention: the whole patches are found in sound-unstable git tree test/hda-gen-parser branch. git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-unstable.git
The test/hda-migrate branch contains the patches for converting codec drivers to use generic parser.
Takashi
On 01/08/2013 12:37 PM, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs.
So it's really happening, wow :-)
Do you have a tree somewhere to make it easier for me to run it through the automated hda-emu tester, to check for regressions and such?
At Tue, 08 Jan 2013 13:20:04 +0100, David Henningsson wrote:
On 01/08/2013 12:37 PM, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs.
So it's really happening, wow :-)
Do you have a tree somewhere to make it easier for me to run it through the automated hda-emu tester, to check for regressions and such?
It's in sound-unstable git tree test/hda-gen-parser branch.
git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-unstable.git
This branch doesn't contain the patches to convert codec drivers, though. The branch including all patches is test/hda-migrate branch.
I posted the same info after git-send-email, but it seems that ML server is still loaded and my post doesn't come up yet...
Takashi
Date 8.1.2013 12:37, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs. It's based on the Realtek codec parser, so I started hacking patch_realtek.c for more generalization and improvements. Then it's merged to the existing generic parser code, hda_generic.c, and let Realtek parser just uses this new parser code. In the end, patch_realtek.c contains only the device specific fixups and the call of generic parser functions.
Great step forward. Thanks.
In my opinion, it would be great to handle all fixups in some "user space" compatible mode. I mean, do not use the C code for fixups, but use an universal description code which can be loaded from the user space probably as ASCII text (firmware like behaviour), too. This code can be buildin to the kernel modules in a default version. I don't mean the current implemented patch processing, but full codec initialization. I think that it may be last change to bring the HDA code to more maintenable state. Just an idea to avoid further HDA code rewrites which happen periodically. And now (with your rewrite) it's really good time to propose also this change.
Please, provide the GIT URL with these changes.
Thanks, Jaroslav
At Tue, 08 Jan 2013 14:03:23 +0100, Jaroslav Kysela wrote:
Date 8.1.2013 12:37, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs. It's based on the Realtek codec parser, so I started hacking patch_realtek.c for more generalization and improvements. Then it's merged to the existing generic parser code, hda_generic.c, and let Realtek parser just uses this new parser code. In the end, patch_realtek.c contains only the device specific fixups and the call of generic parser functions.
Great step forward. Thanks.
In my opinion, it would be great to handle all fixups in some "user space" compatible mode. I mean, do not use the C code for fixups, but use an universal description code which can be loaded from the user space probably as ASCII text (firmware like behaviour), too. This code can be buildin to the kernel modules in a default version. I don't mean the current implemented patch processing, but full codec initialization. I think that it may be last change to bring the HDA code to more maintenable state. Just an idea to avoid further HDA code rewrites which happen periodically. And now (with your rewrite) it's really good time to propose also this change.
Well, most of required fixups are corrections of the wrong BIOS information and/or the missing information like some extra flags. They can be well covered by the current "patch" firmware. And we can build this text into the kernel driver as a default database instead of hard-coded C array (although I'm not sure whether this will give so much benefit).
The rest is some exceptions where some real codes are required. This can't be expressed in a static form. And any byte-code interpreter in the driver is a bad idea, and it doesn't make the whole maintenance scheme easier...
Please, provide the GIT URL with these changes.
Yep, already posted.
thanks,
Takashi
Date 8.1.2013 14:20, Takashi Iwai wrote:
At Tue, 08 Jan 2013 14:03:23 +0100, Jaroslav Kysela wrote:
Date 8.1.2013 12:37, Takashi Iwai wrote:
Hi,
this is a large series of patches to implement the really generic HD-audio codec parser usable for various codecs. It's based on the Realtek codec parser, so I started hacking patch_realtek.c for more generalization and improvements. Then it's merged to the existing generic parser code, hda_generic.c, and let Realtek parser just uses this new parser code. In the end, patch_realtek.c contains only the device specific fixups and the call of generic parser functions.
Great step forward. Thanks.
In my opinion, it would be great to handle all fixups in some "user space" compatible mode. I mean, do not use the C code for fixups, but use an universal description code which can be loaded from the user space probably as ASCII text (firmware like behaviour), too. This code can be buildin to the kernel modules in a default version. I don't mean the current implemented patch processing, but full codec initialization. I think that it may be last change to bring the HDA code to more maintenable state. Just an idea to avoid further HDA code rewrites which happen periodically. And now (with your rewrite) it's really good time to propose also this change.
Well, most of required fixups are corrections of the wrong BIOS information and/or the missing information like some extra flags. They can be well covered by the current "patch" firmware. And we can build this text into the kernel driver as a default database instead of hard-coded C array (although I'm not sure whether this will give so much benefit).
The rest is some exceptions where some real codes are required. This can't be expressed in a static form. And any byte-code interpreter in the driver is a bad idea, and it doesn't make the whole maintenance scheme easier...
I believe that a good compromise might be implemented. The special cases can be also part of the parser having configurable values if possible, so the whole codec description can be static.
The discussion seems to reach another point: One parser handling all possible information or multiple parsers overriding the generic one (actual state)?
Please, provide the GIT URL with these changes.
Yep, already posted.
Got it. Thanks.
Jaroslav
participants (5)
-
David Henningsson
-
FF
-
Jaroslav Kysela
-
Raymond Yau
-
Takashi Iwai