diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index a7592f5e97d4..ca1a87a4812c 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -333,6 +333,12 @@ struct alc_spec { hda_nid_t *capsrc_nids; hda_nid_t dig_in_nid; /* digital-in NID; optional */ + /* capture setup for dynamic dual-adc switch */ + unsigned int cur_adc_idx; + hda_nid_t cur_adc; + unsigned int cur_adc_stream_tag; + unsigned int cur_adc_format; + /* capture source */ unsigned int num_mux_defs; const struct hda_input_mux *input_mux; @@ -374,6 +380,7 @@ struct alc_spec { /* other flags */ unsigned int no_analog :1; /* digital I/O only */ + unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */ int init_amp; /* for virtual master */ @@ -1010,6 +1017,29 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, return -1; } +/* switch the current ADC according to the jack state */ +static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + unsigned int present; + hda_nid_t new_adc; + + present = snd_hda_jack_detect(codec, spec->ext_mic.pin); + if (present) + spec->cur_adc_idx = 1; + else + spec->cur_adc_idx = 0; + new_adc = spec->adc_nids[spec->cur_adc_idx]; + 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); + spec->cur_adc = new_adc; + snd_hda_codec_setup_stream(codec, new_adc, + spec->cur_adc_stream_tag, 0, + spec->cur_adc_format); + } +} + static void alc_mic_automute(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -1024,6 +1054,11 @@ static void alc_mic_automute(struct hda_codec *codec) if (snd_BUG_ON(!spec->adc_nids)) return; + if (spec->dual_adc_switch) { + alc_dual_mic_adc_auto_switch(codec); + return; + } + cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; present = snd_hda_jack_detect(codec, spec->ext_mic.pin); @@ -3614,6 +3649,41 @@ static int alc880_alt_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, return 0; } +/* analog capture with dynamic dual-adc changes */ +static int dualmic_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->cur_adc_idx]; + 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 dualmic_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 struct hda_pcm_stream dualmic_pcm_analog_capture = { + .substreams = 1, + .channels_min = 2, + .channels_max = 2, + .nid = 0, /* fill later */ + .ops = { + .prepare = dualmic_capture_pcm_prepare, + .cleanup = dualmic_capture_pcm_cleanup + }, +}; /* */ @@ -5052,6 +5122,33 @@ static void fixup_automic_adc(struct hda_codec *codec) spec->auto_mic = 0; /* disable auto-mic to be sure */ } +/* 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; + + for (i = 0; i < spec->num_adc_nids; i++) { + hda_nid_t cap = spec->capsrc_nids ? + spec->capsrc_nids[i] : spec->adc_nids[i]; + int idx; + + idx = get_connection_index(codec, cap, pin); + if (idx < 0) + continue; + /* select or unmute this route */ + 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 { + snd_hda_codec_write_cache(codec, cap, 0, + AC_VERB_SET_CONNECT_SEL, idx); + } + return i; /* return the found index */ + } + return -1; /* not found */ +} + /* choose the ADC/MUX containing the input pin and initialize the setup */ static void fixup_single_adc(struct hda_codec *codec) { @@ -5068,33 +5165,24 @@ static void fixup_single_adc(struct hda_codec *codec) } if (!pin) return; - - /* set the default connection to that pin */ - for (i = 0; i < spec->num_adc_nids; i++) { - hda_nid_t cap = spec->capsrc_nids ? - spec->capsrc_nids[i] : spec->adc_nids[i]; - int idx; - - idx = get_connection_index(codec, cap, pin); - if (idx < 0) - continue; + i = init_capsrc_for_pin(codec, pin); + if (i >= 0) { /* use only this ADC */ if (spec->capsrc_nids) spec->capsrc_nids += i; spec->adc_nids += i; spec->num_adc_nids = 1; - /* select or unmute this route */ - 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 { - snd_hda_codec_write_cache(codec, cap, 0, - AC_VERB_SET_CONNECT_SEL, idx); - } - return; } } +/* initialize dual adcs */ +static void fixup_dual_adc_switch(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + init_capsrc_for_pin(codec, spec->ext_mic.pin); + init_capsrc_for_pin(codec, spec->int_mic.pin); +} + static void set_capture_mixer(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; @@ -5108,7 +5196,10 @@ static void set_capture_mixer(struct hda_codec *codec) }; if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { int mux = 0; - if (spec->auto_mic) + int num_adcs = spec->num_adc_nids; + if (spec->dual_adc_switch) + fixup_dual_adc_switch(codec); + else if (spec->auto_mic) fixup_automic_adc(codec); else if (spec->input_mux) { if (spec->input_mux->num_items > 1) @@ -5116,7 +5207,9 @@ static void set_capture_mixer(struct hda_codec *codec) else if (spec->input_mux->num_items == 1) fixup_single_adc(codec); } - spec->cap_mixer = caps[mux][spec->num_adc_nids - 1]; + if (spec->dual_adc_switch) + num_adcs = 1; + spec->cap_mixer = caps[mux][num_adcs - 1]; } } @@ -14141,6 +14234,36 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid) } #endif /* CONFIG_SND_HDA_POWER_SAVE */ +static int alc275_setup_dual_adc(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic) + return 0; + if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) || + (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) { + if (spec->ext_mic.pin <= 0x12) { + spec->private_adc_nids[0] = 0x08; + spec->private_adc_nids[1] = 0x11; + spec->private_capsrc_nids[0] = 0x23; + spec->private_capsrc_nids[1] = 0x22; + } else { + spec->private_adc_nids[0] = 0x11; + spec->private_adc_nids[1] = 0x08; + spec->private_capsrc_nids[0] = 0x22; + spec->private_capsrc_nids[1] = 0x23; + } + spec->adc_nids = spec->private_adc_nids; + spec->capsrc_nids = spec->private_capsrc_nids; + spec->num_adc_nids = 2; + spec->dual_adc_switch = 1; + snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n", + spec->adc_nids[0], spec->adc_nids[1]); + return 1; + } + return 0; +} + /* * BIOS auto configuration */ @@ -14180,11 +14303,14 @@ static int alc269_parse_auto_config(struct hda_codec *codec) spec->num_mux_defs = 1; spec->input_mux = &spec->private_imux[0]; - fillup_priv_adc_nids(codec, alc269_adc_candidates, - sizeof(alc269_adc_candidates)); + + if (!alc275_setup_dual_adc(codec)) + fillup_priv_adc_nids(codec, alc269_adc_candidates, + sizeof(alc269_adc_candidates)); /* set default input source */ - snd_hda_codec_write_cache(codec, spec->capsrc_nids[0], + if (!spec->dual_adc_switch) + snd_hda_codec_write_cache(codec, spec->capsrc_nids[0], 0, AC_VERB_SET_CONNECT_SEL, spec->input_mux->items[0].index); @@ -14480,6 +14606,10 @@ static int patch_alc269(struct hda_codec *codec) */ spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; + } else if (spec->dual_adc_switch) { + spec->stream_analog_playback = &alc269_pcm_analog_playback; + /* switch ADC dynamically */ + spec->stream_analog_capture = &dualmic_pcm_analog_capture; } else { spec->stream_analog_playback = &alc269_pcm_analog_playback; spec->stream_analog_capture = &alc269_pcm_analog_capture;