aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-08-30 06:15:59 -0400
committerTakashi Iwai <tiwai@suse.de>2009-08-31 02:32:27 -0400
commit17bbaa6f600a36930879fd58e4e38a8c25bf9b60 (patch)
tree759d5cf06f7d6f26ae061037eb5354ce0478204e /sound
parentbe0ae923a40bbee8a0f02c1cd4b70656e729096f (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')
-rw-r--r--sound/pci/hda/patch_analog.c121
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
3706static struct hda_input_mux ad1884a_laptop_capture_source = { 3706static 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
3716static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { 3719static 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
3750static 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
3763static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { 3746static 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 */
3816static 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 */
3834static 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 */
3850static 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 */
3864static 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 */
3833static struct hda_verb ad1884a_laptop_verbs[] = { 3873static 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 */