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 | /* |
