diff options
author | Takashi Iwai <tiwai@suse.de> | 2007-09-06 08:29:53 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2007-10-16 10:49:04 -0400 |
commit | 8ab78c7424588c6b1600dcfd70418617a09326b8 (patch) | |
tree | 7535eeedf62221171962dbdb69d4d872f550e04b /sound | |
parent | 966a4d595ca7ef848cec7673ad03961d8303a40a (diff) |
[ALSA] hda-codec - Add laptop-automute model for AD1986A
Added a new model laptop-automute for AD1986A, which has the HP jack
detection and auto-muting of the speaker. Currently, it's used for
Lenovo N100.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/hda/patch_analog.c | 126 |
1 files changed, 125 insertions, 1 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index bc4b797aa97b..54cfd4526d20 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c | |||
@@ -74,6 +74,8 @@ struct ad198x_spec { | |||
74 | struct hda_input_mux private_imux; | 74 | struct hda_input_mux private_imux; |
75 | hda_nid_t private_dac_nids[4]; | 75 | hda_nid_t private_dac_nids[4]; |
76 | 76 | ||
77 | unsigned int jack_present :1; | ||
78 | |||
77 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 79 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
78 | struct hda_loopback_check loopback; | 80 | struct hda_loopback_check loopback; |
79 | #endif | 81 | #endif |
@@ -588,6 +590,106 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { | |||
588 | { } /* end */ | 590 | { } /* end */ |
589 | }; | 591 | }; |
590 | 592 | ||
593 | /* laptop-automute - 2ch only */ | ||
594 | |||
595 | static void ad1986a_update_hp(struct hda_codec *codec) | ||
596 | { | ||
597 | struct ad198x_spec *spec = codec->spec; | ||
598 | unsigned int mute; | ||
599 | |||
600 | if (spec->jack_present) | ||
601 | mute = HDA_AMP_MUTE; /* mute internal speaker */ | ||
602 | else | ||
603 | /* unmute internal speaker if necessary */ | ||
604 | mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0); | ||
605 | snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0, | ||
606 | HDA_AMP_MUTE, mute); | ||
607 | } | ||
608 | |||
609 | static void ad1986a_hp_automute(struct hda_codec *codec) | ||
610 | { | ||
611 | struct ad198x_spec *spec = codec->spec; | ||
612 | unsigned int present; | ||
613 | |||
614 | present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0); | ||
615 | spec->jack_present = (present & 0x80000000) != 0; | ||
616 | ad1986a_update_hp(codec); | ||
617 | } | ||
618 | |||
619 | #define AD1986A_HP_EVENT 0x37 | ||
620 | |||
621 | static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res) | ||
622 | { | ||
623 | if ((res >> 26) != AD1986A_HP_EVENT) | ||
624 | return; | ||
625 | ad1986a_hp_automute(codec); | ||
626 | } | ||
627 | |||
628 | static int ad1986a_hp_init(struct hda_codec *codec) | ||
629 | { | ||
630 | ad198x_init(codec); | ||
631 | ad1986a_hp_automute(codec); | ||
632 | return 0; | ||
633 | } | ||
634 | |||
635 | /* bind hp and internal speaker mute (with plug check) */ | ||
636 | static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, | ||
637 | struct snd_ctl_elem_value *ucontrol) | ||
638 | { | ||
639 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
640 | long *valp = ucontrol->value.integer.value; | ||
641 | int change; | ||
642 | |||
643 | change = snd_hda_codec_amp_update(codec, 0x1a, 0, HDA_OUTPUT, 0, | ||
644 | HDA_AMP_MUTE, | ||
645 | valp[0] ? 0 : HDA_AMP_MUTE); | ||
646 | change |= snd_hda_codec_amp_update(codec, 0x1a, 1, HDA_OUTPUT, 0, | ||
647 | HDA_AMP_MUTE, | ||
648 | valp[1] ? 0 : HDA_AMP_MUTE); | ||
649 | if (change) | ||
650 | ad1986a_update_hp(codec); | ||
651 | return change; | ||
652 | } | ||
653 | |||
654 | static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = { | ||
655 | HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), | ||
656 | { | ||
657 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
658 | .name = "Master Playback Switch", | ||
659 | .info = snd_hda_mixer_amp_switch_info, | ||
660 | .get = snd_hda_mixer_amp_switch_get, | ||
661 | .put = ad1986a_hp_master_sw_put, | ||
662 | .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), | ||
663 | }, | ||
664 | HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), | ||
665 | HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), | ||
666 | HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), | ||
667 | HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT), | ||
668 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), | ||
669 | HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), | ||
670 | HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), | ||
671 | HDA_CODEC_VOLUME("Beep Playback Volume", 0x18, 0x0, HDA_OUTPUT), | ||
672 | HDA_CODEC_MUTE("Beep Playback Switch", 0x18, 0x0, HDA_OUTPUT), | ||
673 | HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), | ||
674 | HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), | ||
675 | { | ||
676 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
677 | .name = "Capture Source", | ||
678 | .info = ad198x_mux_enum_info, | ||
679 | .get = ad198x_mux_enum_get, | ||
680 | .put = ad198x_mux_enum_put, | ||
681 | }, | ||
682 | { | ||
683 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
684 | .name = "External Amplifier", | ||
685 | .info = ad198x_eapd_info, | ||
686 | .get = ad198x_eapd_get, | ||
687 | .put = ad198x_eapd_put, | ||
688 | .private_value = 0x1b | (1 << 8), /* port-D, inversed */ | ||
689 | }, | ||
690 | { } /* end */ | ||
691 | }; | ||
692 | |||
591 | /* | 693 | /* |
592 | * initialization verbs | 694 | * initialization verbs |
593 | */ | 695 | */ |
@@ -701,12 +803,20 @@ static struct hda_verb ad1986a_ultra_init[] = { | |||
701 | { } /* end */ | 803 | { } /* end */ |
702 | }; | 804 | }; |
703 | 805 | ||
806 | /* pin sensing on HP jack */ | ||
807 | static struct hda_verb ad1986a_hp_init_verbs[] = { | ||
808 | {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT}, | ||
809 | {} | ||
810 | }; | ||
811 | |||
812 | |||
704 | /* models */ | 813 | /* models */ |
705 | enum { | 814 | enum { |
706 | AD1986A_6STACK, | 815 | AD1986A_6STACK, |
707 | AD1986A_3STACK, | 816 | AD1986A_3STACK, |
708 | AD1986A_LAPTOP, | 817 | AD1986A_LAPTOP, |
709 | AD1986A_LAPTOP_EAPD, | 818 | AD1986A_LAPTOP_EAPD, |
819 | AD1986A_LAPTOP_AUTOMUTE, | ||
710 | AD1986A_ULTRA, | 820 | AD1986A_ULTRA, |
711 | AD1986A_MODELS | 821 | AD1986A_MODELS |
712 | }; | 822 | }; |
@@ -716,6 +826,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = { | |||
716 | [AD1986A_3STACK] = "3stack", | 826 | [AD1986A_3STACK] = "3stack", |
717 | [AD1986A_LAPTOP] = "laptop", | 827 | [AD1986A_LAPTOP] = "laptop", |
718 | [AD1986A_LAPTOP_EAPD] = "laptop-eapd", | 828 | [AD1986A_LAPTOP_EAPD] = "laptop-eapd", |
829 | [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", | ||
719 | [AD1986A_ULTRA] = "ultra", | 830 | [AD1986A_ULTRA] = "ultra", |
720 | }; | 831 | }; |
721 | 832 | ||
@@ -744,7 +855,7 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { | |||
744 | SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), | 855 | SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), |
745 | SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), | 856 | SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP), |
746 | SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), | 857 | SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK), |
747 | SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_EAPD), | 858 | SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE), |
748 | SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), | 859 | SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP), |
749 | {} | 860 | {} |
750 | }; | 861 | }; |
@@ -821,6 +932,19 @@ static int patch_ad1986a(struct hda_codec *codec) | |||
821 | spec->multiout.dig_out_nid = 0; | 932 | spec->multiout.dig_out_nid = 0; |
822 | spec->input_mux = &ad1986a_laptop_eapd_capture_source; | 933 | spec->input_mux = &ad1986a_laptop_eapd_capture_source; |
823 | break; | 934 | break; |
935 | case AD1986A_LAPTOP_AUTOMUTE: | ||
936 | spec->mixers[0] = ad1986a_laptop_automute_mixers; | ||
937 | spec->num_init_verbs = 3; | ||
938 | spec->init_verbs[1] = ad1986a_eapd_init_verbs; | ||
939 | spec->init_verbs[2] = ad1986a_hp_init_verbs; | ||
940 | spec->multiout.max_channels = 2; | ||
941 | spec->multiout.num_dacs = 1; | ||
942 | spec->multiout.dac_nids = ad1986a_laptop_dac_nids; | ||
943 | spec->multiout.dig_out_nid = 0; | ||
944 | spec->input_mux = &ad1986a_laptop_eapd_capture_source; | ||
945 | codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; | ||
946 | codec->patch_ops.init = ad1986a_hp_init; | ||
947 | break; | ||
824 | case AD1986A_ULTRA: | 948 | case AD1986A_ULTRA: |
825 | spec->mixers[0] = ad1986a_laptop_eapd_mixers; | 949 | spec->mixers[0] = ad1986a_laptop_eapd_mixers; |
826 | spec->num_init_verbs = 2; | 950 | spec->num_init_verbs = 2; |