diff options
author | Takashi Iwai <tiwai@suse.de> | 2009-08-30 06:15:59 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-08-31 02:32:27 -0400 |
commit | 17bbaa6f600a36930879fd58e4e38a8c25bf9b60 (patch) | |
tree | 759d5cf06f7d6f26ae061037eb5354ce0478204e /sound/pci/hda/patch_analog.c | |
parent | be0ae923a40bbee8a0f02c1cd4b70656e729096f (diff) |
ALSA: hda - Add support of docking auto-mute/mic for AD1984A laptop model
Add the support of automatic mute and mic-switching of the docking
station HP and mic plugs for AD1984A laptop model for some HP machines.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_analog.c')
-rw-r--r-- | sound/pci/hda/patch_analog.c | 121 |
1 files changed, 81 insertions, 40 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index f22c3c08a9f0..570e072938ca 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c | |||
@@ -3703,19 +3703,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = { | |||
3703 | * Port F: Internal speakers | 3703 | * Port F: Internal speakers |
3704 | */ | 3704 | */ |
3705 | 3705 | ||
3706 | static struct hda_input_mux ad1884a_laptop_capture_source = { | 3706 | static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, |
3707 | .num_items = 4, | 3707 | struct snd_ctl_elem_value *ucontrol) |
3708 | .items = { | 3708 | { |
3709 | { "Mic", 0x0 }, /* port-B */ | 3709 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); |
3710 | { "Internal Mic", 0x1 }, /* port-C */ | 3710 | int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); |
3711 | { "Dock Mic", 0x4 }, /* port-E */ | 3711 | int mute = (!ucontrol->value.integer.value[0] && |
3712 | { "Mix", 0x3 }, | 3712 | !ucontrol->value.integer.value[1]); |
3713 | }, | 3713 | /* toggle GPIO1 according to the mute state */ |
3714 | }; | 3714 | snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, |
3715 | mute ? 0x02 : 0x0); | ||
3716 | return ret; | ||
3717 | } | ||
3715 | 3718 | ||
3716 | static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { | 3719 | static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { |
3717 | HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), | 3720 | HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), |
3718 | HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), | 3721 | { |
3722 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
3723 | .name = "Master Playback Switch", | ||
3724 | .info = snd_hda_mixer_amp_switch_info, | ||
3725 | .get = snd_hda_mixer_amp_switch_get, | ||
3726 | .put = ad1884a_mobile_master_sw_put, | ||
3727 | .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), | ||
3728 | }, | ||
3719 | HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), | 3729 | HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), |
3720 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), | 3730 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), |
3721 | HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), | 3731 | HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), |
@@ -3730,36 +3740,9 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { | |||
3730 | HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), | 3740 | HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), |
3731 | HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), | 3741 | HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), |
3732 | HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), | 3742 | HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), |
3733 | HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), | ||
3734 | HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), | ||
3735 | { | ||
3736 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
3737 | /* The multiple "Capture Source" controls confuse alsamixer | ||
3738 | * So call somewhat different.. | ||
3739 | */ | ||
3740 | /* .name = "Capture Source", */ | ||
3741 | .name = "Input Source", | ||
3742 | .count = 2, | ||
3743 | .info = ad198x_mux_enum_info, | ||
3744 | .get = ad198x_mux_enum_get, | ||
3745 | .put = ad198x_mux_enum_put, | ||
3746 | }, | ||
3747 | { } /* end */ | 3743 | { } /* end */ |
3748 | }; | 3744 | }; |
3749 | 3745 | ||
3750 | static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, | ||
3751 | struct snd_ctl_elem_value *ucontrol) | ||
3752 | { | ||
3753 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3754 | int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); | ||
3755 | int mute = (!ucontrol->value.integer.value[0] && | ||
3756 | !ucontrol->value.integer.value[1]); | ||
3757 | /* toggle GPIO1 according to the mute state */ | ||
3758 | snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, | ||
3759 | mute ? 0x02 : 0x0); | ||
3760 | return ret; | ||
3761 | } | ||
3762 | |||
3763 | static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { | 3746 | static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { |
3764 | HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), | 3747 | HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), |
3765 | /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ | 3748 | /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/ |
@@ -3829,6 +3812,63 @@ static int ad1884a_hp_init(struct hda_codec *codec) | |||
3829 | return 0; | 3812 | return 0; |
3830 | } | 3813 | } |
3831 | 3814 | ||
3815 | /* mute internal speaker if HP or docking HP is plugged */ | ||
3816 | static void ad1884a_laptop_automute(struct hda_codec *codec) | ||
3817 | { | ||
3818 | unsigned int present; | ||
3819 | |||
3820 | present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0); | ||
3821 | present &= AC_PINSENSE_PRESENCE; | ||
3822 | if (!present) { | ||
3823 | present = snd_hda_codec_read(codec, 0x12, 0, | ||
3824 | AC_VERB_GET_PIN_SENSE, 0); | ||
3825 | present &= AC_PINSENSE_PRESENCE; | ||
3826 | } | ||
3827 | snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, | ||
3828 | HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); | ||
3829 | snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, | ||
3830 | present ? 0x00 : 0x02); | ||
3831 | } | ||
3832 | |||
3833 | /* switch to external mic if plugged */ | ||
3834 | static void ad1884a_laptop_automic(struct hda_codec *codec) | ||
3835 | { | ||
3836 | unsigned int idx; | ||
3837 | |||
3838 | if (snd_hda_codec_read(codec, 0x14, 0, AC_VERB_GET_PIN_SENSE, 0) & | ||
3839 | AC_PINSENSE_PRESENCE) | ||
3840 | idx = 0; | ||
3841 | else if (snd_hda_codec_read(codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & | ||
3842 | AC_PINSENSE_PRESENCE) | ||
3843 | idx = 4; | ||
3844 | else | ||
3845 | idx = 1; | ||
3846 | snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); | ||
3847 | } | ||
3848 | |||
3849 | /* unsolicited event for HP jack sensing */ | ||
3850 | static void ad1884a_laptop_unsol_event(struct hda_codec *codec, | ||
3851 | unsigned int res) | ||
3852 | { | ||
3853 | switch (res >> 26) { | ||
3854 | case AD1884A_HP_EVENT: | ||
3855 | ad1884a_laptop_automute(codec); | ||
3856 | break; | ||
3857 | case AD1884A_MIC_EVENT: | ||
3858 | ad1884a_laptop_automic(codec); | ||
3859 | break; | ||
3860 | } | ||
3861 | } | ||
3862 | |||
3863 | /* initialize jack-sensing, too */ | ||
3864 | static int ad1884a_laptop_init(struct hda_codec *codec) | ||
3865 | { | ||
3866 | ad198x_init(codec); | ||
3867 | ad1884a_laptop_automute(codec); | ||
3868 | ad1884a_laptop_automic(codec); | ||
3869 | return 0; | ||
3870 | } | ||
3871 | |||
3832 | /* additional verbs for laptop model */ | 3872 | /* additional verbs for laptop model */ |
3833 | static struct hda_verb ad1884a_laptop_verbs[] = { | 3873 | static struct hda_verb ad1884a_laptop_verbs[] = { |
3834 | /* Port-A (HP) pin - always unmuted */ | 3874 | /* Port-A (HP) pin - always unmuted */ |
@@ -3849,7 +3889,9 @@ static struct hda_verb ad1884a_laptop_verbs[] = { | |||
3849 | {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | 3889 | {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, |
3850 | /* unsolicited event for pin-sense */ | 3890 | /* unsolicited event for pin-sense */ |
3851 | {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, | 3891 | {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, |
3892 | {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, | ||
3852 | {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, | 3893 | {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, |
3894 | {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, | ||
3853 | { } /* end */ | 3895 | { } /* end */ |
3854 | }; | 3896 | }; |
3855 | 3897 | ||
@@ -4059,9 +4101,8 @@ static int patch_ad1884a(struct hda_codec *codec) | |||
4059 | spec->mixers[0] = ad1884a_laptop_mixers; | 4101 | spec->mixers[0] = ad1884a_laptop_mixers; |
4060 | spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; | 4102 | spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; |
4061 | spec->multiout.dig_out_nid = 0; | 4103 | spec->multiout.dig_out_nid = 0; |
4062 | spec->input_mux = &ad1884a_laptop_capture_source; | 4104 | codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; |
4063 | codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; | 4105 | codec->patch_ops.init = ad1884a_laptop_init; |
4064 | codec->patch_ops.init = ad1884a_hp_init; | ||
4065 | /* set the upper-limit for mixer amp to 0dB for avoiding the | 4106 | /* set the upper-limit for mixer amp to 0dB for avoiding the |
4066 | * possible damage by overloading | 4107 | * possible damage by overloading |
4067 | */ | 4108 | */ |