diff options
author | Kailang Yang <kailang@realtek.com.tw> | 2007-04-12 07:06:07 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2007-05-11 10:55:58 -0400 |
commit | bc9f98a9815c452a74e5eb9cbd2ed61b337fdcd2 (patch) | |
tree | dd8e465a66fa8082ff50fa04eedadc7d95a36512 /sound/pci/hda | |
parent | 7d4b4380d37025f0b13ae951e0cb2ff7184dc5bb (diff) |
[ALSA] hda-codec - Add ALC662 support
- Add ALC662 support
- Fixed no sound for [0x1631, 0xc017, 'PB V7900', ALC260_WILL]
- Fixed no sound for [0x161f, 0x2057, 'Replacer 672V', ALC260_REPLACER_672V]
- Add SKU ID for auto mode
Signed-off-by: Kailang Yang <kailang@realtek.com.tw>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 1162 |
1 files changed, 1139 insertions, 23 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d3f7a3dab1c4..5d7f61982001 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -74,6 +74,8 @@ enum { | |||
74 | ALC260_HP_3013, | 74 | ALC260_HP_3013, |
75 | ALC260_FUJITSU_S702X, | 75 | ALC260_FUJITSU_S702X, |
76 | ALC260_ACER, | 76 | ALC260_ACER, |
77 | ALC260_WILL, | ||
78 | ALC260_REPLACER_672V, | ||
77 | #ifdef CONFIG_SND_DEBUG | 79 | #ifdef CONFIG_SND_DEBUG |
78 | ALC260_TEST, | 80 | ALC260_TEST, |
79 | #endif | 81 | #endif |
@@ -119,6 +121,17 @@ enum { | |||
119 | ALC861VD_MODEL_LAST, | 121 | ALC861VD_MODEL_LAST, |
120 | }; | 122 | }; |
121 | 123 | ||
124 | /* ALC662 models */ | ||
125 | enum { | ||
126 | ALC662_3ST_2ch_DIG, | ||
127 | ALC662_3ST_6ch_DIG, | ||
128 | ALC662_3ST_6ch, | ||
129 | ALC662_5ST_DIG, | ||
130 | ALC662_LENOVO_101E, | ||
131 | ALC662_AUTO, | ||
132 | ALC662_MODEL_LAST, | ||
133 | }; | ||
134 | |||
122 | /* ALC882 models */ | 135 | /* ALC882 models */ |
123 | enum { | 136 | enum { |
124 | ALC882_3ST_DIG, | 137 | ALC882_3ST_DIG, |
@@ -141,6 +154,7 @@ enum { | |||
141 | ALC883_ACER, | 154 | ALC883_ACER, |
142 | ALC883_MEDION, | 155 | ALC883_MEDION, |
143 | ALC883_LAPTOP_EAPD, | 156 | ALC883_LAPTOP_EAPD, |
157 | ALC883_LENOVO_101E_2ch, | ||
144 | ALC883_AUTO, | 158 | ALC883_AUTO, |
145 | ALC883_MODEL_LAST, | 159 | ALC883_MODEL_LAST, |
146 | }; | 160 | }; |
@@ -604,6 +618,68 @@ static void setup_preset(struct alc_spec *spec, | |||
604 | spec->init_hook = preset->init_hook; | 618 | spec->init_hook = preset->init_hook; |
605 | } | 619 | } |
606 | 620 | ||
621 | /* Enable GPIO mask and set output */ | ||
622 | static struct hda_verb alc_gpio1_init_verbs[] = { | ||
623 | {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, | ||
624 | {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, | ||
625 | {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, | ||
626 | { } | ||
627 | }; | ||
628 | |||
629 | static struct hda_verb alc_gpio2_init_verbs[] = { | ||
630 | {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, | ||
631 | {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, | ||
632 | {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, | ||
633 | { } | ||
634 | }; | ||
635 | |||
636 | /* 32-bit subsystem ID for BIOS loading in HD Audio codec. | ||
637 | * 31 ~ 16 : Manufacture ID | ||
638 | * 15 ~ 8 : SKU ID | ||
639 | * 7 ~ 0 : Assembly ID | ||
640 | * port-A --> pin 39/41, port-E --> pin 14/15, port-D --> pin 35/36 | ||
641 | */ | ||
642 | static void alc_subsystem_id(struct hda_codec *codec, | ||
643 | unsigned int porta, unsigned int porte, | ||
644 | unsigned int portd) | ||
645 | { | ||
646 | unsigned int ass, tmp; | ||
647 | |||
648 | ass = codec->subsystem_id; | ||
649 | if (!(ass & 1)) | ||
650 | return; | ||
651 | |||
652 | /* Override */ | ||
653 | tmp = (ass & 0x38) >> 3; /* external Amp control */ | ||
654 | switch (tmp) { | ||
655 | case 1: | ||
656 | snd_hda_sequence_write(codec, alc_gpio1_init_verbs); | ||
657 | break; | ||
658 | case 3: | ||
659 | snd_hda_sequence_write(codec, alc_gpio2_init_verbs); | ||
660 | break; | ||
661 | case 5: | ||
662 | case 6: | ||
663 | if (ass & 4) { /* bit 2 : 0 = Desktop, 1 = Laptop */ | ||
664 | hda_nid_t port = 0; | ||
665 | tmp = (ass & 0x1800) >> 11; | ||
666 | switch (tmp) { | ||
667 | case 0: port = porta; break; | ||
668 | case 1: port = porte; break; | ||
669 | case 2: port = portd; break; | ||
670 | } | ||
671 | if (port) | ||
672 | snd_hda_codec_write(codec, port, 0, | ||
673 | AC_VERB_SET_EAPD_BTLENABLE, | ||
674 | 2); | ||
675 | } | ||
676 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_COEF_INDEX, 7); | ||
677 | snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PROC_COEF, | ||
678 | (tmp == 5 ? 0x3040 : 0x3050)); | ||
679 | break; | ||
680 | } | ||
681 | } | ||
682 | |||
607 | /* | 683 | /* |
608 | * ALC880 3-stack model | 684 | * ALC880 3-stack model |
609 | * | 685 | * |
@@ -1547,22 +1623,8 @@ static struct hda_verb alc880_pin_asus_init_verbs[] = { | |||
1547 | }; | 1623 | }; |
1548 | 1624 | ||
1549 | /* Enable GPIO mask and set output */ | 1625 | /* Enable GPIO mask and set output */ |
1550 | static struct hda_verb alc880_gpio1_init_verbs[] = { | 1626 | #define alc880_gpio1_init_verbs alc_gpio1_init_verbs |
1551 | {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, | 1627 | #define alc880_gpio2_init_verbs alc_gpio2_init_verbs |
1552 | {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, | ||
1553 | {0x01, AC_VERB_SET_GPIO_DATA, 0x01}, | ||
1554 | |||
1555 | { } | ||
1556 | }; | ||
1557 | |||
1558 | /* Enable GPIO mask and set output */ | ||
1559 | static struct hda_verb alc880_gpio2_init_verbs[] = { | ||
1560 | {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, | ||
1561 | {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, | ||
1562 | {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, | ||
1563 | |||
1564 | { } | ||
1565 | }; | ||
1566 | 1628 | ||
1567 | /* Clevo m520g init */ | 1629 | /* Clevo m520g init */ |
1568 | static struct hda_verb alc880_pin_clevo_init_verbs[] = { | 1630 | static struct hda_verb alc880_pin_clevo_init_verbs[] = { |
@@ -2980,7 +3042,8 @@ static void alc880_auto_init_multi_out(struct hda_codec *codec) | |||
2980 | { | 3042 | { |
2981 | struct alc_spec *spec = codec->spec; | 3043 | struct alc_spec *spec = codec->spec; |
2982 | int i; | 3044 | int i; |
2983 | 3045 | ||
3046 | alc_subsystem_id(codec, 0x15, 0x1b, 0x14); | ||
2984 | for (i = 0; i < spec->autocfg.line_outs; i++) { | 3047 | for (i = 0; i < spec->autocfg.line_outs; i++) { |
2985 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | 3048 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; |
2986 | alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); | 3049 | alc880_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); |
@@ -3361,6 +3424,42 @@ static struct snd_kcontrol_new alc260_acer_mixer[] = { | |||
3361 | { } /* end */ | 3424 | { } /* end */ |
3362 | }; | 3425 | }; |
3363 | 3426 | ||
3427 | /* Packard bell V7900 ALC260 pin usage: HP = 0x0f, Mic jack = 0x12, | ||
3428 | * Line In jack = 0x14, CD audio = 0x16, pc beep = 0x17. | ||
3429 | */ | ||
3430 | static struct snd_kcontrol_new alc260_will_mixer[] = { | ||
3431 | HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), | ||
3432 | HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), | ||
3433 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), | ||
3434 | HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), | ||
3435 | ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), | ||
3436 | HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), | ||
3437 | HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), | ||
3438 | ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), | ||
3439 | HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT), | ||
3440 | HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT), | ||
3441 | HDA_CODEC_VOLUME("Beep Playback Volume", 0x07, 0x05, HDA_INPUT), | ||
3442 | HDA_CODEC_MUTE("Beep Playback Switch", 0x07, 0x05, HDA_INPUT), | ||
3443 | { } /* end */ | ||
3444 | }; | ||
3445 | |||
3446 | /* Replacer 672V ALC260 pin usage: Mic jack = 0x12, | ||
3447 | * Line In jack = 0x14, ATAPI Mic = 0x13, speaker = 0x0f. | ||
3448 | */ | ||
3449 | static struct snd_kcontrol_new alc260_replacer_672v_mixer[] = { | ||
3450 | HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT), | ||
3451 | HDA_BIND_MUTE("Master Playback Switch", 0x08, 0x2, HDA_INPUT), | ||
3452 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT), | ||
3453 | HDA_CODEC_MUTE("Mic Playback Switch", 0x07, 0x0, HDA_INPUT), | ||
3454 | ALC_PIN_MODE("Mic Jack Mode", 0x12, ALC_PIN_DIR_IN), | ||
3455 | HDA_CODEC_VOLUME("ATAPI Mic Playback Volume", 0x07, 0x1, HDA_INPUT), | ||
3456 | HDA_CODEC_MUTE("ATATI Mic Playback Switch", 0x07, 0x1, HDA_INPUT), | ||
3457 | HDA_CODEC_VOLUME("Line Playback Volume", 0x07, 0x02, HDA_INPUT), | ||
3458 | HDA_CODEC_MUTE("Line Playback Switch", 0x07, 0x02, HDA_INPUT), | ||
3459 | ALC_PIN_MODE("Line Jack Mode", 0x14, ALC_PIN_DIR_INOUT), | ||
3460 | { } /* end */ | ||
3461 | }; | ||
3462 | |||
3364 | /* capture mixer elements */ | 3463 | /* capture mixer elements */ |
3365 | static struct snd_kcontrol_new alc260_capture_mixer[] = { | 3464 | static struct snd_kcontrol_new alc260_capture_mixer[] = { |
3366 | HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), | 3465 | HDA_CODEC_VOLUME("Capture Volume", 0x04, 0x0, HDA_INPUT), |
@@ -3731,6 +3830,55 @@ static struct hda_verb alc260_acer_init_verbs[] = { | |||
3731 | { } | 3830 | { } |
3732 | }; | 3831 | }; |
3733 | 3832 | ||
3833 | static struct hda_verb alc260_will_verbs[] = { | ||
3834 | {0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
3835 | {0x0b, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
3836 | {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
3837 | {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, | ||
3838 | {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, | ||
3839 | {0x1a, AC_VERB_SET_PROC_COEF, 0x3040}, | ||
3840 | {} | ||
3841 | }; | ||
3842 | |||
3843 | static struct hda_verb alc260_replacer_672v_verbs[] = { | ||
3844 | {0x0f, AC_VERB_SET_EAPD_BTLENABLE, 0x02}, | ||
3845 | {0x1a, AC_VERB_SET_COEF_INDEX, 0x07}, | ||
3846 | {0x1a, AC_VERB_SET_PROC_COEF, 0x3050}, | ||
3847 | |||
3848 | {0x01, AC_VERB_SET_GPIO_MASK, 0x01}, | ||
3849 | {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x01}, | ||
3850 | {0x01, AC_VERB_SET_GPIO_DATA, 0x00}, | ||
3851 | |||
3852 | {0x0f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, | ||
3853 | {} | ||
3854 | }; | ||
3855 | |||
3856 | /* toggle speaker-output according to the hp-jack state */ | ||
3857 | static void alc260_replacer_672v_automute(struct hda_codec *codec) | ||
3858 | { | ||
3859 | unsigned int present; | ||
3860 | |||
3861 | /* speaker --> GPIO Data 0, hp or spdif --> GPIO data 1 */ | ||
3862 | present = snd_hda_codec_read(codec, 0x0f, 0, | ||
3863 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
3864 | if (present) { | ||
3865 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 1); | ||
3866 | snd_hda_codec_write(codec, 0x0f, 0, | ||
3867 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP); | ||
3868 | } else { | ||
3869 | snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, 0); | ||
3870 | snd_hda_codec_write(codec, 0x0f, 0, | ||
3871 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT); | ||
3872 | } | ||
3873 | } | ||
3874 | |||
3875 | static void alc260_replacer_672v_unsol_event(struct hda_codec *codec, | ||
3876 | unsigned int res) | ||
3877 | { | ||
3878 | if ((res >> 26) == ALC880_HP_EVENT) | ||
3879 | alc260_replacer_672v_automute(codec); | ||
3880 | } | ||
3881 | |||
3734 | /* Test configuration for debugging, modelled after the ALC880 test | 3882 | /* Test configuration for debugging, modelled after the ALC880 test |
3735 | * configuration. | 3883 | * configuration. |
3736 | */ | 3884 | */ |
@@ -4053,6 +4201,7 @@ static void alc260_auto_init_multi_out(struct hda_codec *codec) | |||
4053 | struct alc_spec *spec = codec->spec; | 4201 | struct alc_spec *spec = codec->spec; |
4054 | hda_nid_t nid; | 4202 | hda_nid_t nid; |
4055 | 4203 | ||
4204 | alc_subsystem_id(codec, 0x10, 0x15, 0x0f); | ||
4056 | nid = spec->autocfg.line_out_pins[0]; | 4205 | nid = spec->autocfg.line_out_pins[0]; |
4057 | if (nid) | 4206 | if (nid) |
4058 | alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); | 4207 | alc260_auto_set_output_and_unmute(codec, nid, PIN_OUT, 0); |
@@ -4189,6 +4338,8 @@ static const char *alc260_models[ALC260_MODEL_LAST] = { | |||
4189 | [ALC260_HP_3013] = "hp-3013", | 4338 | [ALC260_HP_3013] = "hp-3013", |
4190 | [ALC260_FUJITSU_S702X] = "fujitsu", | 4339 | [ALC260_FUJITSU_S702X] = "fujitsu", |
4191 | [ALC260_ACER] = "acer", | 4340 | [ALC260_ACER] = "acer", |
4341 | [ALC260_WILL] = "will", | ||
4342 | [ALC260_REPLACER_672V] = "replacer", | ||
4192 | #ifdef CONFIG_SND_DEBUG | 4343 | #ifdef CONFIG_SND_DEBUG |
4193 | [ALC260_TEST] = "test", | 4344 | [ALC260_TEST] = "test", |
4194 | #endif | 4345 | #endif |
@@ -4212,6 +4363,8 @@ static struct snd_pci_quirk alc260_cfg_tbl[] = { | |||
4212 | SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), | 4363 | SND_PCI_QUIRK(0x104d, 0x81cd, "Sony VAIO", ALC260_BASIC), |
4213 | SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), | 4364 | SND_PCI_QUIRK(0x10cf, 0x1326, "Fujitsu S702X", ALC260_FUJITSU_S702X), |
4214 | SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), | 4365 | SND_PCI_QUIRK(0x152d, 0x0729, "CTL U553W", ALC260_BASIC), |
4366 | SND_PCI_QUIRK(0x1631, 0xc017, "PB V7900", ALC260_WILL), | ||
4367 | SND_PCI_QUIRK(0x161f, 0x2057, "Replacer 672V", ALC260_REPLACER_672V), | ||
4215 | {} | 4368 | {} |
4216 | }; | 4369 | }; |
4217 | 4370 | ||
@@ -4282,6 +4435,34 @@ static struct alc_config_preset alc260_presets[] = { | |||
4282 | .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), | 4435 | .num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources), |
4283 | .input_mux = alc260_acer_capture_sources, | 4436 | .input_mux = alc260_acer_capture_sources, |
4284 | }, | 4437 | }, |
4438 | [ALC260_WILL] = { | ||
4439 | .mixers = { alc260_will_mixer, | ||
4440 | alc260_capture_mixer }, | ||
4441 | .init_verbs = { alc260_init_verbs, alc260_will_verbs }, | ||
4442 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), | ||
4443 | .dac_nids = alc260_dac_nids, | ||
4444 | .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), | ||
4445 | .adc_nids = alc260_adc_nids, | ||
4446 | .dig_out_nid = ALC260_DIGOUT_NID, | ||
4447 | .num_channel_mode = ARRAY_SIZE(alc260_modes), | ||
4448 | .channel_mode = alc260_modes, | ||
4449 | .input_mux = &alc260_capture_source, | ||
4450 | }, | ||
4451 | [ALC260_REPLACER_672V] = { | ||
4452 | .mixers = { alc260_replacer_672v_mixer, | ||
4453 | alc260_capture_mixer }, | ||
4454 | .init_verbs = { alc260_init_verbs, alc260_replacer_672v_verbs }, | ||
4455 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), | ||
4456 | .dac_nids = alc260_dac_nids, | ||
4457 | .num_adc_nids = ARRAY_SIZE(alc260_adc_nids), | ||
4458 | .adc_nids = alc260_adc_nids, | ||
4459 | .dig_out_nid = ALC260_DIGOUT_NID, | ||
4460 | .num_channel_mode = ARRAY_SIZE(alc260_modes), | ||
4461 | .channel_mode = alc260_modes, | ||
4462 | .input_mux = &alc260_capture_source, | ||
4463 | .unsol_event = alc260_replacer_672v_unsol_event, | ||
4464 | .init_hook = alc260_replacer_672v_automute, | ||
4465 | }, | ||
4285 | #ifdef CONFIG_SND_DEBUG | 4466 | #ifdef CONFIG_SND_DEBUG |
4286 | [ALC260_TEST] = { | 4467 | [ALC260_TEST] = { |
4287 | .mixers = { alc260_test_mixer, | 4468 | .mixers = { alc260_test_mixer, |
@@ -4881,6 +5062,7 @@ static void alc882_auto_init_multi_out(struct hda_codec *codec) | |||
4881 | struct alc_spec *spec = codec->spec; | 5062 | struct alc_spec *spec = codec->spec; |
4882 | int i; | 5063 | int i; |
4883 | 5064 | ||
5065 | alc_subsystem_id(codec, 0x15, 0x1b, 0x14); | ||
4884 | for (i = 0; i <= HDA_SIDE; i++) { | 5066 | for (i = 0; i <= HDA_SIDE; i++) { |
4885 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | 5067 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; |
4886 | if (nid) | 5068 | if (nid) |
@@ -5057,6 +5239,15 @@ static struct hda_input_mux alc883_capture_source = { | |||
5057 | { "CD", 0x4 }, | 5239 | { "CD", 0x4 }, |
5058 | }, | 5240 | }, |
5059 | }; | 5241 | }; |
5242 | |||
5243 | static struct hda_input_mux alc883_lenovo_101e_capture_source = { | ||
5244 | .num_items = 2, | ||
5245 | .items = { | ||
5246 | { "Mic", 0x1 }, | ||
5247 | { "Line", 0x2 }, | ||
5248 | }, | ||
5249 | }; | ||
5250 | |||
5060 | #define alc883_mux_enum_info alc_mux_enum_info | 5251 | #define alc883_mux_enum_info alc_mux_enum_info |
5061 | #define alc883_mux_enum_get alc_mux_enum_get | 5252 | #define alc883_mux_enum_get alc_mux_enum_get |
5062 | 5253 | ||
@@ -5364,6 +5555,29 @@ static struct snd_kcontrol_new alc883_tagra_2ch_mixer[] = { | |||
5364 | { } /* end */ | 5555 | { } /* end */ |
5365 | }; | 5556 | }; |
5366 | 5557 | ||
5558 | static struct snd_kcontrol_new alc883_lenovo_101e_2ch_mixer[] = { | ||
5559 | HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT), | ||
5560 | HDA_BIND_MUTE("Front Playback Switch", 0x0c, 2, HDA_INPUT), | ||
5561 | HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x0d, 0x0, HDA_OUTPUT), | ||
5562 | HDA_BIND_MUTE("iSpeaker Playback Switch", 0x0d, 2, HDA_INPUT), | ||
5563 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
5564 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
5565 | HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), | ||
5566 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
5567 | HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), | ||
5568 | HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), | ||
5569 | { | ||
5570 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
5571 | /* .name = "Capture Source", */ | ||
5572 | .name = "Input Source", | ||
5573 | .count = 1, | ||
5574 | .info = alc883_mux_enum_info, | ||
5575 | .get = alc883_mux_enum_get, | ||
5576 | .put = alc883_mux_enum_put, | ||
5577 | }, | ||
5578 | { } /* end */ | ||
5579 | }; | ||
5580 | |||
5367 | static struct snd_kcontrol_new alc883_chmode_mixer[] = { | 5581 | static struct snd_kcontrol_new alc883_chmode_mixer[] = { |
5368 | { | 5582 | { |
5369 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | 5583 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
@@ -5471,6 +5685,13 @@ static struct hda_verb alc883_tagra_verbs[] = { | |||
5471 | { } /* end */ | 5685 | { } /* end */ |
5472 | }; | 5686 | }; |
5473 | 5687 | ||
5688 | static struct hda_verb alc883_lenovo_101e_verbs[] = { | ||
5689 | {0x15, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
5690 | {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_FRONT_EVENT|AC_USRSP_EN}, | ||
5691 | {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, ALC880_HP_EVENT|AC_USRSP_EN}, | ||
5692 | { } /* end */ | ||
5693 | }; | ||
5694 | |||
5474 | /* toggle speaker-output according to the hp-jack state */ | 5695 | /* toggle speaker-output according to the hp-jack state */ |
5475 | static void alc883_tagra_automute(struct hda_codec *codec) | 5696 | static void alc883_tagra_automute(struct hda_codec *codec) |
5476 | { | 5697 | { |
@@ -5491,6 +5712,45 @@ static void alc883_tagra_unsol_event(struct hda_codec *codec, unsigned int res) | |||
5491 | alc883_tagra_automute(codec); | 5712 | alc883_tagra_automute(codec); |
5492 | } | 5713 | } |
5493 | 5714 | ||
5715 | static void alc883_lenovo_101e_ispeaker_automute(struct hda_codec *codec) | ||
5716 | { | ||
5717 | unsigned int present; | ||
5718 | |||
5719 | present = snd_hda_codec_read(codec, 0x14, 0, | ||
5720 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
5721 | |||
5722 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
5723 | 0x80, present ? 0x80 : 0); | ||
5724 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
5725 | 0x80, present ? 0x80 : 0); | ||
5726 | } | ||
5727 | |||
5728 | static void alc883_lenovo_101e_all_automute(struct hda_codec *codec) | ||
5729 | { | ||
5730 | unsigned int present; | ||
5731 | |||
5732 | present = snd_hda_codec_read(codec, 0x1b, 0, | ||
5733 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
5734 | |||
5735 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
5736 | 0x80, present ? 0x80 : 0); | ||
5737 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
5738 | 0x80, present ? 0x80 : 0); | ||
5739 | snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, | ||
5740 | 0x80, present ? 0x80 : 0); | ||
5741 | snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, | ||
5742 | 0x80, present ? 0x80 : 0); | ||
5743 | } | ||
5744 | |||
5745 | static void alc883_lenovo_101e_unsol_event(struct hda_codec *codec, | ||
5746 | unsigned int res) | ||
5747 | { | ||
5748 | if ((res >> 26) == ALC880_HP_EVENT) | ||
5749 | alc883_lenovo_101e_all_automute(codec); | ||
5750 | if ((res >> 26) == ALC880_FRONT_EVENT) | ||
5751 | alc883_lenovo_101e_ispeaker_automute(codec); | ||
5752 | } | ||
5753 | |||
5494 | /* | 5754 | /* |
5495 | * generic initialization of ADC, input mixers and output mixers | 5755 | * generic initialization of ADC, input mixers and output mixers |
5496 | */ | 5756 | */ |
@@ -5596,6 +5856,7 @@ static const char *alc883_models[ALC883_MODEL_LAST] = { | |||
5596 | [ALC883_ACER] = "acer", | 5856 | [ALC883_ACER] = "acer", |
5597 | [ALC883_MEDION] = "medion", | 5857 | [ALC883_MEDION] = "medion", |
5598 | [ALC883_LAPTOP_EAPD] = "laptop-eapd", | 5858 | [ALC883_LAPTOP_EAPD] = "laptop-eapd", |
5859 | [ALC883_LENOVO_101E_2ch] = "lenovo-101e", | ||
5599 | [ALC883_AUTO] = "auto", | 5860 | [ALC883_AUTO] = "auto", |
5600 | }; | 5861 | }; |
5601 | 5862 | ||
@@ -5621,6 +5882,7 @@ static struct snd_pci_quirk alc883_cfg_tbl[] = { | |||
5621 | SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), | 5882 | SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_MEDION), |
5622 | SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), | 5883 | SND_PCI_QUIRK(0x1071, 0x8258, "Evesham Voyaeger", ALC883_LAPTOP_EAPD), |
5623 | SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), | 5884 | SND_PCI_QUIRK(0x8086, 0xd601, "D102GGC", ALC883_3ST_6ch), |
5885 | SND_PCI_QUIRK(0x17aa, 0x101e, "lenovo 101e", ALC883_LENOVO_101E_2ch), | ||
5624 | {} | 5886 | {} |
5625 | }; | 5887 | }; |
5626 | 5888 | ||
@@ -5761,6 +6023,19 @@ static struct alc_config_preset alc883_presets[] = { | |||
5761 | .channel_mode = alc883_3ST_2ch_modes, | 6023 | .channel_mode = alc883_3ST_2ch_modes, |
5762 | .input_mux = &alc883_capture_source, | 6024 | .input_mux = &alc883_capture_source, |
5763 | }, | 6025 | }, |
6026 | [ALC883_LENOVO_101E_2ch] = { | ||
6027 | .mixers = { alc883_lenovo_101e_2ch_mixer}, | ||
6028 | .init_verbs = { alc883_init_verbs, alc883_lenovo_101e_verbs}, | ||
6029 | .num_dacs = ARRAY_SIZE(alc883_dac_nids), | ||
6030 | .dac_nids = alc883_dac_nids, | ||
6031 | .num_adc_nids = ARRAY_SIZE(alc883_adc_nids), | ||
6032 | .adc_nids = alc883_adc_nids, | ||
6033 | .num_channel_mode = ARRAY_SIZE(alc883_3ST_2ch_modes), | ||
6034 | .channel_mode = alc883_3ST_2ch_modes, | ||
6035 | .input_mux = &alc883_lenovo_101e_capture_source, | ||
6036 | .unsol_event = alc883_lenovo_101e_unsol_event, | ||
6037 | .init_hook = alc883_lenovo_101e_all_automute, | ||
6038 | }, | ||
5764 | }; | 6039 | }; |
5765 | 6040 | ||
5766 | 6041 | ||
@@ -5793,6 +6068,7 @@ static void alc883_auto_init_multi_out(struct hda_codec *codec) | |||
5793 | struct alc_spec *spec = codec->spec; | 6068 | struct alc_spec *spec = codec->spec; |
5794 | int i; | 6069 | int i; |
5795 | 6070 | ||
6071 | alc_subsystem_id(codec, 0x15, 0x1b, 0x14); | ||
5796 | for (i = 0; i <= HDA_SIDE; i++) { | 6072 | for (i = 0; i <= HDA_SIDE; i++) { |
5797 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | 6073 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; |
5798 | if (nid) | 6074 | if (nid) |
@@ -5845,8 +6121,8 @@ static int alc883_parse_auto_config(struct hda_codec *codec) | |||
5845 | else if (err > 0) | 6121 | else if (err > 0) |
5846 | /* hack - override the init verbs */ | 6122 | /* hack - override the init verbs */ |
5847 | spec->init_verbs[0] = alc883_auto_init_verbs; | 6123 | spec->init_verbs[0] = alc883_auto_init_verbs; |
5848 | spec->mixers[spec->num_mixers] = alc883_capture_mixer; | 6124 | spec->mixers[spec->num_mixers] = alc883_capture_mixer; |
5849 | spec->num_mixers++; | 6125 | spec->num_mixers++; |
5850 | return err; | 6126 | return err; |
5851 | } | 6127 | } |
5852 | 6128 | ||
@@ -7533,9 +7809,6 @@ static void alc861_toshiba_automute(struct hda_codec *codec) | |||
7533 | static void alc861_toshiba_unsol_event(struct hda_codec *codec, | 7809 | static void alc861_toshiba_unsol_event(struct hda_codec *codec, |
7534 | unsigned int res) | 7810 | unsigned int res) |
7535 | { | 7811 | { |
7536 | /* Looks like the unsol event is incompatible with the standard | ||
7537 | * definition. 6bit tag is placed at 26 bit! | ||
7538 | */ | ||
7539 | if ((res >> 26) == ALC880_HP_EVENT) | 7812 | if ((res >> 26) == ALC880_HP_EVENT) |
7540 | alc861_toshiba_automute(codec); | 7813 | alc861_toshiba_automute(codec); |
7541 | } | 7814 | } |
@@ -7729,6 +8002,7 @@ static void alc861_auto_init_multi_out(struct hda_codec *codec) | |||
7729 | struct alc_spec *spec = codec->spec; | 8002 | struct alc_spec *spec = codec->spec; |
7730 | int i; | 8003 | int i; |
7731 | 8004 | ||
8005 | alc_subsystem_id(codec, 0x0e, 0x0f, 0x0b); | ||
7732 | for (i = 0; i < spec->autocfg.line_outs; i++) { | 8006 | for (i = 0; i < spec->autocfg.line_outs; i++) { |
7733 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | 8007 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; |
7734 | if (nid) | 8008 | if (nid) |
@@ -8423,6 +8697,7 @@ static void alc861vd_auto_init_multi_out(struct hda_codec *codec) | |||
8423 | struct alc_spec *spec = codec->spec; | 8697 | struct alc_spec *spec = codec->spec; |
8424 | int i; | 8698 | int i; |
8425 | 8699 | ||
8700 | alc_subsystem_id(codec, 0x15, 0x1b, 0x14); | ||
8426 | for (i = 0; i <= HDA_SIDE; i++) { | 8701 | for (i = 0; i <= HDA_SIDE; i++) { |
8427 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | 8702 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; |
8428 | if (nid) | 8703 | if (nid) |
@@ -8689,16 +8964,857 @@ static int patch_alc861vd(struct hda_codec *codec) | |||
8689 | } | 8964 | } |
8690 | 8965 | ||
8691 | /* | 8966 | /* |
8967 | * ALC662 support | ||
8968 | * | ||
8969 | * ALC662 is almost identical with ALC880 but has cleaner and more flexible | ||
8970 | * configuration. Each pin widget can choose any input DACs and a mixer. | ||
8971 | * Each ADC is connected from a mixer of all inputs. This makes possible | ||
8972 | * 6-channel independent captures. | ||
8973 | * | ||
8974 | * In addition, an independent DAC for the multi-playback (not used in this | ||
8975 | * driver yet). | ||
8976 | */ | ||
8977 | #define ALC662_DIGOUT_NID 0x06 | ||
8978 | #define ALC662_DIGIN_NID 0x0a | ||
8979 | |||
8980 | static hda_nid_t alc662_dac_nids[4] = { | ||
8981 | /* front, rear, clfe, rear_surr */ | ||
8982 | 0x02, 0x03, 0x04 | ||
8983 | }; | ||
8984 | |||
8985 | static hda_nid_t alc662_adc_nids[1] = { | ||
8986 | /* ADC1-2 */ | ||
8987 | 0x09, | ||
8988 | }; | ||
8989 | /* input MUX */ | ||
8990 | /* FIXME: should be a matrix-type input source selection */ | ||
8991 | |||
8992 | static struct hda_input_mux alc662_capture_source = { | ||
8993 | .num_items = 4, | ||
8994 | .items = { | ||
8995 | { "Mic", 0x0 }, | ||
8996 | { "Front Mic", 0x1 }, | ||
8997 | { "Line", 0x2 }, | ||
8998 | { "CD", 0x4 }, | ||
8999 | }, | ||
9000 | }; | ||
9001 | |||
9002 | static struct hda_input_mux alc662_lenovo_101e_capture_source = { | ||
9003 | .num_items = 2, | ||
9004 | .items = { | ||
9005 | { "Mic", 0x1 }, | ||
9006 | { "Line", 0x2 }, | ||
9007 | }, | ||
9008 | }; | ||
9009 | #define alc662_mux_enum_info alc_mux_enum_info | ||
9010 | #define alc662_mux_enum_get alc_mux_enum_get | ||
9011 | |||
9012 | static int alc662_mux_enum_put(struct snd_kcontrol *kcontrol, | ||
9013 | struct snd_ctl_elem_value *ucontrol) | ||
9014 | { | ||
9015 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
9016 | struct alc_spec *spec = codec->spec; | ||
9017 | const struct hda_input_mux *imux = spec->input_mux; | ||
9018 | unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); | ||
9019 | static hda_nid_t capture_mixers[3] = { 0x24, 0x23, 0x22 }; | ||
9020 | hda_nid_t nid = capture_mixers[adc_idx]; | ||
9021 | unsigned int *cur_val = &spec->cur_mux[adc_idx]; | ||
9022 | unsigned int i, idx; | ||
9023 | |||
9024 | idx = ucontrol->value.enumerated.item[0]; | ||
9025 | if (idx >= imux->num_items) | ||
9026 | idx = imux->num_items - 1; | ||
9027 | if (*cur_val == idx && ! codec->in_resume) | ||
9028 | return 0; | ||
9029 | for (i = 0; i < imux->num_items; i++) { | ||
9030 | unsigned int v = (i == idx) ? 0x7000 : 0x7080; | ||
9031 | snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, | ||
9032 | v | (imux->items[i].index << 8)); | ||
9033 | } | ||
9034 | *cur_val = idx; | ||
9035 | return 1; | ||
9036 | } | ||
9037 | /* | ||
9038 | * 2ch mode | ||
9039 | */ | ||
9040 | static struct hda_channel_mode alc662_3ST_2ch_modes[1] = { | ||
9041 | { 2, NULL } | ||
9042 | }; | ||
9043 | |||
9044 | /* | ||
9045 | * 2ch mode | ||
9046 | */ | ||
9047 | static struct hda_verb alc662_3ST_ch2_init[] = { | ||
9048 | { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 }, | ||
9049 | { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, | ||
9050 | { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN }, | ||
9051 | { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE }, | ||
9052 | { } /* end */ | ||
9053 | }; | ||
9054 | |||
9055 | /* | ||
9056 | * 6ch mode | ||
9057 | */ | ||
9058 | static struct hda_verb alc662_3ST_ch6_init[] = { | ||
9059 | { 0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9060 | { 0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, | ||
9061 | { 0x18, AC_VERB_SET_CONNECT_SEL, 0x02 }, | ||
9062 | { 0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9063 | { 0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE }, | ||
9064 | { 0x1a, AC_VERB_SET_CONNECT_SEL, 0x01 }, | ||
9065 | { } /* end */ | ||
9066 | }; | ||
9067 | |||
9068 | static struct hda_channel_mode alc662_3ST_6ch_modes[2] = { | ||
9069 | { 2, alc662_3ST_ch2_init }, | ||
9070 | { 6, alc662_3ST_ch6_init }, | ||
9071 | }; | ||
9072 | |||
9073 | /* | ||
9074 | * 2ch mode | ||
9075 | */ | ||
9076 | static struct hda_verb alc662_sixstack_ch6_init[] = { | ||
9077 | { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, | ||
9078 | { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x00 }, | ||
9079 | { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9080 | { } /* end */ | ||
9081 | }; | ||
9082 | |||
9083 | /* | ||
9084 | * 6ch mode | ||
9085 | */ | ||
9086 | static struct hda_verb alc662_sixstack_ch8_init[] = { | ||
9087 | { 0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9088 | { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9089 | { 0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT }, | ||
9090 | { } /* end */ | ||
9091 | }; | ||
9092 | |||
9093 | static struct hda_channel_mode alc662_5stack_modes[2] = { | ||
9094 | { 2, alc662_sixstack_ch6_init }, | ||
9095 | { 6, alc662_sixstack_ch8_init }, | ||
9096 | }; | ||
9097 | |||
9098 | /* Pin assignment: Front=0x14, Rear=0x15, CLFE=0x16, Side=0x17 | ||
9099 | * Mic=0x18, Front Mic=0x19, Line-In=0x1a, HP=0x1b | ||
9100 | */ | ||
9101 | |||
9102 | static struct snd_kcontrol_new alc662_base_mixer[] = { | ||
9103 | /* output mixer control */ | ||
9104 | HDA_CODEC_VOLUME("Front Playback Volume", 0x2, 0x0, HDA_OUTPUT), | ||
9105 | HDA_CODEC_MUTE("Front Playback Switch", 0x02, 0x0, HDA_OUTPUT), | ||
9106 | HDA_CODEC_VOLUME("Surround Playback Volume", 0x3, 0x0, HDA_OUTPUT), | ||
9107 | HDA_CODEC_MUTE("Surround Playback Switch", 0x03, 0x0, HDA_OUTPUT), | ||
9108 | HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), | ||
9109 | HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), | ||
9110 | HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), | ||
9111 | HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), | ||
9112 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
9113 | |||
9114 | /*Input mixer control */ | ||
9115 | HDA_CODEC_VOLUME("CD Playback Volume", 0xb, 0x4, HDA_INPUT), | ||
9116 | HDA_CODEC_MUTE("CD Playback Switch", 0xb, 0x4, HDA_INPUT), | ||
9117 | HDA_CODEC_VOLUME("Line Playback Volume", 0xb, 0x02, HDA_INPUT), | ||
9118 | HDA_CODEC_MUTE("Line Playback Switch", 0xb, 0x02, HDA_INPUT), | ||
9119 | HDA_CODEC_VOLUME("Mic Playback Volume", 0xb, 0x0, HDA_INPUT), | ||
9120 | HDA_CODEC_MUTE("Mic Playback Switch", 0xb, 0x0, HDA_INPUT), | ||
9121 | HDA_CODEC_VOLUME("Front Mic Playback Volume", 0xb, 0x01, HDA_INPUT), | ||
9122 | HDA_CODEC_MUTE("Front Mic Playback Switch", 0xb, 0x01, HDA_INPUT), | ||
9123 | |||
9124 | /* Capture mixer control */ | ||
9125 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
9126 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
9127 | { | ||
9128 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9129 | .name = "Capture Source", | ||
9130 | .count = 1, | ||
9131 | .info = alc_mux_enum_info, | ||
9132 | .get = alc_mux_enum_get, | ||
9133 | .put = alc_mux_enum_put, | ||
9134 | }, | ||
9135 | { } /* end */ | ||
9136 | }; | ||
9137 | |||
9138 | static struct snd_kcontrol_new alc662_3ST_2ch_mixer[] = { | ||
9139 | HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), | ||
9140 | HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), | ||
9141 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
9142 | HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), | ||
9143 | HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), | ||
9144 | HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), | ||
9145 | HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), | ||
9146 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), | ||
9147 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), | ||
9148 | HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
9149 | HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
9150 | HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), | ||
9151 | HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), | ||
9152 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
9153 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
9154 | { | ||
9155 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9156 | /* .name = "Capture Source", */ | ||
9157 | .name = "Input Source", | ||
9158 | .count = 1, | ||
9159 | .info = alc662_mux_enum_info, | ||
9160 | .get = alc662_mux_enum_get, | ||
9161 | .put = alc662_mux_enum_put, | ||
9162 | }, | ||
9163 | { } /* end */ | ||
9164 | }; | ||
9165 | |||
9166 | static struct snd_kcontrol_new alc662_3ST_6ch_mixer[] = { | ||
9167 | HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), | ||
9168 | HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), | ||
9169 | HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT), | ||
9170 | HDA_BIND_MUTE("Surround Playback Switch", 0x03, 2, HDA_INPUT), | ||
9171 | HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x04, 1, 0x0, HDA_OUTPUT), | ||
9172 | HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x04, 2, 0x0, HDA_OUTPUT), | ||
9173 | HDA_BIND_MUTE_MONO("Center Playback Switch", 0x04, 1, 2, HDA_INPUT), | ||
9174 | HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x04, 2, 2, HDA_INPUT), | ||
9175 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
9176 | HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), | ||
9177 | HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), | ||
9178 | HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), | ||
9179 | HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), | ||
9180 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), | ||
9181 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), | ||
9182 | HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
9183 | HDA_CODEC_MUTE("Front Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
9184 | HDA_CODEC_VOLUME("PC Speaker Playback Volume", 0x0b, 0x05, HDA_INPUT), | ||
9185 | HDA_CODEC_MUTE("PC Speaker Playback Switch", 0x0b, 0x05, HDA_INPUT), | ||
9186 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
9187 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
9188 | { | ||
9189 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9190 | /* .name = "Capture Source", */ | ||
9191 | .name = "Input Source", | ||
9192 | .count = 1, | ||
9193 | .info = alc662_mux_enum_info, | ||
9194 | .get = alc662_mux_enum_get, | ||
9195 | .put = alc662_mux_enum_put, | ||
9196 | }, | ||
9197 | { } /* end */ | ||
9198 | }; | ||
9199 | |||
9200 | static struct snd_kcontrol_new alc662_lenovo_101e_mixer[] = { | ||
9201 | HDA_CODEC_VOLUME("Front Playback Volume", 0x02, 0x0, HDA_OUTPUT), | ||
9202 | HDA_BIND_MUTE("Front Playback Switch", 0x02, 2, HDA_INPUT), | ||
9203 | HDA_CODEC_VOLUME("iSpeaker Playback Volume", 0x03, 0x0, HDA_OUTPUT), | ||
9204 | HDA_BIND_MUTE("iSpeaker Playback Switch", 0x03, 2, HDA_INPUT), | ||
9205 | HDA_CODEC_MUTE("Headphone Playback Switch", 0x1b, 0x0, HDA_OUTPUT), | ||
9206 | HDA_CODEC_VOLUME("Line Playback Volume", 0x0b, 0x02, HDA_INPUT), | ||
9207 | HDA_CODEC_MUTE("Line Playback Switch", 0x0b, 0x02, HDA_INPUT), | ||
9208 | HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x1, HDA_INPUT), | ||
9209 | HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x1, HDA_INPUT), | ||
9210 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
9211 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
9212 | { | ||
9213 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9214 | /* .name = "Capture Source", */ | ||
9215 | .name = "Input Source", | ||
9216 | .count = 1, | ||
9217 | .info = alc662_mux_enum_info, | ||
9218 | .get = alc662_mux_enum_get, | ||
9219 | .put = alc662_mux_enum_put, | ||
9220 | }, | ||
9221 | { } /* end */ | ||
9222 | }; | ||
9223 | |||
9224 | static struct snd_kcontrol_new alc662_chmode_mixer[] = { | ||
9225 | { | ||
9226 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9227 | .name = "Channel Mode", | ||
9228 | .info = alc_ch_mode_info, | ||
9229 | .get = alc_ch_mode_get, | ||
9230 | .put = alc_ch_mode_put, | ||
9231 | }, | ||
9232 | { } /* end */ | ||
9233 | }; | ||
9234 | |||
9235 | static struct hda_verb alc662_init_verbs[] = { | ||
9236 | /* ADC: mute amp left and right */ | ||
9237 | {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
9238 | {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
9239 | /* Front mixer: unmute input/output amp left and right (volume = 0) */ | ||
9240 | |||
9241 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9242 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9243 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | ||
9244 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, | ||
9245 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
9246 | |||
9247 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9248 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9249 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9250 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9251 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9252 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9253 | |||
9254 | /* Front Pin: output 0 (0x0c) */ | ||
9255 | {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
9256 | {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
9257 | |||
9258 | /* Rear Pin: output 1 (0x0d) */ | ||
9259 | {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
9260 | {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
9261 | |||
9262 | /* CLFE Pin: output 2 (0x0e) */ | ||
9263 | {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, | ||
9264 | {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
9265 | |||
9266 | /* Mic (rear) pin: input vref at 80% */ | ||
9267 | {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
9268 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
9269 | /* Front Mic pin: input vref at 80% */ | ||
9270 | {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, | ||
9271 | {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
9272 | /* Line In pin: input */ | ||
9273 | {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
9274 | {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, | ||
9275 | /* Line-2 In: Headphone output (output 0 - 0x0c) */ | ||
9276 | {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, | ||
9277 | {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, | ||
9278 | {0x1b, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
9279 | /* CD pin widget for input */ | ||
9280 | {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, | ||
9281 | |||
9282 | /* FIXME: use matrix-type input source selection */ | ||
9283 | /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ | ||
9284 | /* Input mixer */ | ||
9285 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9286 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9287 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | ||
9288 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
9289 | { } | ||
9290 | }; | ||
9291 | |||
9292 | static struct hda_verb alc662_sue_init_verbs[] = { | ||
9293 | {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_FRONT_EVENT}, | ||
9294 | {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|ALC880_HP_EVENT}, | ||
9295 | {} | ||
9296 | }; | ||
9297 | |||
9298 | /* | ||
9299 | * generic initialization of ADC, input mixers and output mixers | ||
9300 | */ | ||
9301 | static struct hda_verb alc662_auto_init_verbs[] = { | ||
9302 | /* | ||
9303 | * Unmute ADC and set the default input to mic-in | ||
9304 | */ | ||
9305 | {0x09, AC_VERB_SET_CONNECT_SEL, 0x00}, | ||
9306 | {0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9307 | |||
9308 | /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback | ||
9309 | * mixer widget | ||
9310 | * Note: PASD motherboards uses the Line In 2 as the input for front | ||
9311 | * panel mic (mic 2) | ||
9312 | */ | ||
9313 | /* Amp Indices: Mic1 = 0, Mic2 = 1, Line1 = 2, Line2 = 3, CD = 4 */ | ||
9314 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9315 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9316 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | ||
9317 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, | ||
9318 | {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
9319 | |||
9320 | /* | ||
9321 | * Set up output mixers (0x0c - 0x0f) | ||
9322 | */ | ||
9323 | /* set vol=0 to output mixers */ | ||
9324 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
9325 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
9326 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, | ||
9327 | |||
9328 | /* set up input amps for analog loopback */ | ||
9329 | /* Amp Indices: DAC = 0, mixer = 1 */ | ||
9330 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9331 | {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9332 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9333 | {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9334 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9335 | {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9336 | |||
9337 | |||
9338 | /* FIXME: use matrix-type input source selection */ | ||
9339 | /* Mixer elements: 0x18, 19, 1a, 1b, 1c, 1d, 14, 15, 16, 17, 0b */ | ||
9340 | /* Input mixer */ | ||
9341 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
9342 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
9343 | {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | ||
9344 | /*{0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},*/ | ||
9345 | {0x23, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | ||
9346 | |||
9347 | { } | ||
9348 | }; | ||
9349 | |||
9350 | /* capture mixer elements */ | ||
9351 | static struct snd_kcontrol_new alc662_capture_mixer[] = { | ||
9352 | HDA_CODEC_VOLUME("Capture Volume", 0x09, 0x0, HDA_INPUT), | ||
9353 | HDA_CODEC_MUTE("Capture Switch", 0x09, 0x0, HDA_INPUT), | ||
9354 | { | ||
9355 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
9356 | /* The multiple "Capture Source" controls confuse alsamixer | ||
9357 | * So call somewhat different.. | ||
9358 | * FIXME: the controls appear in the "playback" view! | ||
9359 | */ | ||
9360 | /* .name = "Capture Source", */ | ||
9361 | .name = "Input Source", | ||
9362 | .count = 1, | ||
9363 | .info = alc882_mux_enum_info, | ||
9364 | .get = alc882_mux_enum_get, | ||
9365 | .put = alc882_mux_enum_put, | ||
9366 | }, | ||
9367 | { } /* end */ | ||
9368 | }; | ||
9369 | |||
9370 | static void alc662_lenovo_101e_ispeaker_automute(struct hda_codec *codec) | ||
9371 | { | ||
9372 | unsigned int present; | ||
9373 | |||
9374 | present = snd_hda_codec_read(codec, 0x14, 0, | ||
9375 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
9376 | |||
9377 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
9378 | 0x80, present ? 0x80 : 0); | ||
9379 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
9380 | 0x80, present ? 0x80 : 0); | ||
9381 | } | ||
9382 | |||
9383 | static void alc662_lenovo_101e_all_automute(struct hda_codec *codec) | ||
9384 | { | ||
9385 | unsigned int present; | ||
9386 | |||
9387 | present = snd_hda_codec_read(codec, 0x1b, 0, | ||
9388 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
9389 | |||
9390 | snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, | ||
9391 | 0x80, present ? 0x80 : 0); | ||
9392 | snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, | ||
9393 | 0x80, present ? 0x80 : 0); | ||
9394 | snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, | ||
9395 | 0x80, present ? 0x80 : 0); | ||
9396 | snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, | ||
9397 | 0x80, present ? 0x80 : 0); | ||
9398 | } | ||
9399 | |||
9400 | static void alc662_lenovo_101e_unsol_event(struct hda_codec *codec, | ||
9401 | unsigned int res) | ||
9402 | { | ||
9403 | if ((res >> 26) == ALC880_HP_EVENT) | ||
9404 | alc662_lenovo_101e_all_automute(codec); | ||
9405 | if ((res >> 26) == ALC880_FRONT_EVENT) | ||
9406 | alc662_lenovo_101e_ispeaker_automute(codec); | ||
9407 | } | ||
9408 | |||
9409 | |||
9410 | /* pcm configuration: identiacal with ALC880 */ | ||
9411 | #define alc662_pcm_analog_playback alc880_pcm_analog_playback | ||
9412 | #define alc662_pcm_analog_capture alc880_pcm_analog_capture | ||
9413 | #define alc662_pcm_digital_playback alc880_pcm_digital_playback | ||
9414 | #define alc662_pcm_digital_capture alc880_pcm_digital_capture | ||
9415 | |||
9416 | /* | ||
9417 | * configuration and preset | ||
9418 | */ | ||
9419 | static const char *alc662_models[ALC662_MODEL_LAST] = { | ||
9420 | [ALC662_3ST_2ch_DIG] = "3stack-dig", | ||
9421 | [ALC662_3ST_6ch_DIG] = "3stack-6ch-dig", | ||
9422 | [ALC662_3ST_6ch] = "3stack-6ch", | ||
9423 | [ALC662_5ST_DIG] = "6stack-dig", | ||
9424 | [ALC662_LENOVO_101E] = "lenovo-101e", | ||
9425 | [ALC662_AUTO] = "auto", | ||
9426 | }; | ||
9427 | |||
9428 | static struct snd_pci_quirk alc662_cfg_tbl[] = { | ||
9429 | SND_PCI_QUIRK(0x17aa, 0x101e, "Lenovo", ALC662_LENOVO_101E), | ||
9430 | {} | ||
9431 | }; | ||
9432 | |||
9433 | static struct alc_config_preset alc662_presets[] = { | ||
9434 | [ALC662_3ST_2ch_DIG] = { | ||
9435 | .mixers = { alc662_3ST_2ch_mixer }, | ||
9436 | .init_verbs = { alc662_init_verbs }, | ||
9437 | .num_dacs = ARRAY_SIZE(alc662_dac_nids), | ||
9438 | .dac_nids = alc662_dac_nids, | ||
9439 | .dig_out_nid = ALC662_DIGOUT_NID, | ||
9440 | .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), | ||
9441 | .adc_nids = alc662_adc_nids, | ||
9442 | .dig_in_nid = ALC662_DIGIN_NID, | ||
9443 | .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), | ||
9444 | .channel_mode = alc662_3ST_2ch_modes, | ||
9445 | .input_mux = &alc662_capture_source, | ||
9446 | }, | ||
9447 | [ALC662_3ST_6ch_DIG] = { | ||
9448 | .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, | ||
9449 | .init_verbs = { alc662_init_verbs }, | ||
9450 | .num_dacs = ARRAY_SIZE(alc662_dac_nids), | ||
9451 | .dac_nids = alc662_dac_nids, | ||
9452 | .dig_out_nid = ALC662_DIGOUT_NID, | ||
9453 | .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), | ||
9454 | .adc_nids = alc662_adc_nids, | ||
9455 | .dig_in_nid = ALC662_DIGIN_NID, | ||
9456 | .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), | ||
9457 | .channel_mode = alc662_3ST_6ch_modes, | ||
9458 | .need_dac_fix = 1, | ||
9459 | .input_mux = &alc662_capture_source, | ||
9460 | }, | ||
9461 | [ALC662_3ST_6ch] = { | ||
9462 | .mixers = { alc662_3ST_6ch_mixer, alc662_chmode_mixer }, | ||
9463 | .init_verbs = { alc662_init_verbs }, | ||
9464 | .num_dacs = ARRAY_SIZE(alc662_dac_nids), | ||
9465 | .dac_nids = alc662_dac_nids, | ||
9466 | .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), | ||
9467 | .adc_nids = alc662_adc_nids, | ||
9468 | .num_channel_mode = ARRAY_SIZE(alc662_3ST_6ch_modes), | ||
9469 | .channel_mode = alc662_3ST_6ch_modes, | ||
9470 | .need_dac_fix = 1, | ||
9471 | .input_mux = &alc662_capture_source, | ||
9472 | }, | ||
9473 | [ALC662_5ST_DIG] = { | ||
9474 | .mixers = { alc662_base_mixer, alc662_chmode_mixer }, | ||
9475 | .init_verbs = { alc662_init_verbs }, | ||
9476 | .num_dacs = ARRAY_SIZE(alc662_dac_nids), | ||
9477 | .dac_nids = alc662_dac_nids, | ||
9478 | .dig_out_nid = ALC662_DIGOUT_NID, | ||
9479 | .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), | ||
9480 | .adc_nids = alc662_adc_nids, | ||
9481 | .dig_in_nid = ALC662_DIGIN_NID, | ||
9482 | .num_channel_mode = ARRAY_SIZE(alc662_5stack_modes), | ||
9483 | .channel_mode = alc662_5stack_modes, | ||
9484 | .input_mux = &alc662_capture_source, | ||
9485 | }, | ||
9486 | [ALC662_LENOVO_101E] = { | ||
9487 | .mixers = { alc662_lenovo_101e_mixer }, | ||
9488 | .init_verbs = { alc662_init_verbs, alc662_sue_init_verbs }, | ||
9489 | .num_dacs = ARRAY_SIZE(alc662_dac_nids), | ||
9490 | .dac_nids = alc662_dac_nids, | ||
9491 | .num_adc_nids = ARRAY_SIZE(alc662_adc_nids), | ||
9492 | .adc_nids = alc662_adc_nids, | ||
9493 | .num_channel_mode = ARRAY_SIZE(alc662_3ST_2ch_modes), | ||
9494 | .channel_mode = alc662_3ST_2ch_modes, | ||
9495 | .input_mux = &alc662_lenovo_101e_capture_source, | ||
9496 | .unsol_event = alc662_lenovo_101e_unsol_event, | ||
9497 | .init_hook = alc662_lenovo_101e_all_automute, | ||
9498 | }, | ||
9499 | |||
9500 | }; | ||
9501 | |||
9502 | |||
9503 | /* | ||
9504 | * BIOS auto configuration | ||
9505 | */ | ||
9506 | |||
9507 | /* add playback controls from the parsed DAC table */ | ||
9508 | static int alc662_auto_create_multi_out_ctls(struct alc_spec *spec, | ||
9509 | const struct auto_pin_cfg *cfg) | ||
9510 | { | ||
9511 | char name[32]; | ||
9512 | static const char *chname[4] = { | ||
9513 | "Front", "Surround", NULL /*CLFE*/, "Side" | ||
9514 | }; | ||
9515 | hda_nid_t nid; | ||
9516 | int i, err; | ||
9517 | |||
9518 | for (i = 0; i < cfg->line_outs; i++) { | ||
9519 | if (!spec->multiout.dac_nids[i]) | ||
9520 | continue; | ||
9521 | nid = alc880_idx_to_dac(i); | ||
9522 | if (i == 2) { | ||
9523 | /* Center/LFE */ | ||
9524 | err = add_control(spec, ALC_CTL_WIDGET_VOL, | ||
9525 | "Center Playback Volume", | ||
9526 | HDA_COMPOSE_AMP_VAL(nid, 1, 0, HDA_OUTPUT)); | ||
9527 | if (err < 0) | ||
9528 | return err; | ||
9529 | err = add_control(spec, ALC_CTL_WIDGET_VOL, | ||
9530 | "LFE Playback Volume", | ||
9531 | HDA_COMPOSE_AMP_VAL(nid, 2, 0, HDA_OUTPUT)); | ||
9532 | if (err < 0) | ||
9533 | return err; | ||
9534 | err = add_control(spec, ALC_CTL_BIND_MUTE, | ||
9535 | "Center Playback Switch", | ||
9536 | HDA_COMPOSE_AMP_VAL(nid, 1, 2, HDA_INPUT)); | ||
9537 | if (err < 0) | ||
9538 | return err; | ||
9539 | err = add_control(spec, ALC_CTL_BIND_MUTE, | ||
9540 | "LFE Playback Switch", | ||
9541 | HDA_COMPOSE_AMP_VAL(nid, 2, 2, HDA_INPUT)); | ||
9542 | if (err < 0) | ||
9543 | return err; | ||
9544 | } else { | ||
9545 | sprintf(name, "%s Playback Volume", chname[i]); | ||
9546 | err = add_control(spec, ALC_CTL_WIDGET_VOL, name, | ||
9547 | HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); | ||
9548 | if (err < 0) | ||
9549 | return err; | ||
9550 | sprintf(name, "%s Playback Switch", chname[i]); | ||
9551 | err = add_control(spec, ALC_CTL_BIND_MUTE, name, | ||
9552 | HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); | ||
9553 | if (err < 0) | ||
9554 | return err; | ||
9555 | } | ||
9556 | } | ||
9557 | return 0; | ||
9558 | } | ||
9559 | |||
9560 | /* add playback controls for speaker and HP outputs */ | ||
9561 | static int alc662_auto_create_extra_out(struct alc_spec *spec, hda_nid_t pin, | ||
9562 | const char *pfx) | ||
9563 | { | ||
9564 | hda_nid_t nid; | ||
9565 | int err; | ||
9566 | char name[32]; | ||
9567 | |||
9568 | if (!pin) | ||
9569 | return 0; | ||
9570 | |||
9571 | if (alc880_is_fixed_pin(pin)) { | ||
9572 | nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); | ||
9573 | /* printk("DAC nid=%x\n",nid); */ | ||
9574 | /* specify the DAC as the extra output */ | ||
9575 | if (!spec->multiout.hp_nid) | ||
9576 | spec->multiout.hp_nid = nid; | ||
9577 | else | ||
9578 | spec->multiout.extra_out_nid[0] = nid; | ||
9579 | /* control HP volume/switch on the output mixer amp */ | ||
9580 | nid = alc880_idx_to_dac(alc880_fixed_pin_idx(pin)); | ||
9581 | sprintf(name, "%s Playback Volume", pfx); | ||
9582 | err = add_control(spec, ALC_CTL_WIDGET_VOL, name, | ||
9583 | HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_OUTPUT)); | ||
9584 | if (err < 0) | ||
9585 | return err; | ||
9586 | sprintf(name, "%s Playback Switch", pfx); | ||
9587 | err = add_control(spec, ALC_CTL_BIND_MUTE, name, | ||
9588 | HDA_COMPOSE_AMP_VAL(nid, 3, 2, HDA_INPUT)); | ||
9589 | if (err < 0) | ||
9590 | return err; | ||
9591 | } else if (alc880_is_multi_pin(pin)) { | ||
9592 | /* set manual connection */ | ||
9593 | /* we have only a switch on HP-out PIN */ | ||
9594 | sprintf(name, "%s Playback Switch", pfx); | ||
9595 | err = add_control(spec, ALC_CTL_WIDGET_MUTE, name, | ||
9596 | HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); | ||
9597 | if (err < 0) | ||
9598 | return err; | ||
9599 | } | ||
9600 | return 0; | ||
9601 | } | ||
9602 | |||
9603 | /* create playback/capture controls for input pins */ | ||
9604 | static int alc662_auto_create_analog_input_ctls(struct alc_spec *spec, | ||
9605 | const struct auto_pin_cfg *cfg) | ||
9606 | { | ||
9607 | struct hda_input_mux *imux = &spec->private_imux; | ||
9608 | int i, err, idx; | ||
9609 | |||
9610 | for (i = 0; i < AUTO_PIN_LAST; i++) { | ||
9611 | if (alc880_is_input_pin(cfg->input_pins[i])) { | ||
9612 | idx = alc880_input_pin_idx(cfg->input_pins[i]); | ||
9613 | err = new_analog_input(spec, cfg->input_pins[i], | ||
9614 | auto_pin_cfg_labels[i], | ||
9615 | idx, 0x0b); | ||
9616 | if (err < 0) | ||
9617 | return err; | ||
9618 | imux->items[imux->num_items].label = | ||
9619 | auto_pin_cfg_labels[i]; | ||
9620 | imux->items[imux->num_items].index = | ||
9621 | alc880_input_pin_idx(cfg->input_pins[i]); | ||
9622 | imux->num_items++; | ||
9623 | } | ||
9624 | } | ||
9625 | return 0; | ||
9626 | } | ||
9627 | |||
9628 | static void alc662_auto_set_output_and_unmute(struct hda_codec *codec, | ||
9629 | hda_nid_t nid, int pin_type, | ||
9630 | int dac_idx) | ||
9631 | { | ||
9632 | /* set as output */ | ||
9633 | snd_hda_codec_write(codec, nid, 0, | ||
9634 | AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type); | ||
9635 | snd_hda_codec_write(codec, nid, 0, | ||
9636 | AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE); | ||
9637 | /* need the manual connection? */ | ||
9638 | if (alc880_is_multi_pin(nid)) { | ||
9639 | struct alc_spec *spec = codec->spec; | ||
9640 | int idx = alc880_multi_pin_idx(nid); | ||
9641 | snd_hda_codec_write(codec, alc880_idx_to_selector(idx), 0, | ||
9642 | AC_VERB_SET_CONNECT_SEL, | ||
9643 | alc880_dac_to_idx(spec->multiout.dac_nids[dac_idx])); | ||
9644 | } | ||
9645 | } | ||
9646 | |||
9647 | static void alc662_auto_init_multi_out(struct hda_codec *codec) | ||
9648 | { | ||
9649 | struct alc_spec *spec = codec->spec; | ||
9650 | int i; | ||
9651 | |||
9652 | for (i = 0; i <= HDA_SIDE; i++) { | ||
9653 | hda_nid_t nid = spec->autocfg.line_out_pins[i]; | ||
9654 | if (nid) | ||
9655 | alc662_auto_set_output_and_unmute(codec, nid, PIN_OUT, | ||
9656 | i); | ||
9657 | } | ||
9658 | } | ||
9659 | |||
9660 | static void alc662_auto_init_hp_out(struct hda_codec *codec) | ||
9661 | { | ||
9662 | struct alc_spec *spec = codec->spec; | ||
9663 | hda_nid_t pin; | ||
9664 | |||
9665 | pin = spec->autocfg.hp_pins[0]; | ||
9666 | if (pin) /* connect to front */ | ||
9667 | /* use dac 0 */ | ||
9668 | alc662_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); | ||
9669 | } | ||
9670 | |||
9671 | #define alc662_is_input_pin(nid) alc880_is_input_pin(nid) | ||
9672 | #define ALC662_PIN_CD_NID ALC880_PIN_CD_NID | ||
9673 | |||
9674 | static void alc662_auto_init_analog_input(struct hda_codec *codec) | ||
9675 | { | ||
9676 | struct alc_spec *spec = codec->spec; | ||
9677 | int i; | ||
9678 | |||
9679 | for (i = 0; i < AUTO_PIN_LAST; i++) { | ||
9680 | hda_nid_t nid = spec->autocfg.input_pins[i]; | ||
9681 | if (alc662_is_input_pin(nid)) { | ||
9682 | snd_hda_codec_write(codec, nid, 0, | ||
9683 | AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
9684 | (i <= AUTO_PIN_FRONT_MIC ? | ||
9685 | PIN_VREF80 : PIN_IN)); | ||
9686 | if (nid != ALC662_PIN_CD_NID) | ||
9687 | snd_hda_codec_write(codec, nid, 0, | ||
9688 | AC_VERB_SET_AMP_GAIN_MUTE, | ||
9689 | AMP_OUT_MUTE); | ||
9690 | } | ||
9691 | } | ||
9692 | } | ||
9693 | |||
9694 | static int alc662_parse_auto_config(struct hda_codec *codec) | ||
9695 | { | ||
9696 | struct alc_spec *spec = codec->spec; | ||
9697 | int err; | ||
9698 | static hda_nid_t alc662_ignore[] = { 0x1d, 0 }; | ||
9699 | |||
9700 | err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, | ||
9701 | alc662_ignore); | ||
9702 | if (err < 0) | ||
9703 | return err; | ||
9704 | if (!spec->autocfg.line_outs) | ||
9705 | return 0; /* can't find valid BIOS pin config */ | ||
9706 | |||
9707 | if ((err = alc880_auto_fill_dac_nids(spec, &spec->autocfg)) < 0 || | ||
9708 | (err = alc662_auto_create_multi_out_ctls(spec, &spec->autocfg)) < 0 || | ||
9709 | (err = alc662_auto_create_extra_out(spec, | ||
9710 | spec->autocfg.speaker_pins[0], | ||
9711 | "Speaker")) < 0 || | ||
9712 | (err = alc662_auto_create_extra_out(spec, spec->autocfg.hp_pins[0], | ||
9713 | "Headphone")) < 0 || | ||
9714 | (err = alc662_auto_create_analog_input_ctls(spec, &spec->autocfg)) < 0) | ||
9715 | return err; | ||
9716 | |||
9717 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; | ||
9718 | |||
9719 | if (spec->autocfg.dig_out_pin) | ||
9720 | spec->multiout.dig_out_nid = ALC880_DIGOUT_NID; | ||
9721 | |||
9722 | if (spec->kctl_alloc) | ||
9723 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | ||
9724 | |||
9725 | spec->num_mux_defs = 1; | ||
9726 | spec->input_mux = &spec->private_imux; | ||
9727 | |||
9728 | if (err < 0) | ||
9729 | return err; | ||
9730 | else if (err > 0) | ||
9731 | /* hack - override the init verbs */ | ||
9732 | spec->init_verbs[0] = alc662_auto_init_verbs; | ||
9733 | spec->mixers[spec->num_mixers] = alc662_capture_mixer; | ||
9734 | spec->num_mixers++; | ||
9735 | return err; | ||
9736 | } | ||
9737 | |||
9738 | /* additional initialization for auto-configuration model */ | ||
9739 | static void alc662_auto_init(struct hda_codec *codec) | ||
9740 | { | ||
9741 | alc662_auto_init_multi_out(codec); | ||
9742 | alc662_auto_init_hp_out(codec); | ||
9743 | alc662_auto_init_analog_input(codec); | ||
9744 | } | ||
9745 | |||
9746 | static int patch_alc662(struct hda_codec *codec) | ||
9747 | { | ||
9748 | struct alc_spec *spec; | ||
9749 | int err, board_config; | ||
9750 | |||
9751 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||
9752 | if (!spec) | ||
9753 | return -ENOMEM; | ||
9754 | |||
9755 | codec->spec = spec; | ||
9756 | |||
9757 | board_config = snd_hda_check_board_config(codec, ALC662_MODEL_LAST, | ||
9758 | alc662_models, | ||
9759 | alc662_cfg_tbl); | ||
9760 | if (board_config < 0) { | ||
9761 | printk(KERN_INFO "hda_codec: Unknown model for ALC662, " | ||
9762 | "trying auto-probe from BIOS...\n"); | ||
9763 | board_config = ALC662_AUTO; | ||
9764 | } | ||
9765 | |||
9766 | if (board_config == ALC662_AUTO) { | ||
9767 | /* automatic parse from the BIOS config */ | ||
9768 | err = alc662_parse_auto_config(codec); | ||
9769 | if (err < 0) { | ||
9770 | alc_free(codec); | ||
9771 | return err; | ||
9772 | } else if (err) { | ||
9773 | printk(KERN_INFO | ||
9774 | "hda_codec: Cannot set up configuration " | ||
9775 | "from BIOS. Using base mode...\n"); | ||
9776 | board_config = ALC662_3ST_2ch_DIG; | ||
9777 | } | ||
9778 | } | ||
9779 | |||
9780 | if (board_config != ALC662_AUTO) | ||
9781 | setup_preset(spec, &alc662_presets[board_config]); | ||
9782 | |||
9783 | spec->stream_name_analog = "ALC662 Analog"; | ||
9784 | spec->stream_analog_playback = &alc662_pcm_analog_playback; | ||
9785 | spec->stream_analog_capture = &alc662_pcm_analog_capture; | ||
9786 | |||
9787 | spec->stream_name_digital = "ALC662 Digital"; | ||
9788 | spec->stream_digital_playback = &alc662_pcm_digital_playback; | ||
9789 | spec->stream_digital_capture = &alc662_pcm_digital_capture; | ||
9790 | |||
9791 | if (!spec->adc_nids && spec->input_mux) { | ||
9792 | spec->adc_nids = alc662_adc_nids; | ||
9793 | spec->num_adc_nids = ARRAY_SIZE(alc662_adc_nids); | ||
9794 | } | ||
9795 | |||
9796 | codec->patch_ops = alc_patch_ops; | ||
9797 | if (board_config == ALC662_AUTO) | ||
9798 | spec->init_hook = alc662_auto_init; | ||
9799 | |||
9800 | return 0; | ||
9801 | } | ||
9802 | |||
9803 | /* | ||
8692 | * patch entries | 9804 | * patch entries |
8693 | */ | 9805 | */ |
8694 | struct hda_codec_preset snd_hda_preset_realtek[] = { | 9806 | struct hda_codec_preset snd_hda_preset_realtek[] = { |
8695 | { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, | 9807 | { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 }, |
8696 | { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, | 9808 | { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 }, |
8697 | { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", | 9809 | { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660", |
8698 | .patch = patch_alc861 }, | 9810 | .patch = patch_alc861 }, |
8699 | { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, | 9811 | { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd }, |
8700 | { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, | 9812 | { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 }, |
8701 | { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, | 9813 | { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd }, |
9814 | { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2", | ||
9815 | .patch = patch_alc883 }, | ||
9816 | { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1", | ||
9817 | .patch = patch_alc662 }, | ||
8702 | { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, | 9818 | { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 }, |
8703 | { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, | 9819 | { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 }, |
8704 | { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, | 9820 | { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc883 }, |