diff options
author | Lydia Wang <lydiawang@viatech.com.cn> | 2009-10-10 07:08:43 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-10-11 11:58:59 -0400 |
commit | 25eaba2f8a6877ba6f58197c4723c2433a316e09 (patch) | |
tree | c5a68bbd3f91dac351b366a3811563608e879650 /sound/pci/hda/patch_via.c | |
parent | f3db423df84570c9950754a5771ad26f0111235f (diff) |
ALSA: HDA VIA: Add VT2002P support.
Signed-off-by: Lydia Wang <lydiawang@viatech.com.cn>
Signed-off-by: Logan Li <loganli@viatech.com.cn>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r-- | sound/pci/hda/patch_via.c | 665 |
1 files changed, 660 insertions, 5 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index 2977004677ec..a94cc91c18ff 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c | |||
@@ -88,6 +88,7 @@ enum VIA_HDA_CODEC { | |||
88 | VT1702, | 88 | VT1702, |
89 | VT1718S, | 89 | VT1718S, |
90 | VT1716S, | 90 | VT1716S, |
91 | VT2002P, | ||
91 | CODEC_TYPES, | 92 | CODEC_TYPES, |
92 | }; | 93 | }; |
93 | 94 | ||
@@ -184,6 +185,8 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) | |||
184 | codec_type = VT1716S; | 185 | codec_type = VT1716S; |
185 | else if (dev_id == 0x0441 || dev_id == 0x4441) | 186 | else if (dev_id == 0x0441 || dev_id == 0x4441) |
186 | codec_type = VT1718S; | 187 | codec_type = VT1718S; |
188 | else if (dev_id == 0x0438 || dev_id == 0x4438) | ||
189 | codec_type = VT2002P; | ||
187 | else | 190 | else |
188 | codec_type = UNKNOWN; | 191 | codec_type = UNKNOWN; |
189 | return codec_type; | 192 | return codec_type; |
@@ -193,11 +196,14 @@ static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) | |||
193 | #define VIA_GPIO_EVENT 0x02 | 196 | #define VIA_GPIO_EVENT 0x02 |
194 | #define VIA_JACK_EVENT 0x04 | 197 | #define VIA_JACK_EVENT 0x04 |
195 | #define VIA_MONO_EVENT 0x08 | 198 | #define VIA_MONO_EVENT 0x08 |
199 | #define VIA_SPEAKER_EVENT 0x10 | ||
200 | #define VIA_BIND_HP_EVENT 0x20 | ||
196 | 201 | ||
197 | enum { | 202 | enum { |
198 | VIA_CTL_WIDGET_VOL, | 203 | VIA_CTL_WIDGET_VOL, |
199 | VIA_CTL_WIDGET_MUTE, | 204 | VIA_CTL_WIDGET_MUTE, |
200 | VIA_CTL_WIDGET_ANALOG_MUTE, | 205 | VIA_CTL_WIDGET_ANALOG_MUTE, |
206 | VIA_CTL_WIDGET_BIND_PIN_MUTE, | ||
201 | }; | 207 | }; |
202 | 208 | ||
203 | enum { | 209 | enum { |
@@ -235,6 +241,7 @@ static void vt1708_stop_hp_work(struct via_spec *spec) | |||
235 | flush_scheduled_work(); | 241 | flush_scheduled_work(); |
236 | } | 242 | } |
237 | 243 | ||
244 | |||
238 | static int analog_input_switch_put(struct snd_kcontrol *kcontrol, | 245 | static int analog_input_switch_put(struct snd_kcontrol *kcontrol, |
239 | struct snd_ctl_elem_value *ucontrol) | 246 | struct snd_ctl_elem_value *ucontrol) |
240 | { | 247 | { |
@@ -262,13 +269,108 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, | |||
262 | .put = analog_input_switch_put, \ | 269 | .put = analog_input_switch_put, \ |
263 | .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } | 270 | .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } |
264 | 271 | ||
272 | static void via_hp_bind_automute(struct hda_codec *codec); | ||
273 | |||
274 | static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, | ||
275 | struct snd_ctl_elem_value *ucontrol) | ||
276 | { | ||
277 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
278 | struct via_spec *spec = codec->spec; | ||
279 | int i; | ||
280 | int change = 0; | ||
281 | |||
282 | long *valp = ucontrol->value.integer.value; | ||
283 | int lmute, rmute; | ||
284 | if (strstr(kcontrol->id.name, "Switch") == NULL) { | ||
285 | snd_printd("Invalid control!\n"); | ||
286 | return change; | ||
287 | } | ||
288 | change = snd_hda_mixer_amp_switch_put(kcontrol, | ||
289 | ucontrol); | ||
290 | /* Get mute value */ | ||
291 | lmute = *valp ? 0 : HDA_AMP_MUTE; | ||
292 | valp++; | ||
293 | rmute = *valp ? 0 : HDA_AMP_MUTE; | ||
294 | |||
295 | /* Set hp pins */ | ||
296 | if (!spec->hp_independent_mode) { | ||
297 | for (i = 0; i < spec->autocfg.hp_outs; i++) { | ||
298 | snd_hda_codec_amp_update( | ||
299 | codec, spec->autocfg.hp_pins[i], | ||
300 | 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
301 | lmute); | ||
302 | snd_hda_codec_amp_update( | ||
303 | codec, spec->autocfg.hp_pins[i], | ||
304 | 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
305 | rmute); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | if (!lmute && !rmute) { | ||
310 | /* Line Outs */ | ||
311 | for (i = 0; i < spec->autocfg.line_outs; i++) | ||
312 | snd_hda_codec_amp_stereo( | ||
313 | codec, spec->autocfg.line_out_pins[i], | ||
314 | HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); | ||
315 | /* Speakers */ | ||
316 | for (i = 0; i < spec->autocfg.speaker_outs; i++) | ||
317 | snd_hda_codec_amp_stereo( | ||
318 | codec, spec->autocfg.speaker_pins[i], | ||
319 | HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); | ||
320 | /* unmute */ | ||
321 | via_hp_bind_automute(codec); | ||
322 | |||
323 | } else { | ||
324 | if (lmute) { | ||
325 | /* Mute all left channels */ | ||
326 | for (i = 1; i < spec->autocfg.line_outs; i++) | ||
327 | snd_hda_codec_amp_update( | ||
328 | codec, | ||
329 | spec->autocfg.line_out_pins[i], | ||
330 | 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
331 | lmute); | ||
332 | for (i = 0; i < spec->autocfg.speaker_outs; i++) | ||
333 | snd_hda_codec_amp_update( | ||
334 | codec, | ||
335 | spec->autocfg.speaker_pins[i], | ||
336 | 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
337 | lmute); | ||
338 | } | ||
339 | if (rmute) { | ||
340 | /* mute all right channels */ | ||
341 | for (i = 1; i < spec->autocfg.line_outs; i++) | ||
342 | snd_hda_codec_amp_update( | ||
343 | codec, | ||
344 | spec->autocfg.line_out_pins[i], | ||
345 | 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
346 | rmute); | ||
347 | for (i = 0; i < spec->autocfg.speaker_outs; i++) | ||
348 | snd_hda_codec_amp_update( | ||
349 | codec, | ||
350 | spec->autocfg.speaker_pins[i], | ||
351 | 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, | ||
352 | rmute); | ||
353 | } | ||
354 | } | ||
355 | return change; | ||
356 | } | ||
357 | |||
358 | #define BIND_PIN_MUTE \ | ||
359 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
360 | .name = NULL, \ | ||
361 | .index = 0, \ | ||
362 | .info = snd_hda_mixer_amp_switch_info, \ | ||
363 | .get = snd_hda_mixer_amp_switch_get, \ | ||
364 | .put = bind_pin_switch_put, \ | ||
365 | .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } | ||
366 | |||
265 | static struct snd_kcontrol_new vt1708_control_templates[] = { | 367 | static struct snd_kcontrol_new vt1708_control_templates[] = { |
266 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), | 368 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), |
267 | HDA_CODEC_MUTE(NULL, 0, 0, 0), | 369 | HDA_CODEC_MUTE(NULL, 0, 0, 0), |
268 | ANALOG_INPUT_MUTE, | 370 | ANALOG_INPUT_MUTE, |
371 | BIND_PIN_MUTE, | ||
269 | }; | 372 | }; |
270 | 373 | ||
271 | |||
272 | static hda_nid_t vt1708_adc_nids[2] = { | 374 | static hda_nid_t vt1708_adc_nids[2] = { |
273 | /* ADC1-2 */ | 375 | /* ADC1-2 */ |
274 | 0x15, 0x27 | 376 | 0x15, 0x27 |
@@ -304,6 +406,11 @@ static hda_nid_t vt1716S_adc_nids[2] = { | |||
304 | 0x13, 0x14 | 406 | 0x13, 0x14 |
305 | }; | 407 | }; |
306 | 408 | ||
409 | static hda_nid_t vt2002P_adc_nids[2] = { | ||
410 | /* ADC1-2 */ | ||
411 | 0x10, 0x11 | ||
412 | }; | ||
413 | |||
307 | /* add dynamic controls */ | 414 | /* add dynamic controls */ |
308 | static int via_add_control(struct via_spec *spec, int type, const char *name, | 415 | static int via_add_control(struct via_spec *spec, int type, const char *name, |
309 | unsigned long val) | 416 | unsigned long val) |
@@ -386,10 +493,13 @@ static void via_auto_init_hp_out(struct hda_codec *codec) | |||
386 | { | 493 | { |
387 | struct via_spec *spec = codec->spec; | 494 | struct via_spec *spec = codec->spec; |
388 | hda_nid_t pin; | 495 | hda_nid_t pin; |
496 | int i; | ||
389 | 497 | ||
390 | pin = spec->autocfg.hp_pins[0]; | 498 | for (i = 0; i < spec->autocfg.hp_outs; i++) { |
391 | if (pin) /* connect to front */ | 499 | pin = spec->autocfg.hp_pins[i]; |
392 | via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); | 500 | if (pin) /* connect to front */ |
501 | via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); | ||
502 | } | ||
393 | } | 503 | } |
394 | 504 | ||
395 | static void via_auto_init_analog_input(struct hda_codec *codec) | 505 | static void via_auto_init_analog_input(struct hda_codec *codec) |
@@ -693,6 +803,107 @@ static void set_jack_power_state(struct hda_codec *codec) | |||
693 | imux_is_smixer ? AC_PWRST_D0 : parm); | 803 | imux_is_smixer ? AC_PWRST_D0 : parm); |
694 | snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, | 804 | snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, |
695 | mono_out ? AC_PWRST_D0 : parm); | 805 | mono_out ? AC_PWRST_D0 : parm); |
806 | } else if (spec->codec_type == VT2002P) { | ||
807 | unsigned int present; | ||
808 | /* MUX9 (1eh) = stereo mixer */ | ||
809 | imux_is_smixer = snd_hda_codec_read( | ||
810 | codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; | ||
811 | /* inputs */ | ||
812 | /* PW 5/6/7 (29h/2ah/2bh) */ | ||
813 | parm = AC_PWRST_D3; | ||
814 | set_pin_power_state(codec, 0x29, &parm); | ||
815 | set_pin_power_state(codec, 0x2a, &parm); | ||
816 | set_pin_power_state(codec, 0x2b, &parm); | ||
817 | if (imux_is_smixer) | ||
818 | parm = AC_PWRST_D0; | ||
819 | /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ | ||
820 | snd_hda_codec_write(codec, 0x1e, 0, | ||
821 | AC_VERB_SET_POWER_STATE, parm); | ||
822 | snd_hda_codec_write(codec, 0x1f, 0, | ||
823 | AC_VERB_SET_POWER_STATE, parm); | ||
824 | snd_hda_codec_write(codec, 0x10, 0, | ||
825 | AC_VERB_SET_POWER_STATE, parm); | ||
826 | snd_hda_codec_write(codec, 0x11, 0, | ||
827 | AC_VERB_SET_POWER_STATE, parm); | ||
828 | |||
829 | /* outputs */ | ||
830 | /* AOW0 (8h)*/ | ||
831 | snd_hda_codec_write(codec, 0x8, 0, | ||
832 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
833 | |||
834 | /* PW4 (26h), MW4 (1ch), MUX4(37h) */ | ||
835 | parm = AC_PWRST_D3; | ||
836 | set_pin_power_state(codec, 0x26, &parm); | ||
837 | snd_hda_codec_write(codec, 0x1c, 0, | ||
838 | AC_VERB_SET_POWER_STATE, parm); | ||
839 | snd_hda_codec_write(codec, 0x37, | ||
840 | 0, AC_VERB_SET_POWER_STATE, parm); | ||
841 | |||
842 | /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ | ||
843 | parm = AC_PWRST_D3; | ||
844 | set_pin_power_state(codec, 0x25, &parm); | ||
845 | snd_hda_codec_write(codec, 0x19, 0, | ||
846 | AC_VERB_SET_POWER_STATE, parm); | ||
847 | snd_hda_codec_write(codec, 0x35, 0, | ||
848 | AC_VERB_SET_POWER_STATE, parm); | ||
849 | if (spec->hp_independent_mode) { | ||
850 | snd_hda_codec_write(codec, 0x9, 0, | ||
851 | AC_VERB_SET_POWER_STATE, parm); | ||
852 | } | ||
853 | |||
854 | /* Class-D */ | ||
855 | /* PW0 (24h), MW0(18h), MUX0(34h) */ | ||
856 | present = snd_hda_codec_read( | ||
857 | codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
858 | parm = AC_PWRST_D3; | ||
859 | set_pin_power_state(codec, 0x24, &parm); | ||
860 | if (present) { | ||
861 | snd_hda_codec_write( | ||
862 | codec, 0x18, 0, | ||
863 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | ||
864 | snd_hda_codec_write( | ||
865 | codec, 0x34, 0, | ||
866 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | ||
867 | } else { | ||
868 | snd_hda_codec_write( | ||
869 | codec, 0x18, 0, | ||
870 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
871 | snd_hda_codec_write( | ||
872 | codec, 0x34, 0, | ||
873 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
874 | } | ||
875 | |||
876 | /* Mono Out */ | ||
877 | /* PW15 (31h), MW8(17h), MUX8(3bh) */ | ||
878 | present = snd_hda_codec_read( | ||
879 | codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
880 | parm = AC_PWRST_D3; | ||
881 | set_pin_power_state(codec, 0x31, &parm); | ||
882 | if (present) { | ||
883 | snd_hda_codec_write( | ||
884 | codec, 0x17, 0, | ||
885 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | ||
886 | snd_hda_codec_write( | ||
887 | codec, 0x3b, 0, | ||
888 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | ||
889 | } else { | ||
890 | snd_hda_codec_write( | ||
891 | codec, 0x17, 0, | ||
892 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
893 | snd_hda_codec_write( | ||
894 | codec, 0x3b, 0, | ||
895 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
896 | } | ||
897 | |||
898 | /* MW9 (21h) */ | ||
899 | if (imux_is_smixer || !is_aa_path_mute(codec)) | ||
900 | snd_hda_codec_write( | ||
901 | codec, 0x21, 0, | ||
902 | AC_VERB_SET_POWER_STATE, AC_PWRST_D0); | ||
903 | else | ||
904 | snd_hda_codec_write( | ||
905 | codec, 0x21, 0, | ||
906 | AC_VERB_SET_POWER_STATE, AC_PWRST_D3); | ||
696 | } | 907 | } |
697 | } | 908 | } |
698 | 909 | ||
@@ -760,6 +971,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol, | |||
760 | case VT1718S: | 971 | case VT1718S: |
761 | nid = 0x34; | 972 | nid = 0x34; |
762 | break; | 973 | break; |
974 | case VT2002P: | ||
975 | nid = 0x35; | ||
976 | break; | ||
763 | default: | 977 | default: |
764 | nid = spec->autocfg.hp_pins[0]; | 978 | nid = spec->autocfg.hp_pins[0]; |
765 | break; | 979 | break; |
@@ -832,6 +1046,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, | |||
832 | pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ | 1046 | pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */ |
833 | spec->multiout.num_dacs = 4; | 1047 | spec->multiout.num_dacs = 4; |
834 | break; | 1048 | break; |
1049 | case VT2002P: | ||
1050 | nid = 0x35; | ||
1051 | break; | ||
835 | default: | 1052 | default: |
836 | nid = spec->autocfg.hp_pins[0]; | 1053 | nid = spec->autocfg.hp_pins[0]; |
837 | break; | 1054 | break; |
@@ -848,7 +1065,8 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol, | |||
848 | if (spec->codec_type == VT1708S | 1065 | if (spec->codec_type == VT1708S |
849 | || spec->codec_type == VT1702 | 1066 | || spec->codec_type == VT1702 |
850 | || spec->codec_type == VT1718S | 1067 | || spec->codec_type == VT1718S |
851 | || spec->codec_type == VT1716S) { | 1068 | || spec->codec_type == VT1716S |
1069 | || spec->codec_type == VT2002P) { | ||
852 | activate_ctl(codec, "Headphone Playback Volume", | 1070 | activate_ctl(codec, "Headphone Playback Volume", |
853 | spec->hp_independent_mode); | 1071 | spec->hp_independent_mode); |
854 | activate_ctl(codec, "Headphone Playback Switch", | 1072 | activate_ctl(codec, "Headphone Playback Switch", |
@@ -1088,6 +1306,11 @@ static int is_aa_path_mute(struct hda_codec *codec) | |||
1088 | start_idx = 1; | 1306 | start_idx = 1; |
1089 | end_idx = 3; | 1307 | end_idx = 3; |
1090 | break; | 1308 | break; |
1309 | case VT2002P: | ||
1310 | nid_mixer = 0x21; | ||
1311 | start_idx = 0; | ||
1312 | end_idx = 2; | ||
1313 | break; | ||
1091 | default: | 1314 | default: |
1092 | return 0; | 1315 | return 0; |
1093 | } | 1316 | } |
@@ -1146,6 +1369,10 @@ static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) | |||
1146 | verb = 0xf73; | 1369 | verb = 0xf73; |
1147 | parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ | 1370 | parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ |
1148 | break; | 1371 | break; |
1372 | case VT2002P: | ||
1373 | verb = 0xf93; | ||
1374 | parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ | ||
1375 | break; | ||
1149 | default: | 1376 | default: |
1150 | return; /* other codecs are not supported */ | 1377 | return; /* other codecs are not supported */ |
1151 | } | 1378 | } |
@@ -1645,6 +1872,66 @@ static void via_gpio_control(struct hda_codec *codec) | |||
1645 | } | 1872 | } |
1646 | } | 1873 | } |
1647 | 1874 | ||
1875 | /* mute Internal-Speaker if HP is plugged */ | ||
1876 | static void via_speaker_automute(struct hda_codec *codec) | ||
1877 | { | ||
1878 | unsigned int hp_present; | ||
1879 | struct via_spec *spec = codec->spec; | ||
1880 | |||
1881 | if (spec->codec_type != VT2002P) | ||
1882 | return; | ||
1883 | |||
1884 | hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, | ||
1885 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
1886 | |||
1887 | if (!spec->hp_independent_mode) { | ||
1888 | struct snd_ctl_elem_id id; | ||
1889 | snd_hda_codec_amp_stereo( | ||
1890 | codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, | ||
1891 | HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); | ||
1892 | /* notify change */ | ||
1893 | memset(&id, 0, sizeof(id)); | ||
1894 | id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
1895 | strcpy(id.name, "Speaker Playback Switch"); | ||
1896 | snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
1897 | &id); | ||
1898 | } | ||
1899 | } | ||
1900 | |||
1901 | /* mute line-out and internal speaker if HP is plugged */ | ||
1902 | static void via_hp_bind_automute(struct hda_codec *codec) | ||
1903 | { | ||
1904 | unsigned int hp_present, present = 0; | ||
1905 | struct via_spec *spec = codec->spec; | ||
1906 | int i; | ||
1907 | |||
1908 | if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) | ||
1909 | return; | ||
1910 | |||
1911 | hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, | ||
1912 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
1913 | |||
1914 | present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0, | ||
1915 | AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | ||
1916 | |||
1917 | if (!spec->hp_independent_mode) { | ||
1918 | /* Mute Line-Outs */ | ||
1919 | for (i = 0; i < spec->autocfg.line_outs; i++) | ||
1920 | snd_hda_codec_amp_stereo( | ||
1921 | codec, spec->autocfg.line_out_pins[i], | ||
1922 | HDA_OUTPUT, 0, | ||
1923 | HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); | ||
1924 | if (hp_present) | ||
1925 | present = hp_present; | ||
1926 | } | ||
1927 | /* Speakers */ | ||
1928 | for (i = 0; i < spec->autocfg.speaker_outs; i++) | ||
1929 | snd_hda_codec_amp_stereo( | ||
1930 | codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, | ||
1931 | HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); | ||
1932 | } | ||
1933 | |||
1934 | |||
1648 | /* unsolicited event for jack sensing */ | 1935 | /* unsolicited event for jack sensing */ |
1649 | static void via_unsol_event(struct hda_codec *codec, | 1936 | static void via_unsol_event(struct hda_codec *codec, |
1650 | unsigned int res) | 1937 | unsigned int res) |
@@ -1658,6 +1945,10 @@ static void via_unsol_event(struct hda_codec *codec, | |||
1658 | set_jack_power_state(codec); | 1945 | set_jack_power_state(codec); |
1659 | if (res & VIA_MONO_EVENT) | 1946 | if (res & VIA_MONO_EVENT) |
1660 | via_mono_automute(codec); | 1947 | via_mono_automute(codec); |
1948 | if (res & VIA_SPEAKER_EVENT) | ||
1949 | via_speaker_automute(codec); | ||
1950 | if (res & VIA_BIND_HP_EVENT) | ||
1951 | via_hp_bind_automute(codec); | ||
1661 | } | 1952 | } |
1662 | 1953 | ||
1663 | static int via_init(struct hda_codec *codec) | 1954 | static int via_init(struct hda_codec *codec) |
@@ -2067,10 +2358,19 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) | |||
2067 | /* init callback for auto-configuration model -- overriding the default init */ | 2358 | /* init callback for auto-configuration model -- overriding the default init */ |
2068 | static int via_auto_init(struct hda_codec *codec) | 2359 | static int via_auto_init(struct hda_codec *codec) |
2069 | { | 2360 | { |
2361 | struct via_spec *spec = codec->spec; | ||
2362 | |||
2070 | via_init(codec); | 2363 | via_init(codec); |
2071 | via_auto_init_multi_out(codec); | 2364 | via_auto_init_multi_out(codec); |
2072 | via_auto_init_hp_out(codec); | 2365 | via_auto_init_hp_out(codec); |
2073 | via_auto_init_analog_input(codec); | 2366 | via_auto_init_analog_input(codec); |
2367 | if (spec->codec_type == VT2002P) { | ||
2368 | via_hp_bind_automute(codec); | ||
2369 | } else { | ||
2370 | via_hp_automute(codec); | ||
2371 | via_speaker_automute(codec); | ||
2372 | } | ||
2373 | |||
2074 | return 0; | 2374 | return 0; |
2075 | } | 2375 | } |
2076 | 2376 | ||
@@ -5001,6 +5301,359 @@ static int patch_vt1716S(struct hda_codec *codec) | |||
5001 | 5301 | ||
5002 | return 0; | 5302 | return 0; |
5003 | } | 5303 | } |
5304 | |||
5305 | /* for vt2002P */ | ||
5306 | |||
5307 | /* capture mixer elements */ | ||
5308 | static struct snd_kcontrol_new vt2002P_capture_mixer[] = { | ||
5309 | HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), | ||
5310 | HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), | ||
5311 | HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), | ||
5312 | HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), | ||
5313 | HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), | ||
5314 | HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, | ||
5315 | HDA_INPUT), | ||
5316 | { | ||
5317 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
5318 | /* The multiple "Capture Source" controls confuse alsamixer | ||
5319 | * So call somewhat different.. | ||
5320 | */ | ||
5321 | /* .name = "Capture Source", */ | ||
5322 | .name = "Input Source", | ||
5323 | .count = 2, | ||
5324 | .info = via_mux_enum_info, | ||
5325 | .get = via_mux_enum_get, | ||
5326 | .put = via_mux_enum_put, | ||
5327 | }, | ||
5328 | { } /* end */ | ||
5329 | }; | ||
5330 | |||
5331 | static struct hda_verb vt2002P_volume_init_verbs[] = { | ||
5332 | /* | ||
5333 | * Unmute ADC0-1 and set the default input to mic-in | ||
5334 | */ | ||
5335 | {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5336 | {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5337 | |||
5338 | |||
5339 | /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback | ||
5340 | * mixer widget | ||
5341 | */ | ||
5342 | /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ | ||
5343 | {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, | ||
5344 | {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, | ||
5345 | {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, | ||
5346 | {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, | ||
5347 | {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | ||
5348 | |||
5349 | /* MUX Indices: Mic = 0 */ | ||
5350 | {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5351 | {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5352 | |||
5353 | /* PW9 Output enable */ | ||
5354 | {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, | ||
5355 | |||
5356 | /* Enable Boost Volume backdoor */ | ||
5357 | {0x1, 0xfb9, 0x24}, | ||
5358 | |||
5359 | /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ | ||
5360 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5361 | {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5362 | {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5363 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | ||
5364 | {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
5365 | {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
5366 | {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
5367 | {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | ||
5368 | |||
5369 | /* set MUX0/1/4/8 = 0 (AOW0) */ | ||
5370 | {0x34, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5371 | {0x35, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5372 | {0x37, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5373 | {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5374 | |||
5375 | /* set PW0 index=0 (MW0) */ | ||
5376 | {0x24, AC_VERB_SET_CONNECT_SEL, 0}, | ||
5377 | |||
5378 | /* Enable AOW0 to MW9 */ | ||
5379 | {0x1, 0xfb8, 0x88}, | ||
5380 | { } | ||
5381 | }; | ||
5382 | |||
5383 | |||
5384 | static struct hda_verb vt2002P_uniwill_init_verbs[] = { | ||
5385 | {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, | ||
5386 | AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, | ||
5387 | {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, | ||
5388 | AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, | ||
5389 | {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, | ||
5390 | {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, | ||
5391 | {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, | ||
5392 | { } | ||
5393 | }; | ||
5394 | |||
5395 | static struct hda_pcm_stream vt2002P_pcm_analog_playback = { | ||
5396 | .substreams = 2, | ||
5397 | .channels_min = 2, | ||
5398 | .channels_max = 2, | ||
5399 | .nid = 0x8, /* NID to query formats and rates */ | ||
5400 | .ops = { | ||
5401 | .open = via_playback_pcm_open, | ||
5402 | .prepare = via_playback_multi_pcm_prepare, | ||
5403 | .cleanup = via_playback_multi_pcm_cleanup, | ||
5404 | .close = via_pcm_open_close, | ||
5405 | }, | ||
5406 | }; | ||
5407 | |||
5408 | static struct hda_pcm_stream vt2002P_pcm_analog_capture = { | ||
5409 | .substreams = 2, | ||
5410 | .channels_min = 2, | ||
5411 | .channels_max = 2, | ||
5412 | .nid = 0x10, /* NID to query formats and rates */ | ||
5413 | .ops = { | ||
5414 | .open = via_pcm_open_close, | ||
5415 | .prepare = via_capture_pcm_prepare, | ||
5416 | .cleanup = via_capture_pcm_cleanup, | ||
5417 | .close = via_pcm_open_close, | ||
5418 | }, | ||
5419 | }; | ||
5420 | |||
5421 | static struct hda_pcm_stream vt2002P_pcm_digital_playback = { | ||
5422 | .substreams = 1, | ||
5423 | .channels_min = 2, | ||
5424 | .channels_max = 2, | ||
5425 | .rates = SNDRV_PCM_RATE_48000, | ||
5426 | /* NID is set in via_build_pcms */ | ||
5427 | .ops = { | ||
5428 | .open = via_dig_playback_pcm_open, | ||
5429 | .close = via_dig_playback_pcm_close, | ||
5430 | .prepare = via_dig_playback_pcm_prepare, | ||
5431 | .cleanup = via_dig_playback_pcm_cleanup | ||
5432 | }, | ||
5433 | }; | ||
5434 | |||
5435 | /* fill in the dac_nids table from the parsed pin configuration */ | ||
5436 | static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, | ||
5437 | const struct auto_pin_cfg *cfg) | ||
5438 | { | ||
5439 | spec->multiout.num_dacs = 1; | ||
5440 | spec->multiout.dac_nids = spec->private_dac_nids; | ||
5441 | if (cfg->line_out_pins[0]) | ||
5442 | spec->multiout.dac_nids[0] = 0x8; | ||
5443 | return 0; | ||
5444 | } | ||
5445 | |||
5446 | /* add playback controls from the parsed DAC table */ | ||
5447 | static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, | ||
5448 | const struct auto_pin_cfg *cfg) | ||
5449 | { | ||
5450 | int err; | ||
5451 | |||
5452 | if (!cfg->line_out_pins[0]) | ||
5453 | return -1; | ||
5454 | |||
5455 | |||
5456 | /* Line-Out: PortE */ | ||
5457 | err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | ||
5458 | "Master Front Playback Volume", | ||
5459 | HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); | ||
5460 | if (err < 0) | ||
5461 | return err; | ||
5462 | err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, | ||
5463 | "Master Front Playback Switch", | ||
5464 | HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); | ||
5465 | if (err < 0) | ||
5466 | return err; | ||
5467 | |||
5468 | return 0; | ||
5469 | } | ||
5470 | |||
5471 | static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) | ||
5472 | { | ||
5473 | int err; | ||
5474 | |||
5475 | if (!pin) | ||
5476 | return 0; | ||
5477 | |||
5478 | spec->multiout.hp_nid = 0x9; | ||
5479 | spec->hp_independent_mode_index = 1; | ||
5480 | |||
5481 | err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | ||
5482 | "Headphone Playback Volume", | ||
5483 | HDA_COMPOSE_AMP_VAL( | ||
5484 | spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); | ||
5485 | if (err < 0) | ||
5486 | return err; | ||
5487 | |||
5488 | err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | ||
5489 | "Headphone Playback Switch", | ||
5490 | HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); | ||
5491 | if (err < 0) | ||
5492 | return err; | ||
5493 | |||
5494 | create_hp_imux(spec); | ||
5495 | return 0; | ||
5496 | } | ||
5497 | |||
5498 | /* create playback/capture controls for input pins */ | ||
5499 | static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, | ||
5500 | const struct auto_pin_cfg *cfg) | ||
5501 | { | ||
5502 | static char *labels[] = { | ||
5503 | "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | ||
5504 | }; | ||
5505 | struct hda_input_mux *imux = &spec->private_imux[0]; | ||
5506 | int i, err, idx = 0; | ||
5507 | |||
5508 | for (i = 0; i < AUTO_PIN_LAST; i++) { | ||
5509 | if (!cfg->input_pins[i]) | ||
5510 | continue; | ||
5511 | |||
5512 | switch (cfg->input_pins[i]) { | ||
5513 | case 0x2b: /* Mic */ | ||
5514 | idx = 0; | ||
5515 | break; | ||
5516 | |||
5517 | case 0x2a: /* Line In */ | ||
5518 | idx = 1; | ||
5519 | break; | ||
5520 | |||
5521 | case 0x29: /* Front Mic */ | ||
5522 | idx = 2; | ||
5523 | break; | ||
5524 | } | ||
5525 | err = via_new_analog_input(spec, labels[i], idx, 0x21); | ||
5526 | if (err < 0) | ||
5527 | return err; | ||
5528 | imux->items[imux->num_items].label = labels[i]; | ||
5529 | imux->items[imux->num_items].index = idx; | ||
5530 | imux->num_items++; | ||
5531 | } | ||
5532 | |||
5533 | /* build volume/mute control of loopback */ | ||
5534 | err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); | ||
5535 | if (err < 0) | ||
5536 | return err; | ||
5537 | |||
5538 | /* for internal loopback recording select */ | ||
5539 | imux->items[imux->num_items].label = "Stereo Mixer"; | ||
5540 | imux->items[imux->num_items].index = 3; | ||
5541 | imux->num_items++; | ||
5542 | |||
5543 | /* for digital mic select */ | ||
5544 | imux->items[imux->num_items].label = "Digital Mic"; | ||
5545 | imux->items[imux->num_items].index = 4; | ||
5546 | imux->num_items++; | ||
5547 | |||
5548 | return 0; | ||
5549 | } | ||
5550 | |||
5551 | static int vt2002P_parse_auto_config(struct hda_codec *codec) | ||
5552 | { | ||
5553 | struct via_spec *spec = codec->spec; | ||
5554 | int err; | ||
5555 | |||
5556 | |||
5557 | err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); | ||
5558 | if (err < 0) | ||
5559 | return err; | ||
5560 | |||
5561 | err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); | ||
5562 | if (err < 0) | ||
5563 | return err; | ||
5564 | |||
5565 | if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) | ||
5566 | return 0; /* can't find valid BIOS pin config */ | ||
5567 | |||
5568 | err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); | ||
5569 | if (err < 0) | ||
5570 | return err; | ||
5571 | err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); | ||
5572 | if (err < 0) | ||
5573 | return err; | ||
5574 | err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); | ||
5575 | if (err < 0) | ||
5576 | return err; | ||
5577 | |||
5578 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; | ||
5579 | |||
5580 | fill_dig_outs(codec); | ||
5581 | |||
5582 | if (spec->kctls.list) | ||
5583 | spec->mixers[spec->num_mixers++] = spec->kctls.list; | ||
5584 | |||
5585 | spec->input_mux = &spec->private_imux[0]; | ||
5586 | |||
5587 | if (spec->hp_mux) | ||
5588 | spec->mixers[spec->num_mixers++] = via_hp_mixer; | ||
5589 | |||
5590 | return 1; | ||
5591 | } | ||
5592 | |||
5593 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
5594 | static struct hda_amp_list vt2002P_loopbacks[] = { | ||
5595 | { 0x21, HDA_INPUT, 0 }, | ||
5596 | { 0x21, HDA_INPUT, 1 }, | ||
5597 | { 0x21, HDA_INPUT, 2 }, | ||
5598 | { } /* end */ | ||
5599 | }; | ||
5600 | #endif | ||
5601 | |||
5602 | |||
5603 | /* patch for vt2002P */ | ||
5604 | static int patch_vt2002P(struct hda_codec *codec) | ||
5605 | { | ||
5606 | struct via_spec *spec; | ||
5607 | int err; | ||
5608 | |||
5609 | /* create a codec specific record */ | ||
5610 | spec = kzalloc(sizeof(*spec), GFP_KERNEL); | ||
5611 | if (spec == NULL) | ||
5612 | return -ENOMEM; | ||
5613 | |||
5614 | codec->spec = spec; | ||
5615 | |||
5616 | /* automatic parse from the BIOS config */ | ||
5617 | err = vt2002P_parse_auto_config(codec); | ||
5618 | if (err < 0) { | ||
5619 | via_free(codec); | ||
5620 | return err; | ||
5621 | } else if (!err) { | ||
5622 | printk(KERN_INFO "hda_codec: Cannot set up configuration " | ||
5623 | "from BIOS. Using genenic mode...\n"); | ||
5624 | } | ||
5625 | |||
5626 | spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; | ||
5627 | spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; | ||
5628 | |||
5629 | spec->stream_name_analog = "VT2002P Analog"; | ||
5630 | spec->stream_analog_playback = &vt2002P_pcm_analog_playback; | ||
5631 | spec->stream_analog_capture = &vt2002P_pcm_analog_capture; | ||
5632 | |||
5633 | spec->stream_name_digital = "VT2002P Digital"; | ||
5634 | spec->stream_digital_playback = &vt2002P_pcm_digital_playback; | ||
5635 | |||
5636 | if (!spec->adc_nids && spec->input_mux) { | ||
5637 | spec->adc_nids = vt2002P_adc_nids; | ||
5638 | spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); | ||
5639 | get_mux_nids(codec); | ||
5640 | override_mic_boost(codec, 0x2b, 0, 3, 40); | ||
5641 | override_mic_boost(codec, 0x29, 0, 3, 40); | ||
5642 | spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; | ||
5643 | spec->num_mixers++; | ||
5644 | } | ||
5645 | |||
5646 | codec->patch_ops = via_patch_ops; | ||
5647 | |||
5648 | codec->patch_ops.init = via_auto_init; | ||
5649 | codec->patch_ops.unsol_event = via_unsol_event, | ||
5650 | |||
5651 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
5652 | spec->loopback.amplist = vt2002P_loopbacks; | ||
5653 | #endif | ||
5654 | |||
5655 | return 0; | ||
5656 | } | ||
5004 | /* | 5657 | /* |
5005 | * patch entries | 5658 | * patch entries |
5006 | */ | 5659 | */ |
@@ -5085,6 +5738,8 @@ static struct hda_codec_preset snd_hda_preset_via[] = { | |||
5085 | .patch = patch_vt1716S}, | 5738 | .patch = patch_vt1716S}, |
5086 | { .id = 0x1106a721, .name = "VT1716S", | 5739 | { .id = 0x1106a721, .name = "VT1716S", |
5087 | .patch = patch_vt1716S}, | 5740 | .patch = patch_vt1716S}, |
5741 | { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, | ||
5742 | { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, | ||
5088 | {} /* terminator */ | 5743 | {} /* terminator */ |
5089 | }; | 5744 | }; |
5090 | 5745 | ||