blob: de4839e467626c3da7cba68d4e0474e43220e9e7 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 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 */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +010057#define NID_MAPPING (-1)
58
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* amp values */
60#define AMP_VAL_IDX_SHIFT 19
61#define AMP_VAL_IDX_MASK (0x0f<<19)
62
Joseph Chanc577b8a2006-11-29 15:29:40 +010063/* Pin Widget NID */
64#define VT1708_HP_NID 0x13
65#define VT1708_DIGOUT_NID 0x14
66#define VT1708_DIGIN_NID 0x16
Josepch Chanf7278fd2007-12-13 16:40:40 +010067#define VT1708_DIGIN_PIN 0x26
Harald Welted949cac2008-09-09 15:56:01 +080068#define VT1708_HP_PIN_NID 0x20
69#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010070
71#define VT1709_HP_DAC_NID 0x28
72#define VT1709_DIGOUT_NID 0x13
73#define VT1709_DIGIN_NID 0x17
Josepch Chanf7278fd2007-12-13 16:40:40 +010074#define VT1709_DIGIN_PIN 0x25
75
76#define VT1708B_HP_NID 0x25
77#define VT1708B_DIGOUT_NID 0x12
78#define VT1708B_DIGIN_NID 0x15
79#define VT1708B_DIGIN_PIN 0x21
Joseph Chanc577b8a2006-11-29 15:29:40 +010080
Harald Welted949cac2008-09-09 15:56:01 +080081#define VT1708S_HP_NID 0x25
82#define VT1708S_DIGOUT_NID 0x12
83
84#define VT1702_HP_NID 0x17
85#define VT1702_DIGOUT_NID 0x11
86
Harald Welted7426322008-09-15 22:43:23 +080087enum VIA_HDA_CODEC {
88 UNKNOWN = -1,
89 VT1708,
90 VT1709_10CH,
91 VT1709_6CH,
92 VT1708B_8CH,
93 VT1708B_4CH,
94 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080095 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080096 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080097 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080098 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080099 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +0800100 VT1812,
Harald Welted7426322008-09-15 22:43:23 +0800101 CODEC_TYPES,
102};
103
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800104struct via_spec {
105 /* codec parameterization */
Lydia Wangf3db4232009-10-10 19:08:41 +0800106 struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800107 unsigned int num_mixers;
108
109 struct hda_verb *init_verbs[5];
110 unsigned int num_iverbs;
111
112 char *stream_name_analog;
113 struct hda_pcm_stream *stream_analog_playback;
114 struct hda_pcm_stream *stream_analog_capture;
115
116 char *stream_name_digital;
117 struct hda_pcm_stream *stream_digital_playback;
118 struct hda_pcm_stream *stream_digital_capture;
119
120 /* playback */
121 struct hda_multi_out multiout;
122 hda_nid_t slave_dig_outs[2];
123
124 /* capture */
125 unsigned int num_adc_nids;
126 hda_nid_t *adc_nids;
127 hda_nid_t mux_nids[3];
128 hda_nid_t dig_in_nid;
129 hda_nid_t dig_in_pin;
130
131 /* capture source */
132 const struct hda_input_mux *input_mux;
133 unsigned int cur_mux[3];
134
135 /* PCM information */
136 struct hda_pcm pcm_rec[3];
137
138 /* dynamic controls, init_verbs and input_mux */
139 struct auto_pin_cfg autocfg;
140 struct snd_array kctls;
141 struct hda_input_mux private_imux[2];
142 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
143
144 /* HP mode source */
145 const struct hda_input_mux *hp_mux;
146 unsigned int hp_independent_mode;
147 unsigned int hp_independent_mode_index;
148 unsigned int smart51_enabled;
Lydia Wangf3db4232009-10-10 19:08:41 +0800149 unsigned int dmic_enabled;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800150 enum VIA_HDA_CODEC codec_type;
151
152 /* work to check hp jack state */
153 struct hda_codec *codec;
154 struct delayed_work vt1708_hp_work;
155 int vt1708_jack_detectect;
156 int vt1708_hp_present;
157#ifdef CONFIG_SND_HDA_POWER_SAVE
158 struct hda_loopback_check loopback;
159#endif
160};
161
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100162static struct via_spec * via_new_spec(struct hda_codec *codec)
163{
164 struct via_spec *spec;
165
166 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
167 if (spec == NULL)
168 return NULL;
169
170 codec->spec = spec;
171 spec->codec = codec;
172 return spec;
173}
174
Lydia Wang744ff5f2009-10-10 19:07:26 +0800175static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800176{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800177 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800178 u16 ven_id = vendor_id >> 16;
179 u16 dev_id = vendor_id & 0xffff;
180 enum VIA_HDA_CODEC codec_type;
181
182 /* get codec type */
183 if (ven_id != 0x1106)
184 codec_type = UNKNOWN;
185 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
186 codec_type = VT1708;
187 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
188 codec_type = VT1709_10CH;
189 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
190 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800191 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800192 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800193 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
194 codec_type = VT1708BCE;
195 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800196 codec_type = VT1708B_4CH;
197 else if ((dev_id & 0xfff) == 0x397
198 && (dev_id >> 12) < 8)
199 codec_type = VT1708S;
200 else if ((dev_id & 0xfff) == 0x398
201 && (dev_id >> 12) < 8)
202 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800203 else if ((dev_id & 0xfff) == 0x428
204 && (dev_id >> 12) < 8)
205 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800206 else if (dev_id == 0x0433 || dev_id == 0xa721)
207 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800208 else if (dev_id == 0x0441 || dev_id == 0x4441)
209 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800210 else if (dev_id == 0x0438 || dev_id == 0x4438)
211 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800212 else if (dev_id == 0x0448)
213 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800214 else if (dev_id == 0x0440)
215 codec_type = VT1708S;
Harald Welted7426322008-09-15 22:43:23 +0800216 else
217 codec_type = UNKNOWN;
218 return codec_type;
219};
220
Harald Welte69e52a82008-09-09 15:57:32 +0800221#define VIA_HP_EVENT 0x01
222#define VIA_GPIO_EVENT 0x02
Lydia Wanga34df192009-10-10 19:08:01 +0800223#define VIA_JACK_EVENT 0x04
Lydia Wangf3db4232009-10-10 19:08:41 +0800224#define VIA_MONO_EVENT 0x08
Lydia Wang25eaba22009-10-10 19:08:43 +0800225#define VIA_SPEAKER_EVENT 0x10
226#define VIA_BIND_HP_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800227
Joseph Chanc577b8a2006-11-29 15:29:40 +0100228enum {
229 VIA_CTL_WIDGET_VOL,
230 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800231 VIA_CTL_WIDGET_ANALOG_MUTE,
Lydia Wang25eaba22009-10-10 19:08:43 +0800232 VIA_CTL_WIDGET_BIND_PIN_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100233};
234
235enum {
Harald Welteeb14a462008-09-09 15:40:38 +0800236 AUTO_SEQ_FRONT = 0,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237 AUTO_SEQ_SURROUND,
238 AUTO_SEQ_CENLFE,
239 AUTO_SEQ_SIDE
240};
241
Lydia Wangf5271102009-10-10 19:07:35 +0800242static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
243static void set_jack_power_state(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800244static int is_aa_path_mute(struct hda_codec *codec);
245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
251 !spec->vt1708_jack_detectect);
252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
265 !spec->vt1708_jack_detectect);
266 cancel_delayed_work(&spec->vt1708_hp_work);
267 flush_scheduled_work();
268}
Lydia Wangf5271102009-10-10 19:07:35 +0800269
Lydia Wang25eaba22009-10-10 19:08:43 +0800270
Lydia Wangf5271102009-10-10 19:07:35 +0800271static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
272 struct snd_ctl_elem_value *ucontrol)
273{
274 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
275 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
276
277 set_jack_power_state(codec);
278 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800279 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
280 if (is_aa_path_mute(codec))
281 vt1708_start_hp_work(codec->spec);
282 else
283 vt1708_stop_hp_work(codec->spec);
284 }
Lydia Wangf5271102009-10-10 19:07:35 +0800285 return change;
286}
287
288/* modify .put = snd_hda_mixer_amp_switch_put */
289#define ANALOG_INPUT_MUTE \
290 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
291 .name = NULL, \
292 .index = 0, \
293 .info = snd_hda_mixer_amp_switch_info, \
294 .get = snd_hda_mixer_amp_switch_get, \
295 .put = analog_input_switch_put, \
296 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
297
Lydia Wang25eaba22009-10-10 19:08:43 +0800298static void via_hp_bind_automute(struct hda_codec *codec);
299
300static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
301 struct snd_ctl_elem_value *ucontrol)
302{
303 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
304 struct via_spec *spec = codec->spec;
305 int i;
306 int change = 0;
307
308 long *valp = ucontrol->value.integer.value;
309 int lmute, rmute;
310 if (strstr(kcontrol->id.name, "Switch") == NULL) {
311 snd_printd("Invalid control!\n");
312 return change;
313 }
314 change = snd_hda_mixer_amp_switch_put(kcontrol,
315 ucontrol);
316 /* Get mute value */
317 lmute = *valp ? 0 : HDA_AMP_MUTE;
318 valp++;
319 rmute = *valp ? 0 : HDA_AMP_MUTE;
320
321 /* Set hp pins */
322 if (!spec->hp_independent_mode) {
323 for (i = 0; i < spec->autocfg.hp_outs; i++) {
324 snd_hda_codec_amp_update(
325 codec, spec->autocfg.hp_pins[i],
326 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
327 lmute);
328 snd_hda_codec_amp_update(
329 codec, spec->autocfg.hp_pins[i],
330 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
331 rmute);
332 }
333 }
334
335 if (!lmute && !rmute) {
336 /* Line Outs */
337 for (i = 0; i < spec->autocfg.line_outs; i++)
338 snd_hda_codec_amp_stereo(
339 codec, spec->autocfg.line_out_pins[i],
340 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
341 /* Speakers */
342 for (i = 0; i < spec->autocfg.speaker_outs; i++)
343 snd_hda_codec_amp_stereo(
344 codec, spec->autocfg.speaker_pins[i],
345 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
346 /* unmute */
347 via_hp_bind_automute(codec);
348
349 } else {
350 if (lmute) {
351 /* Mute all left channels */
352 for (i = 1; i < spec->autocfg.line_outs; i++)
353 snd_hda_codec_amp_update(
354 codec,
355 spec->autocfg.line_out_pins[i],
356 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
357 lmute);
358 for (i = 0; i < spec->autocfg.speaker_outs; i++)
359 snd_hda_codec_amp_update(
360 codec,
361 spec->autocfg.speaker_pins[i],
362 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
363 lmute);
364 }
365 if (rmute) {
366 /* mute all right channels */
367 for (i = 1; i < spec->autocfg.line_outs; i++)
368 snd_hda_codec_amp_update(
369 codec,
370 spec->autocfg.line_out_pins[i],
371 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
372 rmute);
373 for (i = 0; i < spec->autocfg.speaker_outs; i++)
374 snd_hda_codec_amp_update(
375 codec,
376 spec->autocfg.speaker_pins[i],
377 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
378 rmute);
379 }
380 }
381 return change;
382}
383
384#define BIND_PIN_MUTE \
385 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
386 .name = NULL, \
387 .index = 0, \
388 .info = snd_hda_mixer_amp_switch_info, \
389 .get = snd_hda_mixer_amp_switch_get, \
390 .put = bind_pin_switch_put, \
391 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
392
Lydia Wang71eb7dc2009-10-10 19:08:49 +0800393static struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100394 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
395 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800396 ANALOG_INPUT_MUTE,
Lydia Wang25eaba22009-10-10 19:08:43 +0800397 BIND_PIN_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100398};
399
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400static hda_nid_t vt1708_adc_nids[2] = {
401 /* ADC1-2 */
402 0x15, 0x27
403};
404
405static hda_nid_t vt1709_adc_nids[3] = {
406 /* ADC1-2 */
407 0x14, 0x15, 0x16
408};
409
Josepch Chanf7278fd2007-12-13 16:40:40 +0100410static hda_nid_t vt1708B_adc_nids[2] = {
411 /* ADC1-2 */
412 0x13, 0x14
413};
414
Harald Welted949cac2008-09-09 15:56:01 +0800415static hda_nid_t vt1708S_adc_nids[2] = {
416 /* ADC1-2 */
417 0x13, 0x14
418};
419
420static hda_nid_t vt1702_adc_nids[3] = {
421 /* ADC1-2 */
422 0x12, 0x20, 0x1F
423};
424
Lydia Wangeb7188c2009-10-10 19:08:34 +0800425static hda_nid_t vt1718S_adc_nids[2] = {
426 /* ADC1-2 */
427 0x10, 0x11
428};
429
Lydia Wangf3db4232009-10-10 19:08:41 +0800430static hda_nid_t vt1716S_adc_nids[2] = {
431 /* ADC1-2 */
432 0x13, 0x14
433};
434
Lydia Wang25eaba22009-10-10 19:08:43 +0800435static hda_nid_t vt2002P_adc_nids[2] = {
436 /* ADC1-2 */
437 0x10, 0x11
438};
439
Lydia Wangab6734e2009-10-10 19:08:46 +0800440static hda_nid_t vt1812_adc_nids[2] = {
441 /* ADC1-2 */
442 0x10, 0x11
443};
444
445
Joseph Chanc577b8a2006-11-29 15:29:40 +0100446/* add dynamic controls */
447static int via_add_control(struct via_spec *spec, int type, const char *name,
448 unsigned long val)
449{
450 struct snd_kcontrol_new *knew;
451
Takashi Iwai603c4012008-07-30 15:01:44 +0200452 snd_array_init(&spec->kctls, sizeof(*knew), 32);
453 knew = snd_array_new(&spec->kctls);
454 if (!knew)
455 return -ENOMEM;
Lydia Wang71eb7dc2009-10-10 19:08:49 +0800456 *knew = via_control_templates[type];
Joseph Chanc577b8a2006-11-29 15:29:40 +0100457 knew->name = kstrdup(name, GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100458 if (!knew->name)
459 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100460 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100461 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100462 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100463 return 0;
464}
465
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100466static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
467 struct snd_kcontrol_new *tmpl)
468{
469 struct snd_kcontrol_new *knew;
470
471 snd_array_init(&spec->kctls, sizeof(*knew), 32);
472 knew = snd_array_new(&spec->kctls);
473 if (!knew)
474 return NULL;
475 *knew = *tmpl;
476 knew->name = kstrdup(tmpl->name, GFP_KERNEL);
477 if (!knew->name)
478 return NULL;
479 return 0;
480}
481
Takashi Iwai603c4012008-07-30 15:01:44 +0200482static void via_free_kctls(struct hda_codec *codec)
483{
484 struct via_spec *spec = codec->spec;
485
486 if (spec->kctls.list) {
487 struct snd_kcontrol_new *kctl = spec->kctls.list;
488 int i;
489 for (i = 0; i < spec->kctls.used; i++)
490 kfree(kctl[i].name);
491 }
492 snd_array_free(&spec->kctls);
493}
494
Joseph Chanc577b8a2006-11-29 15:29:40 +0100495/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800496static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
497 int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100498{
499 char name[32];
500 int err;
501
502 sprintf(name, "%s Playback Volume", ctlname);
503 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
504 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
505 if (err < 0)
506 return err;
507 sprintf(name, "%s Playback Switch", ctlname);
Lydia Wangf5271102009-10-10 19:07:35 +0800508 err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100509 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
510 if (err < 0)
511 return err;
512 return 0;
513}
514
515static void via_auto_set_output_and_unmute(struct hda_codec *codec,
516 hda_nid_t nid, int pin_type,
517 int dac_idx)
518{
519 /* set as output */
520 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
521 pin_type);
522 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
523 AMP_OUT_UNMUTE);
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200524 if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
Lydia Wang377ff312009-10-10 19:08:55 +0800525 snd_hda_codec_write(codec, nid, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200526 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100527}
528
529
530static void via_auto_init_multi_out(struct hda_codec *codec)
531{
532 struct via_spec *spec = codec->spec;
533 int i;
534
535 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
536 hda_nid_t nid = spec->autocfg.line_out_pins[i];
537 if (nid)
538 via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
539 }
540}
541
542static void via_auto_init_hp_out(struct hda_codec *codec)
543{
544 struct via_spec *spec = codec->spec;
545 hda_nid_t pin;
Lydia Wang25eaba22009-10-10 19:08:43 +0800546 int i;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547
Lydia Wang25eaba22009-10-10 19:08:43 +0800548 for (i = 0; i < spec->autocfg.hp_outs; i++) {
549 pin = spec->autocfg.hp_pins[i];
550 if (pin) /* connect to front */
551 via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
552 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100553}
554
555static void via_auto_init_analog_input(struct hda_codec *codec)
556{
557 struct via_spec *spec = codec->spec;
558 int i;
559
560 for (i = 0; i < AUTO_PIN_LAST; i++) {
561 hda_nid_t nid = spec->autocfg.input_pins[i];
562
563 snd_hda_codec_write(codec, nid, 0,
564 AC_VERB_SET_PIN_WIDGET_CONTROL,
565 (i <= AUTO_PIN_FRONT_MIC ?
566 PIN_VREF50 : PIN_IN));
567
568 }
569}
Lydia Wangf5271102009-10-10 19:07:35 +0800570
Lydia Wang1564b282009-10-10 19:07:52 +0800571static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
572
Lydia Wangf5271102009-10-10 19:07:35 +0800573static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
574 unsigned int *affected_parm)
575{
576 unsigned parm;
577 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
578 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
579 >> AC_DEFCFG_MISC_SHIFT
580 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Takashi Iwaid56757a2009-11-18 08:00:14 +0100581 unsigned present = snd_hda_jack_detect(codec, nid);
Lydia Wang1564b282009-10-10 19:07:52 +0800582 struct via_spec *spec = codec->spec;
583 if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
584 || ((no_presence || present)
585 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800586 *affected_parm = AC_PWRST_D0; /* if it's connected */
587 parm = AC_PWRST_D0;
588 } else
589 parm = AC_PWRST_D3;
590
591 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
592}
593
594static void set_jack_power_state(struct hda_codec *codec)
595{
596 struct via_spec *spec = codec->spec;
597 int imux_is_smixer;
598 unsigned int parm;
599
600 if (spec->codec_type == VT1702) {
601 imux_is_smixer = snd_hda_codec_read(
602 codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
603 /* inputs */
604 /* PW 1/2/5 (14h/15h/18h) */
605 parm = AC_PWRST_D3;
606 set_pin_power_state(codec, 0x14, &parm);
607 set_pin_power_state(codec, 0x15, &parm);
608 set_pin_power_state(codec, 0x18, &parm);
609 if (imux_is_smixer)
610 parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
611 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
612 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
613 parm);
614 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
615 parm);
616 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
617 parm);
618 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
619 parm);
620
621 /* outputs */
622 /* PW 3/4 (16h/17h) */
623 parm = AC_PWRST_D3;
624 set_pin_power_state(codec, 0x16, &parm);
625 set_pin_power_state(codec, 0x17, &parm);
626 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
627 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
628 imux_is_smixer ? AC_PWRST_D0 : parm);
629 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
630 parm);
631 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
632 parm);
633 } else if (spec->codec_type == VT1708B_8CH
634 || spec->codec_type == VT1708B_4CH
635 || spec->codec_type == VT1708S) {
636 /* SW0 (17h) = stereo mixer */
637 int is_8ch = spec->codec_type != VT1708B_4CH;
638 imux_is_smixer = snd_hda_codec_read(
639 codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
640 == ((spec->codec_type == VT1708S) ? 5 : 0);
641 /* inputs */
642 /* PW 1/2/5 (1ah/1bh/1eh) */
643 parm = AC_PWRST_D3;
644 set_pin_power_state(codec, 0x1a, &parm);
645 set_pin_power_state(codec, 0x1b, &parm);
646 set_pin_power_state(codec, 0x1e, &parm);
647 if (imux_is_smixer)
648 parm = AC_PWRST_D0;
649 /* SW0 (17h), AIW 0/1 (13h/14h) */
650 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
651 parm);
652 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
653 parm);
654 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
655 parm);
656
657 /* outputs */
658 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
659 parm = AC_PWRST_D3;
660 set_pin_power_state(codec, 0x19, &parm);
661 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
662 parm);
663 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
664 parm);
665
666 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
667 if (is_8ch) {
668 parm = AC_PWRST_D3;
669 set_pin_power_state(codec, 0x22, &parm);
670 snd_hda_codec_write(codec, 0x26, 0,
671 AC_VERB_SET_POWER_STATE, parm);
672 snd_hda_codec_write(codec, 0x24, 0,
673 AC_VERB_SET_POWER_STATE, parm);
674 }
675
676 /* PW 3/4/7 (1ch/1dh/23h) */
677 parm = AC_PWRST_D3;
678 /* force to D0 for internal Speaker */
679 set_pin_power_state(codec, 0x1c, &parm);
680 set_pin_power_state(codec, 0x1d, &parm);
681 if (is_8ch)
682 set_pin_power_state(codec, 0x23, &parm);
683 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
684 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
685 imux_is_smixer ? AC_PWRST_D0 : parm);
686 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
687 parm);
688 if (is_8ch) {
689 snd_hda_codec_write(codec, 0x25, 0,
690 AC_VERB_SET_POWER_STATE, parm);
691 snd_hda_codec_write(codec, 0x27, 0,
692 AC_VERB_SET_POWER_STATE, parm);
693 }
Lydia Wangeb7188c2009-10-10 19:08:34 +0800694 } else if (spec->codec_type == VT1718S) {
695 /* MUX6 (1eh) = stereo mixer */
696 imux_is_smixer = snd_hda_codec_read(
697 codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
698 /* inputs */
699 /* PW 5/6/7 (29h/2ah/2bh) */
700 parm = AC_PWRST_D3;
701 set_pin_power_state(codec, 0x29, &parm);
702 set_pin_power_state(codec, 0x2a, &parm);
703 set_pin_power_state(codec, 0x2b, &parm);
704 if (imux_is_smixer)
705 parm = AC_PWRST_D0;
706 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
707 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
708 parm);
709 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
710 parm);
711 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
712 parm);
713 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
714 parm);
715
716 /* outputs */
717 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
718 parm = AC_PWRST_D3;
719 set_pin_power_state(codec, 0x27, &parm);
720 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
721 parm);
722 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
723 parm);
724
725 /* PW2 (26h), AOW2 (ah) */
726 parm = AC_PWRST_D3;
727 set_pin_power_state(codec, 0x26, &parm);
728 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
729 parm);
730
731 /* PW0/1 (24h/25h) */
732 parm = AC_PWRST_D3;
733 set_pin_power_state(codec, 0x24, &parm);
734 set_pin_power_state(codec, 0x25, &parm);
735 if (!spec->hp_independent_mode) /* check for redirected HP */
736 set_pin_power_state(codec, 0x28, &parm);
737 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
738 parm);
739 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
740 parm);
741 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
742 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
743 imux_is_smixer ? AC_PWRST_D0 : parm);
744 if (spec->hp_independent_mode) {
745 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
746 parm = AC_PWRST_D3;
747 set_pin_power_state(codec, 0x28, &parm);
748 snd_hda_codec_write(codec, 0x1b, 0,
749 AC_VERB_SET_POWER_STATE, parm);
750 snd_hda_codec_write(codec, 0x34, 0,
751 AC_VERB_SET_POWER_STATE, parm);
752 snd_hda_codec_write(codec, 0xc, 0,
753 AC_VERB_SET_POWER_STATE, parm);
754 }
Lydia Wangf3db4232009-10-10 19:08:41 +0800755 } else if (spec->codec_type == VT1716S) {
756 unsigned int mono_out, present;
757 /* SW0 (17h) = stereo mixer */
758 imux_is_smixer = snd_hda_codec_read(
759 codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
760 /* inputs */
761 /* PW 1/2/5 (1ah/1bh/1eh) */
762 parm = AC_PWRST_D3;
763 set_pin_power_state(codec, 0x1a, &parm);
764 set_pin_power_state(codec, 0x1b, &parm);
765 set_pin_power_state(codec, 0x1e, &parm);
766 if (imux_is_smixer)
767 parm = AC_PWRST_D0;
768 /* SW0 (17h), AIW0(13h) */
769 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
770 parm);
771 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
772 parm);
773
774 parm = AC_PWRST_D3;
775 set_pin_power_state(codec, 0x1e, &parm);
776 /* PW11 (22h) */
777 if (spec->dmic_enabled)
778 set_pin_power_state(codec, 0x22, &parm);
779 else
780 snd_hda_codec_write(
781 codec, 0x22, 0,
782 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
783
784 /* SW2(26h), AIW1(14h) */
785 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
786 parm);
787 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
788 parm);
789
790 /* outputs */
791 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
792 parm = AC_PWRST_D3;
793 set_pin_power_state(codec, 0x19, &parm);
794 /* Smart 5.1 PW2(1bh) */
795 if (spec->smart51_enabled)
796 set_pin_power_state(codec, 0x1b, &parm);
797 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
798 parm);
799 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
800 parm);
801
802 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
803 parm = AC_PWRST_D3;
804 set_pin_power_state(codec, 0x23, &parm);
805 /* Smart 5.1 PW1(1ah) */
806 if (spec->smart51_enabled)
807 set_pin_power_state(codec, 0x1a, &parm);
808 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
809 parm);
810
811 /* Smart 5.1 PW5(1eh) */
812 if (spec->smart51_enabled)
813 set_pin_power_state(codec, 0x1e, &parm);
814 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
815 parm);
816
817 /* Mono out */
818 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
Takashi Iwaid56757a2009-11-18 08:00:14 +0100819 present = snd_hda_jack_detect(codec, 0x1c);
Lydia Wangf3db4232009-10-10 19:08:41 +0800820 if (present)
821 mono_out = 0;
822 else {
Takashi Iwaid56757a2009-11-18 08:00:14 +0100823 present = snd_hda_jack_detect(codec, 0x1d);
Lydia Wangf3db4232009-10-10 19:08:41 +0800824 if (!spec->hp_independent_mode && present)
825 mono_out = 0;
826 else
827 mono_out = 1;
828 }
829 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
830 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
831 parm);
832 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
833 parm);
834 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
835 parm);
836
837 /* PW 3/4 (1ch/1dh) */
838 parm = AC_PWRST_D3;
839 set_pin_power_state(codec, 0x1c, &parm);
840 set_pin_power_state(codec, 0x1d, &parm);
841 /* HP Independent Mode, power on AOW3 */
842 if (spec->hp_independent_mode)
843 snd_hda_codec_write(codec, 0x25, 0,
844 AC_VERB_SET_POWER_STATE, parm);
845
846 /* force to D0 for internal Speaker */
847 /* MW0 (16h), AOW0 (10h) */
848 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
849 imux_is_smixer ? AC_PWRST_D0 : parm);
850 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
851 mono_out ? AC_PWRST_D0 : parm);
Lydia Wang25eaba22009-10-10 19:08:43 +0800852 } else if (spec->codec_type == VT2002P) {
853 unsigned int present;
854 /* MUX9 (1eh) = stereo mixer */
855 imux_is_smixer = snd_hda_codec_read(
856 codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
857 /* inputs */
858 /* PW 5/6/7 (29h/2ah/2bh) */
859 parm = AC_PWRST_D3;
860 set_pin_power_state(codec, 0x29, &parm);
861 set_pin_power_state(codec, 0x2a, &parm);
862 set_pin_power_state(codec, 0x2b, &parm);
863 if (imux_is_smixer)
864 parm = AC_PWRST_D0;
865 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
866 snd_hda_codec_write(codec, 0x1e, 0,
867 AC_VERB_SET_POWER_STATE, parm);
868 snd_hda_codec_write(codec, 0x1f, 0,
869 AC_VERB_SET_POWER_STATE, parm);
870 snd_hda_codec_write(codec, 0x10, 0,
871 AC_VERB_SET_POWER_STATE, parm);
872 snd_hda_codec_write(codec, 0x11, 0,
873 AC_VERB_SET_POWER_STATE, parm);
874
875 /* outputs */
876 /* AOW0 (8h)*/
877 snd_hda_codec_write(codec, 0x8, 0,
878 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
879
880 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
881 parm = AC_PWRST_D3;
882 set_pin_power_state(codec, 0x26, &parm);
883 snd_hda_codec_write(codec, 0x1c, 0,
884 AC_VERB_SET_POWER_STATE, parm);
885 snd_hda_codec_write(codec, 0x37,
886 0, AC_VERB_SET_POWER_STATE, parm);
887
888 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
889 parm = AC_PWRST_D3;
890 set_pin_power_state(codec, 0x25, &parm);
891 snd_hda_codec_write(codec, 0x19, 0,
892 AC_VERB_SET_POWER_STATE, parm);
893 snd_hda_codec_write(codec, 0x35, 0,
894 AC_VERB_SET_POWER_STATE, parm);
895 if (spec->hp_independent_mode) {
896 snd_hda_codec_write(codec, 0x9, 0,
897 AC_VERB_SET_POWER_STATE, parm);
898 }
899
900 /* Class-D */
901 /* PW0 (24h), MW0(18h), MUX0(34h) */
Takashi Iwaid56757a2009-11-18 08:00:14 +0100902 present = snd_hda_jack_detect(codec, 0x25);
Lydia Wang25eaba22009-10-10 19:08:43 +0800903 parm = AC_PWRST_D3;
904 set_pin_power_state(codec, 0x24, &parm);
905 if (present) {
906 snd_hda_codec_write(
907 codec, 0x18, 0,
908 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
909 snd_hda_codec_write(
910 codec, 0x34, 0,
911 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
912 } else {
913 snd_hda_codec_write(
914 codec, 0x18, 0,
915 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
916 snd_hda_codec_write(
917 codec, 0x34, 0,
918 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
919 }
920
921 /* Mono Out */
922 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwaid56757a2009-11-18 08:00:14 +0100923 present = snd_hda_jack_detect(codec, 0x26);
Lydia Wang25eaba22009-10-10 19:08:43 +0800924 parm = AC_PWRST_D3;
925 set_pin_power_state(codec, 0x31, &parm);
926 if (present) {
927 snd_hda_codec_write(
928 codec, 0x17, 0,
929 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
930 snd_hda_codec_write(
931 codec, 0x3b, 0,
932 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
933 } else {
934 snd_hda_codec_write(
935 codec, 0x17, 0,
936 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
937 snd_hda_codec_write(
938 codec, 0x3b, 0,
939 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
940 }
941
942 /* MW9 (21h) */
943 if (imux_is_smixer || !is_aa_path_mute(codec))
944 snd_hda_codec_write(
945 codec, 0x21, 0,
946 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
947 else
948 snd_hda_codec_write(
949 codec, 0x21, 0,
950 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
Lydia Wangab6734e2009-10-10 19:08:46 +0800951 } else if (spec->codec_type == VT1812) {
952 unsigned int present;
953 /* MUX10 (1eh) = stereo mixer */
954 imux_is_smixer = snd_hda_codec_read(
955 codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
956 /* inputs */
957 /* PW 5/6/7 (29h/2ah/2bh) */
958 parm = AC_PWRST_D3;
959 set_pin_power_state(codec, 0x29, &parm);
960 set_pin_power_state(codec, 0x2a, &parm);
961 set_pin_power_state(codec, 0x2b, &parm);
962 if (imux_is_smixer)
963 parm = AC_PWRST_D0;
964 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
965 snd_hda_codec_write(codec, 0x1e, 0,
966 AC_VERB_SET_POWER_STATE, parm);
967 snd_hda_codec_write(codec, 0x1f, 0,
968 AC_VERB_SET_POWER_STATE, parm);
969 snd_hda_codec_write(codec, 0x10, 0,
970 AC_VERB_SET_POWER_STATE, parm);
971 snd_hda_codec_write(codec, 0x11, 0,
972 AC_VERB_SET_POWER_STATE, parm);
973
974 /* outputs */
975 /* AOW0 (8h)*/
976 snd_hda_codec_write(codec, 0x8, 0,
977 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
978
979 /* PW4 (28h), MW4 (18h), MUX4(38h) */
980 parm = AC_PWRST_D3;
981 set_pin_power_state(codec, 0x28, &parm);
982 snd_hda_codec_write(codec, 0x18, 0,
983 AC_VERB_SET_POWER_STATE, parm);
984 snd_hda_codec_write(codec, 0x38, 0,
985 AC_VERB_SET_POWER_STATE, parm);
986
987 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
988 parm = AC_PWRST_D3;
989 set_pin_power_state(codec, 0x25, &parm);
990 snd_hda_codec_write(codec, 0x15, 0,
991 AC_VERB_SET_POWER_STATE, parm);
992 snd_hda_codec_write(codec, 0x35, 0,
993 AC_VERB_SET_POWER_STATE, parm);
994 if (spec->hp_independent_mode) {
995 snd_hda_codec_write(codec, 0x9, 0,
996 AC_VERB_SET_POWER_STATE, parm);
997 }
998
999 /* Internal Speaker */
1000 /* PW0 (24h), MW0(14h), MUX0(34h) */
Takashi Iwaid56757a2009-11-18 08:00:14 +01001001 present = snd_hda_jack_detect(codec, 0x25);
Lydia Wangab6734e2009-10-10 19:08:46 +08001002 parm = AC_PWRST_D3;
1003 set_pin_power_state(codec, 0x24, &parm);
1004 if (present) {
1005 snd_hda_codec_write(codec, 0x14, 0,
1006 AC_VERB_SET_POWER_STATE,
1007 AC_PWRST_D3);
1008 snd_hda_codec_write(codec, 0x34, 0,
1009 AC_VERB_SET_POWER_STATE,
1010 AC_PWRST_D3);
1011 } else {
1012 snd_hda_codec_write(codec, 0x14, 0,
1013 AC_VERB_SET_POWER_STATE,
1014 AC_PWRST_D0);
1015 snd_hda_codec_write(codec, 0x34, 0,
1016 AC_VERB_SET_POWER_STATE,
1017 AC_PWRST_D0);
1018 }
1019 /* Mono Out */
1020 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
Takashi Iwaid56757a2009-11-18 08:00:14 +01001021 present = snd_hda_jack_detect(codec, 0x28);
Lydia Wangab6734e2009-10-10 19:08:46 +08001022 parm = AC_PWRST_D3;
1023 set_pin_power_state(codec, 0x31, &parm);
1024 if (present) {
1025 snd_hda_codec_write(codec, 0x1c, 0,
1026 AC_VERB_SET_POWER_STATE,
1027 AC_PWRST_D3);
1028 snd_hda_codec_write(codec, 0x3c, 0,
1029 AC_VERB_SET_POWER_STATE,
1030 AC_PWRST_D3);
1031 snd_hda_codec_write(codec, 0x3e, 0,
1032 AC_VERB_SET_POWER_STATE,
1033 AC_PWRST_D3);
1034 } else {
1035 snd_hda_codec_write(codec, 0x1c, 0,
1036 AC_VERB_SET_POWER_STATE,
1037 AC_PWRST_D0);
1038 snd_hda_codec_write(codec, 0x3c, 0,
1039 AC_VERB_SET_POWER_STATE,
1040 AC_PWRST_D0);
1041 snd_hda_codec_write(codec, 0x3e, 0,
1042 AC_VERB_SET_POWER_STATE,
1043 AC_PWRST_D0);
1044 }
1045
1046 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
1047 parm = AC_PWRST_D3;
1048 set_pin_power_state(codec, 0x33, &parm);
1049 snd_hda_codec_write(codec, 0x1d, 0,
1050 AC_VERB_SET_POWER_STATE, parm);
1051 snd_hda_codec_write(codec, 0x3d, 0,
1052 AC_VERB_SET_POWER_STATE, parm);
1053
1054 /* MW9 (21h) */
1055 if (imux_is_smixer || !is_aa_path_mute(codec))
1056 snd_hda_codec_write(
1057 codec, 0x21, 0,
1058 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1059 else
1060 snd_hda_codec_write(
1061 codec, 0x21, 0,
1062 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
Lydia Wangf5271102009-10-10 19:07:35 +08001063 }
1064}
1065
Joseph Chanc577b8a2006-11-29 15:29:40 +01001066/*
1067 * input MUX handling
1068 */
1069static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1070 struct snd_ctl_elem_info *uinfo)
1071{
1072 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1073 struct via_spec *spec = codec->spec;
1074 return snd_hda_input_mux_info(spec->input_mux, uinfo);
1075}
1076
1077static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1078 struct snd_ctl_elem_value *ucontrol)
1079{
1080 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1081 struct via_spec *spec = codec->spec;
1082 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1083
1084 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
1085 return 0;
1086}
1087
1088static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1089 struct snd_ctl_elem_value *ucontrol)
1090{
1091 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1092 struct via_spec *spec = codec->spec;
1093 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001094
Takashi Iwai337b9d02009-07-07 18:18:59 +02001095 if (!spec->mux_nids[adc_idx])
1096 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +08001097 /* switch to D0 beofre change index */
1098 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
1099 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1100 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
1101 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1102 /* update jack power state */
1103 set_jack_power_state(codec);
1104
Takashi Iwai337b9d02009-07-07 18:18:59 +02001105 return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
1106 spec->mux_nids[adc_idx],
1107 &spec->cur_mux[adc_idx]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001108}
1109
Harald Welte0aa62ae2008-09-09 15:58:27 +08001110static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
1111 struct snd_ctl_elem_info *uinfo)
1112{
1113 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1114 struct via_spec *spec = codec->spec;
1115 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
1116}
1117
1118static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
1119 struct snd_ctl_elem_value *ucontrol)
1120{
1121 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001122 hda_nid_t nid = kcontrol->private_value;
Lydia Wangeb7188c2009-10-10 19:08:34 +08001123 unsigned int pinsel;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001124
Lydia Wangeb7188c2009-10-10 19:08:34 +08001125 /* use !! to translate conn sel 2 for VT1718S */
1126 pinsel = !!snd_hda_codec_read(codec, nid, 0,
1127 AC_VERB_GET_CONNECT_SEL,
1128 0x00);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001129 ucontrol->value.enumerated.item[0] = pinsel;
1130
1131 return 0;
1132}
1133
Lydia Wang0713efe2009-10-10 19:07:43 +08001134static void activate_ctl(struct hda_codec *codec, const char *name, int active)
1135{
1136 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
1137 if (ctl) {
1138 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1139 ctl->vd[0].access |= active
1140 ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
1141 snd_ctl_notify(codec->bus->card,
1142 SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
1143 }
1144}
1145
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001146static hda_nid_t side_mute_channel(struct via_spec *spec)
1147{
1148 switch (spec->codec_type) {
1149 case VT1708: return 0x1b;
1150 case VT1709_10CH: return 0x29;
1151 case VT1708B_8CH: /* fall thru */
1152 case VT1708S: return 0x27;
1153 default: return 0;
1154 }
1155}
1156
Lydia Wangcdc17842009-10-10 19:07:47 +08001157static int update_side_mute_status(struct hda_codec *codec)
1158{
1159 /* mute side channel */
1160 struct via_spec *spec = codec->spec;
1161 unsigned int parm = spec->hp_independent_mode
1162 ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001163 hda_nid_t sw3 = side_mute_channel(spec);
Lydia Wangcdc17842009-10-10 19:07:47 +08001164
1165 if (sw3)
1166 snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1167 parm);
1168 return 0;
1169}
1170
Harald Welte0aa62ae2008-09-09 15:58:27 +08001171static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
1172 struct snd_ctl_elem_value *ucontrol)
1173{
1174 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1175 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001176 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001177 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +08001178 /* Get Independent Mode index of headphone pin widget */
1179 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
1180 ? 1 : 0;
Lydia Wangcdc17842009-10-10 19:07:47 +08001181 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001182
Lydia Wangcdc17842009-10-10 19:07:47 +08001183 if (spec->multiout.hp_nid && spec->multiout.hp_nid
1184 != spec->multiout.dac_nids[HDA_FRONT])
1185 snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
1186 0, 0, 0);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001187
Lydia Wangcdc17842009-10-10 19:07:47 +08001188 update_side_mute_status(codec);
Lydia Wang0713efe2009-10-10 19:07:43 +08001189 /* update HP volume/swtich active state */
1190 if (spec->codec_type == VT1708S
Lydia Wangeb7188c2009-10-10 19:08:34 +08001191 || spec->codec_type == VT1702
Lydia Wangf3db4232009-10-10 19:08:41 +08001192 || spec->codec_type == VT1718S
Lydia Wang25eaba22009-10-10 19:08:43 +08001193 || spec->codec_type == VT1716S
Lydia Wangab6734e2009-10-10 19:08:46 +08001194 || spec->codec_type == VT2002P
1195 || spec->codec_type == VT1812) {
Lydia Wang0713efe2009-10-10 19:07:43 +08001196 activate_ctl(codec, "Headphone Playback Volume",
1197 spec->hp_independent_mode);
1198 activate_ctl(codec, "Headphone Playback Switch",
1199 spec->hp_independent_mode);
1200 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001201 return 0;
1202}
1203
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001204static struct snd_kcontrol_new via_hp_mixer[2] = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001205 {
1206 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1207 .name = "Independent HP",
Harald Welte0aa62ae2008-09-09 15:58:27 +08001208 .info = via_independent_hp_info,
1209 .get = via_independent_hp_get,
1210 .put = via_independent_hp_put,
1211 },
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001212 {
1213 .iface = NID_MAPPING,
1214 .name = "Independent HP",
1215 },
Harald Welte0aa62ae2008-09-09 15:58:27 +08001216};
1217
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001218static int via_hp_build(struct via_spec *spec)
1219{
1220 struct snd_kcontrol_new *knew;
1221 hda_nid_t nid;
1222
1223 knew = via_clone_control(spec, &via_hp_mixer[0]);
1224 if (knew == NULL)
1225 return -ENOMEM;
1226
1227 switch (spec->codec_type) {
1228 case VT1718S:
1229 nid = 0x34;
1230 break;
1231 case VT2002P:
1232 nid = 0x35;
1233 break;
1234 case VT1812:
1235 nid = 0x3d;
1236 break;
1237 default:
1238 nid = spec->autocfg.hp_pins[0];
1239 break;
1240 }
1241
1242 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
1243 knew->private_value = nid;
1244
1245 knew = via_clone_control(spec, &via_hp_mixer[1]);
1246 if (knew == NULL)
1247 return -ENOMEM;
1248 knew->subdevice = side_mute_channel(spec);
1249
1250 return 0;
1251}
1252
Lydia Wang1564b282009-10-10 19:07:52 +08001253static void notify_aa_path_ctls(struct hda_codec *codec)
1254{
1255 int i;
1256 struct snd_ctl_elem_id id;
1257 const char *labels[] = {"Mic", "Front Mic", "Line"};
1258
1259 memset(&id, 0, sizeof(id));
1260 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1261 for (i = 0; i < ARRAY_SIZE(labels); i++) {
1262 sprintf(id.name, "%s Playback Volume", labels[i]);
1263 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1264 &id);
1265 }
1266}
1267
1268static void mute_aa_path(struct hda_codec *codec, int mute)
1269{
1270 struct via_spec *spec = codec->spec;
1271 hda_nid_t nid_mixer;
1272 int start_idx;
1273 int end_idx;
1274 int i;
1275 /* get nid of MW0 and start & end index */
1276 switch (spec->codec_type) {
1277 case VT1708:
1278 nid_mixer = 0x17;
1279 start_idx = 2;
1280 end_idx = 4;
1281 break;
1282 case VT1709_10CH:
1283 case VT1709_6CH:
1284 nid_mixer = 0x18;
1285 start_idx = 2;
1286 end_idx = 4;
1287 break;
1288 case VT1708B_8CH:
1289 case VT1708B_4CH:
1290 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001291 case VT1716S:
Lydia Wang1564b282009-10-10 19:07:52 +08001292 nid_mixer = 0x16;
1293 start_idx = 2;
1294 end_idx = 4;
1295 break;
1296 default:
1297 return;
1298 }
1299 /* check AA path's mute status */
1300 for (i = start_idx; i <= end_idx; i++) {
1301 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
1302 snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
1303 HDA_AMP_MUTE, val);
1304 }
1305}
1306static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
1307{
1308 int res = 0;
1309 int index;
1310 for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
1311 if (pin == spec->autocfg.input_pins[index]) {
1312 res = 1;
1313 break;
1314 }
1315 }
1316 return res;
1317}
1318
1319static int via_smart51_info(struct snd_kcontrol *kcontrol,
1320 struct snd_ctl_elem_info *uinfo)
1321{
1322 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1323 uinfo->count = 1;
1324 uinfo->value.integer.min = 0;
1325 uinfo->value.integer.max = 1;
1326 return 0;
1327}
1328
1329static int via_smart51_get(struct snd_kcontrol *kcontrol,
1330 struct snd_ctl_elem_value *ucontrol)
1331{
1332 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1333 struct via_spec *spec = codec->spec;
1334 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
1335 int on = 1;
1336 int i;
1337
1338 for (i = 0; i < ARRAY_SIZE(index); i++) {
1339 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
1340 if (nid) {
1341 int ctl =
1342 snd_hda_codec_read(codec, nid, 0,
1343 AC_VERB_GET_PIN_WIDGET_CONTROL,
1344 0);
1345 if (i == AUTO_PIN_FRONT_MIC
Lydia Wangeb7188c2009-10-10 19:08:34 +08001346 && spec->hp_independent_mode
1347 && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +08001348 continue; /* ignore FMic for independent HP */
1349 if (ctl & AC_PINCTL_IN_EN
1350 && !(ctl & AC_PINCTL_OUT_EN))
1351 on = 0;
1352 }
1353 }
1354 *ucontrol->value.integer.value = on;
1355 return 0;
1356}
1357
1358static int via_smart51_put(struct snd_kcontrol *kcontrol,
1359 struct snd_ctl_elem_value *ucontrol)
1360{
1361 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1362 struct via_spec *spec = codec->spec;
1363 int out_in = *ucontrol->value.integer.value
1364 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
1365 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
1366 int i;
1367
1368 for (i = 0; i < ARRAY_SIZE(index); i++) {
1369 hda_nid_t nid = spec->autocfg.input_pins[index[i]];
1370 if (i == AUTO_PIN_FRONT_MIC
Lydia Wangeb7188c2009-10-10 19:08:34 +08001371 && spec->hp_independent_mode
1372 && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +08001373 continue; /* don't retask FMic for independent HP */
1374 if (nid) {
1375 unsigned int parm = snd_hda_codec_read(
1376 codec, nid, 0,
1377 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1378 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
1379 parm |= out_in;
1380 snd_hda_codec_write(codec, nid, 0,
1381 AC_VERB_SET_PIN_WIDGET_CONTROL,
1382 parm);
1383 if (out_in == AC_PINCTL_OUT_EN) {
1384 mute_aa_path(codec, 1);
1385 notify_aa_path_ctls(codec);
1386 }
Lydia Wangeb7188c2009-10-10 19:08:34 +08001387 if (spec->codec_type == VT1718S)
1388 snd_hda_codec_amp_stereo(
1389 codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1390 HDA_AMP_UNMUTE);
Lydia Wang1564b282009-10-10 19:07:52 +08001391 }
1392 if (i == AUTO_PIN_FRONT_MIC) {
Lydia Wangf3db4232009-10-10 19:08:41 +08001393 if (spec->codec_type == VT1708S
1394 || spec->codec_type == VT1716S) {
Lydia Wang1564b282009-10-10 19:07:52 +08001395 /* input = index 1 (AOW3) */
1396 snd_hda_codec_write(
1397 codec, nid, 0,
1398 AC_VERB_SET_CONNECT_SEL, 1);
1399 snd_hda_codec_amp_stereo(
1400 codec, nid, HDA_OUTPUT,
1401 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
1402 }
1403 }
1404 }
1405 spec->smart51_enabled = *ucontrol->value.integer.value;
1406 set_jack_power_state(codec);
1407 return 1;
1408}
1409
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001410static struct snd_kcontrol_new via_smart51_mixer[2] = {
Lydia Wang1564b282009-10-10 19:07:52 +08001411 {
1412 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1413 .name = "Smart 5.1",
1414 .count = 1,
1415 .info = via_smart51_info,
1416 .get = via_smart51_get,
1417 .put = via_smart51_put,
1418 },
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001419 {
1420 .iface = NID_MAPPING,
1421 .name = "Smart 5.1",
1422 }
Lydia Wang1564b282009-10-10 19:07:52 +08001423};
1424
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001425static int via_smart51_build(struct via_spec *spec)
1426{
1427 struct snd_kcontrol_new *knew;
1428 int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
1429 hda_nid_t nid;
1430 int i;
1431
1432 knew = via_clone_control(spec, &via_smart51_mixer[0]);
1433 if (knew == NULL)
1434 return -ENOMEM;
1435
1436 for (i = 0; i < ARRAY_SIZE(index); i++) {
1437 nid = spec->autocfg.input_pins[index[i]];
1438 if (nid) {
1439 knew = via_clone_control(spec, &via_smart51_mixer[1]);
1440 if (knew == NULL)
1441 return -ENOMEM;
1442 knew->subdevice = nid;
1443 }
1444 }
1445
1446 return 0;
1447}
1448
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449/* capture mixer elements */
1450static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1451 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1452 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1453 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1454 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1455 {
1456 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1457 /* The multiple "Capture Source" controls confuse alsamixer
1458 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01001459 */
1460 /* .name = "Capture Source", */
1461 .name = "Input Source",
1462 .count = 1,
1463 .info = via_mux_enum_info,
1464 .get = via_mux_enum_get,
1465 .put = via_mux_enum_put,
1466 },
1467 { } /* end */
1468};
Lydia Wangf5271102009-10-10 19:07:35 +08001469
1470/* check AA path's mute statue */
1471static int is_aa_path_mute(struct hda_codec *codec)
1472{
1473 int mute = 1;
1474 hda_nid_t nid_mixer;
1475 int start_idx;
1476 int end_idx;
1477 int i;
1478 struct via_spec *spec = codec->spec;
1479 /* get nid of MW0 and start & end index */
1480 switch (spec->codec_type) {
1481 case VT1708B_8CH:
1482 case VT1708B_4CH:
1483 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001484 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001485 nid_mixer = 0x16;
1486 start_idx = 2;
1487 end_idx = 4;
1488 break;
1489 case VT1702:
1490 nid_mixer = 0x1a;
1491 start_idx = 1;
1492 end_idx = 3;
1493 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +08001494 case VT1718S:
1495 nid_mixer = 0x21;
1496 start_idx = 1;
1497 end_idx = 3;
1498 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001499 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001500 case VT1812:
Lydia Wang25eaba22009-10-10 19:08:43 +08001501 nid_mixer = 0x21;
1502 start_idx = 0;
1503 end_idx = 2;
1504 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001505 default:
1506 return 0;
1507 }
1508 /* check AA path's mute status */
1509 for (i = start_idx; i <= end_idx; i++) {
1510 unsigned int con_list = snd_hda_codec_read(
1511 codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1512 int shift = 8 * (i % 4);
1513 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1514 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1515 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1516 /* check mute status while the pin is connected */
1517 int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1518 HDA_INPUT, i) >> 7;
1519 int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1520 HDA_INPUT, i) >> 7;
1521 if (!mute_l || !mute_r) {
1522 mute = 0;
1523 break;
1524 }
1525 }
1526 }
1527 return mute;
1528}
1529
1530/* enter/exit analog low-current mode */
1531static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1532{
1533 struct via_spec *spec = codec->spec;
1534 static int saved_stream_idle = 1; /* saved stream idle status */
1535 int enable = is_aa_path_mute(codec);
1536 unsigned int verb = 0;
1537 unsigned int parm = 0;
1538
1539 if (stream_idle == -1) /* stream status did not change */
1540 enable = enable && saved_stream_idle;
1541 else {
1542 enable = enable && stream_idle;
1543 saved_stream_idle = stream_idle;
1544 }
1545
1546 /* decide low current mode's verb & parameter */
1547 switch (spec->codec_type) {
1548 case VT1708B_8CH:
1549 case VT1708B_4CH:
1550 verb = 0xf70;
1551 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1552 break;
1553 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001554 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001555 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001556 verb = 0xf73;
1557 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1558 break;
1559 case VT1702:
1560 verb = 0xf73;
1561 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1562 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001563 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001564 case VT1812:
Lydia Wang25eaba22009-10-10 19:08:43 +08001565 verb = 0xf93;
1566 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1567 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001568 default:
1569 return; /* other codecs are not supported */
1570 }
1571 /* send verb */
1572 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1573}
1574
Joseph Chanc577b8a2006-11-29 15:29:40 +01001575/*
1576 * generic initialization of ADC, input mixers and output mixers
1577 */
1578static struct hda_verb vt1708_volume_init_verbs[] = {
1579 /*
1580 * Unmute ADC0-1 and set the default input to mic-in
1581 */
1582 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1583 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1584
1585
Josepch Chanf7278fd2007-12-13 16:40:40 +01001586 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587 * mixer widget
1588 */
1589 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001590 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1591 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1592 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1593 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1594 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001595
1596 /*
1597 * Set up output mixers (0x19 - 0x1b)
1598 */
1599 /* set vol=0 to output mixers */
1600 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1601 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1602 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Lydia Wang377ff312009-10-10 19:08:55 +08001603
Lydia Wangbfdc6752009-10-10 19:08:50 +08001604 /* Setup default input MW0 to PW4 */
1605 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001606 /* PW9 Output enable */
1607 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001608 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001609};
1610
1611static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1612 struct hda_codec *codec,
1613 struct snd_pcm_substream *substream)
1614{
1615 struct via_spec *spec = codec->spec;
Lydia Wang17314372009-10-10 19:07:37 +08001616 int idle = substream->pstr->substream_opened == 1
1617 && substream->ref_count == 0;
Lydia Wang17314372009-10-10 19:07:37 +08001618 analog_low_current_mode(codec, idle);
Takashi Iwai9a081602008-02-12 18:37:26 +01001619 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1620 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621}
1622
Harald Welte0aa62ae2008-09-09 15:58:27 +08001623static void playback_multi_pcm_prep_0(struct hda_codec *codec,
1624 unsigned int stream_tag,
1625 unsigned int format,
1626 struct snd_pcm_substream *substream)
1627{
1628 struct via_spec *spec = codec->spec;
1629 struct hda_multi_out *mout = &spec->multiout;
1630 hda_nid_t *nids = mout->dac_nids;
1631 int chs = substream->runtime->channels;
1632 int i;
1633
1634 mutex_lock(&codec->spdif_mutex);
1635 if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
1636 if (chs == 2 &&
1637 snd_hda_is_supported_format(codec, mout->dig_out_nid,
1638 format) &&
1639 !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
1640 mout->dig_out_used = HDA_DIG_ANALOG_DUP;
1641 /* turn off SPDIF once; otherwise the IEC958 bits won't
1642 * be updated */
1643 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1644 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1645 AC_VERB_SET_DIGI_CONVERT_1,
1646 codec->spdif_ctls &
1647 ~AC_DIG1_ENABLE & 0xff);
1648 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1649 stream_tag, 0, format);
1650 /* turn on again (if needed) */
1651 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1652 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1653 AC_VERB_SET_DIGI_CONVERT_1,
1654 codec->spdif_ctls & 0xff);
1655 } else {
1656 mout->dig_out_used = 0;
1657 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1658 0, 0, 0);
1659 }
1660 }
1661 mutex_unlock(&codec->spdif_mutex);
1662
1663 /* front */
1664 snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
1665 0, format);
1666
Lydia Wangeb7188c2009-10-10 19:08:34 +08001667 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1668 && !spec->hp_independent_mode)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001669 /* headphone out will just decode front left/right (stereo) */
1670 snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
1671 0, format);
1672
1673 /* extra outputs copied from front */
1674 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1675 if (mout->extra_out_nid[i])
1676 snd_hda_codec_setup_stream(codec,
1677 mout->extra_out_nid[i],
1678 stream_tag, 0, format);
1679
1680 /* surrounds */
1681 for (i = 1; i < mout->num_dacs; i++) {
1682 if (chs >= (i + 1) * 2) /* independent out */
1683 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1684 i * 2, format);
1685 else /* copy front */
1686 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1687 0, format);
1688 }
1689}
1690
1691static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1692 struct hda_codec *codec,
1693 unsigned int stream_tag,
1694 unsigned int format,
1695 struct snd_pcm_substream *substream)
1696{
1697 struct via_spec *spec = codec->spec;
1698 struct hda_multi_out *mout = &spec->multiout;
1699 hda_nid_t *nids = mout->dac_nids;
1700
1701 if (substream->number == 0)
1702 playback_multi_pcm_prep_0(codec, stream_tag, format,
1703 substream);
1704 else {
1705 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1706 spec->hp_independent_mode)
1707 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1708 stream_tag, 0, format);
1709 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001710 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001711 return 0;
1712}
1713
1714static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1715 struct hda_codec *codec,
1716 struct snd_pcm_substream *substream)
1717{
1718 struct via_spec *spec = codec->spec;
1719 struct hda_multi_out *mout = &spec->multiout;
1720 hda_nid_t *nids = mout->dac_nids;
1721 int i;
1722
1723 if (substream->number == 0) {
1724 for (i = 0; i < mout->num_dacs; i++)
1725 snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
1726
1727 if (mout->hp_nid && !spec->hp_independent_mode)
1728 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1729 0, 0, 0);
1730
1731 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1732 if (mout->extra_out_nid[i])
1733 snd_hda_codec_setup_stream(codec,
1734 mout->extra_out_nid[i],
1735 0, 0, 0);
1736 mutex_lock(&codec->spdif_mutex);
1737 if (mout->dig_out_nid &&
1738 mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
1739 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1740 0, 0, 0);
1741 mout->dig_out_used = 0;
1742 }
1743 mutex_unlock(&codec->spdif_mutex);
1744 } else {
1745 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1746 spec->hp_independent_mode)
1747 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1748 0, 0, 0);
1749 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001750 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001751 return 0;
1752}
1753
Joseph Chanc577b8a2006-11-29 15:29:40 +01001754/*
1755 * Digital out
1756 */
1757static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1758 struct hda_codec *codec,
1759 struct snd_pcm_substream *substream)
1760{
1761 struct via_spec *spec = codec->spec;
1762 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1763}
1764
1765static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1766 struct hda_codec *codec,
1767 struct snd_pcm_substream *substream)
1768{
1769 struct via_spec *spec = codec->spec;
1770 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1771}
1772
Harald Welte5691ec72008-09-15 22:42:26 +08001773static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001774 struct hda_codec *codec,
1775 unsigned int stream_tag,
1776 unsigned int format,
1777 struct snd_pcm_substream *substream)
1778{
1779 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001780 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1781 stream_tag, format, substream);
1782}
Harald Welte5691ec72008-09-15 22:42:26 +08001783
Takashi Iwai9da29272009-05-07 16:31:14 +02001784static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1785 struct hda_codec *codec,
1786 struct snd_pcm_substream *substream)
1787{
1788 struct via_spec *spec = codec->spec;
1789 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001790 return 0;
1791}
1792
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793/*
1794 * Analog capture
1795 */
1796static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1797 struct hda_codec *codec,
1798 unsigned int stream_tag,
1799 unsigned int format,
1800 struct snd_pcm_substream *substream)
1801{
1802 struct via_spec *spec = codec->spec;
1803
1804 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1805 stream_tag, 0, format);
1806 return 0;
1807}
1808
1809static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1810 struct hda_codec *codec,
1811 struct snd_pcm_substream *substream)
1812{
1813 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001814 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001815 return 0;
1816}
1817
1818static struct hda_pcm_stream vt1708_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001819 .substreams = 2,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001820 .channels_min = 2,
1821 .channels_max = 8,
1822 .nid = 0x10, /* NID to query formats and rates */
1823 .ops = {
1824 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001825 .prepare = via_playback_multi_pcm_prepare,
1826 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001827 },
1828};
1829
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001830static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Lydia Wangc873cc22009-10-10 19:08:21 +08001831 .substreams = 2,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001832 .channels_min = 2,
1833 .channels_max = 8,
1834 .nid = 0x10, /* NID to query formats and rates */
1835 /* We got noisy outputs on the right channel on VT1708 when
1836 * 24bit samples are used. Until any workaround is found,
1837 * disable the 24bit format, so far.
1838 */
1839 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1840 .ops = {
1841 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08001842 .prepare = via_playback_multi_pcm_prepare,
1843 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001844 },
1845};
1846
Joseph Chanc577b8a2006-11-29 15:29:40 +01001847static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1848 .substreams = 2,
1849 .channels_min = 2,
1850 .channels_max = 2,
1851 .nid = 0x15, /* NID to query formats and rates */
1852 .ops = {
1853 .prepare = via_capture_pcm_prepare,
1854 .cleanup = via_capture_pcm_cleanup
1855 },
1856};
1857
1858static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1859 .substreams = 1,
1860 .channels_min = 2,
1861 .channels_max = 2,
1862 /* NID is set in via_build_pcms */
1863 .ops = {
1864 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001865 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001866 .prepare = via_dig_playback_pcm_prepare,
1867 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868 },
1869};
1870
1871static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1872 .substreams = 1,
1873 .channels_min = 2,
1874 .channels_max = 2,
1875};
1876
1877static int via_build_controls(struct hda_codec *codec)
1878{
1879 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001880 struct snd_kcontrol *kctl;
1881 struct snd_kcontrol_new *knew;
1882 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001883
1884 for (i = 0; i < spec->num_mixers; i++) {
1885 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1886 if (err < 0)
1887 return err;
1888 }
1889
1890 if (spec->multiout.dig_out_nid) {
1891 err = snd_hda_create_spdif_out_ctls(codec,
1892 spec->multiout.dig_out_nid);
1893 if (err < 0)
1894 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001895 err = snd_hda_create_spdif_share_sw(codec,
1896 &spec->multiout);
1897 if (err < 0)
1898 return err;
1899 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001900 }
1901 if (spec->dig_in_nid) {
1902 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1903 if (err < 0)
1904 return err;
1905 }
Lydia Wang17314372009-10-10 19:07:37 +08001906
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001907 /* assign Capture Source enums to NID */
1908 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1909 for (i = 0; kctl && i < kctl->count; i++) {
1910 err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids,
1911 spec->input_mux->num_items);
1912 if (err < 0)
1913 return err;
1914 }
1915
1916 /* other nid->control mapping */
1917 for (i = 0; i < spec->num_mixers; i++) {
1918 for (knew = spec->mixers[i]; knew->name; knew++) {
1919 if (knew->iface != NID_MAPPING)
1920 continue;
1921 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1922 if (kctl == NULL)
1923 continue;
1924 err = snd_hda_add_nid(codec, kctl, 0,
1925 knew->subdevice);
1926 }
1927 }
1928
Lydia Wang17314372009-10-10 19:07:37 +08001929 /* init power states */
1930 set_jack_power_state(codec);
1931 analog_low_current_mode(codec, 1);
1932
Takashi Iwai603c4012008-07-30 15:01:44 +02001933 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001934 return 0;
1935}
1936
1937static int via_build_pcms(struct hda_codec *codec)
1938{
1939 struct via_spec *spec = codec->spec;
1940 struct hda_pcm *info = spec->pcm_rec;
1941
1942 codec->num_pcms = 1;
1943 codec->pcm_info = info;
1944
1945 info->name = spec->stream_name_analog;
Lydia Wang377ff312009-10-10 19:08:55 +08001946 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1947 *(spec->stream_analog_playback);
1948 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1949 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001950 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1951 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1952
1953 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1954 spec->multiout.max_channels;
1955
1956 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1957 codec->num_pcms++;
1958 info++;
1959 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001960 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001961 if (spec->multiout.dig_out_nid) {
1962 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1963 *(spec->stream_digital_playback);
1964 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1965 spec->multiout.dig_out_nid;
1966 }
1967 if (spec->dig_in_nid) {
1968 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1969 *(spec->stream_digital_capture);
1970 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1971 spec->dig_in_nid;
1972 }
1973 }
1974
1975 return 0;
1976}
1977
1978static void via_free(struct hda_codec *codec)
1979{
1980 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001981
1982 if (!spec)
1983 return;
1984
Takashi Iwai603c4012008-07-30 15:01:44 +02001985 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001986 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001987 kfree(codec->spec);
1988}
1989
Harald Welte69e52a82008-09-09 15:57:32 +08001990/* mute internal speaker if HP is plugged */
1991static void via_hp_automute(struct hda_codec *codec)
1992{
Lydia Wangdcf34c82009-10-10 19:08:15 +08001993 unsigned int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001994 struct via_spec *spec = codec->spec;
1995
Takashi Iwaid56757a2009-11-18 08:00:14 +01001996 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wangdcf34c82009-10-10 19:08:15 +08001997
1998 if (!spec->hp_independent_mode) {
1999 struct snd_ctl_elem_id id;
2000 /* auto mute */
2001 snd_hda_codec_amp_stereo(
2002 codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
2003 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
2004 /* notify change */
2005 memset(&id, 0, sizeof(id));
2006 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
2007 strcpy(id.name, "Front Playback Switch");
2008 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
2009 &id);
2010 }
Harald Welte69e52a82008-09-09 15:57:32 +08002011}
2012
Lydia Wangf3db4232009-10-10 19:08:41 +08002013/* mute mono out if HP or Line out is plugged */
2014static void via_mono_automute(struct hda_codec *codec)
2015{
2016 unsigned int hp_present, lineout_present;
2017 struct via_spec *spec = codec->spec;
2018
2019 if (spec->codec_type != VT1716S)
2020 return;
2021
Takashi Iwaid56757a2009-11-18 08:00:14 +01002022 lineout_present = snd_hda_jack_detect(codec,
2023 spec->autocfg.line_out_pins[0]);
Lydia Wangf3db4232009-10-10 19:08:41 +08002024
2025 /* Mute Mono Out if Line Out is plugged */
2026 if (lineout_present) {
2027 snd_hda_codec_amp_stereo(
2028 codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
2029 return;
2030 }
2031
Takashi Iwaid56757a2009-11-18 08:00:14 +01002032 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wangf3db4232009-10-10 19:08:41 +08002033
2034 if (!spec->hp_independent_mode)
2035 snd_hda_codec_amp_stereo(
2036 codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
2037 hp_present ? HDA_AMP_MUTE : 0);
2038}
2039
Harald Welte69e52a82008-09-09 15:57:32 +08002040static void via_gpio_control(struct hda_codec *codec)
2041{
2042 unsigned int gpio_data;
2043 unsigned int vol_counter;
2044 unsigned int vol;
2045 unsigned int master_vol;
2046
2047 struct via_spec *spec = codec->spec;
2048
2049 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
2050 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
2051
2052 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
2053 0xF84, 0) & 0x3F0000) >> 16;
2054
2055 vol = vol_counter & 0x1F;
2056 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
2057 AC_VERB_GET_AMP_GAIN_MUTE,
2058 AC_AMP_GET_INPUT);
2059
2060 if (gpio_data == 0x02) {
2061 /* unmute line out */
2062 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
2063 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
2064
2065 if (vol_counter & 0x20) {
2066 /* decrease volume */
2067 if (vol > master_vol)
2068 vol = master_vol;
2069 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
2070 0, HDA_AMP_VOLMASK,
2071 master_vol-vol);
2072 } else {
2073 /* increase volume */
2074 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
2075 HDA_AMP_VOLMASK,
2076 ((master_vol+vol) > 0x2A) ? 0x2A :
2077 (master_vol+vol));
2078 }
2079 } else if (!(gpio_data & 0x02)) {
2080 /* mute line out */
2081 snd_hda_codec_amp_stereo(codec,
2082 spec->autocfg.line_out_pins[0],
2083 HDA_OUTPUT, 0, HDA_AMP_MUTE,
2084 HDA_AMP_MUTE);
2085 }
2086}
2087
Lydia Wang25eaba22009-10-10 19:08:43 +08002088/* mute Internal-Speaker if HP is plugged */
2089static void via_speaker_automute(struct hda_codec *codec)
2090{
2091 unsigned int hp_present;
2092 struct via_spec *spec = codec->spec;
2093
Lydia Wangab6734e2009-10-10 19:08:46 +08002094 if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
Lydia Wang25eaba22009-10-10 19:08:43 +08002095 return;
2096
Takashi Iwaid56757a2009-11-18 08:00:14 +01002097 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08002098
2099 if (!spec->hp_independent_mode) {
2100 struct snd_ctl_elem_id id;
2101 snd_hda_codec_amp_stereo(
2102 codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
2103 HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
2104 /* notify change */
2105 memset(&id, 0, sizeof(id));
2106 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
2107 strcpy(id.name, "Speaker Playback Switch");
2108 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
2109 &id);
2110 }
2111}
2112
2113/* mute line-out and internal speaker if HP is plugged */
2114static void via_hp_bind_automute(struct hda_codec *codec)
2115{
akpm@linux-foundation.org01a17962009-11-13 16:47:10 -08002116 /* use long instead of int below just to avoid an internal compiler
2117 * error with gcc 4.0.x
2118 */
2119 unsigned long hp_present, present = 0;
Lydia Wang25eaba22009-10-10 19:08:43 +08002120 struct via_spec *spec = codec->spec;
2121 int i;
2122
2123 if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
2124 return;
2125
Takashi Iwaid56757a2009-11-18 08:00:14 +01002126 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08002127
Takashi Iwaid56757a2009-11-18 08:00:14 +01002128 present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08002129
2130 if (!spec->hp_independent_mode) {
2131 /* Mute Line-Outs */
2132 for (i = 0; i < spec->autocfg.line_outs; i++)
2133 snd_hda_codec_amp_stereo(
2134 codec, spec->autocfg.line_out_pins[i],
2135 HDA_OUTPUT, 0,
2136 HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
2137 if (hp_present)
2138 present = hp_present;
2139 }
2140 /* Speakers */
2141 for (i = 0; i < spec->autocfg.speaker_outs; i++)
2142 snd_hda_codec_amp_stereo(
2143 codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
2144 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
2145}
2146
2147
Harald Welte69e52a82008-09-09 15:57:32 +08002148/* unsolicited event for jack sensing */
2149static void via_unsol_event(struct hda_codec *codec,
2150 unsigned int res)
2151{
2152 res >>= 26;
Lydia Wanga34df192009-10-10 19:08:01 +08002153 if (res & VIA_HP_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08002154 via_hp_automute(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08002155 if (res & VIA_GPIO_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08002156 via_gpio_control(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08002157 if (res & VIA_JACK_EVENT)
2158 set_jack_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002159 if (res & VIA_MONO_EVENT)
2160 via_mono_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002161 if (res & VIA_SPEAKER_EVENT)
2162 via_speaker_automute(codec);
2163 if (res & VIA_BIND_HP_EVENT)
2164 via_hp_bind_automute(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08002165}
2166
Joseph Chanc577b8a2006-11-29 15:29:40 +01002167static int via_init(struct hda_codec *codec)
2168{
2169 struct via_spec *spec = codec->spec;
Harald Welte69e52a82008-09-09 15:57:32 +08002170 int i;
2171 for (i = 0; i < spec->num_iverbs; i++)
2172 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2173
Lydia Wang518bf3b2009-10-10 19:07:29 +08002174 spec->codec_type = get_codec_type(codec);
2175 if (spec->codec_type == VT1708BCE)
2176 spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
2177 same */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002178 /* Lydia Add for EAPD enable */
2179 if (!spec->dig_in_nid) { /* No Digital In connection */
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002180 if (spec->dig_in_pin) {
2181 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002182 AC_VERB_SET_PIN_WIDGET_CONTROL,
Takashi Iwai12b74c82008-01-15 12:39:38 +01002183 PIN_OUT);
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002184 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002185 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
2186 }
Takashi Iwai12b74c82008-01-15 12:39:38 +01002187 } else /* enable SPDIF-input pin */
2188 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2189 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002190
Takashi Iwai9da29272009-05-07 16:31:14 +02002191 /* assign slave outs */
2192 if (spec->slave_dig_outs[0])
2193 codec->slave_dig_outs = spec->slave_dig_outs;
Harald Welte5691ec72008-09-15 22:42:26 +08002194
Lydia Wang377ff312009-10-10 19:08:55 +08002195 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002196}
2197
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002198#ifdef SND_HDA_NEEDS_RESUME
2199static int via_suspend(struct hda_codec *codec, pm_message_t state)
2200{
2201 struct via_spec *spec = codec->spec;
2202 vt1708_stop_hp_work(spec);
2203 return 0;
2204}
2205#endif
2206
Takashi Iwaicb53c622007-08-10 17:21:45 +02002207#ifdef CONFIG_SND_HDA_POWER_SAVE
2208static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
2209{
2210 struct via_spec *spec = codec->spec;
2211 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
2212}
2213#endif
2214
Joseph Chanc577b8a2006-11-29 15:29:40 +01002215/*
2216 */
2217static struct hda_codec_ops via_patch_ops = {
2218 .build_controls = via_build_controls,
2219 .build_pcms = via_build_pcms,
2220 .init = via_init,
2221 .free = via_free,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002222#ifdef SND_HDA_NEEDS_RESUME
2223 .suspend = via_suspend,
2224#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02002225#ifdef CONFIG_SND_HDA_POWER_SAVE
2226 .check_power_status = via_check_power_status,
2227#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002228};
2229
2230/* fill in the dac_nids table from the parsed pin configuration */
2231static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
2232 const struct auto_pin_cfg *cfg)
2233{
2234 int i;
2235 hda_nid_t nid;
2236
2237 spec->multiout.num_dacs = cfg->line_outs;
2238
2239 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang377ff312009-10-10 19:08:55 +08002240
2241 for (i = 0; i < 4; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002242 nid = cfg->line_out_pins[i];
2243 if (nid) {
2244 /* config dac list */
2245 switch (i) {
2246 case AUTO_SEQ_FRONT:
2247 spec->multiout.dac_nids[i] = 0x10;
2248 break;
2249 case AUTO_SEQ_CENLFE:
2250 spec->multiout.dac_nids[i] = 0x12;
2251 break;
2252 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08002253 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002254 break;
2255 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08002256 spec->multiout.dac_nids[i] = 0x13;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257 break;
2258 }
2259 }
2260 }
2261
2262 return 0;
2263}
2264
2265/* add playback controls from the parsed DAC table */
2266static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
2267 const struct auto_pin_cfg *cfg)
2268{
2269 char name[32];
2270 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang9645c202009-10-10 19:08:27 +08002271 hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
Joseph Chanc577b8a2006-11-29 15:29:40 +01002272 int i, err;
2273
2274 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2275 nid = cfg->line_out_pins[i];
2276
2277 if (!nid)
2278 continue;
Lydia Wang377ff312009-10-10 19:08:55 +08002279
Lydia Wang9645c202009-10-10 19:08:27 +08002280 nid_vol = nid_vols[i];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002281
2282 if (i == AUTO_SEQ_CENLFE) {
2283 /* Center/LFE */
2284 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002285 "Center Playback Volume",
2286 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2287 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002288 if (err < 0)
2289 return err;
2290 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2291 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01002292 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2293 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002294 if (err < 0)
2295 return err;
2296 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2297 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01002298 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2299 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002300 if (err < 0)
2301 return err;
2302 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2303 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01002304 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2305 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002306 if (err < 0)
2307 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002308 } else if (i == AUTO_SEQ_FRONT) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002309 /* add control to mixer index 0 */
2310 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2311 "Master Front Playback Volume",
Lydia Wang9645c202009-10-10 19:08:27 +08002312 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002313 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002314 if (err < 0)
2315 return err;
2316 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2317 "Master Front Playback Switch",
Lydia Wang9645c202009-10-10 19:08:27 +08002318 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002319 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002320 if (err < 0)
2321 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002322
Joseph Chanc577b8a2006-11-29 15:29:40 +01002323 /* add control to PW3 */
2324 sprintf(name, "%s Playback Volume", chname[i]);
2325 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002326 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2327 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002328 if (err < 0)
2329 return err;
2330 sprintf(name, "%s Playback Switch", chname[i]);
2331 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002332 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2333 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002334 if (err < 0)
2335 return err;
2336 } else {
2337 sprintf(name, "%s Playback Volume", chname[i]);
2338 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002339 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2340 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002341 if (err < 0)
2342 return err;
2343 sprintf(name, "%s Playback Switch", chname[i]);
2344 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002345 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2346 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002347 if (err < 0)
2348 return err;
2349 }
2350 }
2351
2352 return 0;
2353}
2354
Harald Welte0aa62ae2008-09-09 15:58:27 +08002355static void create_hp_imux(struct via_spec *spec)
2356{
2357 int i;
2358 struct hda_input_mux *imux = &spec->private_imux[1];
2359 static const char *texts[] = { "OFF", "ON", NULL};
2360
2361 /* for hp mode select */
2362 i = 0;
2363 while (texts[i] != NULL) {
2364 imux->items[imux->num_items].label = texts[i];
2365 imux->items[imux->num_items].index = i;
2366 imux->num_items++;
2367 i++;
2368 }
2369
2370 spec->hp_mux = &spec->private_imux[1];
2371}
2372
Joseph Chanc577b8a2006-11-29 15:29:40 +01002373static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2374{
2375 int err;
2376
2377 if (!pin)
2378 return 0;
2379
2380 spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08002381 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002382
2383 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2384 "Headphone Playback Volume",
2385 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2386 if (err < 0)
2387 return err;
2388 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2389 "Headphone Playback Switch",
2390 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2391 if (err < 0)
2392 return err;
2393
Harald Welte0aa62ae2008-09-09 15:58:27 +08002394 create_hp_imux(spec);
2395
Joseph Chanc577b8a2006-11-29 15:29:40 +01002396 return 0;
2397}
2398
2399/* create playback/capture controls for input pins */
2400static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
2401 const struct auto_pin_cfg *cfg)
2402{
2403 static char *labels[] = {
2404 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2405 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08002406 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002407 int i, err, idx = 0;
2408
2409 /* for internal loopback recording select */
2410 imux->items[imux->num_items].label = "Stereo Mixer";
2411 imux->items[imux->num_items].index = idx;
2412 imux->num_items++;
2413
2414 for (i = 0; i < AUTO_PIN_LAST; i++) {
2415 if (!cfg->input_pins[i])
2416 continue;
2417
2418 switch (cfg->input_pins[i]) {
2419 case 0x1d: /* Mic */
2420 idx = 2;
2421 break;
Lydia Wang377ff312009-10-10 19:08:55 +08002422
Joseph Chanc577b8a2006-11-29 15:29:40 +01002423 case 0x1e: /* Line In */
2424 idx = 3;
2425 break;
2426
2427 case 0x21: /* Front Mic */
2428 idx = 4;
2429 break;
2430
2431 case 0x24: /* CD */
2432 idx = 1;
2433 break;
2434 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08002435 err = via_new_analog_input(spec, labels[i], idx, 0x17);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002436 if (err < 0)
2437 return err;
2438 imux->items[imux->num_items].label = labels[i];
2439 imux->items[imux->num_items].index = idx;
2440 imux->num_items++;
2441 }
2442 return 0;
2443}
2444
Takashi Iwaicb53c622007-08-10 17:21:45 +02002445#ifdef CONFIG_SND_HDA_POWER_SAVE
2446static struct hda_amp_list vt1708_loopbacks[] = {
2447 { 0x17, HDA_INPUT, 1 },
2448 { 0x17, HDA_INPUT, 2 },
2449 { 0x17, HDA_INPUT, 3 },
2450 { 0x17, HDA_INPUT, 4 },
2451 { } /* end */
2452};
2453#endif
2454
Harald Welte76d9b0d2008-09-09 15:50:37 +08002455static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2456{
2457 unsigned int def_conf;
2458 unsigned char seqassoc;
2459
Takashi Iwai2f334f92009-02-20 14:37:42 +01002460 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002461 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2462 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002463 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2464 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2465 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2466 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002467 }
2468
2469 return;
2470}
2471
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002472static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
2473 struct snd_ctl_elem_value *ucontrol)
2474{
2475 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2476 struct via_spec *spec = codec->spec;
2477
2478 if (spec->codec_type != VT1708)
2479 return 0;
2480 spec->vt1708_jack_detectect =
2481 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2482 ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
2483 return 0;
2484}
2485
2486static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
2487 struct snd_ctl_elem_value *ucontrol)
2488{
2489 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2490 struct via_spec *spec = codec->spec;
2491 int change;
2492
2493 if (spec->codec_type != VT1708)
2494 return 0;
2495 spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
2496 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2497 == !spec->vt1708_jack_detectect;
2498 if (spec->vt1708_jack_detectect) {
2499 mute_aa_path(codec, 1);
2500 notify_aa_path_ctls(codec);
2501 }
2502 return change;
2503}
2504
2505static struct snd_kcontrol_new vt1708_jack_detectect[] = {
2506 {
2507 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2508 .name = "Jack Detect",
2509 .count = 1,
2510 .info = snd_ctl_boolean_mono_info,
2511 .get = vt1708_jack_detectect_get,
2512 .put = vt1708_jack_detectect_put,
2513 },
2514 {} /* end */
2515};
2516
Joseph Chanc577b8a2006-11-29 15:29:40 +01002517static int vt1708_parse_auto_config(struct hda_codec *codec)
2518{
2519 struct via_spec *spec = codec->spec;
2520 int err;
2521
Harald Welte76d9b0d2008-09-09 15:50:37 +08002522 /* Add HP and CD pin config connect bit re-config action */
2523 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2524 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2525
Joseph Chanc577b8a2006-11-29 15:29:40 +01002526 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2527 if (err < 0)
2528 return err;
2529 err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2530 if (err < 0)
2531 return err;
2532 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2533 return 0; /* can't find valid BIOS pin config */
2534
2535 err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2536 if (err < 0)
2537 return err;
2538 err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2539 if (err < 0)
2540 return err;
2541 err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
2542 if (err < 0)
2543 return err;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002544 /* add jack detect on/off control */
2545 err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
2546 if (err < 0)
2547 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002548
2549 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2550
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002551 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002552 spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002553 spec->dig_in_pin = VT1708_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002554 if (spec->autocfg.dig_in_pin)
2555 spec->dig_in_nid = VT1708_DIGIN_NID;
2556
Takashi Iwai603c4012008-07-30 15:01:44 +02002557 if (spec->kctls.list)
2558 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002559
Harald Welte69e52a82008-09-09 15:57:32 +08002560 spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002561
Harald Welte0aa62ae2008-09-09 15:58:27 +08002562 spec->input_mux = &spec->private_imux[0];
2563
Harald Weltef8fdd492008-09-15 22:41:31 +08002564 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002565 via_hp_build(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002566
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002567 via_smart51_build(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002568 return 1;
2569}
2570
2571/* init callback for auto-configuration model -- overriding the default init */
2572static int via_auto_init(struct hda_codec *codec)
2573{
Lydia Wang25eaba22009-10-10 19:08:43 +08002574 struct via_spec *spec = codec->spec;
2575
Joseph Chanc577b8a2006-11-29 15:29:40 +01002576 via_init(codec);
2577 via_auto_init_multi_out(codec);
2578 via_auto_init_hp_out(codec);
2579 via_auto_init_analog_input(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08002580 if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
Lydia Wang25eaba22009-10-10 19:08:43 +08002581 via_hp_bind_automute(codec);
2582 } else {
2583 via_hp_automute(codec);
2584 via_speaker_automute(codec);
2585 }
2586
Joseph Chanc577b8a2006-11-29 15:29:40 +01002587 return 0;
2588}
2589
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002590static void vt1708_update_hp_jack_state(struct work_struct *work)
2591{
2592 struct via_spec *spec = container_of(work, struct via_spec,
2593 vt1708_hp_work.work);
2594 if (spec->codec_type != VT1708)
2595 return;
2596 /* if jack state toggled */
2597 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002598 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002599 spec->vt1708_hp_present ^= 1;
2600 via_hp_automute(spec->codec);
2601 }
2602 vt1708_start_hp_work(spec);
2603}
2604
Takashi Iwai337b9d02009-07-07 18:18:59 +02002605static int get_mux_nids(struct hda_codec *codec)
2606{
2607 struct via_spec *spec = codec->spec;
2608 hda_nid_t nid, conn[8];
2609 unsigned int type;
2610 int i, n;
2611
2612 for (i = 0; i < spec->num_adc_nids; i++) {
2613 nid = spec->adc_nids[i];
2614 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002615 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002616 if (type == AC_WID_PIN)
2617 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002618 n = snd_hda_get_connections(codec, nid, conn,
2619 ARRAY_SIZE(conn));
2620 if (n <= 0)
2621 break;
2622 if (n > 1) {
2623 spec->mux_nids[i] = nid;
2624 break;
2625 }
2626 nid = conn[0];
2627 }
2628 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002629 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002630}
2631
Joseph Chanc577b8a2006-11-29 15:29:40 +01002632static int patch_vt1708(struct hda_codec *codec)
2633{
2634 struct via_spec *spec;
2635 int err;
2636
2637 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002638 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002639 if (spec == NULL)
2640 return -ENOMEM;
2641
Joseph Chanc577b8a2006-11-29 15:29:40 +01002642 /* automatic parse from the BIOS config */
2643 err = vt1708_parse_auto_config(codec);
2644 if (err < 0) {
2645 via_free(codec);
2646 return err;
2647 } else if (!err) {
2648 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2649 "from BIOS. Using genenic mode...\n");
2650 }
2651
Lydia Wang377ff312009-10-10 19:08:55 +08002652
Joseph Chanc577b8a2006-11-29 15:29:40 +01002653 spec->stream_name_analog = "VT1708 Analog";
2654 spec->stream_analog_playback = &vt1708_pcm_analog_playback;
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002655 /* disable 32bit format on VT1708 */
2656 if (codec->vendor_id == 0x11061708)
2657 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002658 spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2659
2660 spec->stream_name_digital = "VT1708 Digital";
2661 spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2662 spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2663
Lydia Wang377ff312009-10-10 19:08:55 +08002664
Joseph Chanc577b8a2006-11-29 15:29:40 +01002665 if (!spec->adc_nids && spec->input_mux) {
2666 spec->adc_nids = vt1708_adc_nids;
2667 spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
Takashi Iwai0f67a612009-08-31 08:12:29 +02002668 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002669 spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2670 spec->num_mixers++;
2671 }
2672
2673 codec->patch_ops = via_patch_ops;
2674
2675 codec->patch_ops.init = via_auto_init;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002676#ifdef CONFIG_SND_HDA_POWER_SAVE
2677 spec->loopback.amplist = vt1708_loopbacks;
2678#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002679 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002680 return 0;
2681}
2682
2683/* capture mixer elements */
2684static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2685 HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2686 HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2687 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2688 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2689 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2690 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2691 {
2692 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2693 /* The multiple "Capture Source" controls confuse alsamixer
2694 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01002695 */
2696 /* .name = "Capture Source", */
2697 .name = "Input Source",
2698 .count = 1,
2699 .info = via_mux_enum_info,
2700 .get = via_mux_enum_get,
2701 .put = via_mux_enum_put,
2702 },
2703 { } /* end */
2704};
2705
Harald Welte69e52a82008-09-09 15:57:32 +08002706static struct hda_verb vt1709_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002707 {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2708 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002709 { }
2710};
2711
Joseph Chanc577b8a2006-11-29 15:29:40 +01002712/*
2713 * generic initialization of ADC, input mixers and output mixers
2714 */
2715static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2716 /*
2717 * Unmute ADC0-2 and set the default input to mic-in
2718 */
2719 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2720 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2721 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2722
2723
Josepch Chanf7278fd2007-12-13 16:40:40 +01002724 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01002725 * mixer widget
2726 */
2727 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002728 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2729 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2730 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2731 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2732 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002733
2734 /*
2735 * Set up output selector (0x1a, 0x1b, 0x29)
2736 */
2737 /* set vol=0 to output mixers */
2738 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2739 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2740 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2741
2742 /*
2743 * Unmute PW3 and PW4
2744 */
2745 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2746 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2747
Lydia Wangbfdc6752009-10-10 19:08:50 +08002748 /* Set input of PW4 as MW0 */
2749 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002750 /* PW9 Output enable */
2751 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2752 { }
2753};
2754
2755static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2756 .substreams = 1,
2757 .channels_min = 2,
2758 .channels_max = 10,
2759 .nid = 0x10, /* NID to query formats and rates */
2760 .ops = {
2761 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002762 .prepare = via_playback_multi_pcm_prepare,
2763 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002764 },
2765};
2766
2767static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2768 .substreams = 1,
2769 .channels_min = 2,
2770 .channels_max = 6,
2771 .nid = 0x10, /* NID to query formats and rates */
2772 .ops = {
2773 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002774 .prepare = via_playback_multi_pcm_prepare,
2775 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002776 },
2777};
2778
2779static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2780 .substreams = 2,
2781 .channels_min = 2,
2782 .channels_max = 2,
2783 .nid = 0x14, /* NID to query formats and rates */
2784 .ops = {
2785 .prepare = via_capture_pcm_prepare,
2786 .cleanup = via_capture_pcm_cleanup
2787 },
2788};
2789
2790static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2791 .substreams = 1,
2792 .channels_min = 2,
2793 .channels_max = 2,
2794 /* NID is set in via_build_pcms */
2795 .ops = {
2796 .open = via_dig_playback_pcm_open,
2797 .close = via_dig_playback_pcm_close
2798 },
2799};
2800
2801static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2802 .substreams = 1,
2803 .channels_min = 2,
2804 .channels_max = 2,
2805};
2806
2807static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2808 const struct auto_pin_cfg *cfg)
2809{
2810 int i;
2811 hda_nid_t nid;
2812
2813 if (cfg->line_outs == 4) /* 10 channels */
2814 spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2815 else if (cfg->line_outs == 3) /* 6 channels */
2816 spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2817
2818 spec->multiout.dac_nids = spec->private_dac_nids;
2819
2820 if (cfg->line_outs == 4) { /* 10 channels */
2821 for (i = 0; i < cfg->line_outs; i++) {
2822 nid = cfg->line_out_pins[i];
2823 if (nid) {
2824 /* config dac list */
2825 switch (i) {
2826 case AUTO_SEQ_FRONT:
2827 /* AOW0 */
2828 spec->multiout.dac_nids[i] = 0x10;
2829 break;
2830 case AUTO_SEQ_CENLFE:
2831 /* AOW2 */
2832 spec->multiout.dac_nids[i] = 0x12;
2833 break;
2834 case AUTO_SEQ_SURROUND:
2835 /* AOW3 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002836 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002837 break;
2838 case AUTO_SEQ_SIDE:
2839 /* AOW1 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002840 spec->multiout.dac_nids[i] = 0x27;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002841 break;
2842 default:
2843 break;
2844 }
2845 }
2846 }
2847 spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2848
2849 } else if (cfg->line_outs == 3) { /* 6 channels */
Lydia Wang377ff312009-10-10 19:08:55 +08002850 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002851 nid = cfg->line_out_pins[i];
2852 if (nid) {
2853 /* config dac list */
Lydia Wang377ff312009-10-10 19:08:55 +08002854 switch (i) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002855 case AUTO_SEQ_FRONT:
2856 /* AOW0 */
2857 spec->multiout.dac_nids[i] = 0x10;
2858 break;
2859 case AUTO_SEQ_CENLFE:
2860 /* AOW2 */
2861 spec->multiout.dac_nids[i] = 0x12;
2862 break;
2863 case AUTO_SEQ_SURROUND:
2864 /* AOW1 */
2865 spec->multiout.dac_nids[i] = 0x11;
2866 break;
2867 default:
2868 break;
2869 }
2870 }
2871 }
2872 }
2873
2874 return 0;
2875}
2876
2877/* add playback controls from the parsed DAC table */
2878static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2879 const struct auto_pin_cfg *cfg)
2880{
2881 char name[32];
2882 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Lydia Wang4483a2f2009-10-10 19:08:29 +08002883 hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
Joseph Chanc577b8a2006-11-29 15:29:40 +01002884 int i, err;
2885
2886 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2887 nid = cfg->line_out_pins[i];
2888
Lydia Wang377ff312009-10-10 19:08:55 +08002889 if (!nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002890 continue;
2891
Lydia Wang4483a2f2009-10-10 19:08:29 +08002892 nid_vol = nid_vols[i];
2893
Joseph Chanc577b8a2006-11-29 15:29:40 +01002894 if (i == AUTO_SEQ_CENLFE) {
2895 /* Center/LFE */
2896 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2897 "Center Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002898 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002899 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002900 if (err < 0)
2901 return err;
2902 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2903 "LFE Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002904 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002905 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002906 if (err < 0)
2907 return err;
2908 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2909 "Center Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002910 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002911 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002912 if (err < 0)
2913 return err;
2914 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2915 "LFE Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002916 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002917 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002918 if (err < 0)
2919 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002920 } else if (i == AUTO_SEQ_FRONT) {
Lydia Wang4483a2f2009-10-10 19:08:29 +08002921 /* ADD control to mixer index 0 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002922 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2923 "Master Front Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002924 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002925 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002926 if (err < 0)
2927 return err;
2928 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2929 "Master Front Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002930 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002931 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002932 if (err < 0)
2933 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002934
Joseph Chanc577b8a2006-11-29 15:29:40 +01002935 /* add control to PW3 */
2936 sprintf(name, "%s Playback Volume", chname[i]);
2937 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002938 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2939 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002940 if (err < 0)
2941 return err;
2942 sprintf(name, "%s Playback Switch", chname[i]);
2943 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002944 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2945 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002946 if (err < 0)
2947 return err;
2948 } else if (i == AUTO_SEQ_SURROUND) {
2949 sprintf(name, "%s Playback Volume", chname[i]);
2950 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002951 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002952 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002953 if (err < 0)
2954 return err;
2955 sprintf(name, "%s Playback Switch", chname[i]);
2956 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002957 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002958 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002959 if (err < 0)
2960 return err;
2961 } else if (i == AUTO_SEQ_SIDE) {
2962 sprintf(name, "%s Playback Volume", chname[i]);
2963 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002964 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002965 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002966 if (err < 0)
2967 return err;
2968 sprintf(name, "%s Playback Switch", chname[i]);
2969 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002970 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002971 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002972 if (err < 0)
2973 return err;
2974 }
2975 }
2976
2977 return 0;
2978}
2979
2980static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2981{
2982 int err;
2983
2984 if (!pin)
2985 return 0;
2986
2987 if (spec->multiout.num_dacs == 5) /* 10 channels */
2988 spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2989 else if (spec->multiout.num_dacs == 3) /* 6 channels */
2990 spec->multiout.hp_nid = 0;
Lydia Wangcdc17842009-10-10 19:07:47 +08002991 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002992
2993 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2994 "Headphone Playback Volume",
2995 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2996 if (err < 0)
2997 return err;
2998 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2999 "Headphone Playback Switch",
3000 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3001 if (err < 0)
3002 return err;
3003
3004 return 0;
3005}
3006
3007/* create playback/capture controls for input pins */
3008static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
3009 const struct auto_pin_cfg *cfg)
3010{
3011 static char *labels[] = {
3012 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3013 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003014 struct hda_input_mux *imux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01003015 int i, err, idx = 0;
3016
3017 /* for internal loopback recording select */
3018 imux->items[imux->num_items].label = "Stereo Mixer";
3019 imux->items[imux->num_items].index = idx;
3020 imux->num_items++;
3021
3022 for (i = 0; i < AUTO_PIN_LAST; i++) {
3023 if (!cfg->input_pins[i])
3024 continue;
3025
3026 switch (cfg->input_pins[i]) {
3027 case 0x1d: /* Mic */
3028 idx = 2;
3029 break;
Lydia Wang377ff312009-10-10 19:08:55 +08003030
Joseph Chanc577b8a2006-11-29 15:29:40 +01003031 case 0x1e: /* Line In */
3032 idx = 3;
3033 break;
3034
3035 case 0x21: /* Front Mic */
3036 idx = 4;
3037 break;
3038
3039 case 0x23: /* CD */
3040 idx = 1;
3041 break;
3042 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003043 err = via_new_analog_input(spec, labels[i], idx, 0x18);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003044 if (err < 0)
3045 return err;
3046 imux->items[imux->num_items].label = labels[i];
3047 imux->items[imux->num_items].index = idx;
3048 imux->num_items++;
3049 }
3050 return 0;
3051}
3052
3053static int vt1709_parse_auto_config(struct hda_codec *codec)
3054{
3055 struct via_spec *spec = codec->spec;
3056 int err;
3057
3058 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3059 if (err < 0)
3060 return err;
3061 err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
3062 if (err < 0)
3063 return err;
3064 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3065 return 0; /* can't find valid BIOS pin config */
3066
3067 err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
3068 if (err < 0)
3069 return err;
3070 err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3071 if (err < 0)
3072 return err;
3073 err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
3074 if (err < 0)
3075 return err;
3076
3077 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3078
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003079 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01003080 spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02003081 spec->dig_in_pin = VT1709_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01003082 if (spec->autocfg.dig_in_pin)
3083 spec->dig_in_nid = VT1709_DIGIN_NID;
3084
Takashi Iwai603c4012008-07-30 15:01:44 +02003085 if (spec->kctls.list)
3086 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01003087
Harald Welte0aa62ae2008-09-09 15:58:27 +08003088 spec->input_mux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01003089
Harald Weltef8fdd492008-09-15 22:41:31 +08003090 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003091 via_hp_build(spec);
Harald Weltef8fdd492008-09-15 22:41:31 +08003092
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003093 via_smart51_build(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003094 return 1;
3095}
3096
Takashi Iwaicb53c622007-08-10 17:21:45 +02003097#ifdef CONFIG_SND_HDA_POWER_SAVE
3098static struct hda_amp_list vt1709_loopbacks[] = {
3099 { 0x18, HDA_INPUT, 1 },
3100 { 0x18, HDA_INPUT, 2 },
3101 { 0x18, HDA_INPUT, 3 },
3102 { 0x18, HDA_INPUT, 4 },
3103 { } /* end */
3104};
3105#endif
3106
Joseph Chanc577b8a2006-11-29 15:29:40 +01003107static int patch_vt1709_10ch(struct hda_codec *codec)
3108{
3109 struct via_spec *spec;
3110 int err;
3111
3112 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003113 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003114 if (spec == NULL)
3115 return -ENOMEM;
3116
Joseph Chanc577b8a2006-11-29 15:29:40 +01003117 err = vt1709_parse_auto_config(codec);
3118 if (err < 0) {
3119 via_free(codec);
3120 return err;
3121 } else if (!err) {
3122 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
3123 "Using genenic mode...\n");
3124 }
3125
Harald Welte69e52a82008-09-09 15:57:32 +08003126 spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
3127 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01003128
3129 spec->stream_name_analog = "VT1709 Analog";
3130 spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
3131 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3132
3133 spec->stream_name_digital = "VT1709 Digital";
3134 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3135 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3136
Lydia Wang377ff312009-10-10 19:08:55 +08003137
Joseph Chanc577b8a2006-11-29 15:29:40 +01003138 if (!spec->adc_nids && spec->input_mux) {
3139 spec->adc_nids = vt1709_adc_nids;
3140 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003141 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003142 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3143 spec->num_mixers++;
3144 }
3145
3146 codec->patch_ops = via_patch_ops;
3147
3148 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003149 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003150#ifdef CONFIG_SND_HDA_POWER_SAVE
3151 spec->loopback.amplist = vt1709_loopbacks;
3152#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01003153
3154 return 0;
3155}
3156/*
3157 * generic initialization of ADC, input mixers and output mixers
3158 */
3159static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
3160 /*
3161 * Unmute ADC0-2 and set the default input to mic-in
3162 */
3163 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3164 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3165 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3166
3167
3168 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3169 * mixer widget
3170 */
3171 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3172 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3173 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3174 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3175 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3176 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3177
3178 /*
3179 * Set up output selector (0x1a, 0x1b, 0x29)
3180 */
3181 /* set vol=0 to output mixers */
3182 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3183 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3184 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3185
3186 /*
3187 * Unmute PW3 and PW4
3188 */
3189 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3190 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3191
3192 /* Set input of PW4 as MW0 */
3193 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003194 /* PW9 Output enable */
3195 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3196 { }
3197};
3198
3199static int patch_vt1709_6ch(struct hda_codec *codec)
3200{
3201 struct via_spec *spec;
3202 int err;
3203
3204 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003205 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003206 if (spec == NULL)
3207 return -ENOMEM;
3208
Joseph Chanc577b8a2006-11-29 15:29:40 +01003209 err = vt1709_parse_auto_config(codec);
3210 if (err < 0) {
3211 via_free(codec);
3212 return err;
3213 } else if (!err) {
3214 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
3215 "Using genenic mode...\n");
3216 }
3217
Harald Welte69e52a82008-09-09 15:57:32 +08003218 spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
3219 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01003220
3221 spec->stream_name_analog = "VT1709 Analog";
3222 spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
3223 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3224
3225 spec->stream_name_digital = "VT1709 Digital";
3226 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3227 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3228
Lydia Wang377ff312009-10-10 19:08:55 +08003229
Joseph Chanc577b8a2006-11-29 15:29:40 +01003230 if (!spec->adc_nids && spec->input_mux) {
3231 spec->adc_nids = vt1709_adc_nids;
3232 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003233 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01003234 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3235 spec->num_mixers++;
3236 }
3237
3238 codec->patch_ops = via_patch_ops;
3239
3240 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003241 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02003242#ifdef CONFIG_SND_HDA_POWER_SAVE
3243 spec->loopback.amplist = vt1709_loopbacks;
3244#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01003245 return 0;
3246}
3247
3248/* capture mixer elements */
3249static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
3250 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3251 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3252 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3253 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3254 {
3255 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3256 /* The multiple "Capture Source" controls confuse alsamixer
3257 * So call somewhat different..
Josepch Chanf7278fd2007-12-13 16:40:40 +01003258 */
3259 /* .name = "Capture Source", */
3260 .name = "Input Source",
3261 .count = 1,
3262 .info = via_mux_enum_info,
3263 .get = via_mux_enum_get,
3264 .put = via_mux_enum_put,
3265 },
3266 { } /* end */
3267};
3268/*
3269 * generic initialization of ADC, input mixers and output mixers
3270 */
3271static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
3272 /*
3273 * Unmute ADC0-1 and set the default input to mic-in
3274 */
3275 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3276 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3277
3278
3279 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3280 * mixer widget
3281 */
3282 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3283 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3284 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3285 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3286 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3287 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3288
3289 /*
3290 * Set up output mixers
3291 */
3292 /* set vol=0 to output mixers */
3293 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3294 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3295 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3296
3297 /* Setup default input to PW4 */
Lydia Wangbfdc6752009-10-10 19:08:50 +08003298 {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
Josepch Chanf7278fd2007-12-13 16:40:40 +01003299 /* PW9 Output enable */
3300 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3301 /* PW10 Input enable */
3302 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3303 { }
3304};
3305
3306static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
3307 /*
3308 * Unmute ADC0-1 and set the default input to mic-in
3309 */
3310 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3311 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3312
3313
3314 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3315 * mixer widget
3316 */
3317 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3318 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3319 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3320 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3321 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3322 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3323
3324 /*
3325 * Set up output mixers
3326 */
3327 /* set vol=0 to output mixers */
3328 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3329 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3330 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3331
3332 /* Setup default input of PW4 to MW0 */
3333 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3334 /* PW9 Output enable */
3335 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3336 /* PW10 Input enable */
3337 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3338 { }
3339};
3340
Harald Welte69e52a82008-09-09 15:57:32 +08003341static struct hda_verb vt1708B_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003342 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3343 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3344 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3345 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3346 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3347 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3348 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3349 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3350 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003351 { }
3352};
3353
Lydia Wang17314372009-10-10 19:07:37 +08003354static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
3355 struct hda_codec *codec,
3356 struct snd_pcm_substream *substream)
3357{
3358 int idle = substream->pstr->substream_opened == 1
3359 && substream->ref_count == 0;
3360
3361 analog_low_current_mode(codec, idle);
3362 return 0;
3363}
3364
Josepch Chanf7278fd2007-12-13 16:40:40 +01003365static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003366 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01003367 .channels_min = 2,
3368 .channels_max = 8,
3369 .nid = 0x10, /* NID to query formats and rates */
3370 .ops = {
3371 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003372 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003373 .cleanup = via_playback_multi_pcm_cleanup,
3374 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01003375 },
3376};
3377
3378static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003379 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01003380 .channels_min = 2,
3381 .channels_max = 4,
3382 .nid = 0x10, /* NID to query formats and rates */
3383 .ops = {
3384 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003385 .prepare = via_playback_multi_pcm_prepare,
3386 .cleanup = via_playback_multi_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01003387 },
3388};
3389
3390static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3391 .substreams = 2,
3392 .channels_min = 2,
3393 .channels_max = 2,
3394 .nid = 0x13, /* NID to query formats and rates */
3395 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003396 .open = via_pcm_open_close,
Josepch Chanf7278fd2007-12-13 16:40:40 +01003397 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003398 .cleanup = via_capture_pcm_cleanup,
3399 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01003400 },
3401};
3402
3403static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3404 .substreams = 1,
3405 .channels_min = 2,
3406 .channels_max = 2,
3407 /* NID is set in via_build_pcms */
3408 .ops = {
3409 .open = via_dig_playback_pcm_open,
3410 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003411 .prepare = via_dig_playback_pcm_prepare,
3412 .cleanup = via_dig_playback_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01003413 },
3414};
3415
3416static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3417 .substreams = 1,
3418 .channels_min = 2,
3419 .channels_max = 2,
3420};
3421
3422/* fill in the dac_nids table from the parsed pin configuration */
3423static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3424 const struct auto_pin_cfg *cfg)
3425{
3426 int i;
3427 hda_nid_t nid;
3428
3429 spec->multiout.num_dacs = cfg->line_outs;
3430
3431 spec->multiout.dac_nids = spec->private_dac_nids;
3432
3433 for (i = 0; i < 4; i++) {
3434 nid = cfg->line_out_pins[i];
3435 if (nid) {
3436 /* config dac list */
3437 switch (i) {
3438 case AUTO_SEQ_FRONT:
3439 spec->multiout.dac_nids[i] = 0x10;
3440 break;
3441 case AUTO_SEQ_CENLFE:
3442 spec->multiout.dac_nids[i] = 0x24;
3443 break;
3444 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08003445 spec->multiout.dac_nids[i] = 0x11;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003446 break;
3447 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08003448 spec->multiout.dac_nids[i] = 0x25;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003449 break;
3450 }
3451 }
3452 }
3453
3454 return 0;
3455}
3456
3457/* add playback controls from the parsed DAC table */
3458static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3459 const struct auto_pin_cfg *cfg)
3460{
3461 char name[32];
3462 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
Harald Weltefb4cb772008-09-09 15:53:36 +08003463 hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
Josepch Chanf7278fd2007-12-13 16:40:40 +01003464 hda_nid_t nid, nid_vol = 0;
3465 int i, err;
3466
3467 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3468 nid = cfg->line_out_pins[i];
3469
3470 if (!nid)
3471 continue;
3472
3473 nid_vol = nid_vols[i];
3474
3475 if (i == AUTO_SEQ_CENLFE) {
3476 /* Center/LFE */
3477 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3478 "Center Playback Volume",
3479 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3480 HDA_OUTPUT));
3481 if (err < 0)
3482 return err;
3483 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3484 "LFE Playback Volume",
3485 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3486 HDA_OUTPUT));
3487 if (err < 0)
3488 return err;
3489 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3490 "Center Playback Switch",
3491 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3492 HDA_OUTPUT));
3493 if (err < 0)
3494 return err;
3495 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3496 "LFE Playback Switch",
3497 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3498 HDA_OUTPUT));
3499 if (err < 0)
3500 return err;
3501 } else if (i == AUTO_SEQ_FRONT) {
3502 /* add control to mixer index 0 */
3503 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3504 "Master Front Playback Volume",
3505 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3506 HDA_INPUT));
3507 if (err < 0)
3508 return err;
3509 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3510 "Master Front Playback Switch",
3511 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3512 HDA_INPUT));
3513 if (err < 0)
3514 return err;
3515
3516 /* add control to PW3 */
3517 sprintf(name, "%s Playback Volume", chname[i]);
3518 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3519 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3520 HDA_OUTPUT));
3521 if (err < 0)
3522 return err;
3523 sprintf(name, "%s Playback Switch", chname[i]);
3524 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3525 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3526 HDA_OUTPUT));
3527 if (err < 0)
3528 return err;
3529 } else {
3530 sprintf(name, "%s Playback Volume", chname[i]);
3531 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3532 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3533 HDA_OUTPUT));
3534 if (err < 0)
3535 return err;
3536 sprintf(name, "%s Playback Switch", chname[i]);
3537 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3538 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3539 HDA_OUTPUT));
3540 if (err < 0)
3541 return err;
3542 }
3543 }
3544
3545 return 0;
3546}
3547
3548static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3549{
3550 int err;
3551
3552 if (!pin)
3553 return 0;
3554
3555 spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08003556 spec->hp_independent_mode_index = 1;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003557
3558 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3559 "Headphone Playback Volume",
3560 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3561 if (err < 0)
3562 return err;
3563 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3564 "Headphone Playback Switch",
3565 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3566 if (err < 0)
3567 return err;
3568
Harald Welte0aa62ae2008-09-09 15:58:27 +08003569 create_hp_imux(spec);
3570
Josepch Chanf7278fd2007-12-13 16:40:40 +01003571 return 0;
3572}
3573
3574/* create playback/capture controls for input pins */
3575static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
3576 const struct auto_pin_cfg *cfg)
3577{
3578 static char *labels[] = {
3579 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3580 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08003581 struct hda_input_mux *imux = &spec->private_imux[0];
Josepch Chanf7278fd2007-12-13 16:40:40 +01003582 int i, err, idx = 0;
3583
3584 /* for internal loopback recording select */
3585 imux->items[imux->num_items].label = "Stereo Mixer";
3586 imux->items[imux->num_items].index = idx;
3587 imux->num_items++;
3588
3589 for (i = 0; i < AUTO_PIN_LAST; i++) {
3590 if (!cfg->input_pins[i])
3591 continue;
3592
3593 switch (cfg->input_pins[i]) {
3594 case 0x1a: /* Mic */
3595 idx = 2;
3596 break;
3597
3598 case 0x1b: /* Line In */
3599 idx = 3;
3600 break;
3601
3602 case 0x1e: /* Front Mic */
3603 idx = 4;
3604 break;
3605
3606 case 0x1f: /* CD */
3607 idx = 1;
3608 break;
3609 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08003610 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003611 if (err < 0)
3612 return err;
3613 imux->items[imux->num_items].label = labels[i];
3614 imux->items[imux->num_items].index = idx;
3615 imux->num_items++;
3616 }
3617 return 0;
3618}
3619
3620static int vt1708B_parse_auto_config(struct hda_codec *codec)
3621{
3622 struct via_spec *spec = codec->spec;
3623 int err;
3624
3625 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3626 if (err < 0)
3627 return err;
3628 err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3629 if (err < 0)
3630 return err;
3631 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3632 return 0; /* can't find valid BIOS pin config */
3633
3634 err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3635 if (err < 0)
3636 return err;
3637 err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3638 if (err < 0)
3639 return err;
3640 err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
3641 if (err < 0)
3642 return err;
3643
3644 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3645
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003646 if (spec->autocfg.dig_outs)
Josepch Chanf7278fd2007-12-13 16:40:40 +01003647 spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02003648 spec->dig_in_pin = VT1708B_DIGIN_PIN;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003649 if (spec->autocfg.dig_in_pin)
3650 spec->dig_in_nid = VT1708B_DIGIN_NID;
3651
Takashi Iwai603c4012008-07-30 15:01:44 +02003652 if (spec->kctls.list)
3653 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003654
Harald Welte0aa62ae2008-09-09 15:58:27 +08003655 spec->input_mux = &spec->private_imux[0];
3656
Harald Weltef8fdd492008-09-15 22:41:31 +08003657 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003658 via_hp_build(spec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003659
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003660 via_smart51_build(spec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003661 return 1;
3662}
3663
3664#ifdef CONFIG_SND_HDA_POWER_SAVE
3665static struct hda_amp_list vt1708B_loopbacks[] = {
3666 { 0x16, HDA_INPUT, 1 },
3667 { 0x16, HDA_INPUT, 2 },
3668 { 0x16, HDA_INPUT, 3 },
3669 { 0x16, HDA_INPUT, 4 },
3670 { } /* end */
3671};
3672#endif
Lydia Wang518bf3b2009-10-10 19:07:29 +08003673static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003674static int patch_vt1708B_8ch(struct hda_codec *codec)
3675{
3676 struct via_spec *spec;
3677 int err;
3678
Lydia Wang518bf3b2009-10-10 19:07:29 +08003679 if (get_codec_type(codec) == VT1708BCE)
3680 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003681 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003682 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003683 if (spec == NULL)
3684 return -ENOMEM;
3685
Josepch Chanf7278fd2007-12-13 16:40:40 +01003686 /* automatic parse from the BIOS config */
3687 err = vt1708B_parse_auto_config(codec);
3688 if (err < 0) {
3689 via_free(codec);
3690 return err;
3691 } else if (!err) {
3692 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3693 "from BIOS. Using genenic mode...\n");
3694 }
3695
Harald Welte69e52a82008-09-09 15:57:32 +08003696 spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
3697 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003698
3699 spec->stream_name_analog = "VT1708B Analog";
3700 spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3701 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3702
3703 spec->stream_name_digital = "VT1708B Digital";
3704 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3705 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3706
3707 if (!spec->adc_nids && spec->input_mux) {
3708 spec->adc_nids = vt1708B_adc_nids;
3709 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003710 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003711 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3712 spec->num_mixers++;
3713 }
3714
3715 codec->patch_ops = via_patch_ops;
3716
3717 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003718 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003719#ifdef CONFIG_SND_HDA_POWER_SAVE
3720 spec->loopback.amplist = vt1708B_loopbacks;
3721#endif
3722
3723 return 0;
3724}
3725
3726static int patch_vt1708B_4ch(struct hda_codec *codec)
3727{
3728 struct via_spec *spec;
3729 int err;
3730
3731 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003732 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003733 if (spec == NULL)
3734 return -ENOMEM;
3735
Josepch Chanf7278fd2007-12-13 16:40:40 +01003736 /* automatic parse from the BIOS config */
3737 err = vt1708B_parse_auto_config(codec);
3738 if (err < 0) {
3739 via_free(codec);
3740 return err;
3741 } else if (!err) {
3742 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3743 "from BIOS. Using genenic mode...\n");
3744 }
3745
Harald Welte69e52a82008-09-09 15:57:32 +08003746 spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
3747 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003748
3749 spec->stream_name_analog = "VT1708B Analog";
3750 spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3751 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3752
3753 spec->stream_name_digital = "VT1708B Digital";
3754 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3755 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3756
3757 if (!spec->adc_nids && spec->input_mux) {
3758 spec->adc_nids = vt1708B_adc_nids;
3759 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003760 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003761 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3762 spec->num_mixers++;
3763 }
3764
3765 codec->patch_ops = via_patch_ops;
3766
3767 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003768 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003769#ifdef CONFIG_SND_HDA_POWER_SAVE
3770 spec->loopback.amplist = vt1708B_loopbacks;
3771#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01003772
3773 return 0;
3774}
3775
Harald Welted949cac2008-09-09 15:56:01 +08003776/* Patch for VT1708S */
3777
3778/* capture mixer elements */
3779static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3780 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3781 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3782 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3783 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
Lydia Wang6369bcf2009-10-10 19:08:31 +08003784 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3785 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3786 HDA_INPUT),
Harald Welted949cac2008-09-09 15:56:01 +08003787 {
3788 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3789 /* The multiple "Capture Source" controls confuse alsamixer
3790 * So call somewhat different..
3791 */
3792 /* .name = "Capture Source", */
3793 .name = "Input Source",
3794 .count = 1,
3795 .info = via_mux_enum_info,
3796 .get = via_mux_enum_get,
3797 .put = via_mux_enum_put,
3798 },
3799 { } /* end */
3800};
3801
3802static struct hda_verb vt1708S_volume_init_verbs[] = {
3803 /* Unmute ADC0-1 and set the default input to mic-in */
3804 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3805 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3806
3807 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3808 * analog-loopback mixer widget */
3809 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3810 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3811 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3812 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3813 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3814 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3815
3816 /* Setup default input of PW4 to MW0 */
3817 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
Harald Welte5691ec72008-09-15 22:42:26 +08003818 /* PW9, PW10 Output enable */
Harald Welted949cac2008-09-09 15:56:01 +08003819 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welte5691ec72008-09-15 22:42:26 +08003820 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welted7426322008-09-15 22:43:23 +08003821 /* Enable Mic Boost Volume backdoor */
3822 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003823 /* don't bybass mixer */
3824 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003825 { }
3826};
3827
Harald Welte69e52a82008-09-09 15:57:32 +08003828static struct hda_verb vt1708S_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003829 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3830 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3831 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3832 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3833 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3834 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3835 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3836 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3837 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003838 { }
3839};
3840
Harald Welted949cac2008-09-09 15:56:01 +08003841static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3842 .substreams = 2,
3843 .channels_min = 2,
3844 .channels_max = 8,
3845 .nid = 0x10, /* NID to query formats and rates */
3846 .ops = {
3847 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08003848 .prepare = via_playback_multi_pcm_prepare,
3849 .cleanup = via_playback_multi_pcm_cleanup,
Lydia Wang17314372009-10-10 19:07:37 +08003850 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003851 },
3852};
3853
3854static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3855 .substreams = 2,
3856 .channels_min = 2,
3857 .channels_max = 2,
3858 .nid = 0x13, /* NID to query formats and rates */
3859 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003860 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003861 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003862 .cleanup = via_capture_pcm_cleanup,
3863 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003864 },
3865};
3866
3867static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
Takashi Iwai9da29272009-05-07 16:31:14 +02003868 .substreams = 1,
Harald Welted949cac2008-09-09 15:56:01 +08003869 .channels_min = 2,
3870 .channels_max = 2,
3871 /* NID is set in via_build_pcms */
3872 .ops = {
3873 .open = via_dig_playback_pcm_open,
3874 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003875 .prepare = via_dig_playback_pcm_prepare,
3876 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003877 },
3878};
3879
3880/* fill in the dac_nids table from the parsed pin configuration */
3881static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3882 const struct auto_pin_cfg *cfg)
3883{
3884 int i;
3885 hda_nid_t nid;
3886
3887 spec->multiout.num_dacs = cfg->line_outs;
3888
3889 spec->multiout.dac_nids = spec->private_dac_nids;
3890
3891 for (i = 0; i < 4; i++) {
3892 nid = cfg->line_out_pins[i];
3893 if (nid) {
3894 /* config dac list */
3895 switch (i) {
3896 case AUTO_SEQ_FRONT:
3897 spec->multiout.dac_nids[i] = 0x10;
3898 break;
3899 case AUTO_SEQ_CENLFE:
3900 spec->multiout.dac_nids[i] = 0x24;
3901 break;
3902 case AUTO_SEQ_SURROUND:
3903 spec->multiout.dac_nids[i] = 0x11;
3904 break;
3905 case AUTO_SEQ_SIDE:
3906 spec->multiout.dac_nids[i] = 0x25;
3907 break;
3908 }
3909 }
3910 }
3911
3912 return 0;
3913}
3914
3915/* add playback controls from the parsed DAC table */
3916static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3917 const struct auto_pin_cfg *cfg)
3918{
3919 char name[32];
3920 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3921 hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3922 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3923 hda_nid_t nid, nid_vol, nid_mute;
3924 int i, err;
3925
3926 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3927 nid = cfg->line_out_pins[i];
3928
3929 if (!nid)
3930 continue;
3931
3932 nid_vol = nid_vols[i];
3933 nid_mute = nid_mutes[i];
3934
3935 if (i == AUTO_SEQ_CENLFE) {
3936 /* Center/LFE */
3937 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3938 "Center Playback Volume",
3939 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3940 HDA_OUTPUT));
3941 if (err < 0)
3942 return err;
3943 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3944 "LFE Playback Volume",
3945 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3946 HDA_OUTPUT));
3947 if (err < 0)
3948 return err;
3949 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3950 "Center Playback Switch",
3951 HDA_COMPOSE_AMP_VAL(nid_mute,
3952 1, 0,
3953 HDA_OUTPUT));
3954 if (err < 0)
3955 return err;
3956 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3957 "LFE Playback Switch",
3958 HDA_COMPOSE_AMP_VAL(nid_mute,
3959 2, 0,
3960 HDA_OUTPUT));
3961 if (err < 0)
3962 return err;
3963 } else if (i == AUTO_SEQ_FRONT) {
3964 /* add control to mixer index 0 */
3965 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3966 "Master Front Playback Volume",
3967 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3968 HDA_INPUT));
3969 if (err < 0)
3970 return err;
3971 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3972 "Master Front Playback Switch",
3973 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3974 HDA_INPUT));
3975 if (err < 0)
3976 return err;
3977
3978 /* Front */
3979 sprintf(name, "%s Playback Volume", chname[i]);
3980 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3981 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3982 HDA_OUTPUT));
3983 if (err < 0)
3984 return err;
3985 sprintf(name, "%s Playback Switch", chname[i]);
3986 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3987 HDA_COMPOSE_AMP_VAL(nid_mute,
3988 3, 0,
3989 HDA_OUTPUT));
3990 if (err < 0)
3991 return err;
3992 } else {
3993 sprintf(name, "%s Playback Volume", chname[i]);
3994 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3995 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3996 HDA_OUTPUT));
3997 if (err < 0)
3998 return err;
3999 sprintf(name, "%s Playback Switch", chname[i]);
4000 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
4001 HDA_COMPOSE_AMP_VAL(nid_mute,
4002 3, 0,
4003 HDA_OUTPUT));
4004 if (err < 0)
4005 return err;
4006 }
4007 }
4008
4009 return 0;
4010}
4011
4012static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4013{
4014 int err;
4015
4016 if (!pin)
4017 return 0;
4018
4019 spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08004020 spec->hp_independent_mode_index = 1;
Harald Welted949cac2008-09-09 15:56:01 +08004021
4022 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4023 "Headphone Playback Volume",
4024 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
4025 if (err < 0)
4026 return err;
4027
4028 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4029 "Headphone Playback Switch",
4030 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4031 if (err < 0)
4032 return err;
4033
Harald Welte0aa62ae2008-09-09 15:58:27 +08004034 create_hp_imux(spec);
4035
Harald Welted949cac2008-09-09 15:56:01 +08004036 return 0;
4037}
4038
4039/* create playback/capture controls for input pins */
4040static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
4041 const struct auto_pin_cfg *cfg)
4042{
4043 static char *labels[] = {
4044 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4045 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08004046 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08004047 int i, err, idx = 0;
4048
4049 /* for internal loopback recording select */
4050 imux->items[imux->num_items].label = "Stereo Mixer";
4051 imux->items[imux->num_items].index = 5;
4052 imux->num_items++;
4053
4054 for (i = 0; i < AUTO_PIN_LAST; i++) {
4055 if (!cfg->input_pins[i])
4056 continue;
4057
4058 switch (cfg->input_pins[i]) {
4059 case 0x1a: /* Mic */
4060 idx = 2;
4061 break;
4062
4063 case 0x1b: /* Line In */
4064 idx = 3;
4065 break;
4066
4067 case 0x1e: /* Front Mic */
4068 idx = 4;
4069 break;
4070
4071 case 0x1f: /* CD */
4072 idx = 1;
4073 break;
4074 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08004075 err = via_new_analog_input(spec, labels[i], idx, 0x16);
Harald Welted949cac2008-09-09 15:56:01 +08004076 if (err < 0)
4077 return err;
4078 imux->items[imux->num_items].label = labels[i];
4079 imux->items[imux->num_items].index = idx-1;
4080 imux->num_items++;
4081 }
4082 return 0;
4083}
4084
Takashi Iwai9da29272009-05-07 16:31:14 +02004085/* fill out digital output widgets; one for master and one for slave outputs */
4086static void fill_dig_outs(struct hda_codec *codec)
4087{
4088 struct via_spec *spec = codec->spec;
4089 int i;
4090
4091 for (i = 0; i < spec->autocfg.dig_outs; i++) {
4092 hda_nid_t nid;
4093 int conn;
4094
4095 nid = spec->autocfg.dig_out_pins[i];
4096 if (!nid)
4097 continue;
4098 conn = snd_hda_get_connections(codec, nid, &nid, 1);
4099 if (conn < 1)
4100 continue;
4101 if (!spec->multiout.dig_out_nid)
4102 spec->multiout.dig_out_nid = nid;
4103 else {
4104 spec->slave_dig_outs[0] = nid;
4105 break; /* at most two dig outs */
4106 }
4107 }
4108}
4109
Harald Welted949cac2008-09-09 15:56:01 +08004110static int vt1708S_parse_auto_config(struct hda_codec *codec)
4111{
4112 struct via_spec *spec = codec->spec;
4113 int err;
Harald Welted949cac2008-09-09 15:56:01 +08004114
Takashi Iwai9da29272009-05-07 16:31:14 +02004115 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08004116 if (err < 0)
4117 return err;
4118 err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
4119 if (err < 0)
4120 return err;
4121 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4122 return 0; /* can't find valid BIOS pin config */
4123
4124 err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4125 if (err < 0)
4126 return err;
4127 err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4128 if (err < 0)
4129 return err;
4130 err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4131 if (err < 0)
4132 return err;
4133
4134 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4135
Takashi Iwai9da29272009-05-07 16:31:14 +02004136 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08004137
Takashi Iwai603c4012008-07-30 15:01:44 +02004138 if (spec->kctls.list)
4139 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08004140
Harald Welte0aa62ae2008-09-09 15:58:27 +08004141 spec->input_mux = &spec->private_imux[0];
4142
Harald Weltef8fdd492008-09-15 22:41:31 +08004143 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004144 via_hp_build(spec);
Harald Welted949cac2008-09-09 15:56:01 +08004145
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004146 via_smart51_build(spec);
Harald Welted949cac2008-09-09 15:56:01 +08004147 return 1;
4148}
4149
4150#ifdef CONFIG_SND_HDA_POWER_SAVE
4151static struct hda_amp_list vt1708S_loopbacks[] = {
4152 { 0x16, HDA_INPUT, 1 },
4153 { 0x16, HDA_INPUT, 2 },
4154 { 0x16, HDA_INPUT, 3 },
4155 { 0x16, HDA_INPUT, 4 },
4156 { } /* end */
4157};
4158#endif
4159
Lydia Wang6369bcf2009-10-10 19:08:31 +08004160static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
4161 int offset, int num_steps, int step_size)
4162{
4163 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
4164 (offset << AC_AMPCAP_OFFSET_SHIFT) |
4165 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
4166 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
4167 (0 << AC_AMPCAP_MUTE_SHIFT));
4168}
4169
Harald Welted949cac2008-09-09 15:56:01 +08004170static int patch_vt1708S(struct hda_codec *codec)
4171{
4172 struct via_spec *spec;
4173 int err;
4174
4175 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004176 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004177 if (spec == NULL)
4178 return -ENOMEM;
4179
Harald Welted949cac2008-09-09 15:56:01 +08004180 /* automatic parse from the BIOS config */
4181 err = vt1708S_parse_auto_config(codec);
4182 if (err < 0) {
4183 via_free(codec);
4184 return err;
4185 } else if (!err) {
4186 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4187 "from BIOS. Using genenic mode...\n");
4188 }
4189
Harald Welte69e52a82008-09-09 15:57:32 +08004190 spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
4191 spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08004192
Lydia Wang36dd5c42009-10-20 13:18:04 +08004193 if (codec->vendor_id == 0x11060440)
4194 spec->stream_name_analog = "VT1818S Analog";
4195 else
4196 spec->stream_name_analog = "VT1708S Analog";
Harald Welted949cac2008-09-09 15:56:01 +08004197 spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
4198 spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
4199
Lydia Wang36dd5c42009-10-20 13:18:04 +08004200 if (codec->vendor_id == 0x11060440)
4201 spec->stream_name_digital = "VT1818S Digital";
4202 else
4203 spec->stream_name_digital = "VT1708S Digital";
Harald Welted949cac2008-09-09 15:56:01 +08004204 spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
4205
4206 if (!spec->adc_nids && spec->input_mux) {
4207 spec->adc_nids = vt1708S_adc_nids;
4208 spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02004209 get_mux_nids(codec);
Lydia Wang6369bcf2009-10-10 19:08:31 +08004210 override_mic_boost(codec, 0x1a, 0, 3, 40);
4211 override_mic_boost(codec, 0x1e, 0, 3, 40);
Harald Welted949cac2008-09-09 15:56:01 +08004212 spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
4213 spec->num_mixers++;
4214 }
4215
4216 codec->patch_ops = via_patch_ops;
4217
4218 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08004219 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08004220#ifdef CONFIG_SND_HDA_POWER_SAVE
4221 spec->loopback.amplist = vt1708S_loopbacks;
4222#endif
4223
Lydia Wang518bf3b2009-10-10 19:07:29 +08004224 /* correct names for VT1708BCE */
4225 if (get_codec_type(codec) == VT1708BCE) {
4226 kfree(codec->chip_name);
4227 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
4228 snprintf(codec->bus->card->mixername,
4229 sizeof(codec->bus->card->mixername),
4230 "%s %s", codec->vendor_name, codec->chip_name);
4231 spec->stream_name_analog = "VT1708BCE Analog";
4232 spec->stream_name_digital = "VT1708BCE Digital";
4233 }
Harald Welted949cac2008-09-09 15:56:01 +08004234 return 0;
4235}
4236
4237/* Patch for VT1702 */
4238
4239/* capture mixer elements */
4240static struct snd_kcontrol_new vt1702_capture_mixer[] = {
4241 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
4242 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
4243 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
4244 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
4245 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
4246 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
4247 HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
4248 HDA_INPUT),
4249 {
4250 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4251 /* The multiple "Capture Source" controls confuse alsamixer
4252 * So call somewhat different..
4253 */
4254 /* .name = "Capture Source", */
4255 .name = "Input Source",
4256 .count = 1,
4257 .info = via_mux_enum_info,
4258 .get = via_mux_enum_get,
4259 .put = via_mux_enum_put,
4260 },
4261 { } /* end */
4262};
4263
4264static struct hda_verb vt1702_volume_init_verbs[] = {
4265 /*
4266 * Unmute ADC0-1 and set the default input to mic-in
4267 */
4268 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4269 {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4270 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4271
4272
4273 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4274 * mixer widget
4275 */
4276 /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
4277 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4278 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4279 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4280 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4281 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4282
4283 /* Setup default input of PW4 to MW0 */
4284 {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
4285 /* PW6 PW7 Output enable */
4286 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4287 {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08004288 /* mixer enable */
4289 {0x1, 0xF88, 0x3},
4290 /* GPIO 0~2 */
4291 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08004292 { }
4293};
4294
Harald Welte69e52a82008-09-09 15:57:32 +08004295static struct hda_verb vt1702_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08004296 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
4297 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4298 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4299 {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4300 {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4301 {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08004302 { }
4303};
4304
Harald Welted949cac2008-09-09 15:56:01 +08004305static struct hda_pcm_stream vt1702_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08004306 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08004307 .channels_min = 2,
4308 .channels_max = 2,
4309 .nid = 0x10, /* NID to query formats and rates */
4310 .ops = {
4311 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08004312 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08004313 .cleanup = via_playback_multi_pcm_cleanup,
4314 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08004315 },
4316};
4317
4318static struct hda_pcm_stream vt1702_pcm_analog_capture = {
4319 .substreams = 3,
4320 .channels_min = 2,
4321 .channels_max = 2,
4322 .nid = 0x12, /* NID to query formats and rates */
4323 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08004324 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08004325 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08004326 .cleanup = via_capture_pcm_cleanup,
4327 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08004328 },
4329};
4330
4331static struct hda_pcm_stream vt1702_pcm_digital_playback = {
Harald Welte5691ec72008-09-15 22:42:26 +08004332 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08004333 .channels_min = 2,
4334 .channels_max = 2,
4335 /* NID is set in via_build_pcms */
4336 .ops = {
4337 .open = via_dig_playback_pcm_open,
4338 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02004339 .prepare = via_dig_playback_pcm_prepare,
4340 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08004341 },
4342};
4343
4344/* fill in the dac_nids table from the parsed pin configuration */
4345static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4346 const struct auto_pin_cfg *cfg)
4347{
4348 spec->multiout.num_dacs = 1;
4349 spec->multiout.dac_nids = spec->private_dac_nids;
4350
4351 if (cfg->line_out_pins[0]) {
4352 /* config dac list */
4353 spec->multiout.dac_nids[0] = 0x10;
4354 }
4355
4356 return 0;
4357}
4358
4359/* add playback controls from the parsed DAC table */
4360static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4361 const struct auto_pin_cfg *cfg)
4362{
4363 int err;
4364
4365 if (!cfg->line_out_pins[0])
4366 return -1;
4367
4368 /* add control to mixer index 0 */
4369 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4370 "Master Front Playback Volume",
4371 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4372 if (err < 0)
4373 return err;
4374 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4375 "Master Front Playback Switch",
4376 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4377 if (err < 0)
4378 return err;
4379
4380 /* Front */
4381 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4382 "Front Playback Volume",
4383 HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4384 if (err < 0)
4385 return err;
4386 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4387 "Front Playback Switch",
4388 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4389 if (err < 0)
4390 return err;
4391
4392 return 0;
4393}
4394
4395static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4396{
Lydia Wang0713efe2009-10-10 19:07:43 +08004397 int err, i;
4398 struct hda_input_mux *imux;
4399 static const char *texts[] = { "ON", "OFF", NULL};
Harald Welted949cac2008-09-09 15:56:01 +08004400 if (!pin)
4401 return 0;
Harald Welted949cac2008-09-09 15:56:01 +08004402 spec->multiout.hp_nid = 0x1D;
Lydia Wangcdc17842009-10-10 19:07:47 +08004403 spec->hp_independent_mode_index = 0;
Harald Welted949cac2008-09-09 15:56:01 +08004404
4405 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4406 "Headphone Playback Volume",
4407 HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4408 if (err < 0)
4409 return err;
4410
4411 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4412 "Headphone Playback Switch",
4413 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4414 if (err < 0)
4415 return err;
4416
Lydia Wang0713efe2009-10-10 19:07:43 +08004417 imux = &spec->private_imux[1];
Harald Welte0aa62ae2008-09-09 15:58:27 +08004418
Lydia Wang0713efe2009-10-10 19:07:43 +08004419 /* for hp mode select */
4420 i = 0;
4421 while (texts[i] != NULL) {
4422 imux->items[imux->num_items].label = texts[i];
4423 imux->items[imux->num_items].index = i;
4424 imux->num_items++;
4425 i++;
4426 }
4427
4428 spec->hp_mux = &spec->private_imux[1];
Harald Welted949cac2008-09-09 15:56:01 +08004429 return 0;
4430}
4431
4432/* create playback/capture controls for input pins */
4433static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
4434 const struct auto_pin_cfg *cfg)
4435{
4436 static char *labels[] = {
4437 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4438 };
Harald Welte0aa62ae2008-09-09 15:58:27 +08004439 struct hda_input_mux *imux = &spec->private_imux[0];
Harald Welted949cac2008-09-09 15:56:01 +08004440 int i, err, idx = 0;
4441
4442 /* for internal loopback recording select */
4443 imux->items[imux->num_items].label = "Stereo Mixer";
4444 imux->items[imux->num_items].index = 3;
4445 imux->num_items++;
4446
4447 for (i = 0; i < AUTO_PIN_LAST; i++) {
4448 if (!cfg->input_pins[i])
4449 continue;
4450
4451 switch (cfg->input_pins[i]) {
4452 case 0x14: /* Mic */
4453 idx = 1;
4454 break;
4455
4456 case 0x15: /* Line In */
4457 idx = 2;
4458 break;
4459
4460 case 0x18: /* Front Mic */
4461 idx = 3;
4462 break;
4463 }
Lydia Wang9510e8d2009-10-10 19:07:39 +08004464 err = via_new_analog_input(spec, labels[i], idx, 0x1A);
Harald Welted949cac2008-09-09 15:56:01 +08004465 if (err < 0)
4466 return err;
4467 imux->items[imux->num_items].label = labels[i];
4468 imux->items[imux->num_items].index = idx-1;
4469 imux->num_items++;
4470 }
4471 return 0;
4472}
4473
4474static int vt1702_parse_auto_config(struct hda_codec *codec)
4475{
4476 struct via_spec *spec = codec->spec;
4477 int err;
Harald Welted949cac2008-09-09 15:56:01 +08004478
Takashi Iwai9da29272009-05-07 16:31:14 +02004479 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08004480 if (err < 0)
4481 return err;
4482 err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4483 if (err < 0)
4484 return err;
4485 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4486 return 0; /* can't find valid BIOS pin config */
4487
4488 err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4489 if (err < 0)
4490 return err;
4491 err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4492 if (err < 0)
4493 return err;
Lydia Wangc2c02ea2009-10-10 19:07:32 +08004494 /* limit AA path volume to 0 dB */
4495 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4496 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4497 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4498 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4499 (1 << AC_AMPCAP_MUTE_SHIFT));
Harald Welted949cac2008-09-09 15:56:01 +08004500 err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
4501 if (err < 0)
4502 return err;
4503
4504 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4505
Takashi Iwai9da29272009-05-07 16:31:14 +02004506 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08004507
Takashi Iwai603c4012008-07-30 15:01:44 +02004508 if (spec->kctls.list)
4509 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08004510
Harald Welte0aa62ae2008-09-09 15:58:27 +08004511 spec->input_mux = &spec->private_imux[0];
4512
Harald Weltef8fdd492008-09-15 22:41:31 +08004513 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004514 via_hp_build(spec);
Harald Welted949cac2008-09-09 15:56:01 +08004515
4516 return 1;
4517}
4518
4519#ifdef CONFIG_SND_HDA_POWER_SAVE
4520static struct hda_amp_list vt1702_loopbacks[] = {
4521 { 0x1A, HDA_INPUT, 1 },
4522 { 0x1A, HDA_INPUT, 2 },
4523 { 0x1A, HDA_INPUT, 3 },
4524 { 0x1A, HDA_INPUT, 4 },
4525 { } /* end */
4526};
4527#endif
4528
4529static int patch_vt1702(struct hda_codec *codec)
4530{
4531 struct via_spec *spec;
4532 int err;
Harald Welted949cac2008-09-09 15:56:01 +08004533
4534 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004535 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004536 if (spec == NULL)
4537 return -ENOMEM;
4538
Harald Welted949cac2008-09-09 15:56:01 +08004539 /* automatic parse from the BIOS config */
4540 err = vt1702_parse_auto_config(codec);
4541 if (err < 0) {
4542 via_free(codec);
4543 return err;
4544 } else if (!err) {
4545 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4546 "from BIOS. Using genenic mode...\n");
4547 }
4548
Harald Welte69e52a82008-09-09 15:57:32 +08004549 spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
4550 spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08004551
4552 spec->stream_name_analog = "VT1702 Analog";
4553 spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4554 spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4555
4556 spec->stream_name_digital = "VT1702 Digital";
4557 spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4558
4559 if (!spec->adc_nids && spec->input_mux) {
4560 spec->adc_nids = vt1702_adc_nids;
4561 spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02004562 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004563 spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4564 spec->num_mixers++;
4565 }
4566
4567 codec->patch_ops = via_patch_ops;
4568
4569 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08004570 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08004571#ifdef CONFIG_SND_HDA_POWER_SAVE
4572 spec->loopback.amplist = vt1702_loopbacks;
4573#endif
4574
Harald Welted949cac2008-09-09 15:56:01 +08004575 return 0;
4576}
4577
Lydia Wangeb7188c2009-10-10 19:08:34 +08004578/* Patch for VT1718S */
4579
4580/* capture mixer elements */
4581static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4582 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4583 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4584 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4585 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4586 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4587 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4588 HDA_INPUT),
4589 {
4590 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4591 /* The multiple "Capture Source" controls confuse alsamixer
4592 * So call somewhat different..
4593 */
4594 .name = "Input Source",
4595 .count = 2,
4596 .info = via_mux_enum_info,
4597 .get = via_mux_enum_get,
4598 .put = via_mux_enum_put,
4599 },
4600 { } /* end */
4601};
4602
4603static struct hda_verb vt1718S_volume_init_verbs[] = {
4604 /*
4605 * Unmute ADC0-1 and set the default input to mic-in
4606 */
4607 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4608 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4609
4610
4611 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4612 * mixer widget
4613 */
4614 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4615 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4616 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4617 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4618 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4619 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4620
4621 /* Setup default input of Front HP to MW9 */
4622 {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4623 /* PW9 PW10 Output enable */
4624 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4625 {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4626 /* PW11 Input enable */
4627 {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4628 /* Enable Boost Volume backdoor */
4629 {0x1, 0xf88, 0x8},
4630 /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4631 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4632 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4633 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4634 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4635 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4636 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4637 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4638 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4639 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4640 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4641 /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4642 {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4643 {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4644 /* Unmute MW4's index 0 */
4645 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4646 { }
4647};
4648
4649
4650static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4651 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4652 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4653 {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4654 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4655 {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4656 {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4657 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4658 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4659 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4660 { }
4661};
4662
4663static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4664 .substreams = 2,
4665 .channels_min = 2,
4666 .channels_max = 10,
4667 .nid = 0x8, /* NID to query formats and rates */
4668 .ops = {
4669 .open = via_playback_pcm_open,
4670 .prepare = via_playback_multi_pcm_prepare,
4671 .cleanup = via_playback_multi_pcm_cleanup,
4672 .close = via_pcm_open_close,
4673 },
4674};
4675
4676static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4677 .substreams = 2,
4678 .channels_min = 2,
4679 .channels_max = 2,
4680 .nid = 0x10, /* NID to query formats and rates */
4681 .ops = {
4682 .open = via_pcm_open_close,
4683 .prepare = via_capture_pcm_prepare,
4684 .cleanup = via_capture_pcm_cleanup,
4685 .close = via_pcm_open_close,
4686 },
4687};
4688
4689static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4690 .substreams = 2,
4691 .channels_min = 2,
4692 .channels_max = 2,
Lydia Wangeb7188c2009-10-10 19:08:34 +08004693 /* NID is set in via_build_pcms */
4694 .ops = {
4695 .open = via_dig_playback_pcm_open,
4696 .close = via_dig_playback_pcm_close,
4697 .prepare = via_dig_playback_pcm_prepare,
4698 .cleanup = via_dig_playback_pcm_cleanup
4699 },
4700};
4701
4702static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4703 .substreams = 1,
4704 .channels_min = 2,
4705 .channels_max = 2,
4706};
4707
4708/* fill in the dac_nids table from the parsed pin configuration */
4709static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4710 const struct auto_pin_cfg *cfg)
4711{
4712 int i;
4713 hda_nid_t nid;
4714
4715 spec->multiout.num_dacs = cfg->line_outs;
4716
4717 spec->multiout.dac_nids = spec->private_dac_nids;
4718
4719 for (i = 0; i < 4; i++) {
4720 nid = cfg->line_out_pins[i];
4721 if (nid) {
4722 /* config dac list */
4723 switch (i) {
4724 case AUTO_SEQ_FRONT:
4725 spec->multiout.dac_nids[i] = 0x8;
4726 break;
4727 case AUTO_SEQ_CENLFE:
4728 spec->multiout.dac_nids[i] = 0xa;
4729 break;
4730 case AUTO_SEQ_SURROUND:
4731 spec->multiout.dac_nids[i] = 0x9;
4732 break;
4733 case AUTO_SEQ_SIDE:
4734 spec->multiout.dac_nids[i] = 0xb;
4735 break;
4736 }
4737 }
4738 }
4739
4740 return 0;
4741}
4742
4743/* add playback controls from the parsed DAC table */
4744static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4745 const struct auto_pin_cfg *cfg)
4746{
4747 char name[32];
4748 static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
4749 hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4750 hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4751 hda_nid_t nid, nid_vol, nid_mute = 0;
4752 int i, err;
4753
4754 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4755 nid = cfg->line_out_pins[i];
4756
4757 if (!nid)
4758 continue;
4759 nid_vol = nid_vols[i];
4760 nid_mute = nid_mutes[i];
4761
4762 if (i == AUTO_SEQ_CENLFE) {
4763 /* Center/LFE */
4764 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4765 "Center Playback Volume",
4766 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4767 HDA_OUTPUT));
4768 if (err < 0)
4769 return err;
4770 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4771 "LFE Playback Volume",
4772 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4773 HDA_OUTPUT));
4774 if (err < 0)
4775 return err;
4776 err = via_add_control(
4777 spec, VIA_CTL_WIDGET_MUTE,
4778 "Center Playback Switch",
4779 HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4780 HDA_OUTPUT));
4781 if (err < 0)
4782 return err;
4783 err = via_add_control(
4784 spec, VIA_CTL_WIDGET_MUTE,
4785 "LFE Playback Switch",
4786 HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4787 HDA_OUTPUT));
4788 if (err < 0)
4789 return err;
4790 } else if (i == AUTO_SEQ_FRONT) {
4791 /* Front */
4792 sprintf(name, "%s Playback Volume", chname[i]);
4793 err = via_add_control(
4794 spec, VIA_CTL_WIDGET_VOL, name,
4795 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4796 if (err < 0)
4797 return err;
4798 sprintf(name, "%s Playback Switch", chname[i]);
4799 err = via_add_control(
4800 spec, VIA_CTL_WIDGET_MUTE, name,
4801 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4802 HDA_OUTPUT));
4803 if (err < 0)
4804 return err;
4805 } else {
4806 sprintf(name, "%s Playback Volume", chname[i]);
4807 err = via_add_control(
4808 spec, VIA_CTL_WIDGET_VOL, name,
4809 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4810 if (err < 0)
4811 return err;
4812 sprintf(name, "%s Playback Switch", chname[i]);
4813 err = via_add_control(
4814 spec, VIA_CTL_WIDGET_MUTE, name,
4815 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4816 HDA_OUTPUT));
4817 if (err < 0)
4818 return err;
4819 }
4820 }
4821 return 0;
4822}
4823
4824static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4825{
4826 int err;
4827
4828 if (!pin)
4829 return 0;
4830
4831 spec->multiout.hp_nid = 0xc; /* AOW4 */
4832 spec->hp_independent_mode_index = 1;
4833
4834 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4835 "Headphone Playback Volume",
4836 HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4837 if (err < 0)
4838 return err;
4839
4840 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4841 "Headphone Playback Switch",
4842 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4843 if (err < 0)
4844 return err;
4845
4846 create_hp_imux(spec);
4847 return 0;
4848}
4849
4850/* create playback/capture controls for input pins */
4851static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
4852 const struct auto_pin_cfg *cfg)
4853{
4854 static char *labels[] = {
4855 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4856 };
4857 struct hda_input_mux *imux = &spec->private_imux[0];
4858 int i, err, idx = 0;
4859
4860 /* for internal loopback recording select */
4861 imux->items[imux->num_items].label = "Stereo Mixer";
4862 imux->items[imux->num_items].index = 5;
4863 imux->num_items++;
4864
4865 for (i = 0; i < AUTO_PIN_LAST; i++) {
4866 if (!cfg->input_pins[i])
4867 continue;
4868
4869 switch (cfg->input_pins[i]) {
4870 case 0x2b: /* Mic */
4871 idx = 1;
4872 break;
4873
4874 case 0x2a: /* Line In */
4875 idx = 2;
4876 break;
4877
4878 case 0x29: /* Front Mic */
4879 idx = 3;
4880 break;
4881
4882 case 0x2c: /* CD */
4883 idx = 0;
4884 break;
4885 }
4886 err = via_new_analog_input(spec, labels[i], idx, 0x21);
4887 if (err < 0)
4888 return err;
4889 imux->items[imux->num_items].label = labels[i];
4890 imux->items[imux->num_items].index = idx;
4891 imux->num_items++;
4892 }
4893 return 0;
4894}
4895
4896static int vt1718S_parse_auto_config(struct hda_codec *codec)
4897{
4898 struct via_spec *spec = codec->spec;
4899 int err;
4900
4901 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4902
4903 if (err < 0)
4904 return err;
4905 err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4906 if (err < 0)
4907 return err;
4908 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4909 return 0; /* can't find valid BIOS pin config */
4910
4911 err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4912 if (err < 0)
4913 return err;
4914 err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4915 if (err < 0)
4916 return err;
4917 err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4918 if (err < 0)
4919 return err;
4920
4921 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4922
4923 fill_dig_outs(codec);
4924
4925 if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4926 spec->dig_in_nid = 0x13;
4927
4928 if (spec->kctls.list)
4929 spec->mixers[spec->num_mixers++] = spec->kctls.list;
4930
4931 spec->input_mux = &spec->private_imux[0];
4932
4933 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004934 via_hp_build(spec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004935
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004936 via_smart51_build(spec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004937
4938 return 1;
4939}
4940
4941#ifdef CONFIG_SND_HDA_POWER_SAVE
4942static struct hda_amp_list vt1718S_loopbacks[] = {
4943 { 0x21, HDA_INPUT, 1 },
4944 { 0x21, HDA_INPUT, 2 },
4945 { 0x21, HDA_INPUT, 3 },
4946 { 0x21, HDA_INPUT, 4 },
4947 { } /* end */
4948};
4949#endif
4950
4951static int patch_vt1718S(struct hda_codec *codec)
4952{
4953 struct via_spec *spec;
4954 int err;
4955
4956 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004957 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004958 if (spec == NULL)
4959 return -ENOMEM;
4960
Lydia Wangeb7188c2009-10-10 19:08:34 +08004961 /* automatic parse from the BIOS config */
4962 err = vt1718S_parse_auto_config(codec);
4963 if (err < 0) {
4964 via_free(codec);
4965 return err;
4966 } else if (!err) {
4967 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4968 "from BIOS. Using genenic mode...\n");
4969 }
4970
4971 spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4972 spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4973
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08004974 if (codec->vendor_id == 0x11060441)
4975 spec->stream_name_analog = "VT2020 Analog";
4976 else if (codec->vendor_id == 0x11064441)
4977 spec->stream_name_analog = "VT1828S Analog";
4978 else
4979 spec->stream_name_analog = "VT1718S Analog";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004980 spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4981 spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4982
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08004983 if (codec->vendor_id == 0x11060441)
4984 spec->stream_name_digital = "VT2020 Digital";
4985 else if (codec->vendor_id == 0x11064441)
4986 spec->stream_name_digital = "VT1828S Digital";
4987 else
4988 spec->stream_name_digital = "VT1718S Digital";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004989 spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08004990 if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
Lydia Wangeb7188c2009-10-10 19:08:34 +08004991 spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4992
4993 if (!spec->adc_nids && spec->input_mux) {
4994 spec->adc_nids = vt1718S_adc_nids;
4995 spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4996 get_mux_nids(codec);
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08004997 override_mic_boost(codec, 0x2b, 0, 3, 40);
4998 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004999 spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
5000 spec->num_mixers++;
5001 }
5002
5003 codec->patch_ops = via_patch_ops;
5004
5005 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11005006 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangeb7188c2009-10-10 19:08:34 +08005007
5008#ifdef CONFIG_SND_HDA_POWER_SAVE
5009 spec->loopback.amplist = vt1718S_loopbacks;
5010#endif
5011
5012 return 0;
5013}
Lydia Wangf3db4232009-10-10 19:08:41 +08005014
5015/* Patch for VT1716S */
5016
5017static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
5018 struct snd_ctl_elem_info *uinfo)
5019{
5020 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
5021 uinfo->count = 1;
5022 uinfo->value.integer.min = 0;
5023 uinfo->value.integer.max = 1;
5024 return 0;
5025}
5026
5027static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
5028 struct snd_ctl_elem_value *ucontrol)
5029{
5030 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5031 int index = 0;
5032
5033 index = snd_hda_codec_read(codec, 0x26, 0,
5034 AC_VERB_GET_CONNECT_SEL, 0);
5035 if (index != -1)
5036 *ucontrol->value.integer.value = index;
5037
5038 return 0;
5039}
5040
5041static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
5042 struct snd_ctl_elem_value *ucontrol)
5043{
5044 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5045 struct via_spec *spec = codec->spec;
5046 int index = *ucontrol->value.integer.value;
5047
5048 snd_hda_codec_write(codec, 0x26, 0,
5049 AC_VERB_SET_CONNECT_SEL, index);
5050 spec->dmic_enabled = index;
5051 set_jack_power_state(codec);
5052
5053 return 1;
5054}
5055
5056/* capture mixer elements */
5057static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
5058 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
5059 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
5060 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
5061 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
5062 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
5063 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
5064 HDA_INPUT),
5065 {
5066 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5067 .name = "Input Source",
5068 .count = 1,
5069 .info = via_mux_enum_info,
5070 .get = via_mux_enum_get,
5071 .put = via_mux_enum_put,
5072 },
5073 { } /* end */
5074};
5075
5076static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
5077 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
5078 {
5079 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5080 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005081 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08005082 .count = 1,
5083 .info = vt1716s_dmic_info,
5084 .get = vt1716s_dmic_get,
5085 .put = vt1716s_dmic_put,
5086 },
5087 {} /* end */
5088};
5089
5090
5091/* mono-out mixer elements */
5092static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
5093 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
5094 { } /* end */
5095};
5096
5097static struct hda_verb vt1716S_volume_init_verbs[] = {
5098 /*
5099 * Unmute ADC0-1 and set the default input to mic-in
5100 */
5101 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5102 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5103
5104
5105 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5106 * mixer widget
5107 */
5108 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5109 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5110 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5111 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5112 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5113 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5114
5115 /* MUX Indices: Stereo Mixer = 5 */
5116 {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
5117
5118 /* Setup default input of PW4 to MW0 */
5119 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
5120
5121 /* Setup default input of SW1 as MW0 */
5122 {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
5123
5124 /* Setup default input of SW4 as AOW0 */
5125 {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
5126
5127 /* PW9 PW10 Output enable */
5128 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5129 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5130
5131 /* Unmute SW1, PW12 */
5132 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5133 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5134 /* PW12 Output enable */
5135 {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5136 /* Enable Boost Volume backdoor */
5137 {0x1, 0xf8a, 0x80},
5138 /* don't bybass mixer */
5139 {0x1, 0xf88, 0xc0},
5140 /* Enable mono output */
5141 {0x1, 0xf90, 0x08},
5142 { }
5143};
5144
5145
5146static struct hda_verb vt1716S_uniwill_init_verbs[] = {
5147 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
5148 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
5149 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5150 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5151 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5152 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
5153 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
5154 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5155 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5156 { }
5157};
5158
5159static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
5160 .substreams = 2,
5161 .channels_min = 2,
5162 .channels_max = 6,
5163 .nid = 0x10, /* NID to query formats and rates */
5164 .ops = {
5165 .open = via_playback_pcm_open,
5166 .prepare = via_playback_multi_pcm_prepare,
5167 .cleanup = via_playback_multi_pcm_cleanup,
5168 .close = via_pcm_open_close,
5169 },
5170};
5171
5172static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
5173 .substreams = 2,
5174 .channels_min = 2,
5175 .channels_max = 2,
5176 .nid = 0x13, /* NID to query formats and rates */
5177 .ops = {
5178 .open = via_pcm_open_close,
5179 .prepare = via_capture_pcm_prepare,
5180 .cleanup = via_capture_pcm_cleanup,
5181 .close = via_pcm_open_close,
5182 },
5183};
5184
5185static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
5186 .substreams = 2,
5187 .channels_min = 2,
5188 .channels_max = 2,
Lydia Wangf3db4232009-10-10 19:08:41 +08005189 /* NID is set in via_build_pcms */
5190 .ops = {
5191 .open = via_dig_playback_pcm_open,
5192 .close = via_dig_playback_pcm_close,
5193 .prepare = via_dig_playback_pcm_prepare,
5194 .cleanup = via_dig_playback_pcm_cleanup
5195 },
5196};
5197
5198/* fill in the dac_nids table from the parsed pin configuration */
5199static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
5200 const struct auto_pin_cfg *cfg)
5201{ int i;
5202 hda_nid_t nid;
5203
5204 spec->multiout.num_dacs = cfg->line_outs;
5205
5206 spec->multiout.dac_nids = spec->private_dac_nids;
5207
5208 for (i = 0; i < 3; i++) {
5209 nid = cfg->line_out_pins[i];
5210 if (nid) {
5211 /* config dac list */
5212 switch (i) {
5213 case AUTO_SEQ_FRONT:
5214 spec->multiout.dac_nids[i] = 0x10;
5215 break;
5216 case AUTO_SEQ_CENLFE:
5217 spec->multiout.dac_nids[i] = 0x25;
5218 break;
5219 case AUTO_SEQ_SURROUND:
5220 spec->multiout.dac_nids[i] = 0x11;
5221 break;
5222 }
5223 }
5224 }
5225
5226 return 0;
5227}
5228
5229/* add playback controls from the parsed DAC table */
5230static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
5231 const struct auto_pin_cfg *cfg)
5232{
5233 char name[32];
5234 static const char *chname[3] = { "Front", "Surround", "C/LFE" };
5235 hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
5236 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
5237 hda_nid_t nid, nid_vol, nid_mute;
5238 int i, err;
5239
5240 for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
5241 nid = cfg->line_out_pins[i];
5242
5243 if (!nid)
5244 continue;
5245
5246 nid_vol = nid_vols[i];
5247 nid_mute = nid_mutes[i];
5248
5249 if (i == AUTO_SEQ_CENLFE) {
5250 err = via_add_control(
5251 spec, VIA_CTL_WIDGET_VOL,
5252 "Center Playback Volume",
5253 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
5254 if (err < 0)
5255 return err;
5256 err = via_add_control(
5257 spec, VIA_CTL_WIDGET_VOL,
5258 "LFE Playback Volume",
5259 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
5260 if (err < 0)
5261 return err;
5262 err = via_add_control(
5263 spec, VIA_CTL_WIDGET_MUTE,
5264 "Center Playback Switch",
5265 HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
5266 HDA_OUTPUT));
5267 if (err < 0)
5268 return err;
5269 err = via_add_control(
5270 spec, VIA_CTL_WIDGET_MUTE,
5271 "LFE Playback Switch",
5272 HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
5273 HDA_OUTPUT));
5274 if (err < 0)
5275 return err;
5276 } else if (i == AUTO_SEQ_FRONT) {
5277
5278 err = via_add_control(
5279 spec, VIA_CTL_WIDGET_VOL,
5280 "Master Front Playback Volume",
5281 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5282 if (err < 0)
5283 return err;
5284 err = via_add_control(
5285 spec, VIA_CTL_WIDGET_MUTE,
5286 "Master Front Playback Switch",
5287 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5288 if (err < 0)
5289 return err;
5290
5291 sprintf(name, "%s Playback Volume", chname[i]);
5292 err = via_add_control(
5293 spec, VIA_CTL_WIDGET_VOL, name,
5294 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5295 if (err < 0)
5296 return err;
5297 sprintf(name, "%s Playback Switch", chname[i]);
5298 err = via_add_control(
5299 spec, VIA_CTL_WIDGET_MUTE, name,
5300 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5301 HDA_OUTPUT));
5302 if (err < 0)
5303 return err;
5304 } else {
5305 sprintf(name, "%s Playback Volume", chname[i]);
5306 err = via_add_control(
5307 spec, VIA_CTL_WIDGET_VOL, name,
5308 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5309 if (err < 0)
5310 return err;
5311 sprintf(name, "%s Playback Switch", chname[i]);
5312 err = via_add_control(
5313 spec, VIA_CTL_WIDGET_MUTE, name,
5314 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5315 HDA_OUTPUT));
5316 if (err < 0)
5317 return err;
5318 }
5319 }
5320 return 0;
5321}
5322
5323static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5324{
5325 int err;
5326
5327 if (!pin)
5328 return 0;
5329
5330 spec->multiout.hp_nid = 0x25; /* AOW3 */
5331 spec->hp_independent_mode_index = 1;
5332
5333 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5334 "Headphone Playback Volume",
5335 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5336 if (err < 0)
5337 return err;
5338
5339 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5340 "Headphone Playback Switch",
5341 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5342 if (err < 0)
5343 return err;
5344
5345 create_hp_imux(spec);
5346 return 0;
5347}
5348
5349/* create playback/capture controls for input pins */
5350static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
5351 const struct auto_pin_cfg *cfg)
5352{
5353 static char *labels[] = {
5354 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5355 };
5356 struct hda_input_mux *imux = &spec->private_imux[0];
5357 int i, err, idx = 0;
5358
5359 /* for internal loopback recording select */
5360 imux->items[imux->num_items].label = "Stereo Mixer";
5361 imux->items[imux->num_items].index = 5;
5362 imux->num_items++;
5363
5364 for (i = 0; i < AUTO_PIN_LAST; i++) {
5365 if (!cfg->input_pins[i])
5366 continue;
5367
5368 switch (cfg->input_pins[i]) {
5369 case 0x1a: /* Mic */
5370 idx = 2;
5371 break;
5372
5373 case 0x1b: /* Line In */
5374 idx = 3;
5375 break;
5376
5377 case 0x1e: /* Front Mic */
5378 idx = 4;
5379 break;
5380
5381 case 0x1f: /* CD */
5382 idx = 1;
5383 break;
5384 }
5385 err = via_new_analog_input(spec, labels[i], idx, 0x16);
5386 if (err < 0)
5387 return err;
5388 imux->items[imux->num_items].label = labels[i];
5389 imux->items[imux->num_items].index = idx-1;
5390 imux->num_items++;
5391 }
5392 return 0;
5393}
5394
5395static int vt1716S_parse_auto_config(struct hda_codec *codec)
5396{
5397 struct via_spec *spec = codec->spec;
5398 int err;
5399
5400 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5401 if (err < 0)
5402 return err;
5403 err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5404 if (err < 0)
5405 return err;
5406 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5407 return 0; /* can't find valid BIOS pin config */
5408
5409 err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5410 if (err < 0)
5411 return err;
5412 err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5413 if (err < 0)
5414 return err;
5415 err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
5416 if (err < 0)
5417 return err;
5418
5419 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5420
5421 fill_dig_outs(codec);
5422
5423 if (spec->kctls.list)
5424 spec->mixers[spec->num_mixers++] = spec->kctls.list;
5425
5426 spec->input_mux = &spec->private_imux[0];
5427
5428 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005429 via_hp_build(spec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005430
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005431 via_smart51_build(spec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005432
5433 return 1;
5434}
5435
5436#ifdef CONFIG_SND_HDA_POWER_SAVE
5437static struct hda_amp_list vt1716S_loopbacks[] = {
5438 { 0x16, HDA_INPUT, 1 },
5439 { 0x16, HDA_INPUT, 2 },
5440 { 0x16, HDA_INPUT, 3 },
5441 { 0x16, HDA_INPUT, 4 },
5442 { } /* end */
5443};
5444#endif
5445
5446static int patch_vt1716S(struct hda_codec *codec)
5447{
5448 struct via_spec *spec;
5449 int err;
5450
5451 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005452 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005453 if (spec == NULL)
5454 return -ENOMEM;
5455
Lydia Wangf3db4232009-10-10 19:08:41 +08005456 /* automatic parse from the BIOS config */
5457 err = vt1716S_parse_auto_config(codec);
5458 if (err < 0) {
5459 via_free(codec);
5460 return err;
5461 } else if (!err) {
5462 printk(KERN_INFO "hda_codec: Cannot set up configuration "
5463 "from BIOS. Using genenic mode...\n");
5464 }
5465
5466 spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
5467 spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5468
5469 spec->stream_name_analog = "VT1716S Analog";
5470 spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5471 spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5472
5473 spec->stream_name_digital = "VT1716S Digital";
5474 spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5475
5476 if (!spec->adc_nids && spec->input_mux) {
5477 spec->adc_nids = vt1716S_adc_nids;
5478 spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5479 get_mux_nids(codec);
5480 override_mic_boost(codec, 0x1a, 0, 3, 40);
5481 override_mic_boost(codec, 0x1e, 0, 3, 40);
5482 spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5483 spec->num_mixers++;
5484 }
5485
5486 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5487 spec->num_mixers++;
5488
5489 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5490
5491 codec->patch_ops = via_patch_ops;
5492
5493 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11005494 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangf3db4232009-10-10 19:08:41 +08005495
5496#ifdef CONFIG_SND_HDA_POWER_SAVE
5497 spec->loopback.amplist = vt1716S_loopbacks;
5498#endif
5499
5500 return 0;
5501}
Lydia Wang25eaba22009-10-10 19:08:43 +08005502
5503/* for vt2002P */
5504
5505/* capture mixer elements */
5506static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
5507 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5508 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5509 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5510 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5511 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5512 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
5513 HDA_INPUT),
5514 {
5515 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5516 /* The multiple "Capture Source" controls confuse alsamixer
5517 * So call somewhat different..
5518 */
5519 /* .name = "Capture Source", */
5520 .name = "Input Source",
5521 .count = 2,
5522 .info = via_mux_enum_info,
5523 .get = via_mux_enum_get,
5524 .put = via_mux_enum_put,
5525 },
5526 { } /* end */
5527};
5528
5529static struct hda_verb vt2002P_volume_init_verbs[] = {
5530 /*
5531 * Unmute ADC0-1 and set the default input to mic-in
5532 */
5533 {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5534 {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5535
5536
5537 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5538 * mixer widget
5539 */
5540 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5541 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5542 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5543 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5544 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5545 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5546
5547 /* MUX Indices: Mic = 0 */
5548 {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5549 {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5550
5551 /* PW9 Output enable */
5552 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5553
5554 /* Enable Boost Volume backdoor */
5555 {0x1, 0xfb9, 0x24},
5556
5557 /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5558 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5559 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5560 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5561 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5562 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5563 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5564 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5565 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5566
5567 /* set MUX0/1/4/8 = 0 (AOW0) */
5568 {0x34, AC_VERB_SET_CONNECT_SEL, 0},
5569 {0x35, AC_VERB_SET_CONNECT_SEL, 0},
5570 {0x37, AC_VERB_SET_CONNECT_SEL, 0},
5571 {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
5572
5573 /* set PW0 index=0 (MW0) */
5574 {0x24, AC_VERB_SET_CONNECT_SEL, 0},
5575
5576 /* Enable AOW0 to MW9 */
5577 {0x1, 0xfb8, 0x88},
5578 { }
5579};
5580
5581
5582static struct hda_verb vt2002P_uniwill_init_verbs[] = {
5583 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
5584 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5585 {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
5586 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5587 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5588 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5589 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5590 { }
5591};
5592
5593static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
5594 .substreams = 2,
5595 .channels_min = 2,
5596 .channels_max = 2,
5597 .nid = 0x8, /* NID to query formats and rates */
5598 .ops = {
5599 .open = via_playback_pcm_open,
5600 .prepare = via_playback_multi_pcm_prepare,
5601 .cleanup = via_playback_multi_pcm_cleanup,
5602 .close = via_pcm_open_close,
5603 },
5604};
5605
5606static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
5607 .substreams = 2,
5608 .channels_min = 2,
5609 .channels_max = 2,
5610 .nid = 0x10, /* NID to query formats and rates */
5611 .ops = {
5612 .open = via_pcm_open_close,
5613 .prepare = via_capture_pcm_prepare,
5614 .cleanup = via_capture_pcm_cleanup,
5615 .close = via_pcm_open_close,
5616 },
5617};
5618
5619static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
5620 .substreams = 1,
5621 .channels_min = 2,
5622 .channels_max = 2,
Lydia Wang25eaba22009-10-10 19:08:43 +08005623 /* NID is set in via_build_pcms */
5624 .ops = {
5625 .open = via_dig_playback_pcm_open,
5626 .close = via_dig_playback_pcm_close,
5627 .prepare = via_dig_playback_pcm_prepare,
5628 .cleanup = via_dig_playback_pcm_cleanup
5629 },
5630};
5631
5632/* fill in the dac_nids table from the parsed pin configuration */
5633static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
5634 const struct auto_pin_cfg *cfg)
5635{
5636 spec->multiout.num_dacs = 1;
5637 spec->multiout.dac_nids = spec->private_dac_nids;
5638 if (cfg->line_out_pins[0])
5639 spec->multiout.dac_nids[0] = 0x8;
5640 return 0;
5641}
5642
5643/* add playback controls from the parsed DAC table */
5644static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
5645 const struct auto_pin_cfg *cfg)
5646{
5647 int err;
5648
5649 if (!cfg->line_out_pins[0])
5650 return -1;
5651
5652
5653 /* Line-Out: PortE */
5654 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5655 "Master Front Playback Volume",
5656 HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5657 if (err < 0)
5658 return err;
5659 err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
5660 "Master Front Playback Switch",
5661 HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
5662 if (err < 0)
5663 return err;
5664
5665 return 0;
5666}
5667
5668static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5669{
5670 int err;
5671
5672 if (!pin)
5673 return 0;
5674
5675 spec->multiout.hp_nid = 0x9;
5676 spec->hp_independent_mode_index = 1;
5677
5678 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5679 "Headphone Playback Volume",
5680 HDA_COMPOSE_AMP_VAL(
5681 spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5682 if (err < 0)
5683 return err;
5684
5685 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5686 "Headphone Playback Switch",
5687 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5688 if (err < 0)
5689 return err;
5690
5691 create_hp_imux(spec);
5692 return 0;
5693}
5694
5695/* create playback/capture controls for input pins */
5696static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
5697 const struct auto_pin_cfg *cfg)
5698{
5699 static char *labels[] = {
5700 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5701 };
5702 struct hda_input_mux *imux = &spec->private_imux[0];
5703 int i, err, idx = 0;
5704
5705 for (i = 0; i < AUTO_PIN_LAST; i++) {
5706 if (!cfg->input_pins[i])
5707 continue;
5708
5709 switch (cfg->input_pins[i]) {
5710 case 0x2b: /* Mic */
5711 idx = 0;
5712 break;
5713
5714 case 0x2a: /* Line In */
5715 idx = 1;
5716 break;
5717
5718 case 0x29: /* Front Mic */
5719 idx = 2;
5720 break;
5721 }
5722 err = via_new_analog_input(spec, labels[i], idx, 0x21);
5723 if (err < 0)
5724 return err;
5725 imux->items[imux->num_items].label = labels[i];
5726 imux->items[imux->num_items].index = idx;
5727 imux->num_items++;
5728 }
5729
5730 /* build volume/mute control of loopback */
5731 err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
5732 if (err < 0)
5733 return err;
5734
5735 /* for internal loopback recording select */
5736 imux->items[imux->num_items].label = "Stereo Mixer";
5737 imux->items[imux->num_items].index = 3;
5738 imux->num_items++;
5739
5740 /* for digital mic select */
5741 imux->items[imux->num_items].label = "Digital Mic";
5742 imux->items[imux->num_items].index = 4;
5743 imux->num_items++;
5744
5745 return 0;
5746}
5747
5748static int vt2002P_parse_auto_config(struct hda_codec *codec)
5749{
5750 struct via_spec *spec = codec->spec;
5751 int err;
5752
5753
5754 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5755 if (err < 0)
5756 return err;
5757
5758 err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
5759 if (err < 0)
5760 return err;
5761
5762 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5763 return 0; /* can't find valid BIOS pin config */
5764
5765 err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
5766 if (err < 0)
5767 return err;
5768 err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5769 if (err < 0)
5770 return err;
5771 err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
5772 if (err < 0)
5773 return err;
5774
5775 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5776
5777 fill_dig_outs(codec);
5778
5779 if (spec->kctls.list)
5780 spec->mixers[spec->num_mixers++] = spec->kctls.list;
5781
5782 spec->input_mux = &spec->private_imux[0];
5783
5784 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005785 via_hp_build(spec);
Lydia Wang25eaba22009-10-10 19:08:43 +08005786
5787 return 1;
5788}
5789
5790#ifdef CONFIG_SND_HDA_POWER_SAVE
5791static struct hda_amp_list vt2002P_loopbacks[] = {
5792 { 0x21, HDA_INPUT, 0 },
5793 { 0x21, HDA_INPUT, 1 },
5794 { 0x21, HDA_INPUT, 2 },
5795 { } /* end */
5796};
5797#endif
5798
5799
5800/* patch for vt2002P */
5801static int patch_vt2002P(struct hda_codec *codec)
5802{
5803 struct via_spec *spec;
5804 int err;
5805
5806 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005807 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08005808 if (spec == NULL)
5809 return -ENOMEM;
5810
Lydia Wang25eaba22009-10-10 19:08:43 +08005811 /* automatic parse from the BIOS config */
5812 err = vt2002P_parse_auto_config(codec);
5813 if (err < 0) {
5814 via_free(codec);
5815 return err;
5816 } else if (!err) {
5817 printk(KERN_INFO "hda_codec: Cannot set up configuration "
5818 "from BIOS. Using genenic mode...\n");
5819 }
5820
5821 spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs;
5822 spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
5823
5824 spec->stream_name_analog = "VT2002P Analog";
5825 spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
5826 spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
5827
5828 spec->stream_name_digital = "VT2002P Digital";
5829 spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
5830
5831 if (!spec->adc_nids && spec->input_mux) {
5832 spec->adc_nids = vt2002P_adc_nids;
5833 spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
5834 get_mux_nids(codec);
5835 override_mic_boost(codec, 0x2b, 0, 3, 40);
5836 override_mic_boost(codec, 0x29, 0, 3, 40);
5837 spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
5838 spec->num_mixers++;
5839 }
5840
5841 codec->patch_ops = via_patch_ops;
5842
5843 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11005844 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wang25eaba22009-10-10 19:08:43 +08005845
5846#ifdef CONFIG_SND_HDA_POWER_SAVE
5847 spec->loopback.amplist = vt2002P_loopbacks;
5848#endif
5849
5850 return 0;
5851}
Lydia Wangab6734e2009-10-10 19:08:46 +08005852
5853/* for vt1812 */
5854
5855/* capture mixer elements */
5856static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5857 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5858 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5859 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5860 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5861 HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5862 HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5863 HDA_INPUT),
5864 {
5865 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5866 /* The multiple "Capture Source" controls confuse alsamixer
5867 * So call somewhat different..
5868 */
5869 .name = "Input Source",
5870 .count = 2,
5871 .info = via_mux_enum_info,
5872 .get = via_mux_enum_get,
5873 .put = via_mux_enum_put,
5874 },
5875 { } /* end */
5876};
5877
5878static struct hda_verb vt1812_volume_init_verbs[] = {
5879 /*
5880 * Unmute ADC0-1 and set the default input to mic-in
5881 */
5882 {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5883 {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5884
5885
5886 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5887 * mixer widget
5888 */
5889 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5890 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5891 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5892 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5893 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5894 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5895
5896 /* MUX Indices: Mic = 0 */
5897 {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5898 {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5899
5900 /* PW9 Output enable */
5901 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5902
5903 /* Enable Boost Volume backdoor */
5904 {0x1, 0xfb9, 0x24},
5905
5906 /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5907 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5908 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5909 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5910 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5911 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5912 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5913 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5914 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5915 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5916 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5917
5918 /* set MUX0/1/4/13/15 = 0 (AOW0) */
5919 {0x34, AC_VERB_SET_CONNECT_SEL, 0},
5920 {0x35, AC_VERB_SET_CONNECT_SEL, 0},
5921 {0x38, AC_VERB_SET_CONNECT_SEL, 0},
5922 {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5923 {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5924
5925 /* Enable AOW0 to MW9 */
5926 {0x1, 0xfb8, 0xa8},
5927 { }
5928};
5929
5930
5931static struct hda_verb vt1812_uniwill_init_verbs[] = {
5932 {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5933 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5934 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5935 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5936 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5937 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5938 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5939 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5940 { }
5941};
5942
5943static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5944 .substreams = 2,
5945 .channels_min = 2,
5946 .channels_max = 2,
5947 .nid = 0x8, /* NID to query formats and rates */
5948 .ops = {
5949 .open = via_playback_pcm_open,
5950 .prepare = via_playback_multi_pcm_prepare,
5951 .cleanup = via_playback_multi_pcm_cleanup,
5952 .close = via_pcm_open_close,
5953 },
5954};
5955
5956static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5957 .substreams = 2,
5958 .channels_min = 2,
5959 .channels_max = 2,
5960 .nid = 0x10, /* NID to query formats and rates */
5961 .ops = {
5962 .open = via_pcm_open_close,
5963 .prepare = via_capture_pcm_prepare,
5964 .cleanup = via_capture_pcm_cleanup,
5965 .close = via_pcm_open_close,
5966 },
5967};
5968
5969static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5970 .substreams = 1,
5971 .channels_min = 2,
5972 .channels_max = 2,
Lydia Wangab6734e2009-10-10 19:08:46 +08005973 /* NID is set in via_build_pcms */
5974 .ops = {
5975 .open = via_dig_playback_pcm_open,
5976 .close = via_dig_playback_pcm_close,
5977 .prepare = via_dig_playback_pcm_prepare,
5978 .cleanup = via_dig_playback_pcm_cleanup
5979 },
5980};
5981/* fill in the dac_nids table from the parsed pin configuration */
5982static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5983 const struct auto_pin_cfg *cfg)
5984{
5985 spec->multiout.num_dacs = 1;
5986 spec->multiout.dac_nids = spec->private_dac_nids;
5987 if (cfg->line_out_pins[0])
5988 spec->multiout.dac_nids[0] = 0x8;
5989 return 0;
5990}
5991
5992
5993/* add playback controls from the parsed DAC table */
5994static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5995 const struct auto_pin_cfg *cfg)
5996{
5997 int err;
5998
5999 if (!cfg->line_out_pins[0])
6000 return -1;
6001
6002 /* Line-Out: PortE */
6003 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
6004 "Master Front Playback Volume",
6005 HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
6006 if (err < 0)
6007 return err;
6008 err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
6009 "Master Front Playback Switch",
6010 HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
6011 if (err < 0)
6012 return err;
6013
6014 return 0;
6015}
6016
6017static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
6018{
6019 int err;
6020
6021 if (!pin)
6022 return 0;
6023
6024 spec->multiout.hp_nid = 0x9;
6025 spec->hp_independent_mode_index = 1;
6026
6027
6028 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
6029 "Headphone Playback Volume",
6030 HDA_COMPOSE_AMP_VAL(
6031 spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
6032 if (err < 0)
6033 return err;
6034
6035 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
6036 "Headphone Playback Switch",
6037 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
6038 if (err < 0)
6039 return err;
6040
6041 create_hp_imux(spec);
6042 return 0;
6043}
6044
6045/* create playback/capture controls for input pins */
6046static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
6047 const struct auto_pin_cfg *cfg)
6048{
6049 static char *labels[] = {
6050 "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
6051 };
6052 struct hda_input_mux *imux = &spec->private_imux[0];
6053 int i, err, idx = 0;
6054
6055 for (i = 0; i < AUTO_PIN_LAST; i++) {
6056 if (!cfg->input_pins[i])
6057 continue;
6058
6059 switch (cfg->input_pins[i]) {
6060 case 0x2b: /* Mic */
6061 idx = 0;
6062 break;
6063
6064 case 0x2a: /* Line In */
6065 idx = 1;
6066 break;
6067
6068 case 0x29: /* Front Mic */
6069 idx = 2;
6070 break;
6071 }
6072 err = via_new_analog_input(spec, labels[i], idx, 0x21);
6073 if (err < 0)
6074 return err;
6075 imux->items[imux->num_items].label = labels[i];
6076 imux->items[imux->num_items].index = idx;
6077 imux->num_items++;
6078 }
6079 /* build volume/mute control of loopback */
6080 err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
6081 if (err < 0)
6082 return err;
6083
6084 /* for internal loopback recording select */
6085 imux->items[imux->num_items].label = "Stereo Mixer";
6086 imux->items[imux->num_items].index = 5;
6087 imux->num_items++;
6088
6089 /* for digital mic select */
6090 imux->items[imux->num_items].label = "Digital Mic";
6091 imux->items[imux->num_items].index = 6;
6092 imux->num_items++;
6093
6094 return 0;
6095}
6096
6097static int vt1812_parse_auto_config(struct hda_codec *codec)
6098{
6099 struct via_spec *spec = codec->spec;
6100 int err;
6101
6102
6103 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
6104 if (err < 0)
6105 return err;
6106 fill_dig_outs(codec);
6107 err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
6108 if (err < 0)
6109 return err;
6110
6111 if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
6112 return 0; /* can't find valid BIOS pin config */
6113
6114 err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
6115 if (err < 0)
6116 return err;
6117 err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
6118 if (err < 0)
6119 return err;
6120 err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
6121 if (err < 0)
6122 return err;
6123
6124 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
6125
6126 fill_dig_outs(codec);
6127
6128 if (spec->kctls.list)
6129 spec->mixers[spec->num_mixers++] = spec->kctls.list;
6130
6131 spec->input_mux = &spec->private_imux[0];
6132
6133 if (spec->hp_mux)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01006134 via_hp_build(spec);
Lydia Wangab6734e2009-10-10 19:08:46 +08006135
6136 return 1;
6137}
6138
6139#ifdef CONFIG_SND_HDA_POWER_SAVE
6140static struct hda_amp_list vt1812_loopbacks[] = {
6141 { 0x21, HDA_INPUT, 0 },
6142 { 0x21, HDA_INPUT, 1 },
6143 { 0x21, HDA_INPUT, 2 },
6144 { } /* end */
6145};
6146#endif
6147
6148
6149/* patch for vt1812 */
6150static int patch_vt1812(struct hda_codec *codec)
6151{
6152 struct via_spec *spec;
6153 int err;
6154
6155 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01006156 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08006157 if (spec == NULL)
6158 return -ENOMEM;
6159
Lydia Wangab6734e2009-10-10 19:08:46 +08006160 /* automatic parse from the BIOS config */
6161 err = vt1812_parse_auto_config(codec);
6162 if (err < 0) {
6163 via_free(codec);
6164 return err;
6165 } else if (!err) {
6166 printk(KERN_INFO "hda_codec: Cannot set up configuration "
6167 "from BIOS. Using genenic mode...\n");
6168 }
6169
6170
6171 spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
6172 spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6173
6174 spec->stream_name_analog = "VT1812 Analog";
6175 spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6176 spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6177
6178 spec->stream_name_digital = "VT1812 Digital";
6179 spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6180
6181
6182 if (!spec->adc_nids && spec->input_mux) {
6183 spec->adc_nids = vt1812_adc_nids;
6184 spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6185 get_mux_nids(codec);
6186 override_mic_boost(codec, 0x2b, 0, 3, 40);
6187 override_mic_boost(codec, 0x29, 0, 3, 40);
6188 spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6189 spec->num_mixers++;
6190 }
6191
6192 codec->patch_ops = via_patch_ops;
6193
6194 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11006195 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangab6734e2009-10-10 19:08:46 +08006196
6197#ifdef CONFIG_SND_HDA_POWER_SAVE
6198 spec->loopback.amplist = vt1812_loopbacks;
6199#endif
6200
6201 return 0;
6202}
6203
Joseph Chanc577b8a2006-11-29 15:29:40 +01006204/*
6205 * patch entries
6206 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01006207static struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01006208 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
6209 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
6210 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
6211 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
6212 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006213 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006214 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006215 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006216 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006217 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006218 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006219 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006220 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006221 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006222 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006223 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006224 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006225 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006226 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006227 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006228 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006229 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006230 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006231 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006232 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006233 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006234 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006235 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006236 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006237 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006238 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006239 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006240 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006241 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006242 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006243 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006244 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006245 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006246 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006247 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006248 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006249 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006250 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006251 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006252 { .id = 0x11064397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006253 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006254 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006255 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006256 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006257 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006258 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006259 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006260 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006261 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006262 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006263 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006264 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006265 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006266 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006267 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006268 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006269 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006270 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006271 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006272 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006273 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006274 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006275 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08006276 { .id = 0x11060428, .name = "VT1718S",
6277 .patch = patch_vt1718S},
6278 { .id = 0x11064428, .name = "VT1718S",
6279 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08006280 { .id = 0x11060441, .name = "VT2020",
6281 .patch = patch_vt1718S},
6282 { .id = 0x11064441, .name = "VT1828S",
6283 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08006284 { .id = 0x11060433, .name = "VT1716S",
6285 .patch = patch_vt1716S},
6286 { .id = 0x1106a721, .name = "VT1716S",
6287 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08006288 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
6289 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08006290 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08006291 { .id = 0x11060440, .name = "VT1818S",
6292 .patch = patch_vt1708S},
Joseph Chanc577b8a2006-11-29 15:29:40 +01006293 {} /* terminator */
6294};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01006295
6296MODULE_ALIAS("snd-hda-codec-id:1106*");
6297
6298static struct hda_codec_preset_list via_list = {
6299 .preset = snd_hda_preset_via,
6300 .owner = THIS_MODULE,
6301};
6302
6303MODULE_LICENSE("GPL");
6304MODULE_DESCRIPTION("VIA HD-audio codec");
6305
6306static int __init patch_via_init(void)
6307{
6308 return snd_hda_add_codec_preset(&via_list);
6309}
6310
6311static void __exit patch_via_exit(void)
6312{
6313 snd_hda_delete_codec_preset(&via_list);
6314}
6315
6316module_init(patch_via_init)
6317module_exit(patch_via_exit)