]>
Commit | Line | Data |
---|---|---|
2cb7cef9 BS |
1 | From: Takashi Iwai <tiwai@suse.de> |
2 | Subject: ALSA: hda - VIA codecs updates | |
3 | Patch-mainline: 2.6.28-rc1 | |
4 | References: | |
5 | ||
6 | A pile of updates for VIA HD-audio codecs. | |
7 | ||
8 | Signed-off-by: Takashi Iwai <tiwai@suse.de> | |
9 | ||
10 | --- | |
11 | diff -ruN a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c | |
12 | --- a/sound/pci/hda/patch_via.c | |
13 | +++ b/sound/pci/hda/patch_via.c | |
14 | @@ -1,10 +1,10 @@ | |
15 | /* | |
16 | * Universal Interface for Intel High Definition Audio Codec | |
17 | * | |
18 | - * HD audio interface patch for VIA VT1708 codec | |
19 | + * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec | |
20 | * | |
21 | - * Copyright (c) 2006 Lydia Wang <lydiawang@viatech.com> | |
22 | - * Takashi Iwai <tiwai@suse.de> | |
23 | + * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com> | |
24 | + * Takashi Iwai <tiwai@suse.de> | |
25 | * | |
26 | * This driver is free software; you can redistribute it and/or modify | |
27 | * it under the terms of the GNU General Public License as published by | |
28 | @@ -29,6 +29,13 @@ | |
29 | /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ | |
30 | /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ | |
31 | /* 2007-09-17 Lydia Wang Add VT1708B codec support */ | |
32 | +/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ | |
33 | +/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ | |
34 | +/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ | |
35 | +/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ | |
36 | +/* 2008-04-09 Lydia Wang Add Independent HP feature */ | |
37 | +/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ | |
38 | +/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ | |
39 | /* */ | |
40 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
41 | ||
42 | @@ -37,6 +44,7 @@ | |
43 | #include <linux/delay.h> | |
44 | #include <linux/slab.h> | |
45 | #include <sound/core.h> | |
46 | +#include <sound/asoundef.h> | |
47 | #include "hda_codec.h" | |
48 | #include "hda_local.h" | |
49 | #include "hda_patch.h" | |
50 | @@ -53,6 +61,8 @@ | |
51 | #define VT1708_DIGOUT_NID 0x14 | |
52 | #define VT1708_DIGIN_NID 0x16 | |
53 | #define VT1708_DIGIN_PIN 0x26 | |
54 | +#define VT1708_HP_PIN_NID 0x20 | |
55 | +#define VT1708_CD_PIN_NID 0x24 | |
56 | ||
57 | #define VT1709_HP_DAC_NID 0x28 | |
58 | #define VT1709_DIGOUT_NID 0x13 | |
59 | @@ -64,12 +74,64 @@ | |
60 | #define VT1708B_DIGIN_NID 0x15 | |
61 | #define VT1708B_DIGIN_PIN 0x21 | |
62 | ||
63 | +#define VT1708S_HP_NID 0x25 | |
64 | +#define VT1708S_DIGOUT_NID 0x12 | |
65 | + | |
66 | +#define VT1702_HP_NID 0x17 | |
67 | +#define VT1702_DIGOUT_NID 0x11 | |
68 | + | |
69 | #define IS_VT1708_VENDORID(x) ((x) >= 0x11061708 && (x) <= 0x1106170b) | |
70 | #define IS_VT1709_10CH_VENDORID(x) ((x) >= 0x1106e710 && (x) <= 0x1106e713) | |
71 | #define IS_VT1709_6CH_VENDORID(x) ((x) >= 0x1106e714 && (x) <= 0x1106e717) | |
72 | #define IS_VT1708B_8CH_VENDORID(x) ((x) >= 0x1106e720 && (x) <= 0x1106e723) | |
73 | #define IS_VT1708B_4CH_VENDORID(x) ((x) >= 0x1106e724 && (x) <= 0x1106e727) | |
74 | +#define IS_VT1708S_VENDORID(x) ((x) >= 0x11060397 && (x) <= 0x11067397) | |
75 | +#define IS_VT1702_VENDORID(x) ((x) >= 0x11060398 && (x) <= 0x11067398) | |
76 | + | |
77 | +enum VIA_HDA_CODEC { | |
78 | + UNKNOWN = -1, | |
79 | + VT1708, | |
80 | + VT1709_10CH, | |
81 | + VT1709_6CH, | |
82 | + VT1708B_8CH, | |
83 | + VT1708B_4CH, | |
84 | + VT1708S, | |
85 | + VT1702, | |
86 | + CODEC_TYPES, | |
87 | +}; | |
88 | + | |
89 | +static enum VIA_HDA_CODEC get_codec_type(u32 vendor_id) | |
90 | +{ | |
91 | + u16 ven_id = vendor_id >> 16; | |
92 | + u16 dev_id = vendor_id & 0xffff; | |
93 | + enum VIA_HDA_CODEC codec_type; | |
94 | + | |
95 | + /* get codec type */ | |
96 | + if (ven_id != 0x1106) | |
97 | + codec_type = UNKNOWN; | |
98 | + else if (dev_id >= 0x1708 && dev_id <= 0x170b) | |
99 | + codec_type = VT1708; | |
100 | + else if (dev_id >= 0xe710 && dev_id <= 0xe713) | |
101 | + codec_type = VT1709_10CH; | |
102 | + else if (dev_id >= 0xe714 && dev_id <= 0xe717) | |
103 | + codec_type = VT1709_6CH; | |
104 | + else if (dev_id >= 0xe720 && dev_id <= 0xe723) | |
105 | + codec_type = VT1708B_8CH; | |
106 | + else if (dev_id >= 0xe724 && dev_id <= 0xe727) | |
107 | + codec_type = VT1708B_4CH; | |
108 | + else if ((dev_id & 0xfff) == 0x397 | |
109 | + && (dev_id >> 12) < 8) | |
110 | + codec_type = VT1708S; | |
111 | + else if ((dev_id & 0xfff) == 0x398 | |
112 | + && (dev_id >> 12) < 8) | |
113 | + codec_type = VT1702; | |
114 | + else | |
115 | + codec_type = UNKNOWN; | |
116 | + return codec_type; | |
117 | +}; | |
118 | ||
119 | +#define VIA_HP_EVENT 0x01 | |
120 | +#define VIA_GPIO_EVENT 0x02 | |
121 | ||
122 | enum { | |
123 | VIA_CTL_WIDGET_VOL, | |
124 | @@ -77,12 +139,54 @@ | |
125 | }; | |
126 | ||
127 | enum { | |
128 | - AUTO_SEQ_FRONT, | |
129 | + AUTO_SEQ_FRONT = 0, | |
130 | AUTO_SEQ_SURROUND, | |
131 | AUTO_SEQ_CENLFE, | |
132 | AUTO_SEQ_SIDE | |
133 | }; | |
134 | ||
135 | +#define get_amp_nid(kc) ((kc)->private_value & 0xffff) | |
136 | + | |
137 | +/* Some VT1708S based boards gets the micboost setting wrong, so we have | |
138 | + * to apply some brute-force and re-write the TLV's by software. */ | |
139 | +static int mic_boost_tlv(struct snd_kcontrol *kcontrol, int op_flag, | |
140 | + unsigned int size, unsigned int __user *_tlv) | |
141 | +{ | |
142 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
143 | + hda_nid_t nid = get_amp_nid(kcontrol); | |
144 | + | |
145 | + if (get_codec_type(codec->vendor_id) == VT1708S | |
146 | + && (nid == 0x1a || nid == 0x1e)) { | |
147 | + if (size < 4 * sizeof(unsigned int)) | |
148 | + return -ENOMEM; | |
149 | + if (put_user(1, _tlv)) /* SNDRV_CTL_TLVT_DB_SCALE */ | |
150 | + return -EFAULT; | |
151 | + if (put_user(2 * sizeof(unsigned int), _tlv + 1)) | |
152 | + return -EFAULT; | |
153 | + if (put_user(0, _tlv + 2)) /* offset = 0 */ | |
154 | + return -EFAULT; | |
155 | + if (put_user(1000, _tlv + 3)) /* step size = 10 dB */ | |
156 | + return -EFAULT; | |
157 | + } | |
158 | + return 0; | |
159 | +} | |
160 | + | |
161 | +static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, | |
162 | + struct snd_ctl_elem_info *uinfo) | |
163 | +{ | |
164 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
165 | + hda_nid_t nid = get_amp_nid(kcontrol); | |
166 | + | |
167 | + if (get_codec_type(codec->vendor_id) == VT1708S | |
168 | + && (nid == 0x1a || nid == 0x1e)) { | |
169 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | |
170 | + uinfo->count = 2; | |
171 | + uinfo->value.integer.min = 0; | |
172 | + uinfo->value.integer.max = 3; | |
173 | + } | |
174 | + return 0; | |
175 | +} | |
176 | + | |
177 | static struct snd_kcontrol_new vt1708_control_templates[] = { | |
178 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), | |
179 | HDA_CODEC_MUTE(NULL, 0, 0, 0), | |
180 | @@ -94,7 +198,8 @@ | |
181 | struct snd_kcontrol_new *mixers[3]; | |
182 | unsigned int num_mixers; | |
183 | ||
184 | - struct hda_verb *init_verbs; | |
185 | + struct hda_verb *init_verbs[5]; | |
186 | + unsigned int num_iverbs; | |
187 | ||
188 | char *stream_name_analog; | |
189 | struct hda_pcm_stream *stream_analog_playback; | |
190 | @@ -106,6 +211,7 @@ | |
191 | ||
192 | /* playback */ | |
193 | struct hda_multi_out multiout; | |
194 | + hda_nid_t extra_dig_out_nid; | |
195 | ||
196 | /* capture */ | |
197 | unsigned int num_adc_nids; | |
198 | @@ -117,15 +223,19 @@ | |
199 | unsigned int cur_mux[3]; | |
200 | ||
201 | /* PCM information */ | |
202 | - struct hda_pcm pcm_rec[2]; | |
203 | + struct hda_pcm pcm_rec[3]; | |
204 | ||
205 | /* dynamic controls, init_verbs and input_mux */ | |
206 | struct auto_pin_cfg autocfg; | |
207 | unsigned int num_kctl_alloc, num_kctl_used; | |
208 | struct snd_kcontrol_new *kctl_alloc; | |
209 | - struct hda_input_mux private_imux; | |
210 | + struct hda_input_mux private_imux[2]; | |
211 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | |
212 | ||
213 | + /* HP mode source */ | |
214 | + const struct hda_input_mux *hp_mux; | |
215 | + unsigned int hp_independent_mode; | |
216 | + | |
217 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
218 | struct hda_loopback_check loopback; | |
219 | #endif | |
220 | @@ -146,6 +256,16 @@ | |
221 | 0x13, 0x14 | |
222 | }; | |
223 | ||
224 | +static hda_nid_t vt1708S_adc_nids[2] = { | |
225 | + /* ADC1-2 */ | |
226 | + 0x13, 0x14 | |
227 | +}; | |
228 | + | |
229 | +static hda_nid_t vt1702_adc_nids[3] = { | |
230 | + /* ADC1-2 */ | |
231 | + 0x12, 0x20, 0x1F | |
232 | +}; | |
233 | + | |
234 | /* add dynamic controls */ | |
235 | static int via_add_control(struct via_spec *spec, int type, const char *name, | |
236 | unsigned long val) | |
237 | @@ -283,19 +403,108 @@ | |
238 | return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, | |
239 | 0x18, &spec->cur_mux[adc_idx]); | |
240 | else if ((IS_VT1709_10CH_VENDORID(vendor_id) || | |
241 | - IS_VT1709_6CH_VENDORID(vendor_id)) && adc_idx == 0) | |
242 | + IS_VT1709_6CH_VENDORID(vendor_id)) && (adc_idx == 0)) | |
243 | return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, | |
244 | 0x19, &spec->cur_mux[adc_idx]); | |
245 | else if ((IS_VT1708B_8CH_VENDORID(vendor_id) || | |
246 | - IS_VT1708B_4CH_VENDORID(vendor_id)) && adc_idx == 0) | |
247 | + IS_VT1708B_4CH_VENDORID(vendor_id)) && (adc_idx == 0)) | |
248 | return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, | |
249 | 0x17, &spec->cur_mux[adc_idx]); | |
250 | + else if (IS_VT1702_VENDORID(vendor_id) && (adc_idx == 0)) | |
251 | + return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, | |
252 | + 0x13, &spec->cur_mux[adc_idx]); | |
253 | else | |
254 | return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, | |
255 | spec->adc_nids[adc_idx], | |
256 | &spec->cur_mux[adc_idx]); | |
257 | } | |
258 | ||
259 | +static int via_independent_hp_info(struct snd_kcontrol *kcontrol, | |
260 | + struct snd_ctl_elem_info *uinfo) | |
261 | +{ | |
262 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
263 | + struct via_spec *spec = codec->spec; | |
264 | + return snd_hda_input_mux_info(spec->hp_mux, uinfo); | |
265 | +} | |
266 | + | |
267 | +static int via_independent_hp_get(struct snd_kcontrol *kcontrol, | |
268 | + struct snd_ctl_elem_value *ucontrol) | |
269 | +{ | |
270 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
271 | + struct via_spec *spec = codec->spec; | |
272 | + hda_nid_t nid = spec->autocfg.hp_pins[0]; | |
273 | + unsigned int pinsel = snd_hda_codec_read(codec, nid, 0, | |
274 | + AC_VERB_GET_CONNECT_SEL, | |
275 | + 0x00); | |
276 | + | |
277 | + ucontrol->value.enumerated.item[0] = pinsel; | |
278 | + | |
279 | + return 0; | |
280 | +} | |
281 | + | |
282 | +static int via_independent_hp_put(struct snd_kcontrol *kcontrol, | |
283 | + struct snd_ctl_elem_value *ucontrol) | |
284 | +{ | |
285 | + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | |
286 | + struct via_spec *spec = codec->spec; | |
287 | + hda_nid_t nid = spec->autocfg.hp_pins[0]; | |
288 | + unsigned int pinsel = ucontrol->value.enumerated.item[0]; | |
289 | + unsigned int con_nid = snd_hda_codec_read(codec, nid, 0, | |
290 | + AC_VERB_GET_CONNECT_LIST, 0) & 0xff; | |
291 | + | |
292 | + if (con_nid == spec->multiout.hp_nid) { | |
293 | + if (pinsel == 0) { | |
294 | + if (!spec->hp_independent_mode) { | |
295 | + if (spec->multiout.num_dacs > 1) | |
296 | + spec->multiout.num_dacs -= 1; | |
297 | + spec->hp_independent_mode = 1; | |
298 | + } | |
299 | + } else if (pinsel == 1) { | |
300 | + if (spec->hp_independent_mode) { | |
301 | + if (spec->multiout.num_dacs > 1) | |
302 | + spec->multiout.num_dacs += 1; | |
303 | + spec->hp_independent_mode = 0; | |
304 | + } | |
305 | + } | |
306 | + } else { | |
307 | + if (pinsel == 0) { | |
308 | + if (spec->hp_independent_mode) { | |
309 | + if (spec->multiout.num_dacs > 1) | |
310 | + spec->multiout.num_dacs += 1; | |
311 | + spec->hp_independent_mode = 0; | |
312 | + } | |
313 | + } else if (pinsel == 1) { | |
314 | + if (!spec->hp_independent_mode) { | |
315 | + if (spec->multiout.num_dacs > 1) | |
316 | + spec->multiout.num_dacs -= 1; | |
317 | + spec->hp_independent_mode = 1; | |
318 | + } | |
319 | + } | |
320 | + } | |
321 | + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, | |
322 | + pinsel); | |
323 | + | |
324 | + if (spec->multiout.hp_nid && | |
325 | + spec->multiout.hp_nid != spec->multiout.dac_nids[HDA_FRONT]) | |
326 | + snd_hda_codec_setup_stream(codec, | |
327 | + spec->multiout.hp_nid, | |
328 | + 0, 0, 0); | |
329 | + | |
330 | + return 0; | |
331 | +} | |
332 | + | |
333 | +static struct snd_kcontrol_new via_hp_mixer[] = { | |
334 | + { | |
335 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
336 | + .name = "Independent HP", | |
337 | + .count = 1, | |
338 | + .info = via_independent_hp_info, | |
339 | + .get = via_independent_hp_get, | |
340 | + .put = via_independent_hp_put, | |
341 | + }, | |
342 | + { } /* end */ | |
343 | +}; | |
344 | + | |
345 | /* capture mixer elements */ | |
346 | static struct snd_kcontrol_new vt1708_capture_mixer[] = { | |
347 | HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), | |
348 | @@ -380,6 +589,138 @@ | |
349 | return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); | |
350 | } | |
351 | ||
352 | + | |
353 | +static void playback_multi_pcm_prep_0(struct hda_codec *codec, | |
354 | + unsigned int stream_tag, | |
355 | + unsigned int format, | |
356 | + struct snd_pcm_substream *substream) | |
357 | +{ | |
358 | + struct via_spec *spec = codec->spec; | |
359 | + struct hda_multi_out *mout = &spec->multiout; | |
360 | + hda_nid_t *nids = mout->dac_nids; | |
361 | + int chs = substream->runtime->channels; | |
362 | + int i; | |
363 | + | |
364 | + mutex_lock(&codec->spdif_mutex); | |
365 | + if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { | |
366 | + if (chs == 2 && | |
367 | + snd_hda_is_supported_format(codec, mout->dig_out_nid, | |
368 | + format) && | |
369 | + !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { | |
370 | + mout->dig_out_used = HDA_DIG_ANALOG_DUP; | |
371 | + /* turn off SPDIF once; otherwise the IEC958 bits won't | |
372 | + * be updated */ | |
373 | + if (codec->spdif_ctls & AC_DIG1_ENABLE) | |
374 | + snd_hda_codec_write(codec, mout->dig_out_nid, 0, | |
375 | + AC_VERB_SET_DIGI_CONVERT_1, | |
376 | + codec->spdif_ctls & | |
377 | + ~AC_DIG1_ENABLE & 0xff); | |
378 | + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, | |
379 | + stream_tag, 0, format); | |
380 | + /* turn on again (if needed) */ | |
381 | + if (codec->spdif_ctls & AC_DIG1_ENABLE) | |
382 | + snd_hda_codec_write(codec, mout->dig_out_nid, 0, | |
383 | + AC_VERB_SET_DIGI_CONVERT_1, | |
384 | + codec->spdif_ctls & 0xff); | |
385 | + } else { | |
386 | + mout->dig_out_used = 0; | |
387 | + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, | |
388 | + 0, 0, 0); | |
389 | + } | |
390 | + } | |
391 | + mutex_unlock(&codec->spdif_mutex); | |
392 | + | |
393 | + /* front */ | |
394 | + snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, | |
395 | + 0, format); | |
396 | + | |
397 | + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && | |
398 | + !spec->hp_independent_mode) | |
399 | + /* headphone out will just decode front left/right (stereo) */ | |
400 | + snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, | |
401 | + 0, format); | |
402 | + | |
403 | + /* extra outputs copied from front */ | |
404 | + for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) | |
405 | + if (mout->extra_out_nid[i]) | |
406 | + snd_hda_codec_setup_stream(codec, | |
407 | + mout->extra_out_nid[i], | |
408 | + stream_tag, 0, format); | |
409 | + | |
410 | + /* surrounds */ | |
411 | + for (i = 1; i < mout->num_dacs; i++) { | |
412 | + if (chs >= (i + 1) * 2) /* independent out */ | |
413 | + snd_hda_codec_setup_stream(codec, nids[i], stream_tag, | |
414 | + i * 2, format); | |
415 | + else /* copy front */ | |
416 | + snd_hda_codec_setup_stream(codec, nids[i], stream_tag, | |
417 | + 0, format); | |
418 | + } | |
419 | +} | |
420 | + | |
421 | +static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, | |
422 | + struct hda_codec *codec, | |
423 | + unsigned int stream_tag, | |
424 | + unsigned int format, | |
425 | + struct snd_pcm_substream *substream) | |
426 | +{ | |
427 | + struct via_spec *spec = codec->spec; | |
428 | + struct hda_multi_out *mout = &spec->multiout; | |
429 | + hda_nid_t *nids = mout->dac_nids; | |
430 | + | |
431 | + if (substream->number == 0) | |
432 | + playback_multi_pcm_prep_0(codec, stream_tag, format, | |
433 | + substream); | |
434 | + else { | |
435 | + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && | |
436 | + spec->hp_independent_mode) | |
437 | + snd_hda_codec_setup_stream(codec, mout->hp_nid, | |
438 | + stream_tag, 0, format); | |
439 | + } | |
440 | + | |
441 | + return 0; | |
442 | +} | |
443 | + | |
444 | +static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, | |
445 | + struct hda_codec *codec, | |
446 | + struct snd_pcm_substream *substream) | |
447 | +{ | |
448 | + struct via_spec *spec = codec->spec; | |
449 | + struct hda_multi_out *mout = &spec->multiout; | |
450 | + hda_nid_t *nids = mout->dac_nids; | |
451 | + int i; | |
452 | + | |
453 | + if (substream->number == 0) { | |
454 | + for (i = 0; i < mout->num_dacs; i++) | |
455 | + snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); | |
456 | + | |
457 | + if (mout->hp_nid && !spec->hp_independent_mode) | |
458 | + snd_hda_codec_setup_stream(codec, mout->hp_nid, | |
459 | + 0, 0, 0); | |
460 | + | |
461 | + for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) | |
462 | + if (mout->extra_out_nid[i]) | |
463 | + snd_hda_codec_setup_stream(codec, | |
464 | + mout->extra_out_nid[i], | |
465 | + 0, 0, 0); | |
466 | + mutex_lock(&codec->spdif_mutex); | |
467 | + if (mout->dig_out_nid && | |
468 | + mout->dig_out_used == HDA_DIG_ANALOG_DUP) { | |
469 | + snd_hda_codec_setup_stream(codec, mout->dig_out_nid, | |
470 | + 0, 0, 0); | |
471 | + mout->dig_out_used = 0; | |
472 | + } | |
473 | + mutex_unlock(&codec->spdif_mutex); | |
474 | + } else { | |
475 | + if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && | |
476 | + spec->hp_independent_mode) | |
477 | + snd_hda_codec_setup_stream(codec, mout->hp_nid, | |
478 | + 0, 0, 0); | |
479 | + } | |
480 | + | |
481 | + return 0; | |
482 | +} | |
483 | + | |
484 | /* | |
485 | * Digital out | |
486 | */ | |
487 | @@ -399,6 +740,21 @@ | |
488 | return snd_hda_multi_out_dig_close(codec, &spec->multiout); | |
489 | } | |
490 | ||
491 | +/* setup SPDIF output stream */ | |
492 | +static void setup_dig_playback_stream(struct hda_codec *codec, hda_nid_t nid, | |
493 | + unsigned int stream_tag, unsigned int format) | |
494 | +{ | |
495 | + /* turn off SPDIF once; otherwise the IEC958 bits won't be updated */ | |
496 | + if (codec->spdif_ctls & AC_DIG1_ENABLE) | |
497 | + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, | |
498 | + codec->spdif_ctls & ~AC_DIG1_ENABLE & 0xff); | |
499 | + snd_hda_codec_setup_stream(codec, nid, stream_tag, 0, format); | |
500 | + /* turn on again (if needed) */ | |
501 | + if (codec->spdif_ctls & AC_DIG1_ENABLE) | |
502 | + snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_DIGI_CONVERT_1, | |
503 | + codec->spdif_ctls & 0xff); | |
504 | +} | |
505 | + | |
506 | static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, | |
507 | struct hda_codec *codec, | |
508 | unsigned int stream_tag, | |
509 | @@ -406,8 +762,20 @@ | |
510 | struct snd_pcm_substream *substream) | |
511 | { | |
512 | struct via_spec *spec = codec->spec; | |
513 | - return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, | |
514 | - stream_tag, format, substream); | |
515 | + hda_nid_t nid; | |
516 | + | |
517 | + /* 1st or 2nd S/PDIF */ | |
518 | + if (substream->number == 0) | |
519 | + nid = spec->multiout.dig_out_nid; | |
520 | + else if (substream->number == 1) | |
521 | + nid = spec->extra_dig_out_nid; | |
522 | + else | |
523 | + return -1; | |
524 | + | |
525 | + mutex_lock(&codec->spdif_mutex); | |
526 | + setup_dig_playback_stream(codec, nid, stream_tag, format); | |
527 | + mutex_unlock(&codec->spdif_mutex); | |
528 | + return 0; | |
529 | } | |
530 | ||
531 | /* | |
532 | @@ -436,14 +804,14 @@ | |
533 | } | |
534 | ||
535 | static struct hda_pcm_stream vt1708_pcm_analog_playback = { | |
536 | - .substreams = 1, | |
537 | + .substreams = 2, | |
538 | .channels_min = 2, | |
539 | .channels_max = 8, | |
540 | .nid = 0x10, /* NID to query formats and rates */ | |
541 | .ops = { | |
542 | .open = via_playback_pcm_open, | |
543 | - .prepare = via_playback_pcm_prepare, | |
544 | - .cleanup = via_playback_pcm_cleanup | |
545 | + .prepare = via_playback_multi_pcm_prepare, | |
546 | + .cleanup = via_playback_multi_pcm_cleanup | |
547 | }, | |
548 | }; | |
549 | ||
550 | @@ -515,6 +883,13 @@ | |
551 | if (err < 0) | |
552 | return err; | |
553 | spec->multiout.share_spdif = 1; | |
554 | + | |
555 | + if (spec->extra_dig_out_nid) { | |
556 | + err = snd_hda_create_spdif_out_ctls(codec, | |
557 | + spec->extra_dig_out_nid); | |
558 | + if (err < 0) | |
559 | + return err; | |
560 | + } | |
561 | } | |
562 | if (spec->dig_in_nid) { | |
563 | err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); | |
564 | @@ -580,10 +955,89 @@ | |
565 | kfree(codec->spec); | |
566 | } | |
567 | ||
568 | +/* mute internal speaker if HP is plugged */ | |
569 | +static void via_hp_automute(struct hda_codec *codec) | |
570 | +{ | |
571 | + unsigned int present; | |
572 | + struct via_spec *spec = codec->spec; | |
573 | + | |
574 | + present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0, | |
575 | + AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; | |
576 | + snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], | |
577 | + HDA_OUTPUT, 0, HDA_AMP_MUTE, | |
578 | + present ? HDA_AMP_MUTE : 0); | |
579 | +} | |
580 | + | |
581 | +static void via_gpio_control(struct hda_codec *codec) | |
582 | +{ | |
583 | + unsigned int gpio_data; | |
584 | + unsigned int vol_counter; | |
585 | + unsigned int vol; | |
586 | + unsigned int master_vol; | |
587 | + | |
588 | + struct via_spec *spec = codec->spec; | |
589 | + | |
590 | + gpio_data = snd_hda_codec_read(codec, codec->afg, 0, | |
591 | + AC_VERB_GET_GPIO_DATA, 0) & 0x03; | |
592 | + | |
593 | + vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, | |
594 | + 0xF84, 0) & 0x3F0000) >> 16; | |
595 | + | |
596 | + vol = vol_counter & 0x1F; | |
597 | + master_vol = snd_hda_codec_read(codec, 0x1A, 0, | |
598 | + AC_VERB_GET_AMP_GAIN_MUTE, | |
599 | + AC_AMP_GET_INPUT); | |
600 | + | |
601 | + if (gpio_data == 0x02) { | |
602 | + /* unmute line out */ | |
603 | + snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], | |
604 | + HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); | |
605 | + | |
606 | + if (vol_counter & 0x20) { | |
607 | + /* decrease volume */ | |
608 | + if (vol > master_vol) | |
609 | + vol = master_vol; | |
610 | + snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, | |
611 | + 0, HDA_AMP_VOLMASK, | |
612 | + master_vol-vol); | |
613 | + } else { | |
614 | + /* increase volume */ | |
615 | + snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, | |
616 | + HDA_AMP_VOLMASK, | |
617 | + ((master_vol+vol) > 0x2A) ? 0x2A : | |
618 | + (master_vol+vol)); | |
619 | + } | |
620 | + } else if (!(gpio_data & 0x02)) { | |
621 | + /* mute line out */ | |
622 | + snd_hda_codec_amp_stereo(codec, | |
623 | + spec->autocfg.line_out_pins[0], | |
624 | + HDA_OUTPUT, 0, HDA_AMP_MUTE, | |
625 | + HDA_AMP_MUTE); | |
626 | + } | |
627 | +} | |
628 | + | |
629 | +/* unsolicited event for jack sensing */ | |
630 | +static void via_unsol_event(struct hda_codec *codec, | |
631 | + unsigned int res) | |
632 | +{ | |
633 | + res >>= 26; | |
634 | + if (res == VIA_HP_EVENT) | |
635 | + via_hp_automute(codec); | |
636 | + else if (res == VIA_GPIO_EVENT) | |
637 | + via_gpio_control(codec); | |
638 | +} | |
639 | + | |
640 | +static hda_nid_t slave_dig_outs[] = { | |
641 | + 0, | |
642 | +}; | |
643 | + | |
644 | static int via_init(struct hda_codec *codec) | |
645 | { | |
646 | struct via_spec *spec = codec->spec; | |
647 | - snd_hda_sequence_write(codec, spec->init_verbs); | |
648 | + int i; | |
649 | + for (i = 0; i < spec->num_iverbs; i++) | |
650 | + snd_hda_sequence_write(codec, spec->init_verbs[i]); | |
651 | + | |
652 | /* Lydia Add for EAPD enable */ | |
653 | if (!spec->dig_in_nid) { /* No Digital In connection */ | |
654 | if (IS_VT1708_VENDORID(codec->vendor_id)) { | |
655 | @@ -611,6 +1065,9 @@ | |
656 | snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, | |
657 | AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); | |
658 | ||
659 | + /* no slave outs */ | |
660 | + codec->slave_dig_outs = slave_dig_outs; | |
661 | + | |
662 | return 0; | |
663 | } | |
664 | ||
665 | @@ -657,10 +1114,10 @@ | |
666 | spec->multiout.dac_nids[i] = 0x12; | |
667 | break; | |
668 | case AUTO_SEQ_SURROUND: | |
669 | - spec->multiout.dac_nids[i] = 0x13; | |
670 | + spec->multiout.dac_nids[i] = 0x11; | |
671 | break; | |
672 | case AUTO_SEQ_SIDE: | |
673 | - spec->multiout.dac_nids[i] = 0x11; | |
674 | + spec->multiout.dac_nids[i] = 0x13; | |
675 | break; | |
676 | } | |
677 | } | |
678 | @@ -685,7 +1142,7 @@ | |
679 | continue; | |
680 | ||
681 | if (i != AUTO_SEQ_FRONT) | |
682 | - nid_vol = 0x1b - i + 1; | |
683 | + nid_vol = 0x18 + i; | |
684 | ||
685 | if (i == AUTO_SEQ_CENLFE) { | |
686 | /* Center/LFE */ | |
687 | @@ -760,6 +1217,24 @@ | |
688 | return 0; | |
689 | } | |
690 | ||
691 | +static void create_hp_imux(struct via_spec *spec) | |
692 | +{ | |
693 | + int i; | |
694 | + struct hda_input_mux *imux = &spec->private_imux[1]; | |
695 | + static const char *texts[] = { "OFF", "ON", NULL}; | |
696 | + | |
697 | + /* for hp mode select */ | |
698 | + i = 0; | |
699 | + while (texts[i] != NULL) { | |
700 | + imux->items[imux->num_items].label = texts[i]; | |
701 | + imux->items[imux->num_items].index = i; | |
702 | + imux->num_items++; | |
703 | + i++; | |
704 | + } | |
705 | + | |
706 | + spec->hp_mux = &spec->private_imux[1]; | |
707 | +} | |
708 | + | |
709 | static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) | |
710 | { | |
711 | int err; | |
712 | @@ -780,6 +1255,8 @@ | |
713 | if (err < 0) | |
714 | return err; | |
715 | ||
716 | + create_hp_imux(spec); | |
717 | + | |
718 | return 0; | |
719 | } | |
720 | ||
721 | @@ -790,7 +1267,7 @@ | |
722 | static char *labels[] = { | |
723 | "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | |
724 | }; | |
725 | - struct hda_input_mux *imux = &spec->private_imux; | |
726 | + struct hda_input_mux *imux = &spec->private_imux[0]; | |
727 | int i, err, idx = 0; | |
728 | ||
729 | /* for internal loopback recording select */ | |
730 | @@ -840,11 +1317,36 @@ | |
731 | }; | |
732 | #endif | |
733 | ||
734 | +static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) | |
735 | +{ | |
736 | + unsigned int def_conf; | |
737 | + unsigned char seqassoc; | |
738 | + | |
739 | + def_conf = snd_hda_codec_read(codec, nid, 0, | |
740 | + AC_VERB_GET_CONFIG_DEFAULT, 0); | |
741 | + seqassoc = (unsigned char) get_defcfg_association(def_conf); | |
742 | + seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); | |
743 | + if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE) { | |
744 | + if (seqassoc == 0xff) { | |
745 | + def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); | |
746 | + snd_hda_codec_write(codec, nid, 0, | |
747 | + AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, | |
748 | + def_conf >> 24); | |
749 | + } | |
750 | + } | |
751 | + | |
752 | + return; | |
753 | +} | |
754 | + | |
755 | static int vt1708_parse_auto_config(struct hda_codec *codec) | |
756 | { | |
757 | struct via_spec *spec = codec->spec; | |
758 | int err; | |
759 | ||
760 | + /* Add HP and CD pin config connect bit re-config action */ | |
761 | + vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); | |
762 | + vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); | |
763 | + | |
764 | err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); | |
765 | if (err < 0) | |
766 | return err; | |
767 | @@ -874,9 +1376,12 @@ | |
768 | if (spec->kctl_alloc) | |
769 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | |
770 | ||
771 | - spec->init_verbs = vt1708_volume_init_verbs; | |
772 | + spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; | |
773 | + | |
774 | + spec->input_mux = &spec->private_imux[0]; | |
775 | ||
776 | - spec->input_mux = &spec->private_imux; | |
777 | + if (spec->hp_mux) | |
778 | + spec->mixers[spec->num_mixers++] = via_hp_mixer; | |
779 | ||
780 | return 1; | |
781 | } | |
782 | @@ -897,7 +1402,7 @@ | |
783 | int err; | |
784 | ||
785 | /* create a codec specific record */ | |
786 | - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); | |
787 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
788 | if (spec == NULL) | |
789 | return -ENOMEM; | |
790 | ||
791 | @@ -966,6 +1471,11 @@ | |
792 | { } /* end */ | |
793 | }; | |
794 | ||
795 | +static struct hda_verb vt1709_uniwill_init_verbs[] = { | |
796 | + {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, | |
797 | + { } | |
798 | +}; | |
799 | + | |
800 | /* | |
801 | * generic initialization of ADC, input mixers and output mixers | |
802 | */ | |
803 | @@ -1090,11 +1600,11 @@ | |
804 | break; | |
805 | case AUTO_SEQ_SURROUND: | |
806 | /* AOW3 */ | |
807 | - spec->multiout.dac_nids[i] = 0x27; | |
808 | + spec->multiout.dac_nids[i] = 0x11; | |
809 | break; | |
810 | case AUTO_SEQ_SIDE: | |
811 | /* AOW1 */ | |
812 | - spec->multiout.dac_nids[i] = 0x11; | |
813 | + spec->multiout.dac_nids[i] = 0x27; | |
814 | break; | |
815 | default: | |
816 | break; | |
817 | @@ -1203,26 +1713,26 @@ | |
818 | } else if (i == AUTO_SEQ_SURROUND) { | |
819 | sprintf(name, "%s Playback Volume", chname[i]); | |
820 | err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, | |
821 | - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, | |
822 | + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, | |
823 | HDA_OUTPUT)); | |
824 | if (err < 0) | |
825 | return err; | |
826 | sprintf(name, "%s Playback Switch", chname[i]); | |
827 | err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, | |
828 | - HDA_COMPOSE_AMP_VAL(0x29, 3, 0, | |
829 | + HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, | |
830 | HDA_OUTPUT)); | |
831 | if (err < 0) | |
832 | return err; | |
833 | } else if (i == AUTO_SEQ_SIDE) { | |
834 | sprintf(name, "%s Playback Volume", chname[i]); | |
835 | err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, | |
836 | - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, | |
837 | + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, | |
838 | HDA_OUTPUT)); | |
839 | if (err < 0) | |
840 | return err; | |
841 | sprintf(name, "%s Playback Switch", chname[i]); | |
842 | err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, | |
843 | - HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, | |
844 | + HDA_COMPOSE_AMP_VAL(0x29, 3, 0, | |
845 | HDA_OUTPUT)); | |
846 | if (err < 0) | |
847 | return err; | |
848 | @@ -1265,7 +1775,7 @@ | |
849 | static char *labels[] = { | |
850 | "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | |
851 | }; | |
852 | - struct hda_input_mux *imux = &spec->private_imux; | |
853 | + struct hda_input_mux *imux = &spec->private_imux[0]; | |
854 | int i, err, idx = 0; | |
855 | ||
856 | /* for internal loopback recording select */ | |
857 | @@ -1339,7 +1849,10 @@ | |
858 | if (spec->kctl_alloc) | |
859 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | |
860 | ||
861 | - spec->input_mux = &spec->private_imux; | |
862 | + spec->input_mux = &spec->private_imux[0]; | |
863 | + | |
864 | + if (spec->hp_mux) | |
865 | + spec->mixers[spec->num_mixers++] = via_hp_mixer; | |
866 | ||
867 | return 1; | |
868 | } | |
869 | @@ -1360,7 +1873,7 @@ | |
870 | int err; | |
871 | ||
872 | /* create a codec specific record */ | |
873 | - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); | |
874 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
875 | if (spec == NULL) | |
876 | return -ENOMEM; | |
877 | ||
878 | @@ -1375,7 +1888,8 @@ | |
879 | "Using genenic mode...\n"); | |
880 | } | |
881 | ||
882 | - spec->init_verbs = vt1709_10ch_volume_init_verbs; | |
883 | + spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; | |
884 | + spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; | |
885 | ||
886 | spec->stream_name_analog = "VT1709 Analog"; | |
887 | spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; | |
888 | @@ -1396,6 +1910,7 @@ | |
889 | codec->patch_ops = via_patch_ops; | |
890 | ||
891 | codec->patch_ops.init = via_auto_init; | |
892 | + codec->patch_ops.unsol_event = via_unsol_event; | |
893 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
894 | spec->loopback.amplist = vt1709_loopbacks; | |
895 | #endif | |
896 | @@ -1451,7 +1966,7 @@ | |
897 | int err; | |
898 | ||
899 | /* create a codec specific record */ | |
900 | - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); | |
901 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
902 | if (spec == NULL) | |
903 | return -ENOMEM; | |
904 | ||
905 | @@ -1466,7 +1981,8 @@ | |
906 | "Using genenic mode...\n"); | |
907 | } | |
908 | ||
909 | - spec->init_verbs = vt1709_6ch_volume_init_verbs; | |
910 | + spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; | |
911 | + spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; | |
912 | ||
913 | spec->stream_name_analog = "VT1709 Analog"; | |
914 | spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; | |
915 | @@ -1487,6 +2003,7 @@ | |
916 | codec->patch_ops = via_patch_ops; | |
917 | ||
918 | codec->patch_ops.init = via_auto_init; | |
919 | + codec->patch_ops.unsol_event = via_unsol_event; | |
920 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
921 | spec->loopback.amplist = vt1709_loopbacks; | |
922 | #endif | |
923 | @@ -1586,27 +2103,32 @@ | |
924 | { } | |
925 | }; | |
926 | ||
927 | +static struct hda_verb vt1708B_uniwill_init_verbs[] = { | |
928 | + {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, | |
929 | + { } | |
930 | +}; | |
931 | + | |
932 | static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { | |
933 | - .substreams = 1, | |
934 | + .substreams = 2, | |
935 | .channels_min = 2, | |
936 | .channels_max = 8, | |
937 | .nid = 0x10, /* NID to query formats and rates */ | |
938 | .ops = { | |
939 | .open = via_playback_pcm_open, | |
940 | - .prepare = via_playback_pcm_prepare, | |
941 | - .cleanup = via_playback_pcm_cleanup | |
942 | + .prepare = via_playback_multi_pcm_prepare, | |
943 | + .cleanup = via_playback_multi_pcm_cleanup | |
944 | }, | |
945 | }; | |
946 | ||
947 | static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { | |
948 | - .substreams = 1, | |
949 | + .substreams = 2, | |
950 | .channels_min = 2, | |
951 | .channels_max = 4, | |
952 | .nid = 0x10, /* NID to query formats and rates */ | |
953 | .ops = { | |
954 | .open = via_playback_pcm_open, | |
955 | - .prepare = via_playback_pcm_prepare, | |
956 | - .cleanup = via_playback_pcm_cleanup | |
957 | + .prepare = via_playback_multi_pcm_prepare, | |
958 | + .cleanup = via_playback_multi_pcm_cleanup | |
959 | }, | |
960 | }; | |
961 | ||
962 | @@ -1662,10 +2184,10 @@ | |
963 | spec->multiout.dac_nids[i] = 0x24; | |
964 | break; | |
965 | case AUTO_SEQ_SURROUND: | |
966 | - spec->multiout.dac_nids[i] = 0x25; | |
967 | + spec->multiout.dac_nids[i] = 0x11; | |
968 | break; | |
969 | case AUTO_SEQ_SIDE: | |
970 | - spec->multiout.dac_nids[i] = 0x11; | |
971 | + spec->multiout.dac_nids[i] = 0x25; | |
972 | break; | |
973 | } | |
974 | } | |
975 | @@ -1680,7 +2202,7 @@ | |
976 | { | |
977 | char name[32]; | |
978 | static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; | |
979 | - hda_nid_t nid_vols[] = {0x16, 0x27, 0x26, 0x18}; | |
980 | + hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; | |
981 | hda_nid_t nid, nid_vol = 0; | |
982 | int i, err; | |
983 | ||
984 | @@ -1785,6 +2307,8 @@ | |
985 | if (err < 0) | |
986 | return err; | |
987 | ||
988 | + create_hp_imux(spec); | |
989 | + | |
990 | return 0; | |
991 | } | |
992 | ||
993 | @@ -1795,7 +2319,7 @@ | |
994 | static char *labels[] = { | |
995 | "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | |
996 | }; | |
997 | - struct hda_input_mux *imux = &spec->private_imux; | |
998 | + struct hda_input_mux *imux = &spec->private_imux[0]; | |
999 | int i, err, idx = 0; | |
1000 | ||
1001 | /* for internal loopback recording select */ | |
1002 | @@ -1869,7 +2393,10 @@ | |
1003 | if (spec->kctl_alloc) | |
1004 | spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | |
1005 | ||
1006 | - spec->input_mux = &spec->private_imux; | |
1007 | + spec->input_mux = &spec->private_imux[0]; | |
1008 | + | |
1009 | + if (spec->hp_mux) | |
1010 | + spec->mixers[spec->num_mixers++] = via_hp_mixer; | |
1011 | ||
1012 | return 1; | |
1013 | } | |
1014 | @@ -1890,7 +2417,7 @@ | |
1015 | int err; | |
1016 | ||
1017 | /* create a codec specific record */ | |
1018 | - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); | |
1019 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
1020 | if (spec == NULL) | |
1021 | return -ENOMEM; | |
1022 | ||
1023 | @@ -1906,7 +2433,8 @@ | |
1024 | "from BIOS. Using genenic mode...\n"); | |
1025 | } | |
1026 | ||
1027 | - spec->init_verbs = vt1708B_8ch_volume_init_verbs; | |
1028 | + spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; | |
1029 | + spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; | |
1030 | ||
1031 | spec->stream_name_analog = "VT1708B Analog"; | |
1032 | spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; | |
1033 | @@ -1926,6 +2454,7 @@ | |
1034 | codec->patch_ops = via_patch_ops; | |
1035 | ||
1036 | codec->patch_ops.init = via_auto_init; | |
1037 | + codec->patch_ops.unsol_event = via_unsol_event; | |
1038 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
1039 | spec->loopback.amplist = vt1708B_loopbacks; | |
1040 | #endif | |
1041 | @@ -1939,7 +2468,7 @@ | |
1042 | int err; | |
1043 | ||
1044 | /* create a codec specific record */ | |
1045 | - spec = kcalloc(1, sizeof(*spec), GFP_KERNEL); | |
1046 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
1047 | if (spec == NULL) | |
1048 | return -ENOMEM; | |
1049 | ||
1050 | @@ -1955,7 +2484,8 @@ | |
1051 | "from BIOS. Using genenic mode...\n"); | |
1052 | } | |
1053 | ||
1054 | - spec->init_verbs = vt1708B_4ch_volume_init_verbs; | |
1055 | + spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; | |
1056 | + spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; | |
1057 | ||
1058 | spec->stream_name_analog = "VT1708B Analog"; | |
1059 | spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; | |
1060 | @@ -1975,6 +2505,7 @@ | |
1061 | codec->patch_ops = via_patch_ops; | |
1062 | ||
1063 | codec->patch_ops.init = via_auto_init; | |
1064 | + codec->patch_ops.unsol_event = via_unsol_event; | |
1065 | #ifdef CONFIG_SND_HDA_POWER_SAVE | |
1066 | spec->loopback.amplist = vt1708B_loopbacks; | |
1067 | #endif | |
1068 | @@ -1982,6 +2513,752 @@ | |
1069 | return 0; | |
1070 | } | |
1071 | ||
1072 | +/* Patch for VT1708S */ | |
1073 | + | |
1074 | +/* VT1708S software backdoor based override for buggy hardware micboost | |
1075 | + * setting */ | |
1076 | +#define MIC_BOOST_VOLUME(xname, nid) { \ | |
1077 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | |
1078 | + .name = xname, \ | |
1079 | + .index = 0, \ | |
1080 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \ | |
1081 | + SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ | |
1082 | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \ | |
1083 | + .info = mic_boost_volume_info, \ | |
1084 | + .get = snd_hda_mixer_amp_volume_get, \ | |
1085 | + .put = snd_hda_mixer_amp_volume_put, \ | |
1086 | + .tlv = { .c = mic_boost_tlv }, \ | |
1087 | + .private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, HDA_INPUT) } | |
1088 | + | |
1089 | +/* capture mixer elements */ | |
1090 | +static struct snd_kcontrol_new vt1708S_capture_mixer[] = { | |
1091 | + HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), | |
1092 | + HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), | |
1093 | + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), | |
1094 | + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), | |
1095 | + MIC_BOOST_VOLUME("Mic Boost Capture Volume", 0x1A), | |
1096 | + MIC_BOOST_VOLUME("Front Mic Boost Capture Volume", 0x1E), | |
1097 | + { | |
1098 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
1099 | + /* The multiple "Capture Source" controls confuse alsamixer | |
1100 | + * So call somewhat different.. | |
1101 | + */ | |
1102 | + /* .name = "Capture Source", */ | |
1103 | + .name = "Input Source", | |
1104 | + .count = 1, | |
1105 | + .info = via_mux_enum_info, | |
1106 | + .get = via_mux_enum_get, | |
1107 | + .put = via_mux_enum_put, | |
1108 | + }, | |
1109 | + { } /* end */ | |
1110 | +}; | |
1111 | + | |
1112 | +static struct hda_verb vt1708S_volume_init_verbs[] = { | |
1113 | + /* Unmute ADC0-1 and set the default input to mic-in */ | |
1114 | + {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1115 | + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1116 | + | |
1117 | + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the | |
1118 | + * analog-loopback mixer widget */ | |
1119 | + /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ | |
1120 | + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1121 | + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | |
1122 | + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | |
1123 | + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, | |
1124 | + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, | |
1125 | + | |
1126 | + /* Setup default input of PW4 to MW0 */ | |
1127 | + {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, | |
1128 | + /* PW9, PW10 Output enable */ | |
1129 | + {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, | |
1130 | + {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, | |
1131 | + /* Enable Mic Boost Volume backdoor */ | |
1132 | + {0x1, 0xf98, 0x1}, | |
1133 | + { } | |
1134 | +}; | |
1135 | + | |
1136 | +static struct hda_verb vt1708S_uniwill_init_verbs[] = { | |
1137 | + {0x1D, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, | |
1138 | + { } | |
1139 | +}; | |
1140 | + | |
1141 | +static struct hda_pcm_stream vt1708S_pcm_analog_playback = { | |
1142 | + .substreams = 2, | |
1143 | + .channels_min = 2, | |
1144 | + .channels_max = 8, | |
1145 | + .nid = 0x10, /* NID to query formats and rates */ | |
1146 | + .ops = { | |
1147 | + .open = via_playback_pcm_open, | |
1148 | + .prepare = via_playback_pcm_prepare, | |
1149 | + .cleanup = via_playback_pcm_cleanup | |
1150 | + }, | |
1151 | +}; | |
1152 | + | |
1153 | +static struct hda_pcm_stream vt1708S_pcm_analog_capture = { | |
1154 | + .substreams = 2, | |
1155 | + .channels_min = 2, | |
1156 | + .channels_max = 2, | |
1157 | + .nid = 0x13, /* NID to query formats and rates */ | |
1158 | + .ops = { | |
1159 | + .prepare = via_capture_pcm_prepare, | |
1160 | + .cleanup = via_capture_pcm_cleanup | |
1161 | + }, | |
1162 | +}; | |
1163 | + | |
1164 | +static struct hda_pcm_stream vt1708S_pcm_digital_playback = { | |
1165 | + .substreams = 2, | |
1166 | + .channels_min = 2, | |
1167 | + .channels_max = 2, | |
1168 | + /* NID is set in via_build_pcms */ | |
1169 | + .ops = { | |
1170 | + .open = via_dig_playback_pcm_open, | |
1171 | + .close = via_dig_playback_pcm_close, | |
1172 | + .prepare = via_dig_playback_pcm_prepare | |
1173 | + }, | |
1174 | +}; | |
1175 | + | |
1176 | +/* fill in the dac_nids table from the parsed pin configuration */ | |
1177 | +static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, | |
1178 | + const struct auto_pin_cfg *cfg) | |
1179 | +{ | |
1180 | + int i; | |
1181 | + hda_nid_t nid; | |
1182 | + | |
1183 | + spec->multiout.num_dacs = cfg->line_outs; | |
1184 | + | |
1185 | + spec->multiout.dac_nids = spec->private_dac_nids; | |
1186 | + | |
1187 | + for (i = 0; i < 4; i++) { | |
1188 | + nid = cfg->line_out_pins[i]; | |
1189 | + if (nid) { | |
1190 | + /* config dac list */ | |
1191 | + switch (i) { | |
1192 | + case AUTO_SEQ_FRONT: | |
1193 | + spec->multiout.dac_nids[i] = 0x10; | |
1194 | + break; | |
1195 | + case AUTO_SEQ_CENLFE: | |
1196 | + spec->multiout.dac_nids[i] = 0x24; | |
1197 | + break; | |
1198 | + case AUTO_SEQ_SURROUND: | |
1199 | + spec->multiout.dac_nids[i] = 0x11; | |
1200 | + break; | |
1201 | + case AUTO_SEQ_SIDE: | |
1202 | + spec->multiout.dac_nids[i] = 0x25; | |
1203 | + break; | |
1204 | + } | |
1205 | + } | |
1206 | + } | |
1207 | + | |
1208 | + return 0; | |
1209 | +} | |
1210 | + | |
1211 | +/* add playback controls from the parsed DAC table */ | |
1212 | +static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, | |
1213 | + const struct auto_pin_cfg *cfg) | |
1214 | +{ | |
1215 | + char name[32]; | |
1216 | + static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; | |
1217 | + hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; | |
1218 | + hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; | |
1219 | + hda_nid_t nid, nid_vol, nid_mute; | |
1220 | + int i, err; | |
1221 | + | |
1222 | + for (i = 0; i <= AUTO_SEQ_SIDE; i++) { | |
1223 | + nid = cfg->line_out_pins[i]; | |
1224 | + | |
1225 | + if (!nid) | |
1226 | + continue; | |
1227 | + | |
1228 | + nid_vol = nid_vols[i]; | |
1229 | + nid_mute = nid_mutes[i]; | |
1230 | + | |
1231 | + if (i == AUTO_SEQ_CENLFE) { | |
1232 | + /* Center/LFE */ | |
1233 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1234 | + "Center Playback Volume", | |
1235 | + HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, | |
1236 | + HDA_OUTPUT)); | |
1237 | + if (err < 0) | |
1238 | + return err; | |
1239 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1240 | + "LFE Playback Volume", | |
1241 | + HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, | |
1242 | + HDA_OUTPUT)); | |
1243 | + if (err < 0) | |
1244 | + return err; | |
1245 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1246 | + "Center Playback Switch", | |
1247 | + HDA_COMPOSE_AMP_VAL(nid_mute, | |
1248 | + 1, 0, | |
1249 | + HDA_OUTPUT)); | |
1250 | + if (err < 0) | |
1251 | + return err; | |
1252 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1253 | + "LFE Playback Switch", | |
1254 | + HDA_COMPOSE_AMP_VAL(nid_mute, | |
1255 | + 2, 0, | |
1256 | + HDA_OUTPUT)); | |
1257 | + if (err < 0) | |
1258 | + return err; | |
1259 | + } else if (i == AUTO_SEQ_FRONT) { | |
1260 | + /* add control to mixer index 0 */ | |
1261 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1262 | + "Master Front Playback Volume", | |
1263 | + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, | |
1264 | + HDA_INPUT)); | |
1265 | + if (err < 0) | |
1266 | + return err; | |
1267 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1268 | + "Master Front Playback Switch", | |
1269 | + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, | |
1270 | + HDA_INPUT)); | |
1271 | + if (err < 0) | |
1272 | + return err; | |
1273 | + | |
1274 | + /* Front */ | |
1275 | + sprintf(name, "%s Playback Volume", chname[i]); | |
1276 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, | |
1277 | + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, | |
1278 | + HDA_OUTPUT)); | |
1279 | + if (err < 0) | |
1280 | + return err; | |
1281 | + sprintf(name, "%s Playback Switch", chname[i]); | |
1282 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, | |
1283 | + HDA_COMPOSE_AMP_VAL(nid_mute, | |
1284 | + 3, 0, | |
1285 | + HDA_OUTPUT)); | |
1286 | + if (err < 0) | |
1287 | + return err; | |
1288 | + } else { | |
1289 | + sprintf(name, "%s Playback Volume", chname[i]); | |
1290 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, | |
1291 | + HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, | |
1292 | + HDA_OUTPUT)); | |
1293 | + if (err < 0) | |
1294 | + return err; | |
1295 | + sprintf(name, "%s Playback Switch", chname[i]); | |
1296 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, | |
1297 | + HDA_COMPOSE_AMP_VAL(nid_mute, | |
1298 | + 3, 0, | |
1299 | + HDA_OUTPUT)); | |
1300 | + if (err < 0) | |
1301 | + return err; | |
1302 | + } | |
1303 | + } | |
1304 | + | |
1305 | + return 0; | |
1306 | +} | |
1307 | + | |
1308 | +static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) | |
1309 | +{ | |
1310 | + int err; | |
1311 | + | |
1312 | + if (!pin) | |
1313 | + return 0; | |
1314 | + | |
1315 | + spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ | |
1316 | + | |
1317 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1318 | + "Headphone Playback Volume", | |
1319 | + HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); | |
1320 | + if (err < 0) | |
1321 | + return err; | |
1322 | + | |
1323 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1324 | + "Headphone Playback Switch", | |
1325 | + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); | |
1326 | + if (err < 0) | |
1327 | + return err; | |
1328 | + | |
1329 | + create_hp_imux(spec); | |
1330 | + | |
1331 | + return 0; | |
1332 | +} | |
1333 | + | |
1334 | +/* create playback/capture controls for input pins */ | |
1335 | +static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, | |
1336 | + const struct auto_pin_cfg *cfg) | |
1337 | +{ | |
1338 | + static char *labels[] = { | |
1339 | + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | |
1340 | + }; | |
1341 | + struct hda_input_mux *imux = &spec->private_imux[0]; | |
1342 | + int i, err, idx = 0; | |
1343 | + | |
1344 | + /* for internal loopback recording select */ | |
1345 | + imux->items[imux->num_items].label = "Stereo Mixer"; | |
1346 | + imux->items[imux->num_items].index = 5; | |
1347 | + imux->num_items++; | |
1348 | + | |
1349 | + for (i = 0; i < AUTO_PIN_LAST; i++) { | |
1350 | + if (!cfg->input_pins[i]) | |
1351 | + continue; | |
1352 | + | |
1353 | + switch (cfg->input_pins[i]) { | |
1354 | + case 0x1a: /* Mic */ | |
1355 | + idx = 2; | |
1356 | + break; | |
1357 | + | |
1358 | + case 0x1b: /* Line In */ | |
1359 | + idx = 3; | |
1360 | + break; | |
1361 | + | |
1362 | + case 0x1e: /* Front Mic */ | |
1363 | + idx = 4; | |
1364 | + break; | |
1365 | + | |
1366 | + case 0x1f: /* CD */ | |
1367 | + idx = 1; | |
1368 | + break; | |
1369 | + } | |
1370 | + err = via_new_analog_input(spec, cfg->input_pins[i], labels[i], | |
1371 | + idx, 0x16); | |
1372 | + if (err < 0) | |
1373 | + return err; | |
1374 | + imux->items[imux->num_items].label = labels[i]; | |
1375 | + imux->items[imux->num_items].index = idx-1; | |
1376 | + imux->num_items++; | |
1377 | + } | |
1378 | + return 0; | |
1379 | +} | |
1380 | + | |
1381 | +static int vt1708S_parse_auto_config(struct hda_codec *codec) | |
1382 | +{ | |
1383 | + struct via_spec *spec = codec->spec; | |
1384 | + int err; | |
1385 | + static hda_nid_t vt1708s_ignore[] = {0x21, 0}; | |
1386 | + | |
1387 | + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, | |
1388 | + vt1708s_ignore); | |
1389 | + if (err < 0) | |
1390 | + return err; | |
1391 | + err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); | |
1392 | + if (err < 0) | |
1393 | + return err; | |
1394 | + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) | |
1395 | + return 0; /* can't find valid BIOS pin config */ | |
1396 | + | |
1397 | + err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); | |
1398 | + if (err < 0) | |
1399 | + return err; | |
1400 | + err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); | |
1401 | + if (err < 0) | |
1402 | + return err; | |
1403 | + err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); | |
1404 | + if (err < 0) | |
1405 | + return err; | |
1406 | + | |
1407 | + spec->multiout.max_channels = spec->multiout.num_dacs * 2; | |
1408 | + | |
1409 | + if (spec->autocfg.dig_out_pin) | |
1410 | + spec->multiout.dig_out_nid = VT1708S_DIGOUT_NID; | |
1411 | + | |
1412 | + spec->extra_dig_out_nid = 0x15; | |
1413 | + | |
1414 | + if (spec->kctl_alloc) | |
1415 | + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | |
1416 | + | |
1417 | + spec->input_mux = &spec->private_imux[0]; | |
1418 | + | |
1419 | + if (spec->hp_mux) | |
1420 | + spec->mixers[spec->num_mixers++] = via_hp_mixer; | |
1421 | + | |
1422 | + return 1; | |
1423 | +} | |
1424 | + | |
1425 | +#ifdef CONFIG_SND_HDA_POWER_SAVE | |
1426 | +static struct hda_amp_list vt1708S_loopbacks[] = { | |
1427 | + { 0x16, HDA_INPUT, 1 }, | |
1428 | + { 0x16, HDA_INPUT, 2 }, | |
1429 | + { 0x16, HDA_INPUT, 3 }, | |
1430 | + { 0x16, HDA_INPUT, 4 }, | |
1431 | + { } /* end */ | |
1432 | +}; | |
1433 | +#endif | |
1434 | + | |
1435 | +static int patch_vt1708S(struct hda_codec *codec) | |
1436 | +{ | |
1437 | + struct via_spec *spec; | |
1438 | + int err; | |
1439 | + | |
1440 | + /* create a codec specific record */ | |
1441 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
1442 | + if (spec == NULL) | |
1443 | + return -ENOMEM; | |
1444 | + | |
1445 | + codec->spec = spec; | |
1446 | + | |
1447 | + /* automatic parse from the BIOS config */ | |
1448 | + err = vt1708S_parse_auto_config(codec); | |
1449 | + if (err < 0) { | |
1450 | + via_free(codec); | |
1451 | + return err; | |
1452 | + } else if (!err) { | |
1453 | + printk(KERN_INFO "hda_codec: Cannot set up configuration " | |
1454 | + "from BIOS. Using genenic mode...\n"); | |
1455 | + } | |
1456 | + | |
1457 | + spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; | |
1458 | + spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; | |
1459 | + | |
1460 | + spec->stream_name_analog = "VT1708S Analog"; | |
1461 | + spec->stream_analog_playback = &vt1708S_pcm_analog_playback; | |
1462 | + spec->stream_analog_capture = &vt1708S_pcm_analog_capture; | |
1463 | + | |
1464 | + spec->stream_name_digital = "VT1708S Digital"; | |
1465 | + spec->stream_digital_playback = &vt1708S_pcm_digital_playback; | |
1466 | + | |
1467 | + if (!spec->adc_nids && spec->input_mux) { | |
1468 | + spec->adc_nids = vt1708S_adc_nids; | |
1469 | + spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); | |
1470 | + spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; | |
1471 | + spec->num_mixers++; | |
1472 | + } | |
1473 | + | |
1474 | + codec->patch_ops = via_patch_ops; | |
1475 | + | |
1476 | + codec->patch_ops.init = via_auto_init; | |
1477 | + codec->patch_ops.unsol_event = via_unsol_event; | |
1478 | +#ifdef CONFIG_SND_HDA_POWER_SAVE | |
1479 | + spec->loopback.amplist = vt1708S_loopbacks; | |
1480 | +#endif | |
1481 | + | |
1482 | + return 0; | |
1483 | +} | |
1484 | + | |
1485 | +/* Patch for VT1702 */ | |
1486 | + | |
1487 | +/* capture mixer elements */ | |
1488 | +static struct snd_kcontrol_new vt1702_capture_mixer[] = { | |
1489 | + HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), | |
1490 | + HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), | |
1491 | + HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), | |
1492 | + HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), | |
1493 | + HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), | |
1494 | + HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), | |
1495 | + HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, | |
1496 | + HDA_INPUT), | |
1497 | + { | |
1498 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | |
1499 | + /* The multiple "Capture Source" controls confuse alsamixer | |
1500 | + * So call somewhat different.. | |
1501 | + */ | |
1502 | + /* .name = "Capture Source", */ | |
1503 | + .name = "Input Source", | |
1504 | + .count = 1, | |
1505 | + .info = via_mux_enum_info, | |
1506 | + .get = via_mux_enum_get, | |
1507 | + .put = via_mux_enum_put, | |
1508 | + }, | |
1509 | + { } /* end */ | |
1510 | +}; | |
1511 | + | |
1512 | +static struct hda_verb vt1702_volume_init_verbs[] = { | |
1513 | + /* | |
1514 | + * Unmute ADC0-1 and set the default input to mic-in | |
1515 | + */ | |
1516 | + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1517 | + {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1518 | + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1519 | + | |
1520 | + | |
1521 | + /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback | |
1522 | + * mixer widget | |
1523 | + */ | |
1524 | + /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ | |
1525 | + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, | |
1526 | + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, | |
1527 | + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, | |
1528 | + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, | |
1529 | + {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, | |
1530 | + | |
1531 | + /* Setup default input of PW4 to MW0 */ | |
1532 | + {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, | |
1533 | + /* PW6 PW7 Output enable */ | |
1534 | + {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, | |
1535 | + {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, | |
1536 | + { } | |
1537 | +}; | |
1538 | + | |
1539 | +static struct hda_verb vt1702_uniwill_init_verbs[] = { | |
1540 | + {0x01, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_GPIO_EVENT}, | |
1541 | + {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_HP_EVENT}, | |
1542 | + { } | |
1543 | +}; | |
1544 | + | |
1545 | +static struct hda_pcm_stream vt1702_pcm_analog_playback = { | |
1546 | + .substreams = 2, | |
1547 | + .channels_min = 2, | |
1548 | + .channels_max = 2, | |
1549 | + .nid = 0x10, /* NID to query formats and rates */ | |
1550 | + .ops = { | |
1551 | + .open = via_playback_pcm_open, | |
1552 | + .prepare = via_playback_multi_pcm_prepare, | |
1553 | + .cleanup = via_playback_multi_pcm_cleanup | |
1554 | + }, | |
1555 | +}; | |
1556 | + | |
1557 | +static struct hda_pcm_stream vt1702_pcm_analog_capture = { | |
1558 | + .substreams = 3, | |
1559 | + .channels_min = 2, | |
1560 | + .channels_max = 2, | |
1561 | + .nid = 0x12, /* NID to query formats and rates */ | |
1562 | + .ops = { | |
1563 | + .prepare = via_capture_pcm_prepare, | |
1564 | + .cleanup = via_capture_pcm_cleanup | |
1565 | + }, | |
1566 | +}; | |
1567 | + | |
1568 | +static struct hda_pcm_stream vt1702_pcm_digital_playback = { | |
1569 | + .substreams = 2, | |
1570 | + .channels_min = 2, | |
1571 | + .channels_max = 2, | |
1572 | + /* NID is set in via_build_pcms */ | |
1573 | + .ops = { | |
1574 | + .open = via_dig_playback_pcm_open, | |
1575 | + .close = via_dig_playback_pcm_close, | |
1576 | + .prepare = via_dig_playback_pcm_prepare | |
1577 | + }, | |
1578 | +}; | |
1579 | + | |
1580 | +/* fill in the dac_nids table from the parsed pin configuration */ | |
1581 | +static int vt1702_auto_fill_dac_nids(struct via_spec *spec, | |
1582 | + const struct auto_pin_cfg *cfg) | |
1583 | +{ | |
1584 | + spec->multiout.num_dacs = 1; | |
1585 | + spec->multiout.dac_nids = spec->private_dac_nids; | |
1586 | + | |
1587 | + if (cfg->line_out_pins[0]) { | |
1588 | + /* config dac list */ | |
1589 | + spec->multiout.dac_nids[0] = 0x10; | |
1590 | + } | |
1591 | + | |
1592 | + return 0; | |
1593 | +} | |
1594 | + | |
1595 | +/* add playback controls from the parsed DAC table */ | |
1596 | +static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, | |
1597 | + const struct auto_pin_cfg *cfg) | |
1598 | +{ | |
1599 | + int err; | |
1600 | + | |
1601 | + if (!cfg->line_out_pins[0]) | |
1602 | + return -1; | |
1603 | + | |
1604 | + /* add control to mixer index 0 */ | |
1605 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1606 | + "Master Front Playback Volume", | |
1607 | + HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); | |
1608 | + if (err < 0) | |
1609 | + return err; | |
1610 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1611 | + "Master Front Playback Switch", | |
1612 | + HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); | |
1613 | + if (err < 0) | |
1614 | + return err; | |
1615 | + | |
1616 | + /* Front */ | |
1617 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1618 | + "Front Playback Volume", | |
1619 | + HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); | |
1620 | + if (err < 0) | |
1621 | + return err; | |
1622 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1623 | + "Front Playback Switch", | |
1624 | + HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); | |
1625 | + if (err < 0) | |
1626 | + return err; | |
1627 | + | |
1628 | + return 0; | |
1629 | +} | |
1630 | + | |
1631 | +static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) | |
1632 | +{ | |
1633 | + int err; | |
1634 | + | |
1635 | + if (!pin) | |
1636 | + return 0; | |
1637 | + | |
1638 | + spec->multiout.hp_nid = 0x1D; | |
1639 | + | |
1640 | + err = via_add_control(spec, VIA_CTL_WIDGET_VOL, | |
1641 | + "Headphone Playback Volume", | |
1642 | + HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); | |
1643 | + if (err < 0) | |
1644 | + return err; | |
1645 | + | |
1646 | + err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, | |
1647 | + "Headphone Playback Switch", | |
1648 | + HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); | |
1649 | + if (err < 0) | |
1650 | + return err; | |
1651 | + | |
1652 | + create_hp_imux(spec); | |
1653 | + | |
1654 | + return 0; | |
1655 | +} | |
1656 | + | |
1657 | +/* create playback/capture controls for input pins */ | |
1658 | +static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, | |
1659 | + const struct auto_pin_cfg *cfg) | |
1660 | +{ | |
1661 | + static char *labels[] = { | |
1662 | + "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL | |
1663 | + }; | |
1664 | + struct hda_input_mux *imux = &spec->private_imux[0]; | |
1665 | + int i, err, idx = 0; | |
1666 | + | |
1667 | + /* for internal loopback recording select */ | |
1668 | + imux->items[imux->num_items].label = "Stereo Mixer"; | |
1669 | + imux->items[imux->num_items].index = 3; | |
1670 | + imux->num_items++; | |
1671 | + | |
1672 | + for (i = 0; i < AUTO_PIN_LAST; i++) { | |
1673 | + if (!cfg->input_pins[i]) | |
1674 | + continue; | |
1675 | + | |
1676 | + switch (cfg->input_pins[i]) { | |
1677 | + case 0x14: /* Mic */ | |
1678 | + idx = 1; | |
1679 | + break; | |
1680 | + | |
1681 | + case 0x15: /* Line In */ | |
1682 | + idx = 2; | |
1683 | + break; | |
1684 | + | |
1685 | + case 0x18: /* Front Mic */ | |
1686 | + idx = 3; | |
1687 | + break; | |
1688 | + } | |
1689 | + err = via_new_analog_input(spec, cfg->input_pins[i], | |
1690 | + labels[i], idx, 0x1A); | |
1691 | + if (err < 0) | |
1692 | + return err; | |
1693 | + imux->items[imux->num_items].label = labels[i]; | |
1694 | + imux->items[imux->num_items].index = idx-1; | |
1695 | + imux->num_items++; | |
1696 | + } | |
1697 | + return 0; | |
1698 | +} | |
1699 | + | |
1700 | +static int vt1702_parse_auto_config(struct hda_codec *codec) | |
1701 | +{ | |
1702 | + struct via_spec *spec = codec->spec; | |
1703 | + int err; | |
1704 | + static hda_nid_t vt1702_ignore[] = {0x1C, 0}; | |
1705 | + | |
1706 | + err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, | |
1707 | + vt1702_ignore); | |
1708 | + if (err < 0) | |
1709 | + return err; | |
1710 | + err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); | |
1711 | + if (err < 0) | |
1712 | + return err; | |
1713 | + if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) | |
1714 | + return 0; /* can't find valid BIOS pin config */ | |
1715 | + | |
1716 | + err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); | |
1717 | + if (err < 0) | |
1718 | + return err; | |
1719 | + err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); | |
1720 | + if (err < 0) | |
1721 | + return err; | |
1722 | + err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); | |
1723 | + if (err < 0) | |
1724 | + return err; | |
1725 | + | |
1726 | + spec->multiout.max_channels = spec->multiout.num_dacs * 2; | |
1727 | + | |
1728 | + if (spec->autocfg.dig_out_pin) | |
1729 | + spec->multiout.dig_out_nid = VT1702_DIGOUT_NID; | |
1730 | + | |
1731 | + spec->extra_dig_out_nid = 0x1B; | |
1732 | + | |
1733 | + if (spec->kctl_alloc) | |
1734 | + spec->mixers[spec->num_mixers++] = spec->kctl_alloc; | |
1735 | + | |
1736 | + spec->input_mux = &spec->private_imux[0]; | |
1737 | + | |
1738 | + if (spec->hp_mux) | |
1739 | + spec->mixers[spec->num_mixers++] = via_hp_mixer; | |
1740 | + | |
1741 | + return 1; | |
1742 | +} | |
1743 | + | |
1744 | +#ifdef CONFIG_SND_HDA_POWER_SAVE | |
1745 | +static struct hda_amp_list vt1702_loopbacks[] = { | |
1746 | + { 0x1A, HDA_INPUT, 1 }, | |
1747 | + { 0x1A, HDA_INPUT, 2 }, | |
1748 | + { 0x1A, HDA_INPUT, 3 }, | |
1749 | + { 0x1A, HDA_INPUT, 4 }, | |
1750 | + { } /* end */ | |
1751 | +}; | |
1752 | +#endif | |
1753 | + | |
1754 | +static int patch_vt1702(struct hda_codec *codec) | |
1755 | +{ | |
1756 | + struct via_spec *spec; | |
1757 | + int err; | |
1758 | + unsigned int response; | |
1759 | + unsigned char control; | |
1760 | + | |
1761 | + /* create a codec specific record */ | |
1762 | + spec = kzalloc(sizeof(*spec), GFP_KERNEL); | |
1763 | + if (spec == NULL) | |
1764 | + return -ENOMEM; | |
1765 | + | |
1766 | + codec->spec = spec; | |
1767 | + | |
1768 | + /* automatic parse from the BIOS config */ | |
1769 | + err = vt1702_parse_auto_config(codec); | |
1770 | + if (err < 0) { | |
1771 | + via_free(codec); | |
1772 | + return err; | |
1773 | + } else if (!err) { | |
1774 | + printk(KERN_INFO "hda_codec: Cannot set up configuration " | |
1775 | + "from BIOS. Using genenic mode...\n"); | |
1776 | + } | |
1777 | + | |
1778 | + spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; | |
1779 | + spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; | |
1780 | + | |
1781 | + spec->stream_name_analog = "VT1702 Analog"; | |
1782 | + spec->stream_analog_playback = &vt1702_pcm_analog_playback; | |
1783 | + spec->stream_analog_capture = &vt1702_pcm_analog_capture; | |
1784 | + | |
1785 | + spec->stream_name_digital = "VT1702 Digital"; | |
1786 | + spec->stream_digital_playback = &vt1702_pcm_digital_playback; | |
1787 | + | |
1788 | + if (!spec->adc_nids && spec->input_mux) { | |
1789 | + spec->adc_nids = vt1702_adc_nids; | |
1790 | + spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); | |
1791 | + spec->mixers[spec->num_mixers] = vt1702_capture_mixer; | |
1792 | + spec->num_mixers++; | |
1793 | + } | |
1794 | + | |
1795 | + codec->patch_ops = via_patch_ops; | |
1796 | + | |
1797 | + codec->patch_ops.init = via_auto_init; | |
1798 | + codec->patch_ops.unsol_event = via_unsol_event; | |
1799 | +#ifdef CONFIG_SND_HDA_POWER_SAVE | |
1800 | + spec->loopback.amplist = vt1702_loopbacks; | |
1801 | +#endif | |
1802 | + | |
1803 | + /* Open backdoor */ | |
1804 | + response = snd_hda_codec_read(codec, codec->afg, 0, 0xF8C, 0); | |
1805 | + control = (unsigned char)(response & 0xff); | |
1806 | + control |= 0x3; | |
1807 | + snd_hda_codec_write(codec, codec->afg, 0, 0xF88, control); | |
1808 | + | |
1809 | + /* Enable GPIO 0&1 for volume&mute control */ | |
1810 | + /* Enable GPIO 2 for DMIC-DATA */ | |
1811 | + response = snd_hda_codec_read(codec, codec->afg, 0, 0xF84, 0); | |
1812 | + control = (unsigned char)((response >> 16) & 0x3f); | |
1813 | + snd_hda_codec_write(codec, codec->afg, 0, 0xF82, control); | |
1814 | + | |
1815 | + return 0; | |
1816 | +} | |
1817 | + | |
1818 | /* | |
1819 | * patch entries | |
1820 | */ | |
1821 | @@ -2022,5 +3299,37 @@ | |
1822 | .patch = patch_vt1708B_4ch}, | |
1823 | { .id = 0x1106E727, .name = "VIA VT1708B 4-Ch", | |
1824 | .patch = patch_vt1708B_4ch}, | |
1825 | + { .id = 0x11060397, .name = "VIA VT1708S", | |
1826 | + .patch = patch_vt1708S}, | |
1827 | + { .id = 0x11061397, .name = "VIA VT1708S", | |
1828 | + .patch = patch_vt1708S}, | |
1829 | + { .id = 0x11062397, .name = "VIA VT1708S", | |
1830 | + .patch = patch_vt1708S}, | |
1831 | + { .id = 0x11063397, .name = "VIA VT1708S", | |
1832 | + .patch = patch_vt1708S}, | |
1833 | + { .id = 0x11064397, .name = "VIA VT1708S", | |
1834 | + .patch = patch_vt1708S}, | |
1835 | + { .id = 0x11065397, .name = "VIA VT1708S", | |
1836 | + .patch = patch_vt1708S}, | |
1837 | + { .id = 0x11066397, .name = "VIA VT1708S", | |
1838 | + .patch = patch_vt1708S}, | |
1839 | + { .id = 0x11067397, .name = "VIA VT1708S", | |
1840 | + .patch = patch_vt1708S}, | |
1841 | + { .id = 0x11060398, .name = "VIA VT1702", | |
1842 | + .patch = patch_vt1702}, | |
1843 | + { .id = 0x11061398, .name = "VIA VT1702", | |
1844 | + .patch = patch_vt1702}, | |
1845 | + { .id = 0x11062398, .name = "VIA VT1702", | |
1846 | + .patch = patch_vt1702}, | |
1847 | + { .id = 0x11063398, .name = "VIA VT1702", | |
1848 | + .patch = patch_vt1702}, | |
1849 | + { .id = 0x11064398, .name = "VIA VT1702", | |
1850 | + .patch = patch_vt1702}, | |
1851 | + { .id = 0x11065398, .name = "VIA VT1702", | |
1852 | + .patch = patch_vt1702}, | |
1853 | + { .id = 0x11066398, .name = "VIA VT1702", | |
1854 | + .patch = patch_vt1702}, | |
1855 | + { .id = 0x11067398, .name = "VIA VT1702", | |
1856 | + .patch = patch_vt1702}, | |
1857 | {} /* terminator */ | |
1858 | }; |