aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda/patch_realtek.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2010-07-13 16:49:01 -0400
committerTakashi Iwai <tiwai@suse.de>2010-07-13 16:49:01 -0400
commit840b64c08032a86ab39b85ddd342918da0d559c8 (patch)
tree11d7f647d5afb8a9246c500002b10838465b3389 /sound/pci/hda/patch_realtek.c
parent32e0191d7909022e5016beb75dda6710a28b3c61 (diff)
ALSA: hda - Add support of dual-ADCs for Realtek ALC275
Some VAIO models with ALC275 have dual ADCs for both internal and external mics, and the driver needs to switch one of them appropriately. This patch adds a basic support for this functionality, dynamic switching between two ADCs per jack plug state. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
-rw-r--r--sound/pci/hda/patch_realtek.c178
1 files changed, 154 insertions, 24 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index a7592f5e97d..ca1a87a4812 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -333,6 +333,12 @@ struct alc_spec {
333 hda_nid_t *capsrc_nids; 333 hda_nid_t *capsrc_nids;
334 hda_nid_t dig_in_nid; /* digital-in NID; optional */ 334 hda_nid_t dig_in_nid; /* digital-in NID; optional */
335 335
336 /* capture setup for dynamic dual-adc switch */
337 unsigned int cur_adc_idx;
338 hda_nid_t cur_adc;
339 unsigned int cur_adc_stream_tag;
340 unsigned int cur_adc_format;
341
336 /* capture source */ 342 /* capture source */
337 unsigned int num_mux_defs; 343 unsigned int num_mux_defs;
338 const struct hda_input_mux *input_mux; 344 const struct hda_input_mux *input_mux;
@@ -374,6 +380,7 @@ struct alc_spec {
374 380
375 /* other flags */ 381 /* other flags */
376 unsigned int no_analog :1; /* digital I/O only */ 382 unsigned int no_analog :1; /* digital I/O only */
383 unsigned int dual_adc_switch:1; /* switch ADCs (for ALC275) */
377 int init_amp; 384 int init_amp;
378 385
379 /* for virtual master */ 386 /* for virtual master */
@@ -1010,6 +1017,29 @@ static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
1010 return -1; 1017 return -1;
1011} 1018}
1012 1019
1020/* switch the current ADC according to the jack state */
1021static void alc_dual_mic_adc_auto_switch(struct hda_codec *codec)
1022{
1023 struct alc_spec *spec = codec->spec;
1024 unsigned int present;
1025 hda_nid_t new_adc;
1026
1027 present = snd_hda_jack_detect(codec, spec->ext_mic.pin);
1028 if (present)
1029 spec->cur_adc_idx = 1;
1030 else
1031 spec->cur_adc_idx = 0;
1032 new_adc = spec->adc_nids[spec->cur_adc_idx];
1033 if (spec->cur_adc && spec->cur_adc != new_adc) {
1034 /* stream is running, let's swap the current ADC */
1035 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1036 spec->cur_adc = new_adc;
1037 snd_hda_codec_setup_stream(codec, new_adc,
1038 spec->cur_adc_stream_tag, 0,
1039 spec->cur_adc_format);
1040 }
1041}
1042
1013static void alc_mic_automute(struct hda_codec *codec) 1043static void alc_mic_automute(struct hda_codec *codec)
1014{ 1044{
1015 struct alc_spec *spec = codec->spec; 1045 struct alc_spec *spec = codec->spec;
@@ -1024,6 +1054,11 @@ static void alc_mic_automute(struct hda_codec *codec)
1024 if (snd_BUG_ON(!spec->adc_nids)) 1054 if (snd_BUG_ON(!spec->adc_nids))
1025 return; 1055 return;
1026 1056
1057 if (spec->dual_adc_switch) {
1058 alc_dual_mic_adc_auto_switch(codec);
1059 return;
1060 }
1061
1027 cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0]; 1062 cap_nid = spec->capsrc_nids ? spec->capsrc_nids[0] : spec->adc_nids[0];
1028 1063
1029 present = snd_hda_jack_detect(codec, spec->ext_mic.pin); 1064 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,
3614 return 0; 3649 return 0;
3615} 3650}
3616 3651
3652/* analog capture with dynamic dual-adc changes */
3653static int dualmic_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
3654 struct hda_codec *codec,
3655 unsigned int stream_tag,
3656 unsigned int format,
3657 struct snd_pcm_substream *substream)
3658{
3659 struct alc_spec *spec = codec->spec;
3660 spec->cur_adc = spec->adc_nids[spec->cur_adc_idx];
3661 spec->cur_adc_stream_tag = stream_tag;
3662 spec->cur_adc_format = format;
3663 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
3664 return 0;
3665}
3666
3667static int dualmic_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
3668 struct hda_codec *codec,
3669 struct snd_pcm_substream *substream)
3670{
3671 struct alc_spec *spec = codec->spec;
3672 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
3673 spec->cur_adc = 0;
3674 return 0;
3675}
3676
3677static struct hda_pcm_stream dualmic_pcm_analog_capture = {
3678 .substreams = 1,
3679 .channels_min = 2,
3680 .channels_max = 2,
3681 .nid = 0, /* fill later */
3682 .ops = {
3683 .prepare = dualmic_capture_pcm_prepare,
3684 .cleanup = dualmic_capture_pcm_cleanup
3685 },
3686};
3617 3687
3618/* 3688/*
3619 */ 3689 */
@@ -5052,24 +5122,12 @@ static void fixup_automic_adc(struct hda_codec *codec)
5052 spec->auto_mic = 0; /* disable auto-mic to be sure */ 5122 spec->auto_mic = 0; /* disable auto-mic to be sure */
5053} 5123}
5054 5124
5055/* choose the ADC/MUX containing the input pin and initialize the setup */ 5125/* set the default connection to that pin */
5056static void fixup_single_adc(struct hda_codec *codec) 5126static int init_capsrc_for_pin(struct hda_codec *codec, hda_nid_t pin)
5057{ 5127{
5058 struct alc_spec *spec = codec->spec; 5128 struct alc_spec *spec = codec->spec;
5059 hda_nid_t pin = 0;
5060 int i; 5129 int i;
5061 5130
5062 /* search for the input pin; there must be only one */
5063 for (i = 0; i < AUTO_PIN_LAST; i++) {
5064 if (spec->autocfg.input_pins[i]) {
5065 pin = spec->autocfg.input_pins[i];
5066 break;
5067 }
5068 }
5069 if (!pin)
5070 return;
5071
5072 /* set the default connection to that pin */
5073 for (i = 0; i < spec->num_adc_nids; i++) { 5131 for (i = 0; i < spec->num_adc_nids; i++) {
5074 hda_nid_t cap = spec->capsrc_nids ? 5132 hda_nid_t cap = spec->capsrc_nids ?
5075 spec->capsrc_nids[i] : spec->adc_nids[i]; 5133 spec->capsrc_nids[i] : spec->adc_nids[i];
@@ -5078,11 +5136,6 @@ static void fixup_single_adc(struct hda_codec *codec)
5078 idx = get_connection_index(codec, cap, pin); 5136 idx = get_connection_index(codec, cap, pin);
5079 if (idx < 0) 5137 if (idx < 0)
5080 continue; 5138 continue;
5081 /* use only this ADC */
5082 if (spec->capsrc_nids)
5083 spec->capsrc_nids += i;
5084 spec->adc_nids += i;
5085 spec->num_adc_nids = 1;
5086 /* select or unmute this route */ 5139 /* select or unmute this route */
5087 if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { 5140 if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
5088 snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, 5141 snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
@@ -5091,10 +5144,45 @@ static void fixup_single_adc(struct hda_codec *codec)
5091 snd_hda_codec_write_cache(codec, cap, 0, 5144 snd_hda_codec_write_cache(codec, cap, 0,
5092 AC_VERB_SET_CONNECT_SEL, idx); 5145 AC_VERB_SET_CONNECT_SEL, idx);
5093 } 5146 }
5147 return i; /* return the found index */
5148 }
5149 return -1; /* not found */
5150}
5151
5152/* choose the ADC/MUX containing the input pin and initialize the setup */
5153static void fixup_single_adc(struct hda_codec *codec)
5154{
5155 struct alc_spec *spec = codec->spec;
5156 hda_nid_t pin = 0;
5157 int i;
5158
5159 /* search for the input pin; there must be only one */
5160 for (i = 0; i < AUTO_PIN_LAST; i++) {
5161 if (spec->autocfg.input_pins[i]) {
5162 pin = spec->autocfg.input_pins[i];
5163 break;
5164 }
5165 }
5166 if (!pin)
5094 return; 5167 return;
5168 i = init_capsrc_for_pin(codec, pin);
5169 if (i >= 0) {
5170 /* use only this ADC */
5171 if (spec->capsrc_nids)
5172 spec->capsrc_nids += i;
5173 spec->adc_nids += i;
5174 spec->num_adc_nids = 1;
5095 } 5175 }
5096} 5176}
5097 5177
5178/* initialize dual adcs */
5179static void fixup_dual_adc_switch(struct hda_codec *codec)
5180{
5181 struct alc_spec *spec = codec->spec;
5182 init_capsrc_for_pin(codec, spec->ext_mic.pin);
5183 init_capsrc_for_pin(codec, spec->int_mic.pin);
5184}
5185
5098static void set_capture_mixer(struct hda_codec *codec) 5186static void set_capture_mixer(struct hda_codec *codec)
5099{ 5187{
5100 struct alc_spec *spec = codec->spec; 5188 struct alc_spec *spec = codec->spec;
@@ -5108,7 +5196,10 @@ static void set_capture_mixer(struct hda_codec *codec)
5108 }; 5196 };
5109 if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) { 5197 if (spec->num_adc_nids > 0 && spec->num_adc_nids <= 3) {
5110 int mux = 0; 5198 int mux = 0;
5111 if (spec->auto_mic) 5199 int num_adcs = spec->num_adc_nids;
5200 if (spec->dual_adc_switch)
5201 fixup_dual_adc_switch(codec);
5202 else if (spec->auto_mic)
5112 fixup_automic_adc(codec); 5203 fixup_automic_adc(codec);
5113 else if (spec->input_mux) { 5204 else if (spec->input_mux) {
5114 if (spec->input_mux->num_items > 1) 5205 if (spec->input_mux->num_items > 1)
@@ -5116,7 +5207,9 @@ static void set_capture_mixer(struct hda_codec *codec)
5116 else if (spec->input_mux->num_items == 1) 5207 else if (spec->input_mux->num_items == 1)
5117 fixup_single_adc(codec); 5208 fixup_single_adc(codec);
5118 } 5209 }
5119 spec->cap_mixer = caps[mux][spec->num_adc_nids - 1]; 5210 if (spec->dual_adc_switch)
5211 num_adcs = 1;
5212 spec->cap_mixer = caps[mux][num_adcs - 1];
5120 } 5213 }
5121} 5214}
5122 5215
@@ -14141,6 +14234,36 @@ static int alc269_mic2_mute_check_ps(struct hda_codec *codec, hda_nid_t nid)
14141} 14234}
14142#endif /* CONFIG_SND_HDA_POWER_SAVE */ 14235#endif /* CONFIG_SND_HDA_POWER_SAVE */
14143 14236
14237static int alc275_setup_dual_adc(struct hda_codec *codec)
14238{
14239 struct alc_spec *spec = codec->spec;
14240
14241 if (codec->vendor_id != 0x10ec0275 || !spec->auto_mic)
14242 return 0;
14243 if ((spec->ext_mic.pin >= 0x18 && spec->int_mic.pin <= 0x13) ||
14244 (spec->ext_mic.pin <= 0x12 && spec->int_mic.pin >= 0x18)) {
14245 if (spec->ext_mic.pin <= 0x12) {
14246 spec->private_adc_nids[0] = 0x08;
14247 spec->private_adc_nids[1] = 0x11;
14248 spec->private_capsrc_nids[0] = 0x23;
14249 spec->private_capsrc_nids[1] = 0x22;
14250 } else {
14251 spec->private_adc_nids[0] = 0x11;
14252 spec->private_adc_nids[1] = 0x08;
14253 spec->private_capsrc_nids[0] = 0x22;
14254 spec->private_capsrc_nids[1] = 0x23;
14255 }
14256 spec->adc_nids = spec->private_adc_nids;
14257 spec->capsrc_nids = spec->private_capsrc_nids;
14258 spec->num_adc_nids = 2;
14259 spec->dual_adc_switch = 1;
14260 snd_printdd("realtek: enabling dual ADC switchg (%02x:%02x)\n",
14261 spec->adc_nids[0], spec->adc_nids[1]);
14262 return 1;
14263 }
14264 return 0;
14265}
14266
14144/* 14267/*
14145 * BIOS auto configuration 14268 * BIOS auto configuration
14146 */ 14269 */
@@ -14180,11 +14303,14 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
14180 14303
14181 spec->num_mux_defs = 1; 14304 spec->num_mux_defs = 1;
14182 spec->input_mux = &spec->private_imux[0]; 14305 spec->input_mux = &spec->private_imux[0];
14183 fillup_priv_adc_nids(codec, alc269_adc_candidates, 14306
14184 sizeof(alc269_adc_candidates)); 14307 if (!alc275_setup_dual_adc(codec))
14308 fillup_priv_adc_nids(codec, alc269_adc_candidates,
14309 sizeof(alc269_adc_candidates));
14185 14310
14186 /* set default input source */ 14311 /* set default input source */
14187 snd_hda_codec_write_cache(codec, spec->capsrc_nids[0], 14312 if (!spec->dual_adc_switch)
14313 snd_hda_codec_write_cache(codec, spec->capsrc_nids[0],
14188 0, AC_VERB_SET_CONNECT_SEL, 14314 0, AC_VERB_SET_CONNECT_SEL,
14189 spec->input_mux->items[0].index); 14315 spec->input_mux->items[0].index);
14190 14316
@@ -14480,6 +14606,10 @@ static int patch_alc269(struct hda_codec *codec)
14480 */ 14606 */
14481 spec->stream_analog_playback = &alc269_44k_pcm_analog_playback; 14607 spec->stream_analog_playback = &alc269_44k_pcm_analog_playback;
14482 spec->stream_analog_capture = &alc269_44k_pcm_analog_capture; 14608 spec->stream_analog_capture = &alc269_44k_pcm_analog_capture;
14609 } else if (spec->dual_adc_switch) {
14610 spec->stream_analog_playback = &alc269_pcm_analog_playback;
14611 /* switch ADC dynamically */
14612 spec->stream_analog_capture = &dualmic_pcm_analog_capture;
14483 } else { 14613 } else {
14484 spec->stream_analog_playback = &alc269_pcm_analog_playback; 14614 spec->stream_analog_playback = &alc269_pcm_analog_playback;
14485 spec->stream_analog_capture = &alc269_pcm_analog_capture; 14615 spec->stream_analog_capture = &alc269_pcm_analog_capture;