diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 9218d3086f67..f210ca7575b2 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 */ + 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 */ + } - 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, + /* 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 */ + default: 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) - 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) - 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; + 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->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]); }