From 6a84c305f03425bab09ffedb71be942ebc22454d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Dec 2012 14:08:45 +0100 Subject: ALSA: hda/realtek - Simplify alc_auto_is_dac_reachable() 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 --- sound/pci/hda/patch_realtek.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f5196277b6e..ca077402d6c 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) -- cgit v1.2.3-18-g5258 From 463419de865622e4b13e977e1536375ab897a53f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 5 Dec 2012 14:17:37 +0100 Subject: ALSA: hda/realtek - List up all available DACs 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 --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index ca077402d6c..8b768a566be 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; -- cgit v1.2.3-18-g5258 From 30dcd3b40409a4db272998b0cba1b9e80c15b1c8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Dec 2012 15:45:38 +0100 Subject: ALSA: hda/realtek - Add output path parser 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 --- sound/pci/hda/patch_realtek.c | 139 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 128 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8b768a566be..1f178d6da9d 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) { -- cgit v1.2.3-18-g5258 From ba8111276f2cc10b9851613bc8300cabda3c7e0d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 6 Dec 2012 18:06:23 +0100 Subject: ALSA: hda/realtek - Manage mixer controls in out_path list 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 --- sound/pci/hda/patch_realtek.c | 230 ++++++++++++++++++++++++------------------ 1 file changed, 131 insertions(+), 99 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1f178d6da9d..cacfbc05786 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); -- cgit v1.2.3-18-g5258 From 792cf2fa2ea926c0c07277c0b67ab60745e8f898 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 16:04:30 +0100 Subject: ALSA: hda/realtek - Reduce vol/mute ctl lookups at parsing codec 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 --- sound/pci/hda/patch_realtek.c | 168 ++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 90 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cacfbc05786..870ac583bea 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, -- cgit v1.2.3-18-g5258 From 78e635c93b0e385dc23d18c2a4047fc8857467bd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 17:07:16 +0100 Subject: ALSA: hda/realtek - Simplify the output volume initialization 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 --- sound/pci/hda/patch_realtek.c | 182 +++++++++++++++++++++--------------------- 1 file changed, 93 insertions(+), 89 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 870ac583bea..f893fb1b23d 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, -- cgit v1.2.3-18-g5258 From 95e960cece76cb538fcac03ac80893db0f1e6a15 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 10 Dec 2012 17:27:57 +0100 Subject: ALSA: hda/realtek - Make path->idx[] and path->multi[] consistent 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 --- sound/pci/hda/patch_realtek.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f893fb1b23d..434856376bf 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) && -- cgit v1.2.3-18-g5258 From 36f0fd540e299c7746601ce7bff7d062a6757c2f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 17:25:00 +0100 Subject: ALSA: hda/realtek - Parse input paths 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 --- sound/pci/hda/patch_realtek.c | 103 ++++++++++++++++++++++++++++++------------ 1 file changed, 73 insertions(+), 30 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 434856376bf..fbc4a97ea8f 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) { -- cgit v1.2.3-18-g5258 From c2fd19c2fc3cc5e609c573f3c66711fd809f9d3c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 18:02:41 +0100 Subject: ALSA: hda/realtek - Parse analog loopback paths more generically 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 --- sound/pci/hda/patch_realtek.c | 38 ++++++++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 8 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbc4a97ea8f..8ce0a0d4f08 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) { -- cgit v1.2.3-18-g5258 From bd32f782b956e6a3040696899a818387ecbe17bc Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 12 Dec 2012 18:08:52 +0100 Subject: ALSA: hda/realtek - Check amp capabilities of aa-mixer widget 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 --- sound/pci/hda/patch_realtek.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 8ce0a0d4f08..7a22ab0b047 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; } -- cgit v1.2.3-18-g5258 From 9366ede7fd2c3ac7ed3a79c5d7c5ee4ad97043b8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 13 Dec 2012 16:43:52 +0100 Subject: ALSA: hda/realtek - Fix initialization of input amps in output paths 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 --- sound/pci/hda/patch_realtek.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a22ab0b047..18c4a78efbc 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, -- cgit v1.2.3-18-g5258 From b8a47c79b28c34652acf9594ef48b0c9fc875401 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 14:20:34 +0100 Subject: ALSA: hda/realtek - Remove non-standard automute mode 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 --- sound/pci/hda/patch_realtek.c | 46 ++++++++++--------------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 18c4a78efbc..9046f1c216a 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)) -- cgit v1.2.3-18-g5258 From 130e5f0642de99a61f46c4f0468bfc5db6030967 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:09:29 +0100 Subject: ALSA: hda/realtek - Add path active flag ... 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 --- sound/pci/hda/patch_realtek.c | 261 ++++++++++++++++++++++++++++-------------- 1 file changed, 174 insertions(+), 87 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9046f1c216a..269d41a2fea 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); } -- cgit v1.2.3-18-g5258 From 9c64076e545771566ebd1b5b6b9f8f5681d83ac4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:15:56 +0100 Subject: ALSA: hda/realtek - Consolidate is_reachable_path() 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 --- sound/pci/hda/patch_realtek.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 269d41a2fea..04713a8744b 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) -- cgit v1.2.3-18-g5258 From c9967f1cbadd3a6af2be54a5baed2cc0dcef50e6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:39:22 +0100 Subject: ALSA: hda/realtek - Consolidate to a single path list We don't have to keep three individual path lists for input, output and loopback. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 94 +++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 61 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 04713a8744b..9a38107cf0f 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) { -- cgit v1.2.3-18-g5258 From fef7fbbc5d6643513b27a706ed02ed4c46a0eef2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 16:54:44 +0100 Subject: ALSA: hda/realtek - Use path-based parser for digital outputs Similar like analog output paths, use the path list for parsing and initializing digital outputs as well. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9a38107cf0f..e25b13a09e5 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; -- cgit v1.2.3-18-g5258 From 6518f7ac5183ca77805f10323ea716fe86fd7c89 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 17:34:51 +0100 Subject: ALSA: hda/realtek - Rename get_out_path() to get_nid_path() The function can be used not only for output paths but generically. Also swap the argument order. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e25b13a09e5..76c67407996 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; -- cgit v1.2.3-18-g5258 From 0250f7cbea95c90564253cae8de80f0caad81fc0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 17:53:29 +0100 Subject: ALSA: hda/realtek - Fix the initialization of pin amp-in 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 --- sound/pci/hda/patch_realtek.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 76c67407996..b6b929ef23e 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); } -- cgit v1.2.3-18-g5258 From 3ebf1e940a3ffb05b6bd78190d6dc8389d1082ef Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:04:37 +0100 Subject: ALSA: hda/realtek - Add missing initialization of multi-io routes 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 --- sound/pci/hda/patch_realtek.c | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index b6b929ef23e..74251b950bf 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); -- cgit v1.2.3-18-g5258 From 8dd48678584c4cf2588335f9ee816a5747980cf6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:19:04 +0100 Subject: ALSA: hda/realtek - Add boost volumes to path list 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 --- sound/pci/hda/patch_realtek.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 74251b950bf..9026b60bdc3 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; -- cgit v1.2.3-18-g5258 From 829f69ea590613c7a8b4b053266265947541b3f9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 14 Dec 2012 18:26:02 +0100 Subject: ALSA: hda/realtek - Initialize loopback paths properly 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 --- sound/pci/hda/patch_realtek.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9026b60bdc3..d5181b01737 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); + } } } -- cgit v1.2.3-18-g5258 From 183a444a6d7e601ddfaba4a40304e020ba1f565c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Dec 2012 18:00:02 +0100 Subject: ALSA: hda/realtek - Don't change connection at path deactivation The widget connection selection must be changed only when the path is enabled. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d5181b01737..1ec14ac2423 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]); -- cgit v1.2.3-18-g5258 From 666a70d42b0fc04daaba64635741b649ed918fce Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Dec 2012 20:29:29 +0100 Subject: ALSA: hda/realtek - Make input path parser more generic 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 --- sound/pci/hda/patch_realtek.c | 997 ++++++++++++++++-------------------------- 1 file changed, 374 insertions(+), 623 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 1ec14ac2423..3ee2be544f4 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; } -- cgit v1.2.3-18-g5258 From 27d31536517aa3609c592b68f1a69b6b687061f8 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 08:57:05 +0100 Subject: ALSA: hda/realtek - Clean up some spec fields 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 --- sound/pci/hda/patch_realtek.c | 56 ++++++++++++++++--------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 3ee2be544f4..4fb815c7351 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; -- cgit v1.2.3-18-g5258 From 62343997e475b8c33fef0d8549eef49f6855ae0a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 09:06:01 +0100 Subject: ALSA: hda/realtek - Remove superfluous input amp init 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 --- sound/pci/hda/patch_realtek.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4fb815c7351..954f4a8db00 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) { -- cgit v1.2.3-18-g5258 From 965ccebccdf7199bef866414e56817b18ea6268e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 11:46:37 +0100 Subject: ALSA: hda/realtek - Rename add_new_out_path() with add_new_nid_path() 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 --- sound/pci/hda/patch_realtek.c | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 954f4a8db00..d9c3b4af41d 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; } -- cgit v1.2.3-18-g5258 From df1d1fb09aed7aae0f94a5ba1a39d0477dee6f1d Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 11:55:53 +0100 Subject: ALSA: hda/realtek - Parse digital input path This was the last forgotten path. Now it's parsed via the same path parser. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d9c3b4af41d..0d6e9d88459 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; } -- cgit v1.2.3-18-g5258 From 37c042076521a854239241eaac94788076046231 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:22:45 +0100 Subject: ALSA: hda/realtek - Allow different pins for shared hp/mic vref check 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 --- sound/pci/hda/patch_realtek.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0d6e9d88459..c6cc833f4d2 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 @@ -6431,6 +6429,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); -- cgit v1.2.3-18-g5258 From 480967db6c5ac0c2cd4582a73ee8aaaffda66d51 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:29:52 +0100 Subject: ALSA: hda/realtek - Drop auto_mic_valid_imux flag This flag is superfluous now and it's always as same as spec->auto_mic. Let's drop. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c6cc833f4d2..e3fe7350b35 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; } -- cgit v1.2.3-18-g5258 From 20c18f562a4d0494dd2b99e19cfeb33f55c9c50e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:33:21 +0100 Subject: ALSA: hda/realtek - Remove unused fields and macro definitions Also arranged alc_spec definitions to optimize bit fields. Use a bit field for spec->need_dac_fix, too. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index e3fe7350b35..f10018b732f 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 -- cgit v1.2.3-18-g5258 From 3bd7b644d049feb8df4225492689a324963f42f4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 14:57:09 +0100 Subject: ALSA: hda/realtek - Handle vmaster hook in the parser side ... 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 --- sound/pci/hda/patch_realtek.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f10018b732f..a3046144f14 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); @@ -6025,15 +6030,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 */ @@ -6048,15 +6046,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, -- cgit v1.2.3-18-g5258 From 52a8efab10637ae5f58123be3ab3b9cb6a9ff2b0 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:33:04 +0100 Subject: ALSA: hda/realtek - Assign Master mixer when possible 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 --- sound/pci/hda/patch_realtek.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a3046144f14..f21c53d437d 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: -- cgit v1.2.3-18-g5258 From 2eab694a6c85499710d050f880c6f8ae705e7a19 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 15:30:41 +0100 Subject: ALSA: hda/realtek - Merge a few split functions 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 --- sound/pci/hda/patch_realtek.c | 32 ++++++-------------------------- 1 file changed, 6 insertions(+), 26 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f21c53d437d..25c0fc91166 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); } /* -- cgit v1.2.3-18-g5258 From bc54976721d30f5ec51e90dcd1aca56494e0b0cf Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 15:35:11 +0100 Subject: ALSA: hda/realtek - Allow passing name=NULL to alc_kcontrol_new() This prevents stupid typos. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 25c0fc91166..13d4548a538 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; -- cgit v1.2.3-18-g5258 From 9bf387b6121bc446f275b0de8196d4dea8a3c876 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:18:21 +0100 Subject: ALSA: hda/realtek - Allow multiple individual capture volume/switch controls 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 --- sound/pci/hda/patch_realtek.c | 149 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 18 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 13d4548a538..6fb39221aac 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; -- cgit v1.2.3-18-g5258 From 81fede89eda16a597c2d814113b74677754b0058 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 17:24:25 +0100 Subject: ALSA: hda/realtek - Add conexant-style inverted dmic handling 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 --- sound/pci/hda/patch_realtek.c | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6fb39221aac..fbdcbded341 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; -- cgit v1.2.3-18-g5258 From c9ce6b260b039392b24ad65954788047d13d4c9a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 18 Dec 2012 18:12:44 +0100 Subject: ALSA: hda - Move fixup code into struct hda_codec 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 --- sound/pci/hda/patch_realtek.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fbdcbded341..567d93f6c69 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; -- cgit v1.2.3-18-g5258 From 7e35dd3d6b7eeeb46f8b82a552fefb7cce3f7580 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 11:39:00 +0100 Subject: ALSA: hda/realtek - Fix split stereo dmic code 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 --- sound/pci/hda/patch_realtek.c | 58 +++++++++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 22 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 567d93f6c69..9218d3086f6 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; -- cgit v1.2.3-18-g5258 From ab16c6dd79389761eca1366d809a002b44b7f960 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 29 Nov 2012 09:56:50 +0100 Subject: ALSA: hda - More generic auto-mic switching for Realtek codecs 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 --- sound/pci/hda/patch_realtek.c | 143 +++++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 65 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9218d3086f6..f210ca7575b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #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; } @@ -6199,8 +6210,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]); } -- cgit v1.2.3-18-g5258 From fdf52cab88bc76d0826c42d0f7e014d31e4a7445 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 12:24:03 +0100 Subject: ALSA: hda/realtek - Remove redundant argument from alc_mux_select() The argument "force" is always false in the recent code. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f210ca7575b..c85899d5040 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 */ -- cgit v1.2.3-18-g5258 From 08c189f2c5523eda847cddb89eb8f44c0b957431 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 19 Dec 2012 15:22:24 +0100 Subject: ALSA: hda - Use generic parser codes for Realtek driver 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 --- sound/pci/hda/patch_realtek.c | 4102 +++-------------------------------------- 1 file changed, 297 insertions(+), 3805 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c85899d5040..896bc2cc929 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 }, }; @@ -6127,8 +2618,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, @@ -6149,7 +2640,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); @@ -6168,7 +2659,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 */ @@ -6185,7 +2676,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 */ @@ -6201,7 +2692,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, @@ -6210,11 +2701,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 { @@ -6560,7 +3052,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); @@ -6615,7 +3107,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; @@ -6671,7 +3163,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 */ @@ -6738,7 +3230,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; @@ -6833,7 +3325,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; @@ -7232,7 +3724,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; -- cgit v1.2.3-18-g5258 From c4f3ebed3c5bc59c88eb2ccda825a12686b58341 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:33:20 +0100 Subject: ALSA: hda - Flush dirty amp caches before writing inv_dmic fix 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 --- sound/pci/hda/patch_realtek.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 896bc2cc929..caf6fa5e6a7 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 */ -- cgit v1.2.3-18-g5258 From 0c3d47b007dcc0ec7c26a1a654a281076f2f8545 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 11:41:54 +0100 Subject: ALSA: hda - Add snd_hda_codec_flush_*_cache() aliases It makes easier to understand although these are identical with snd_hda_codec_resume_*() functions. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index caf6fa5e6a7..a6547570c85 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); -- cgit v1.2.3-18-g5258 From fd25a97a97c78e7f09b0b3069a40bf7671654366 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 20 Dec 2012 14:57:18 +0100 Subject: ALSA: hda - Add spec->vmaster_mute_enum flag to generic parser 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 --- sound/pci/hda/patch_realtek.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a6547570c85..fee21625e50 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2678,8 +2678,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 */ @@ -2694,8 +2696,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, -- cgit v1.2.3-18-g5258 From fce52a3bb15bf3db63cbde496f212edf5e6d366e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 7 Jan 2013 12:42:48 +0100 Subject: ALSA: hda - Add snd_hda_gen_free() and snd_hda_gen_check_power_status() Just to remove duplicated codes. Also fixed EXPORT_SYMBOL() in hda_generic.c. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fee21625e50..bcb258bca99 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, }; -- cgit v1.2.3-18-g5258 From 1727a771b4ff0fb62cbf32cad3c51493e8a4c167 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 09:52:52 +0100 Subject: ALSA: hda/realtek - Drop aliases for old fixups Now the whole codebase has been changed from the earlier kernels, it makes little sense to keep these aliases. Simply replace with the official names. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 521 ++++++++++++++++++++---------------------- 1 file changed, 252 insertions(+), 269 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index bcb258bca99..183b951fa63 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -66,23 +66,6 @@ struct alc_customize_define { unsigned int fixup:1; /* Means that this sku is set by driver, not read from hw */ }; -/* make compatible with old code */ -#define alc_apply_pincfgs snd_hda_apply_pincfgs -#define alc_apply_fixup snd_hda_apply_fixup -#define alc_pick_fixup snd_hda_pick_fixup -#define alc_fixup hda_fixup -#define alc_pincfg hda_pintbl -#define alc_model_fixup hda_model_fixup - -#define ALC_FIXUP_PINS HDA_FIXUP_PINS -#define ALC_FIXUP_VERBS HDA_FIXUP_VERBS -#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC - -#define ALC_FIXUP_ACT_PRE_PROBE HDA_FIXUP_ACT_PRE_PROBE -#define ALC_FIXUP_ACT_PROBE HDA_FIXUP_ACT_PROBE -#define ALC_FIXUP_ACT_INIT HDA_FIXUP_ACT_INIT -#define ALC_FIXUP_ACT_BUILD HDA_FIXUP_ACT_BUILD - struct alc_spec { struct hda_gen_spec gen; /* must be at head */ @@ -734,9 +717,9 @@ static int alc_add_inv_dmic_mixer(struct hda_codec *codec, hda_nid_t nid) /* typically the digital mic is put at node 0x12 */ static void alc_fixup_inv_dmic_0x12(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) alc_add_inv_dmic_mixer(codec, 0x12); } @@ -782,7 +765,7 @@ static int alc_build_controls(struct hda_codec *codec) } #endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_BUILD); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_BUILD); return 0; } @@ -803,7 +786,7 @@ static int alc_init(struct hda_codec *codec) snd_hda_gen_init(codec); - alc_apply_fixup(codec, ALC_FIXUP_ACT_INIT); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT); return 0; } @@ -1050,23 +1033,23 @@ enum { /* enable the volume-knob widget support on NID 0x21 */ static void alc880_fixup_vol_knob(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_detect_enable_callback(codec, 0x21, ALC_DCVOL_EVENT, alc_update_knob_master); } -static const struct alc_fixup alc880_fixups[] = { +static const struct hda_fixup alc880_fixups[] = { [ALC880_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC880_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC880_FIXUP_MEDION_RIM] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3060 }, @@ -1076,8 +1059,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_LG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x16, 0x411111f0 }, { 0x18, 0x411111f0 }, @@ -1086,8 +1069,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_W810] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { } @@ -1096,7 +1079,7 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_EAPD_COEF] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1105,7 +1088,7 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_TCL_S700] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1116,13 +1099,13 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_GPIO2, }, [ALC880_FIXUP_VOL_KNOB] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc880_fixup_vol_knob, }, [ALC880_FIXUP_FUJITSU] = { /* override all pins as BIOS on old Amilo is broken */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1141,8 +1124,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_F1734] = { /* almost compatible with FUJITSU, but no bass and SPDIF */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1161,8 +1144,8 @@ static const struct alc_fixup alc880_fixups[] = { }, [ALC880_FIXUP_UNIWILL] = { /* need to fix HP and speaker pins to be parsed correctly */ - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0121411f }, /* HP */ { 0x15, 0x99030120 }, /* speaker */ { 0x16, 0x99030130 }, /* bass speaker */ @@ -1170,8 +1153,8 @@ static const struct alc_fixup alc880_fixups[] = { }, }, [ALC880_FIXUP_UNIWILL_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* disable bogus unused pins */ { 0x17, 0x411111f0 }, { 0x19, 0x411111f0 }, @@ -1181,8 +1164,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_Z71V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { /* set up the whole pins as BIOS is utterly broken */ { 0x14, 0x99030120 }, /* speaker */ { 0x15, 0x0121411f }, /* HP */ @@ -1199,8 +1182,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* line-out */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x411111f0 }, /* N/A */ @@ -1217,8 +1200,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_3ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1226,8 +1209,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_3ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1235,8 +1218,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_3ST_BASE, }, [ALC880_FIXUP_5ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x411111f0 }, /* N/A */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1253,8 +1236,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_5ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1262,8 +1245,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_5ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1271,8 +1254,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_5ST_BASE, }, [ALC880_FIXUP_6ST_BASE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x01014010 }, /* front */ { 0x15, 0x01016412 }, /* surr */ { 0x16, 0x01011411 }, /* CLFE */ @@ -1289,8 +1272,8 @@ static const struct alc_fixup alc880_fixups[] = { } }, [ALC880_FIXUP_6ST] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x411111f0 }, /* N/A */ { } }, @@ -1298,8 +1281,8 @@ static const struct alc_fixup alc880_fixups[] = { .chain_id = ALC880_FIXUP_6ST_BASE, }, [ALC880_FIXUP_6ST_DIG] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1e, 0x0144111e }, /* SPDIF */ { } }, @@ -1375,7 +1358,7 @@ static const struct snd_pci_quirk alc880_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc880_fixup_models[] = { +static const struct hda_model_fixup alc880_fixup_models[] = { {.id = ALC880_FIXUP_3ST, .name = "3stack"}, {.id = ALC880_FIXUP_3ST_DIG, .name = "3stack-digout"}, {.id = ALC880_FIXUP_5ST, .name = "5stack"}, @@ -1401,9 +1384,9 @@ static int patch_alc880(struct hda_codec *codec) spec = codec->spec; spec->gen.need_dac_fix = 1; - alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, + snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl, alc880_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc880_parse_auto_config(codec); @@ -1421,7 +1404,7 @@ static int patch_alc880(struct hda_codec *codec) codec->patch_ops.unsol_event = alc880_unsol_event; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1463,10 +1446,10 @@ static void alc260_gpio1_automute(struct hda_codec *codec) } static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { /* although the machine has only one output pin, we need to * toggle GPIO1 according to the jack state */ @@ -1481,10 +1464,10 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec, } static void alc260_fixup_kn1(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - static const struct alc_pincfg pincfgs[] = { + static const struct hda_pintbl pincfgs[] = { { 0x0f, 0x02214000 }, /* HP/speaker */ { 0x12, 0x90a60160 }, /* int mic */ { 0x13, 0x02a19000 }, /* ext mic */ @@ -1501,32 +1484,32 @@ static void alc260_fixup_kn1(struct hda_codec *codec, }; switch (action) { - case ALC_FIXUP_ACT_PRE_PROBE: - alc_apply_pincfgs(codec, pincfgs); + case HDA_FIXUP_ACT_PRE_PROBE: + snd_hda_apply_pincfgs(codec, pincfgs); break; - case ALC_FIXUP_ACT_PROBE: + case HDA_FIXUP_ACT_PROBE: spec->init_amp = ALC_INIT_NONE; break; } } -static const struct alc_fixup alc260_fixups[] = { +static const struct hda_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x11, 0x90130110 }, /* speaker */ { } } }, [ALC260_FIXUP_HP_PIN_0F] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0f, 0x01214000 }, /* HP */ { } } }, [ALC260_FIXUP_COEF] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3040 }, @@ -1536,17 +1519,17 @@ static const struct alc_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC260_FIXUP_GPIO1_TOGGLE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_HP_PIN_0F, }, [ALC260_FIXUP_REPLACER] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -1556,13 +1539,13 @@ static const struct alc_fixup alc260_fixups[] = { .chain_id = ALC260_FIXUP_GPIO1_TOGGLE, }, [ALC260_FIXUP_HP_B1900] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_gpio1_toggle, .chained = true, .chain_id = ALC260_FIXUP_COEF, }, [ALC260_FIXUP_KN1] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_kn1, }, }; @@ -1593,8 +1576,8 @@ static int patch_alc260(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc260_parse_auto_config(codec); @@ -1611,7 +1594,7 @@ static int patch_alc260(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -1665,9 +1648,9 @@ enum { }; static void alc889_fixup_coef(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; alc889_coef_init(codec); } @@ -1707,9 +1690,9 @@ static void alc882_gpio_mute(struct hda_codec *codec, int pin, int muted) /* set up GPIO at initialization */ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; alc882_gpio_mute(codec, 0, 0); alc882_gpio_mute(codec, 1, 0); @@ -1720,9 +1703,9 @@ static void alc885_fixup_macpro_gpio(struct hda_codec *codec, * work correctly (bko#42740) */ static void alc889_fixup_dac_route(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { /* fake the connections during parsing the tree */ hda_nid_t conn1[2] = { 0x0c, 0x0d }; hda_nid_t conn2[2] = { 0x0e, 0x0f }; @@ -1730,7 +1713,7 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, snd_hda_override_conn_list(codec, 0x15, 2, conn1); snd_hda_override_conn_list(codec, 0x18, 2, conn2); snd_hda_override_conn_list(codec, 0x1a, 2, conn2); - } else if (action == ALC_FIXUP_ACT_PROBE) { + } else if (action == HDA_FIXUP_ACT_PROBE) { /* restore the connections */ hda_nid_t conn[5] = { 0x0c, 0x0d, 0x0e, 0x0f, 0x26 }; snd_hda_override_conn_list(codec, 0x14, 5, conn); @@ -1742,13 +1725,13 @@ static void alc889_fixup_dac_route(struct hda_codec *codec, /* Set VREF on HP pin */ static void alc889_fixup_mbp_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x14, 0x15 }; int i; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); @@ -1765,13 +1748,13 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, /* Set VREF on speaker pins on imac91 */ static void alc889_fixup_imac91_vref(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; static hda_nid_t nids[2] = { 0x18, 0x1a }; int i; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val; @@ -1787,17 +1770,17 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, * strangely, the speaker output doesn't work on VAIO Z through DAC 0x05 */ static void alc882_fixup_no_primary_hp(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.no_primary_hp = 1; } -static const struct alc_fixup alc882_fixups[] = { +static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x01080104 }, /* side */ { 0x16, 0x01011012 }, /* rear */ { 0x17, 0x01016011 }, /* clfe */ @@ -1805,47 +1788,47 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_LENOVO_Y530] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x99130112 }, /* rear int speakers */ { 0x16, 0x99130111 }, /* subwoofer */ { } } }, [ALC882_FIXUP_PB_M5210] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} } }, [ALC882_FIXUP_ACER_ASPIRE_7736] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC882_FIXUP_ASUS_W90V] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130110 }, /* fix sequence for CLFE */ { } } }, [ALC889_FIXUP_CD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1c, 0x993301f0 }, /* CD */ { } } }, [ALC889_FIXUP_VAIO_TT] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x90170111 }, /* hidden surround speaker */ { } } }, [ALC888_FIXUP_EEE1601] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, { 0x20, AC_VERB_SET_PROC_COEF, 0x0838 }, @@ -1853,7 +1836,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1862,7 +1845,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* change to EAPD mode */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1871,7 +1854,7 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC883_FIXUP_ACER_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* eanable EAPD on Acer laptops */ { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, @@ -1880,30 +1863,30 @@ static const struct alc_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, }, [ALC882_FIXUP_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio2_init_verbs, }, [ALC882_FIXUP_GPIO3] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio3_init_verbs, }, [ALC882_FIXUP_ASUS_W2JC] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = alc_gpio1_init_verbs, .chained = true, .chain_id = ALC882_FIXUP_EAPD, }, [ALC889_FIXUP_COEF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_coef, }, [ALC882_FIXUP_ACER_ASPIRE_4930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x17, 0x99130112 }, /* surround speaker */ { } @@ -1912,8 +1895,8 @@ static const struct alc_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_ACER_ASPIRE_8930G] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130111 }, /* CLFE speaker */ { 0x1b, 0x99130112 }, /* surround speaker */ { } @@ -1923,7 +1906,7 @@ static const struct alc_fixup alc882_fixups[] = { }, [ALC882_FIXUP_ASPIRE_8930G_VERBS] = { /* additional init verbs for Acer Aspire 8930G */ - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enable all DACs */ /* DAC DISABLE/MUTE 1? */ @@ -1957,31 +1940,31 @@ static const struct alc_fixup alc882_fixups[] = { .chain_id = ALC882_FIXUP_GPIO1, }, [ALC885_FIXUP_MACPRO_GPIO] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc885_fixup_macpro_gpio, }, [ALC889_FIXUP_DAC_ROUTE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_dac_route, }, [ALC889_FIXUP_MBP_VREF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_mbp_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC889_FIXUP_IMAC91_VREF] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc889_fixup_imac91_vref, .chained = true, .chain_id = ALC882_FIXUP_GPIO1, }, [ALC882_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC882_FIXUP_NO_PRIMARY_HP] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc882_fixup_no_primary_hp, }, }; @@ -2056,7 +2039,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc882_fixup_models[] = { +static const struct hda_model_fixup alc882_fixup_models[] = { {.id = ALC882_FIXUP_ACER_ASPIRE_4930G, .name = "acer-aspire-4930g"}, {.id = ALC882_FIXUP_ACER_ASPIRE_8930G, .name = "acer-aspire-8930g"}, {.id = ALC883_FIXUP_ACER_EAPD, .name = "acer-aspire"}, @@ -2099,9 +2082,9 @@ static int patch_alc882(struct hda_codec *codec) break; } - alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, + snd_hda_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl, alc882_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2119,7 +2102,7 @@ static int patch_alc882(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2152,10 +2135,10 @@ enum { ALC262_FIXUP_INV_DMIC, }; -static const struct alc_fixup alc262_fixups[] = { +static const struct hda_fixup alc262_fixups[] = { [ALC262_FIXUP_FSC_H270] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0221142f }, /* front HP */ { 0x1b, 0x0121141f }, /* rear HP */ @@ -2163,21 +2146,21 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_HP_Z200] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x16, 0x99130120 }, /* internal speaker */ { } } }, [ALC262_FIXUP_TYAN] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x1993e1f0 }, /* int AUX */ { } } }, [ALC262_FIXUP_LENOVO_3000] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, {} @@ -2186,7 +2169,7 @@ static const struct alc_fixup alc262_fixups[] = { .chain_id = ALC262_FIXUP_BENQ, }, [ALC262_FIXUP_BENQ] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3070 }, @@ -2194,7 +2177,7 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_BENQ_T31] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { { 0x20, AC_VERB_SET_COEF_INDEX, 0x07 }, { 0x20, AC_VERB_SET_PROC_COEF, 0x3050 }, @@ -2202,7 +2185,7 @@ static const struct alc_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; @@ -2219,7 +2202,7 @@ static const struct snd_pci_quirk alc262_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc262_fixup_models[] = { +static const struct hda_model_fixup alc262_fixup_models[] = { {.id = ALC262_FIXUP_INV_DMIC, .name = "inv-dmic"}, {} }; @@ -2252,9 +2235,9 @@ static int patch_alc262(struct hda_codec *codec) #endif alc_fix_pll_init(codec, 0x20, 0x0a, 10); - alc_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, + snd_hda_pick_fixup(codec, alc262_fixup_models, alc262_fixup_tbl, alc262_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -2273,7 +2256,7 @@ static int patch_alc262(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2314,13 +2297,13 @@ enum { ALC268_FIXUP_HP_EAPD, }; -static const struct alc_fixup alc268_fixups[] = { +static const struct hda_fixup alc268_fixups[] = { [ALC268_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC268_FIXUP_HP_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x15, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} @@ -2328,7 +2311,7 @@ static const struct alc_fixup alc268_fixups[] = { }, }; -static const struct alc_model_fixup alc268_fixup_models[] = { +static const struct hda_model_fixup alc268_fixup_models[] = { {.id = ALC268_FIXUP_INV_DMIC, .name = "inv-dmic"}, {.id = ALC268_FIXUP_HP_EAPD, .name = "hp-eapd"}, {} @@ -2374,8 +2357,8 @@ static int patch_alc268(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc268_parse_auto_config(codec); @@ -2406,7 +2389,7 @@ static int patch_alc268(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -2564,27 +2547,27 @@ static int alc269_resume(struct hda_codec *codec) #endif /* CONFIG_PM */ static void alc269_fixup_pincfg_no_hp_to_lineout(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP; } static void alc269_fixup_hweq(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { int coef; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; coef = alc_read_coef_idx(codec, 0x1e); alc_write_coef_idx(codec, 0x1e, coef | 0x80); } static void alc271_fixup_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { static const struct hda_verb verbs[] = { {0x20, AC_VERB_SET_COEF_INDEX, 0x0d}, @@ -2601,11 +2584,11 @@ static void alc271_fixup_dmic(struct hda_codec *codec, } static void alc269_fixup_pcm_44k(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; /* Due to a hardware problem on Lenovo Ideadpad, we need to @@ -2616,11 +2599,11 @@ static void alc269_fixup_pcm_44k(struct hda_codec *codec, } static void alc269_fixup_stereo_dmic(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { int coef; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; /* The digital-mic unit sends PDM (differential signal) instead of * the standard PCM, thus you can't record a valid mono stream as is. @@ -2647,10 +2630,10 @@ static void alc269_quanta_automute(struct hda_codec *codec) } static void alc269_fixup_quanta_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; spec->gen.automute_hook = alc269_quanta_automute; } @@ -2665,10 +2648,10 @@ static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) } static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -2683,17 +2666,17 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) } static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == ALC_FIXUP_ACT_PROBE) { + if (action == HDA_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, - const struct alc_fixup *fix, + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; @@ -2701,7 +2684,7 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec, if (snd_BUG_ON(!spec->gen.am_entry[1].pin || !spec->gen.autocfg.hp_pins[0])) return; - if (action == ALC_FIXUP_ACT_PROBE) + if (action == HDA_FIXUP_ACT_PROBE) snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, spec->gen.autocfg.hp_pins[0]); } @@ -2732,16 +2715,16 @@ enum { ALC271_FIXUP_HP_GATE_MIC_JACK, }; -static const struct alc_fixup alc269_fixups[] = { +static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, {} } }, [ALC275_FIXUP_SONY_VAIO_GPIO2] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x01, AC_VERB_SET_GPIO_MASK, 0x04}, {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04}, @@ -2752,7 +2735,7 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_SONY_VAIO }, [ALC269_FIXUP_DELL_M101Z] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* Enables internal speaker */ {0x20, AC_VERB_SET_COEF_INDEX, 13}, @@ -2761,50 +2744,50 @@ static const struct alc_fixup alc269_fixups[] = { } }, [ALC269_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC269_FIXUP_ASUS_G73JW] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x99130111 }, /* subwoofer */ { } } }, [ALC269_FIXUP_LENOVO_EAPD] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC275_FIXUP_SONY_HWEQ] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_hweq, .chained = true, .chain_id = ALC275_FIXUP_SONY_VAIO_GPIO2 }, [ALC271_FIXUP_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc271_fixup_dmic, }, [ALC269_FIXUP_PCM_44K] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pcm_44k, .chained = true, .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_STEREO_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_stereo_dmic, }, [ALC269_FIXUP_QUANTA_MUTE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_quanta_mute, }, [ALC269_FIXUP_LIFEBOOK] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1a, 0x2101103f }, /* dock line-out */ { 0x1b, 0x23a11040 }, /* dock mic-in */ { } @@ -2813,8 +2796,8 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_QUANTA_MUTE }, [ALC269_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2823,8 +2806,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121401f }, /* HP out */ @@ -2833,8 +2816,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_AMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -2843,8 +2826,8 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269VB_FIXUP_DMIC] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x12, 0x99a3092f }, /* int-mic */ { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ @@ -2853,20 +2836,20 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC269_FIXUP_MIC1_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_mic1_mute, }, [ALC269_FIXUP_MIC2_MUTE_LED] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_mic2_mute, }, [ALC269_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, [ALC269_FIXUP_LENOVO_DOCK] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x19, 0x23a11040 }, /* dock mic */ { 0x1b, 0x2121103f }, /* dock headphone */ { } @@ -2875,12 +2858,12 @@ static const struct alc_fixup alc269_fixups[] = { .chain_id = ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT }, [ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc269_fixup_pincfg_no_hp_to_lineout, }, [ALC271_FIXUP_AMIC_MIC2] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x19, 0x01a19c20 }, /* mic */ { 0x1b, 0x99a7012f }, /* int-mic */ @@ -2889,7 +2872,7 @@ static const struct alc_fixup alc269_fixups[] = { }, }, [ALC271_FIXUP_HP_GATE_MIC_JACK] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc271_hp_gate_mic_jack, .chained = true, .chain_id = ALC271_FIXUP_AMIC_MIC2, @@ -2982,7 +2965,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc269_fixup_models[] = { +static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC269_FIXUP_AMIC, .name = "laptop-amic"}, {.id = ALC269_FIXUP_DMIC, .name = "laptop-dmic"}, {.id = ALC269_FIXUP_STEREO_DMIC, .name = "alc269-dmic"}, @@ -3051,9 +3034,9 @@ static int patch_alc269(struct hda_codec *codec) spec = codec->spec; spec->gen.shared_mic_vref_pin = 0x18; - alc_pick_fixup(codec, alc269_fixup_models, + snd_hda_pick_fixup(codec, alc269_fixup_models, alc269_fixup_tbl, alc269_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3117,7 +3100,7 @@ static int patch_alc269(struct hda_codec *codec) #endif spec->shutup = alc269_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3147,12 +3130,12 @@ enum { /* On some laptops, VREF of pin 0x0f is abused for controlling the main amp */ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; unsigned int val; - if (action != ALC_FIXUP_ACT_INIT) + if (action != HDA_FIXUP_ACT_INIT) return; val = snd_hda_codec_read(codec, 0x0f, 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -3165,31 +3148,31 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, /* suppress the jack-detection */ static void alc_fixup_no_jack_detect(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) + if (action == HDA_FIXUP_ACT_PRE_PROBE) codec->no_jack_detect = 1; } -static const struct alc_fixup alc861_fixups[] = { +static const struct hda_fixup alc861_fixups[] = { [ALC861_FIXUP_FSC_AMILO_PI1505] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x0b, 0x0221101f }, /* HP */ { 0x0f, 0x90170310 }, /* speaker */ { } } }, [ALC861_FIXUP_AMP_VREF_0F] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, }, [ALC861_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC861_FIXUP_ASUS_A6RP] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861_fixup_asus_amp_vref_0f, .chained = true, .chain_id = ALC861_FIXUP_NO_JACK_DETECT, @@ -3219,8 +3202,8 @@ static int patch_alc861(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861_parse_auto_config(codec); @@ -3239,7 +3222,7 @@ static int patch_alc861(struct hda_codec *codec) spec->power_hook = alc_power_eapd; #endif - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3269,17 +3252,17 @@ enum { /* exclude VREF80 */ static void alc861vd_fixup_dallas(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action == ALC_FIXUP_ACT_PRE_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { snd_hda_override_pin_caps(codec, 0x18, 0x00000734); snd_hda_override_pin_caps(codec, 0x19, 0x0000073c); } } -static const struct alc_fixup alc861vd_fixups[] = { +static const struct hda_fixup alc861vd_fixups[] = { [ALC660VD_FIX_ASUS_GPIO1] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { /* reset GPIO1 */ {0x01, AC_VERB_SET_GPIO_MASK, 0x03}, @@ -3289,7 +3272,7 @@ static const struct alc_fixup alc861vd_fixups[] = { } }, [ALC861VD_FIX_DALLAS] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc861vd_fixup_dallas, }, }; @@ -3314,8 +3297,8 @@ static int patch_alc861vd(struct hda_codec *codec) spec = codec->spec; - alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); /* automatic parse from the BIOS config */ err = alc861vd_parse_auto_config(codec); @@ -3333,7 +3316,7 @@ static int patch_alc861vd(struct hda_codec *codec) spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; @@ -3374,9 +3357,9 @@ static int alc662_parse_auto_config(struct hda_codec *codec) } static void alc272_fixup_mario(struct hda_codec *codec, - const struct alc_fixup *fix, int action) + const struct hda_fixup *fix, int action) { - if (action != ALC_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PROBE) return; if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, (0x3b << AC_AMPCAP_OFFSET_SHIFT) | @@ -3407,39 +3390,39 @@ enum { ALC662_FIXUP_INV_DMIC, }; -static const struct alc_fixup alc662_fixups[] = { +static const struct hda_fixup alc662_fixups[] = { [ALC662_FIXUP_ASPIRE] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x15, 0x99130112 }, /* subwoofer */ { } } }, [ALC662_FIXUP_IDEAPAD] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x17, 0x99130112 }, /* subwoofer */ { } } }, [ALC272_FIXUP_MARIO] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc272_fixup_mario, }, [ALC662_FIXUP_CZC_P10T] = { - .type = ALC_FIXUP_VERBS, + .type = HDA_FIXUP_VERBS, .v.verbs = (const struct hda_verb[]) { {0x14, AC_VERB_SET_EAPD_BTLENABLE, 0}, {} } }, [ALC662_FIXUP_SKU_IGNORE] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_sku_ignore, }, [ALC662_FIXUP_HP_RP5800] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x0221201f }, /* HP out */ { } }, @@ -3447,8 +3430,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE1] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19c20 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3459,8 +3442,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE2] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x18, 0x01a19820 }, /* mic */ { 0x19, 0x99a3092f }, /* int-mic */ @@ -3471,8 +3454,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE3] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x18, 0x01a19840 }, /* mic */ @@ -3484,8 +3467,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE4] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x16, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3497,8 +3480,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE5] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x0121441f }, /* HP */ { 0x16, 0x99130111 }, /* speaker */ @@ -3510,8 +3493,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE6] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x15, 0x01211420 }, /* HP2 */ { 0x18, 0x01a19840 }, /* mic */ @@ -3523,8 +3506,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE7] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x17, 0x99130111 }, /* speaker */ { 0x18, 0x01a19840 }, /* mic */ @@ -3537,8 +3520,8 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_ASUS_MODE8] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x14, 0x99130110 }, /* speaker */ { 0x12, 0x99a30970 }, /* int-mic */ { 0x15, 0x01214020 }, /* HP */ @@ -3551,18 +3534,18 @@ static const struct alc_fixup alc662_fixups[] = { .chain_id = ALC662_FIXUP_SKU_IGNORE }, [ALC662_FIXUP_NO_JACK_DETECT] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_no_jack_detect, }, [ALC662_FIXUP_ZOTAC_Z68] = { - .type = ALC_FIXUP_PINS, - .v.pins = (const struct alc_pincfg[]) { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { { 0x1b, 0x02214020 }, /* Front HP */ { } } }, [ALC662_FIXUP_INV_DMIC] = { - .type = ALC_FIXUP_FUNC, + .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_inv_dmic_0x12, }, }; @@ -3642,7 +3625,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { {} }; -static const struct alc_model_fixup alc662_fixup_models[] = { +static const struct hda_model_fixup alc662_fixup_models[] = { {.id = ALC272_FIXUP_MARIO, .name = "mario"}, {.id = ALC662_FIXUP_ASUS_MODE1, .name = "asus-mode1"}, {.id = ALC662_FIXUP_ASUS_MODE2, .name = "asus-mode2"}, @@ -3703,9 +3686,9 @@ static int patch_alc662(struct hda_codec *codec) spec->init_hook = alc662_fill_coef; alc662_fill_coef(codec); - alc_pick_fixup(codec, alc662_fixup_models, + snd_hda_pick_fixup(codec, alc662_fixup_models, alc662_fixup_tbl, alc662_fixups); - alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); alc_auto_parse_customize_define(codec); @@ -3743,7 +3726,7 @@ static int patch_alc662(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; spec->shutup = alc_eapd_shutup; - alc_apply_fixup(codec, ALC_FIXUP_ACT_PROBE); + snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); return 0; -- cgit v1.2.3-18-g5258 From d3f02d60eecfc43088a3cb95d35e0cf75b4b8266 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 10:12:22 +0100 Subject: ALSA: hda/realtek - Read the cached pinctl value in fixups ... instead of reading the value from the codec at each time. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 183b951fa63..7a4b7837630 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1737,8 +1737,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec, unsigned int val = snd_hda_codec_get_pincfg(codec, nids[i]); if (get_defcfg_device(val) != AC_JACK_HP_OUT) continue; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, nids[i]); val |= AC_PINCTL_VREF_80; snd_hda_set_pin_ctl(codec, nids[i], val); spec->gen.keep_vref_in_automute = 1; @@ -1758,8 +1757,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec, return; for (i = 0; i < ARRAY_SIZE(nids); i++) { unsigned int val; - val = snd_hda_codec_read(codec, nids[i], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, nids[i]); val |= AC_PINCTL_VREF_50; snd_hda_set_pin_ctl(codec, nids[i], val); } @@ -3137,8 +3135,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec, if (action != HDA_FIXUP_ACT_INIT) return; - val = snd_hda_codec_read(codec, 0x0f, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + val = snd_hda_codec_get_pin_target(codec, 0x0f); if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN))) val |= AC_PINCTL_IN_EN; val |= AC_PINCTL_VREF_50; -- cgit v1.2.3-18-g5258 From fd1082159d1445b0306a4696a2aade251ce2fcb2 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 10:18:14 +0100 Subject: ALSA: hda - Add a new fixup type to override pinctl values Add a new fixup type, HDA_FIXUP_PINCTLS, for overriding the pinctl values of the given pins. It takes the same array of struct pintbl like HDA_FIXUP_PINS, but each entry contains the pinctl value instead of the pin default config value. This patch also replaces the corresponding codes in patch_realtek.c. Without this change, the direct call of verbs may be overridden again by the later call of pinctl restoration by the driver. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 7a4b7837630..c8fcfa83077 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1794,9 +1794,9 @@ static const struct hda_fixup alc882_fixups[] = { } }, [ALC882_FIXUP_PB_M5210] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, {} } }, @@ -2158,9 +2158,9 @@ static const struct hda_fixup alc262_fixups[] = { } }, [ALC262_FIXUP_LENOVO_3000] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - { 0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF50 }, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + { 0x19, PIN_VREF50 }, {} }, .chained = true, @@ -2715,9 +2715,9 @@ enum { static const struct hda_fixup alc269_fixups[] = { [ALC269_FIXUP_SONY_VAIO] = { - .type = HDA_FIXUP_VERBS, - .v.verbs = (const struct hda_verb[]) { - {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREFGRD}, + .type = HDA_FIXUP_PINCTLS, + .v.pins = (const struct hda_pintbl[]) { + {0x19, PIN_VREFGRD}, {} } }, -- cgit v1.2.3-18-g5258 From 39aedee7a1cc3c72d68674ff6ff142299fa0897b Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:10:40 +0100 Subject: ALSA: hda/realtek - Add a fixup for FSC S7020 laptop Try to recover from the regression: set the HP amp for the speaker and add the hp jack mode enum as default. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c8fcfa83077..42fc05c3fdf 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1436,6 +1436,7 @@ enum { ALC260_FIXUP_REPLACER, ALC260_FIXUP_HP_B1900, ALC260_FIXUP_KN1, + ALC260_FIXUP_FSC_S7020, }; static void alc260_gpio1_automute(struct hda_codec *codec) @@ -1493,6 +1494,17 @@ static void alc260_fixup_kn1(struct hda_codec *codec, } } +static void alc260_fixup_fsc_s7020(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->gen.add_out_jack_modes = 1; + else if (action == HDA_FIXUP_ACT_PROBE) + snd_hda_set_pin_ctl_cache(codec, 0x10, PIN_HP); +} + static const struct hda_fixup alc260_fixups[] = { [ALC260_FIXUP_HP_DC5750] = { .type = HDA_FIXUP_PINS, @@ -1548,6 +1560,10 @@ static const struct hda_fixup alc260_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc260_fixup_kn1, }, + [ALC260_FIXUP_FSC_S7020] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc260_fixup_fsc_s7020, + }, }; static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -1556,6 +1572,7 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x008f, "Acer", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x103c, 0x280a, "HP dc5750", ALC260_FIXUP_HP_DC5750), SND_PCI_QUIRK(0x103c, 0x30ba, "HP Presario B1900", ALC260_FIXUP_HP_B1900), + SND_PCI_QUIRK(0x10cf, 0x1326, "FSC LifeBook S7020", ALC260_FIXUP_FSC_S7020), SND_PCI_QUIRK(0x1509, 0x4540, "Favorit 100XS", ALC260_FIXUP_GPIO1), SND_PCI_QUIRK(0x152d, 0x0729, "Quanta KN1", ALC260_FIXUP_KN1), SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_FIXUP_REPLACER), -- cgit v1.2.3-18-g5258 From 9bb1f06fe0844b742ab97326b34229bc8290c9c6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:14:29 +0100 Subject: ALSA: hda/realtek - Fix the timing for some fixups Some fixups such as setting the flags influencing on the parser behavior should be applied before actually parsing the tree. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 42fc05c3fdf..eb889d2e94e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2666,7 +2666,7 @@ static void alc269_fixup_mic1_mute(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -2684,7 +2684,7 @@ static void alc269_fixup_mic2_mute(struct hda_codec *codec, const struct hda_fixup *fix, int action) { struct alc_spec *spec = codec->spec; - if (action == HDA_FIXUP_ACT_PROBE) { + if (action == HDA_FIXUP_ACT_PRE_PROBE) { spec->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; spec->gen.vmaster_mute_enum = 1; } @@ -3373,7 +3373,7 @@ static int alc662_parse_auto_config(struct hda_codec *codec) static void alc272_fixup_mario(struct hda_codec *codec, const struct hda_fixup *fix, int action) { - if (action != HDA_FIXUP_ACT_PROBE) + if (action != HDA_FIXUP_ACT_PRE_PROBE) return; if (snd_hda_override_amp_caps(codec, 0x2, HDA_OUTPUT, (0x3b << AC_AMPCAP_OFFSET_SHIFT) | -- cgit v1.2.3-18-g5258 From 08fb0d0ee1b9c7aef79f54a9ae24470621eb6447 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 10 Jan 2013 17:33:58 +0100 Subject: ALSA: hda/realtek - Generic mute LED implementation for HP laptops As David Henningsson recently suggested, some HP laptops use an unused mic pin for controlling a mute LED, and this information is provided via DMI string "HP_Mute_LED_X_Y" string. This patch adds the generic support for such cases, as we've already done in patch_sigmatel.c. This is applied generically to all devices with ID 0x103c. But as we don't know whether the device 103c:1586 really contains HP_Mute_LED_X_Y DMI string, still keep the static setup for this device using the mic2 pin 0x19. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 76 +++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 28 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index eb889d2e94e..fab31d29fa2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -82,6 +83,10 @@ struct alc_spec { unsigned int inv_dmic_muted:1; /* R-ch of inv d-mic is muted? */ hda_nid_t inv_dmic_pin; + /* mute LED for HP laptops, see alc269_fixup_mic_mute_hook() */ + int mute_led_polarity; + hda_nid_t mute_led_nid; + /* hooks */ void (*init_hook)(struct hda_codec *codec); #ifdef CONFIG_PM @@ -2653,39 +2658,54 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec, spec->gen.automute_hook = alc269_quanta_automute; } -/* update mute-LED according to the speaker mute state via mic1 VREF pin */ -static void alc269_fixup_mic1_mute_hook(void *private_data, int enabled) +/* update mute-LED according to the speaker mute state via mic VREF pin */ +static void alc269_fixup_mic_mute_hook(void *private_data, int enabled) { struct hda_codec *codec = private_data; - unsigned int pinval = AC_PINCTL_IN_EN + (enabled ? - AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); - snd_hda_set_pin_ctl_cache(codec, 0x18, pinval); + struct alc_spec *spec = codec->spec; + unsigned int pinval; + + if (spec->mute_led_polarity) + enabled = !enabled; + pinval = AC_PINCTL_IN_EN | + (enabled ? AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_80); + if (spec->mute_led_nid) + snd_hda_set_pin_ctl_cache(codec, spec->mute_led_nid, pinval); } -static void alc269_fixup_mic1_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led(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->gen.vmaster_mute.hook = alc269_fixup_mic1_mute_hook; + const struct dmi_device *dev = NULL; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + int pol, pin; + if (sscanf(dev->name, "HP_Mute_LED_%d_%x", &pol, &pin) != 2) + continue; + if (pin < 0x0a || pin >= 0x10) + break; + spec->mute_led_polarity = pol; + spec->mute_led_nid = pin - 0x0a + 0x18; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; + snd_printd("Detected mute LED for %x:%d\n", spec->mute_led_nid, + spec->mute_led_polarity); + break; } } -/* update mute-LED according to the speaker mute state via mic2 VREF pin */ -static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled) -{ - struct hda_codec *codec = private_data; - unsigned int pinval = enabled ? 0x20 : 0x24; - snd_hda_set_pin_ctl_cache(codec, 0x19, pinval); -} - -static void alc269_fixup_mic2_mute(struct hda_codec *codec, - const struct hda_fixup *fix, int action) +static void alc269_fixup_hp_mute_led_mic2(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->gen.vmaster_mute.hook = alc269_fixup_mic2_mute_hook; + spec->mute_led_polarity = 0; + spec->mute_led_nid = 0x19; + spec->gen.vmaster_mute.hook = alc269_fixup_mic_mute_hook; spec->gen.vmaster_mute_enum = 1; } } @@ -2721,8 +2741,8 @@ enum { ALC269_FIXUP_DMIC, ALC269VB_FIXUP_AMIC, ALC269VB_FIXUP_DMIC, - ALC269_FIXUP_MIC1_MUTE_LED, - ALC269_FIXUP_MIC2_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED, + ALC269_FIXUP_HP_MUTE_LED_MIC2, ALC269_FIXUP_INV_DMIC, ALC269_FIXUP_LENOVO_DOCK, ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, @@ -2850,13 +2870,13 @@ static const struct hda_fixup alc269_fixups[] = { { } }, }, - [ALC269_FIXUP_MIC1_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic1_mute, + .v.func = alc269_fixup_hp_mute_led, }, - [ALC269_FIXUP_MIC2_MUTE_LED] = { + [ALC269_FIXUP_HP_MUTE_LED_MIC2] = { .type = HDA_FIXUP_FUNC, - .v.func = alc269_fixup_mic2_mute, + .v.func = alc269_fixup_hp_mute_led_mic2, }, [ALC269_FIXUP_INV_DMIC] = { .type = HDA_FIXUP_FUNC, @@ -2897,8 +2917,8 @@ static const struct hda_fixup alc269_fixups[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), - SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_MIC2_MUTE_LED), - SND_PCI_QUIRK(0x103c, 0x1972, "HP Pavilion 17", ALC269_FIXUP_MIC1_MUTE_LED), + SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), + SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), -- cgit v1.2.3-18-g5258 From ea46c3c87c35b90139b4dca43917d0f605d568ed Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 15 Jan 2013 18:45:53 +0100 Subject: ALSA: hda - Add prefer_hp_amp flag to hda_gen_spec Add a new flag to indicate whether HP amp is turned on as default for speaker or line-outs, and enable this for ALC260 codec, as many machines with this codec require the HP amp even for speakers. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index fab31d29fa2..71a8894438a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1506,8 +1506,6 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec, if (action == HDA_FIXUP_ACT_PRE_PROBE) spec->gen.add_out_jack_modes = 1; - else if (action == HDA_FIXUP_ACT_PROBE) - snd_hda_set_pin_ctl_cache(codec, 0x10, PIN_HP); } static const struct hda_fixup alc260_fixups[] = { @@ -1597,6 +1595,11 @@ static int patch_alc260(struct hda_codec *codec) return err; spec = codec->spec; + /* as quite a few machines require HP amp for speaker outputs, + * it's easier to enable it unconditionally; even if it's unneeded, + * it's almost harmless. + */ + spec->gen.prefer_hp_amp = 1; snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -- cgit v1.2.3-18-g5258 From a90229e0517938a5c5d9d682708b6ee7cd0e19b9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 14:10:00 +0100 Subject: ALSA: hda - Consolidate cap_sync_hook and capture_switch_hook Two hooks in hda_gen_spec, cap_sync_hook and capture_switch_hook, play very similar roles. The only differences are that the former is called more often (e.g. at init or switching capsrc) while the latter can take an on/off argument. As a more generic implementation, consolidate these two hooks, and pass snd_ctl_elem_value pointer as the second argument. If the secondary argument is non-NULL, it can take the on/off value, so the caller handles it like the former capture_switch_hook. If it's NULL, it's called in the init or capsrc switch case. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 71a8894438a..70b0e323f5a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -670,7 +670,8 @@ static void alc_inv_dmic_sync(struct hda_codec *codec, bool force) } } -static void alc_inv_dmic_hook(struct hda_codec *codec) +static void alc_inv_dmic_hook(struct hda_codec *codec, + struct snd_ctl_elem_value *ucontrol) { alc_inv_dmic_sync(codec, false); } -- cgit v1.2.3-18-g5258 From 7513e6dae58cf6583ff38bfe684bd5b3afcc564e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 18 Jan 2013 15:41:34 +0100 Subject: ALSA: hda - Fix speaker pin of FSC Lifebook S7110 laptop Some BIOS version of FSC Lifebook S7110 laptop seems to give a wrong default pin config for NID 0x15, which confuses the parser. Give a fixup to correct the value. Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 70b0e323f5a..79ff34df887 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -2151,6 +2151,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec) */ enum { ALC262_FIXUP_FSC_H270, + ALC262_FIXUP_FSC_S7110, ALC262_FIXUP_HP_Z200, ALC262_FIXUP_TYAN, ALC262_FIXUP_LENOVO_3000, @@ -2169,6 +2170,15 @@ static const struct hda_fixup alc262_fixups[] = { { } } }, + [ALC262_FIXUP_FSC_S7110] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x90170110 }, /* speaker */ + { } + }, + .chained = true, + .chain_id = ALC262_FIXUP_BENQ, + }, [ALC262_FIXUP_HP_Z200] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -2216,7 +2226,7 @@ static const struct hda_fixup alc262_fixups[] = { static const struct snd_pci_quirk alc262_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x170b, "HP Z200", ALC262_FIXUP_HP_Z200), - SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu", ALC262_FIXUP_BENQ), + SND_PCI_QUIRK(0x10cf, 0x1397, "Fujitsu Lifebook S7110", ALC262_FIXUP_FSC_S7110), SND_PCI_QUIRK(0x10cf, 0x142d, "Fujitsu Lifebook E8410", ALC262_FIXUP_BENQ), SND_PCI_QUIRK(0x10f1, 0x2915, "Tyan Thunder n6650W", ALC262_FIXUP_TYAN), SND_PCI_QUIRK(0x1734, 0x1147, "FSC Celsius H270", ALC262_FIXUP_FSC_H270), -- cgit v1.2.3-18-g5258 From 6efcc52653676fde888bf7837d01468d4f846465 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 21 Jan 2013 16:10:56 +0100 Subject: ALSA: hda - Remove superfluous header inclusions Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 79ff34df887..6eb9551e972 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include "hda_codec.h" -- cgit v1.2.3-18-g5258 From dc870f38e9faf7dd89355aae2252126688a1a372 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Jan 2013 15:24:30 +0100 Subject: ALSA: hda - Combine snd_hda_codec_flush_*_cache() to a single function Since both snd_hda_codec_flush_amp_cache() and snd_hda_codec_flush_cmd_cache() are called usually at the same time, we can simply combine them to a single function, snd_hda_codec_flush_cache(). Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound/pci/hda/patch_realtek.c') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 6eb9551e972..feb8cef341e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -621,7 +621,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_flush_amp_cache(codec); + snd_hda_codec_flush_cache(codec); /* we care only right channel */ val = snd_hda_codec_amp_read(codec, nid, 1, dir, 0); -- cgit v1.2.3-18-g5258