diff options
author | Kailang Yang <kailang@realtek.com.tw> | 2007-10-16 08:30:01 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2007-10-16 10:51:25 -0400 |
commit | c9b58006be7e471a5f55d171cbaa08f4aa8078ea (patch) | |
tree | 8ec10fb70342dc48ee6ae5125d24e76c09c88c1b | |
parent | 291702f017efdfe556cb87b8530eb7d1ff08cbae (diff) |
[ALSA] hda-codec - Fix SKU ID function for realtek codecs
Fixed SKU ID function for realtek codecs. It's used by the automatic
BIOS configuration mode. Now it supports the correct jack-detection
mechanism, too.
Signed-off-by: Kailang Yang <kailang@realtek.com.tw>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 193 |
1 files changed, 168 insertions, 25 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c8ca97b2c31d..53b0428abfc2 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -662,6 +662,44 @@ static struct hda_verb alc_gpio3_init_verbs[] = { | |||
662 | { } | 662 | { } |
663 | }; | 663 | }; |
664 | 664 | ||
665 | static void alc_sku_automute(struct hda_codec *codec) | ||
666 | { | ||
667 | struct alc_spec *spec = codec->spec; | ||
668 | unsigned int mute; | ||
669 | unsigned int present; | ||
670 | unsigned int hp_nid = spec->autocfg.hp_pins[0]; | ||
671 | unsigned int sp_nid = spec->autocfg.speaker_pins[0]; | ||
672 | |||
673 | /* need to execute and sync at first */ | ||
674 | snd_hda_codec_read(codec, hp_nid, 0, AC_VERB_SET_PIN_SENSE, 0); | ||
675 | present = snd_hda_codec_read(codec, hp_nid, 0, | ||
676 | AC_VERB_GET_PIN_SENSE, 0); | ||
677 | spec->jack_present = (present & 0x80000000) != 0; | ||
678 | if (spec->jack_present) { | ||
679 | /* mute internal speaker */ | ||
680 | snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, | ||
681 | HDA_AMP_MUTE, HDA_AMP_MUTE); | ||
682 | } else { | ||
683 | /* unmute internal speaker if necessary */ | ||
684 | mute = snd_hda_codec_amp_read(codec, hp_nid, 0, HDA_OUTPUT, 0); | ||
685 | snd_hda_codec_amp_stereo(codec, sp_nid, HDA_OUTPUT, 0, | ||
686 | HDA_AMP_MUTE, mute); | ||
687 | } | ||
688 | } | ||
689 | |||
690 | /* unsolicited event for HP jack sensing */ | ||
691 | static void alc_sku_unsol_event(struct hda_codec *codec, unsigned int res) | ||
692 | { | ||
693 | if (codec->vendor_id == 0x10ec0880) | ||
694 | res >>= 28; | ||
695 | else | ||
696 | res >>= 26; | ||
697 | if (res != ALC880_HP_EVENT) | ||
698 | return; | ||
699 | |||
700 | alc_sku_automute(codec); | ||
701 | } | ||
702 | |||
665 | /* 32-bit subsystem ID for BIOS loading in HD Audio codec. | 703 | /* 32-bit subsystem ID for BIOS loading in HD Audio codec. |
666 | * 31 ~ 16 : Manufacture ID | 704 | * 31 ~ 16 : Manufacture ID |
667 | * 15 ~ 8 : SKU ID | 705 | * 15 ~ 8 : SKU ID |
@@ -672,13 +710,48 @@ static void alc_subsystem_id(struct hda_codec *codec, | |||
672 | unsigned int porta, unsigned int porte, | 710 | unsigned int porta, unsigned int porte, |
673 | unsigned int portd) | 711 | unsigned int portd) |
674 | { | 712 | { |
675 | unsigned int ass, tmp; | 713 | unsigned int ass, tmp, i; |
714 | unsigned nid; | ||
715 | struct alc_spec *spec = codec->spec; | ||
676 | 716 | ||
677 | ass = codec->subsystem_id; | 717 | ass = codec->subsystem_id & 0xffff; |
678 | if (!(ass & 1)) | 718 | if ((ass != codec->bus->pci->subsystem_device) && (ass & 1)) |
719 | goto do_sku; | ||
720 | |||
721 | /* | ||
722 | * 31~30 : port conetcivity | ||
723 | * 29~21 : reserve | ||
724 | * 20 : PCBEEP input | ||
725 | * 19~16 : Check sum (15:1) | ||
726 | * 15~1 : Custom | ||
727 | * 0 : override | ||
728 | */ | ||
729 | nid = 0x1d; | ||
730 | if (codec->vendor_id == 0x10ec0260) | ||
731 | nid = 0x17; | ||
732 | ass = snd_hda_codec_read(codec, nid, 0, | ||
733 | AC_VERB_GET_CONFIG_DEFAULT, 0); | ||
734 | if (!(ass & 1) && !(ass & 0x100000)) | ||
735 | return; | ||
736 | if ((ass >> 30) != 1) /* no physical connection */ | ||
679 | return; | 737 | return; |
680 | 738 | ||
681 | /* Override */ | 739 | /* check sum */ |
740 | tmp = 0; | ||
741 | for (i = 1; i < 16; i++) { | ||
742 | if ((ass >> i) && 1) | ||
743 | tmp++; | ||
744 | } | ||
745 | if (((ass >> 16) & 0xf) != tmp) | ||
746 | return; | ||
747 | do_sku: | ||
748 | /* | ||
749 | * 0 : override | ||
750 | * 1 : Swap Jack | ||
751 | * 2 : 0 --> Desktop, 1 --> Laptop | ||
752 | * 3~5 : External Amplifier control | ||
753 | * 7~6 : Reserved | ||
754 | */ | ||
682 | tmp = (ass & 0x38) >> 3; /* external Amp control */ | 755 | tmp = (ass & 0x38) >> 3; /* external Amp control */ |
683 | switch (tmp) { | 756 | switch (tmp) { |
684 | case 1: | 757 | case 1: |
@@ -690,38 +763,108 @@ static void alc_subsystem_id(struct hda_codec *codec, | |||
690 | case 7: | 763 | case 7: |
691 | snd_hda_sequence_write(codec, alc_gpio3_init_verbs); | 764 | snd_hda_sequence_write(codec, alc_gpio3_init_verbs); |
692 | break; | 765 | break; |
693 | case 5: | 766 | case 5: /* set EAPD output high */ |
694 | switch (codec->vendor_id) { | 767 | switch (codec->vendor_id) { |
695 | case 0x10ec0862: | 768 | case 0x10ec0260: |
696 | case 0x10ec0660: | 769 | snd_hda_codec_write(codec, 0x0f, 0, |
697 | case 0x10ec0662: | 770 | AC_VERB_SET_EAPD_BTLENABLE, 2); |
771 | snd_hda_codec_write(codec, 0x10, 0, | ||
772 | AC_VERB_SET_EAPD_BTLENABLE, 2); | ||
773 | break; | ||
774 | case 0x10ec0262: | ||
698 | case 0x10ec0267: | 775 | case 0x10ec0267: |
699 | case 0x10ec0268: | 776 | case 0x10ec0268: |
777 | case 0x10ec0269: | ||
778 | case 0x10ec0862: | ||
779 | case 0x10ec0662: | ||
700 | snd_hda_codec_write(codec, 0x14, 0, | 780 | snd_hda_codec_write(codec, 0x14, 0, |
701 | AC_VERB_SET_EAPD_BTLENABLE, 2); | 781 | AC_VERB_SET_EAPD_BTLENABLE, 2); |
702 | snd_hda_codec_write(codec, 0x15, 0, | 782 | snd_hda_codec_write(codec, 0x15, 0, |
703 | AC_VERB_SET_EAPD_BTLENABLE, 2); | 783 | AC_VERB_SET_EAPD_BTLENABLE, 2); |
704 | return; | 784 | break; |
705 | } | 785 | } |
706 | case 6: | 786 | switch (codec->vendor_id) { |
707 | if (ass & 4) { /* bit 2 : 0 = Desktop, 1 = Laptop */ | 787 | case 0x10ec0260: |
708 | hda_nid_t port = 0; | 788 | snd_hda_codec_write(codec, 0x1a, 0, |
709 | tmp = (ass & 0x1800) >> 11; | 789 | AC_VERB_SET_COEF_INDEX, 7); |
710 | switch (tmp) { | 790 | tmp = snd_hda_codec_read(codec, 0x1a, 0, |
711 | case 0: port = porta; break; | 791 | AC_VERB_GET_PROC_COEF, 0); |
712 | case 1: port = porte; break; | 792 | snd_hda_codec_write(codec, 0x1a, 0, |
713 | case 2: port = portd; break; | 793 | AC_VERB_SET_COEF_INDEX, 7); |
714 | } | 794 | snd_hda_codec_write(codec, 0x1a, 0, |
715 | if (port) | 795 | AC_VERB_SET_PROC_COEF, |
716 | snd_hda_codec_write(codec, port, 0, | 796 | tmp | 0x2010); |
717 | AC_VERB_SET_EAPD_BTLENABLE, | 797 | break; |
718 | 2); | 798 | case 0x10ec0262: |
799 | case 0x10ec0880: | ||
800 | case 0x10ec0882: | ||
801 | case 0x10ec0883: | ||
802 | case 0x10ec0885: | ||
803 | case 0x10ec0888: | ||
804 | snd_hda_codec_write(codec, 0x20, 0, | ||
805 | AC_VERB_SET_COEF_INDEX, 7); | ||
806 | tmp = snd_hda_codec_read(codec, 0x20, 0, | ||
807 | AC_VERB_GET_PROC_COEF, 0); | ||
808 | snd_hda_codec_write(codec, 0x20, 0, | ||
809 | AC_VERB_SET_COEF_INDEX, 7); | ||
810 | snd_hda_codec_write(codec, 0x20, 0, | ||
811 | AC_VERB_SET_PROC_COEF, | ||
812 | tmp | 0x2010); | ||
813 | break; | ||
814 | case 0x10ec0267: | ||
815 | case 0x10ec0268: | ||
816 | snd_hda_codec_write(codec, 0x20, 0, | ||
817 | AC_VERB_SET_COEF_INDEX, 7); | ||
818 | tmp = snd_hda_codec_read(codec, 0x20, 0, | ||
819 | AC_VERB_GET_PROC_COEF, 0); | ||
820 | snd_hda_codec_write(codec, 0x20, 0, | ||
821 | AC_VERB_SET_COEF_INDEX, 7); | ||
822 | snd_hda_codec_write(codec, 0x20, 0, | ||
823 | AC_VERB_SET_PROC_COEF, | ||
824 | tmp | 0x3000); | ||
825 | break; | ||
719 | } | 826 | } |
720 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); | 827 | default: |
721 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, | ||
722 | (tmp == 5 ? 0x3040 : 0x3050)); | ||
723 | break; | 828 | break; |
724 | } | 829 | } |
830 | |||
831 | /* is laptop and enable the function "Mute internal speaker | ||
832 | * when the external headphone out jack is plugged" | ||
833 | */ | ||
834 | if (!(ass & 0x4) || !(ass & 0x8000)) | ||
835 | return; | ||
836 | /* | ||
837 | * 10~8 : Jack location | ||
838 | * 12~11: Headphone out -> 00: PortA, 01: PortE, 02: PortD, 03: Resvered | ||
839 | * 14~13: Resvered | ||
840 | * 15 : 1 --> enable the function "Mute internal speaker | ||
841 | * when the external headphone out jack is plugged" | ||
842 | */ | ||
843 | if (!spec->autocfg.speaker_pins[0]) { | ||
844 | if (spec->multiout.dac_nids[0]) | ||
845 | spec->autocfg.speaker_pins[0] = | ||
846 | spec->multiout.dac_nids[0]; | ||
847 | else | ||
848 | return; | ||
849 | } | ||
850 | |||
851 | if (!spec->autocfg.hp_pins[0]) { | ||
852 | tmp = (ass >> 11) & 0x3; /* HP to chassis */ | ||
853 | if (tmp == 0) | ||
854 | spec->autocfg.hp_pins[0] = porta; | ||
855 | else if (tmp == 1) | ||
856 | spec->autocfg.hp_pins[0] = porte; | ||
857 | else if (tmp == 2) | ||
858 | spec->autocfg.hp_pins[0] = portd; | ||
859 | else | ||
860 | return; | ||
861 | } | ||
862 | |||
863 | snd_hda_codec_write(codec, spec->autocfg.hp_pins[0], 0, | ||
864 | AC_VERB_SET_UNSOLICITED_ENABLE, | ||
865 | AC_USRSP_EN | ALC880_HP_EVENT); | ||
866 | spec->unsol_event = alc_sku_unsol_event; | ||
867 | spec->init_hook = alc_sku_automute; | ||
725 | } | 868 | } |
726 | 869 | ||
727 | /* | 870 | /* |