diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-07-29 08:32:56 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-07-29 08:32:56 -0400 |
commit | 3d21d3f7e7032619f5c5b47d3ee23bbe45de5993 (patch) | |
tree | 8cfb58a9c9d11c3f2aa17a2884bf8ea1bdc8afb3 /sound/pci/hda/patch_sigmatel.c | |
parent | 62558ce15759ee93223132258588320967e1e521 (diff) |
ALSA: hda - Support auto-mic switching with IDT/STAC codec
Support the automatic mic-switching with some devices with IDT/STAC
codecs. The condition is that the device has only two inputs, one
for an external mic and one for an internal mic.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_sigmatel.c')
-rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 170 |
1 files changed, 154 insertions, 16 deletions
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 307e86ceede1..f39dc98519a1 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c | |||
@@ -40,6 +40,7 @@ enum { | |||
40 | STAC_INSERT_EVENT, | 40 | STAC_INSERT_EVENT, |
41 | STAC_PWR_EVENT, | 41 | STAC_PWR_EVENT, |
42 | STAC_HP_EVENT, | 42 | STAC_HP_EVENT, |
43 | STAC_MIC_EVENT, | ||
43 | }; | 44 | }; |
44 | 45 | ||
45 | enum { | 46 | enum { |
@@ -176,6 +177,12 @@ struct sigmatel_jack { | |||
176 | struct snd_jack *jack; | 177 | struct snd_jack *jack; |
177 | }; | 178 | }; |
178 | 179 | ||
180 | struct sigmatel_mic_route { | ||
181 | hda_nid_t pin; | ||
182 | unsigned char mux_idx; | ||
183 | unsigned char dmux_idx; | ||
184 | }; | ||
185 | |||
179 | struct sigmatel_spec { | 186 | struct sigmatel_spec { |
180 | struct snd_kcontrol_new *mixers[4]; | 187 | struct snd_kcontrol_new *mixers[4]; |
181 | unsigned int num_mixers; | 188 | unsigned int num_mixers; |
@@ -187,6 +194,7 @@ struct sigmatel_spec { | |||
187 | unsigned int hp_detect: 1; | 194 | unsigned int hp_detect: 1; |
188 | unsigned int spdif_mute: 1; | 195 | unsigned int spdif_mute: 1; |
189 | unsigned int check_volume_offset:1; | 196 | unsigned int check_volume_offset:1; |
197 | unsigned int auto_mic:1; | ||
190 | 198 | ||
191 | /* gpio lines */ | 199 | /* gpio lines */ |
192 | unsigned int eapd_mask; | 200 | unsigned int eapd_mask; |
@@ -243,6 +251,9 @@ struct sigmatel_spec { | |||
243 | unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ | 251 | unsigned long *capsws; /* amp-mute attr: HDA_COMPOSE_AMP_VAL() */ |
244 | unsigned int num_caps; /* number of capture volume/switch elements */ | 252 | unsigned int num_caps; /* number of capture volume/switch elements */ |
245 | 253 | ||
254 | struct sigmatel_mic_route ext_mic; | ||
255 | struct sigmatel_mic_route int_mic; | ||
256 | |||
246 | const char **spdif_labels; | 257 | const char **spdif_labels; |
247 | 258 | ||
248 | hda_nid_t dig_in_nid; | 259 | hda_nid_t dig_in_nid; |
@@ -1304,7 +1315,7 @@ static int stac92xx_build_controls(struct hda_codec *codec) | |||
1304 | if (err < 0) | 1315 | if (err < 0) |
1305 | return err; | 1316 | return err; |
1306 | } | 1317 | } |
1307 | if (spec->num_dmuxes > 0) { | 1318 | if (!spec->auto_mic && spec->num_dmuxes > 0) { |
1308 | stac_dmux_mixer.count = spec->num_dmuxes; | 1319 | stac_dmux_mixer.count = spec->num_dmuxes; |
1309 | err = snd_hda_ctl_add(codec, | 1320 | err = snd_hda_ctl_add(codec, |
1310 | snd_ctl_new1(&stac_dmux_mixer, codec)); | 1321 | snd_ctl_new1(&stac_dmux_mixer, codec)); |
@@ -2950,6 +2961,8 @@ static int stac92xx_add_input_source(struct sigmatel_spec *spec) | |||
2950 | struct snd_kcontrol_new *knew; | 2961 | struct snd_kcontrol_new *knew; |
2951 | struct hda_input_mux *imux = &spec->private_imux; | 2962 | struct hda_input_mux *imux = &spec->private_imux; |
2952 | 2963 | ||
2964 | if (spec->auto_mic) | ||
2965 | return 0; /* no need for input source */ | ||
2953 | if (!spec->num_adcs || imux->num_items <= 1) | 2966 | if (!spec->num_adcs || imux->num_items <= 1) |
2954 | return 0; /* no need for input source control */ | 2967 | return 0; /* no need for input source control */ |
2955 | knew = stac_control_new(spec, &stac_input_src_temp, | 2968 | knew = stac_control_new(spec, &stac_input_src_temp, |
@@ -3557,14 +3570,26 @@ static const char *stac92xx_dmic_labels[5] = { | |||
3557 | "Digital Mic 3", "Digital Mic 4" | 3570 | "Digital Mic 3", "Digital Mic 4" |
3558 | }; | 3571 | }; |
3559 | 3572 | ||
3573 | static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, | ||
3574 | hda_nid_t nid) | ||
3575 | { | ||
3576 | hda_nid_t conn[HDA_MAX_NUM_INPUTS]; | ||
3577 | int i, nums; | ||
3578 | |||
3579 | nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); | ||
3580 | for (i = 0; i < nums; i++) | ||
3581 | if (conn[i] == nid) | ||
3582 | return i; | ||
3583 | return -1; | ||
3584 | } | ||
3585 | |||
3560 | /* create playback/capture controls for input pins on dmic capable codecs */ | 3586 | /* create playback/capture controls for input pins on dmic capable codecs */ |
3561 | static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, | 3587 | static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, |
3562 | const struct auto_pin_cfg *cfg) | 3588 | const struct auto_pin_cfg *cfg) |
3563 | { | 3589 | { |
3564 | struct sigmatel_spec *spec = codec->spec; | 3590 | struct sigmatel_spec *spec = codec->spec; |
3565 | struct hda_input_mux *dimux = &spec->private_dimux; | 3591 | struct hda_input_mux *dimux = &spec->private_dimux; |
3566 | hda_nid_t con_lst[HDA_MAX_NUM_INPUTS]; | 3592 | int err, i; |
3567 | int err, i, j; | ||
3568 | char name[32]; | 3593 | char name[32]; |
3569 | 3594 | ||
3570 | dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; | 3595 | dimux->items[dimux->num_items].label = stac92xx_dmic_labels[0]; |
@@ -3574,7 +3599,6 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, | |||
3574 | for (i = 0; i < spec->num_dmics; i++) { | 3599 | for (i = 0; i < spec->num_dmics; i++) { |
3575 | hda_nid_t nid; | 3600 | hda_nid_t nid; |
3576 | int index; | 3601 | int index; |
3577 | int num_cons; | ||
3578 | unsigned int wcaps; | 3602 | unsigned int wcaps; |
3579 | unsigned int def_conf; | 3603 | unsigned int def_conf; |
3580 | 3604 | ||
@@ -3583,17 +3607,10 @@ static int stac92xx_auto_create_dmic_input_ctls(struct hda_codec *codec, | |||
3583 | continue; | 3607 | continue; |
3584 | 3608 | ||
3585 | nid = spec->dmic_nids[i]; | 3609 | nid = spec->dmic_nids[i]; |
3586 | num_cons = snd_hda_get_connections(codec, | 3610 | index = get_connection_index(codec, spec->dmux_nids[0], nid); |
3587 | spec->dmux_nids[0], | 3611 | if (index < 0) |
3588 | con_lst, | 3612 | continue; |
3589 | HDA_MAX_NUM_INPUTS); | 3613 | |
3590 | for (j = 0; j < num_cons; j++) | ||
3591 | if (con_lst[j] == nid) { | ||
3592 | index = j; | ||
3593 | goto found; | ||
3594 | } | ||
3595 | continue; | ||
3596 | found: | ||
3597 | wcaps = get_wcaps(codec, nid) & | 3614 | wcaps = get_wcaps(codec, nid) & |
3598 | (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); | 3615 | (AC_WCAP_OUT_AMP | AC_WCAP_IN_AMP); |
3599 | 3616 | ||
@@ -3620,6 +3637,88 @@ found: | |||
3620 | return 0; | 3637 | return 0; |
3621 | } | 3638 | } |
3622 | 3639 | ||
3640 | static int check_mic_pin(struct hda_codec *codec, hda_nid_t nid, | ||
3641 | hda_nid_t *fixed, hda_nid_t *ext) | ||
3642 | { | ||
3643 | unsigned int cfg; | ||
3644 | |||
3645 | if (!nid) | ||
3646 | return 0; | ||
3647 | cfg = snd_hda_codec_get_pincfg(codec, nid); | ||
3648 | switch (get_defcfg_connect(cfg)) { | ||
3649 | case AC_JACK_PORT_FIXED: | ||
3650 | if (*fixed) | ||
3651 | return 1; /* already occupied */ | ||
3652 | *fixed = nid; | ||
3653 | break; | ||
3654 | case AC_JACK_PORT_COMPLEX: | ||
3655 | if (*ext) | ||
3656 | return 1; /* already occupied */ | ||
3657 | *ext = nid; | ||
3658 | break; | ||
3659 | } | ||
3660 | return 0; | ||
3661 | } | ||
3662 | |||
3663 | static int set_mic_route(struct hda_codec *codec, | ||
3664 | struct sigmatel_mic_route *mic, | ||
3665 | hda_nid_t pin) | ||
3666 | { | ||
3667 | struct sigmatel_spec *spec = codec->spec; | ||
3668 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3669 | int i; | ||
3670 | |||
3671 | mic->pin = pin; | ||
3672 | for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) | ||
3673 | if (pin == cfg->input_pins[i]) | ||
3674 | break; | ||
3675 | if (i <= AUTO_PIN_FRONT_MIC) { | ||
3676 | /* analog pin */ | ||
3677 | mic->dmux_idx = 0; | ||
3678 | i = get_connection_index(codec, spec->mux_nids[0], pin); | ||
3679 | if (i < 0) | ||
3680 | return -1; | ||
3681 | mic->mux_idx = i; | ||
3682 | } else { | ||
3683 | /* digital pin */ | ||
3684 | mic->mux_idx = 0; | ||
3685 | i = get_connection_index(codec, spec->dmux_nids[0], pin); | ||
3686 | if (i < 0) | ||
3687 | return -1; | ||
3688 | mic->dmux_idx = i; | ||
3689 | } | ||
3690 | return 0; | ||
3691 | } | ||
3692 | |||
3693 | /* return non-zero if the device is for automatic mic switch */ | ||
3694 | static int stac_check_auto_mic(struct hda_codec *codec) | ||
3695 | { | ||
3696 | struct sigmatel_spec *spec = codec->spec; | ||
3697 | struct auto_pin_cfg *cfg = &spec->autocfg; | ||
3698 | hda_nid_t fixed, ext; | ||
3699 | int i; | ||
3700 | |||
3701 | for (i = AUTO_PIN_LINE; i < AUTO_PIN_LAST; i++) { | ||
3702 | if (cfg->input_pins[i]) | ||
3703 | return 0; /* must be exclusively mics */ | ||
3704 | } | ||
3705 | fixed = ext = 0; | ||
3706 | for (i = AUTO_PIN_MIC; i <= AUTO_PIN_FRONT_MIC; i++) | ||
3707 | if (check_mic_pin(codec, cfg->input_pins[i], &fixed, &ext)) | ||
3708 | return 0; | ||
3709 | for (i = 0; i < spec->num_dmics; i++) | ||
3710 | if (check_mic_pin(codec, spec->dmic_nids[i], &fixed, &ext)) | ||
3711 | return 0; | ||
3712 | if (!fixed || !ext) | ||
3713 | return 0; | ||
3714 | if (!(get_wcaps(codec, ext) & AC_WCAP_UNSOL_CAP)) | ||
3715 | return 0; /* no unsol support */ | ||
3716 | if (set_mic_route(codec, &spec->ext_mic, ext) || | ||
3717 | set_mic_route(codec, &spec->int_mic, fixed)) | ||
3718 | return 0; /* something is wrong */ | ||
3719 | return 1; | ||
3720 | } | ||
3721 | |||
3623 | /* create playback/capture controls for input pins */ | 3722 | /* create playback/capture controls for input pins */ |
3624 | static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) | 3723 | static int stac92xx_auto_create_analog_input_ctls(struct hda_codec *codec, const struct auto_pin_cfg *cfg) |
3625 | { | 3724 | { |
@@ -3837,6 +3936,14 @@ static int stac92xx_parse_auto_config(struct hda_codec *codec, hda_nid_t dig_out | |||
3837 | spec->autocfg.line_outs = 0; | 3936 | spec->autocfg.line_outs = 0; |
3838 | } | 3937 | } |
3839 | 3938 | ||
3939 | if (stac_check_auto_mic(codec)) { | ||
3940 | spec->auto_mic = 1; | ||
3941 | /* only one capture for auto-mic */ | ||
3942 | spec->num_adcs = 1; | ||
3943 | spec->num_caps = 1; | ||
3944 | spec->num_muxes = 1; | ||
3945 | } | ||
3946 | |||
3840 | for (i = 0; i < spec->num_caps; i++) { | 3947 | for (i = 0; i < spec->num_caps; i++) { |
3841 | err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], | 3948 | err = stac92xx_add_capvol_ctls(codec, spec->capvols[i], |
3842 | spec->capsws[i], i); | 3949 | spec->capsws[i], i); |
@@ -4264,6 +4371,10 @@ static int stac92xx_init(struct hda_codec *codec) | |||
4264 | for (i = 0; i < cfg->hp_outs; i++) | 4371 | for (i = 0; i < cfg->hp_outs; i++) |
4265 | stac_toggle_power_map(codec, cfg->hp_pins[i], 1); | 4372 | stac_toggle_power_map(codec, cfg->hp_pins[i], 1); |
4266 | } | 4373 | } |
4374 | if (spec->auto_mic) { | ||
4375 | if (enable_pin_detect(codec, spec->ext_mic.pin, STAC_MIC_EVENT)) | ||
4376 | stac_issue_unsol_event(codec, spec->ext_mic.pin); | ||
4377 | } | ||
4267 | for (i = 0; i < AUTO_PIN_LAST; i++) { | 4378 | for (i = 0; i < AUTO_PIN_LAST; i++) { |
4268 | hda_nid_t nid = cfg->input_pins[i]; | 4379 | hda_nid_t nid = cfg->input_pins[i]; |
4269 | if (nid) { | 4380 | if (nid) { |
@@ -4601,6 +4712,25 @@ static void stac92xx_report_jack(struct hda_codec *codec, hda_nid_t nid) | |||
4601 | } | 4712 | } |
4602 | } | 4713 | } |
4603 | 4714 | ||
4715 | static void stac92xx_mic_detect(struct hda_codec *codec) | ||
4716 | { | ||
4717 | struct sigmatel_spec *spec = codec->spec; | ||
4718 | struct sigmatel_mic_route *mic; | ||
4719 | |||
4720 | if (get_pin_presence(codec, spec->ext_mic.pin)) | ||
4721 | mic = &spec->ext_mic; | ||
4722 | else | ||
4723 | mic = &spec->int_mic; | ||
4724 | if (mic->dmux_idx) | ||
4725 | snd_hda_codec_write_cache(codec, spec->dmux_nids[0], 0, | ||
4726 | AC_VERB_SET_CONNECT_SEL, | ||
4727 | mic->dmux_idx); | ||
4728 | else | ||
4729 | snd_hda_codec_write_cache(codec, spec->mux_nids[0], 0, | ||
4730 | AC_VERB_SET_CONNECT_SEL, | ||
4731 | mic->mux_idx); | ||
4732 | } | ||
4733 | |||
4604 | static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) | 4734 | static void stac_issue_unsol_event(struct hda_codec *codec, hda_nid_t nid) |
4605 | { | 4735 | { |
4606 | struct sigmatel_event *event = stac_get_event(codec, nid); | 4736 | struct sigmatel_event *event = stac_get_event(codec, nid); |
@@ -4623,7 +4753,15 @@ static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res) | |||
4623 | switch (event->type) { | 4753 | switch (event->type) { |
4624 | case STAC_HP_EVENT: | 4754 | case STAC_HP_EVENT: |
4625 | stac92xx_hp_detect(codec); | 4755 | stac92xx_hp_detect(codec); |
4626 | /* fallthru */ | 4756 | break; |
4757 | case STAC_MIC_EVENT: | ||
4758 | stac92xx_mic_detect(codec); | ||
4759 | break; | ||
4760 | } | ||
4761 | |||
4762 | switch (event->type) { | ||
4763 | case STAC_HP_EVENT: | ||
4764 | case STAC_MIC_EVENT: | ||
4627 | case STAC_INSERT_EVENT: | 4765 | case STAC_INSERT_EVENT: |
4628 | case STAC_PWR_EVENT: | 4766 | case STAC_PWR_EVENT: |
4629 | if (spec->num_pwrs > 0) | 4767 | if (spec->num_pwrs > 0) |