blob: ae9fdaa955f008e876b093c55059487cc53b60d7 [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,
Lydia Wang118909562011-03-23 17:57:34 +0800101 VT1802,
Harald Welted7426322008-09-15 22:43:23 +0800102 CODEC_TYPES,
103};
104
Lydia Wang118909562011-03-23 17:57:34 +0800105#define VT2002P_COMPATIBLE(spec) \
106 ((spec)->codec_type == VT2002P ||\
107 (spec)->codec_type == VT1812 ||\
108 (spec)->codec_type == VT1802)
109
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800110struct via_spec {
111 /* codec parameterization */
Lydia Wangf3db4232009-10-10 19:08:41 +0800112 struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800113 unsigned int num_mixers;
114
115 struct hda_verb *init_verbs[5];
116 unsigned int num_iverbs;
117
118 char *stream_name_analog;
119 struct hda_pcm_stream *stream_analog_playback;
120 struct hda_pcm_stream *stream_analog_capture;
121
122 char *stream_name_digital;
123 struct hda_pcm_stream *stream_digital_playback;
124 struct hda_pcm_stream *stream_digital_capture;
125
126 /* playback */
127 struct hda_multi_out multiout;
128 hda_nid_t slave_dig_outs[2];
129
130 /* capture */
131 unsigned int num_adc_nids;
132 hda_nid_t *adc_nids;
133 hda_nid_t mux_nids[3];
134 hda_nid_t dig_in_nid;
135 hda_nid_t dig_in_pin;
136
137 /* capture source */
138 const struct hda_input_mux *input_mux;
139 unsigned int cur_mux[3];
140
141 /* PCM information */
142 struct hda_pcm pcm_rec[3];
143
144 /* dynamic controls, init_verbs and input_mux */
145 struct auto_pin_cfg autocfg;
146 struct snd_array kctls;
147 struct hda_input_mux private_imux[2];
148 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
149
150 /* HP mode source */
151 const struct hda_input_mux *hp_mux;
152 unsigned int hp_independent_mode;
153 unsigned int hp_independent_mode_index;
154 unsigned int smart51_enabled;
Lydia Wangf3db4232009-10-10 19:08:41 +0800155 unsigned int dmic_enabled;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800156 enum VIA_HDA_CODEC codec_type;
157
158 /* work to check hp jack state */
159 struct hda_codec *codec;
160 struct delayed_work vt1708_hp_work;
161 int vt1708_jack_detectect;
162 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800163
164 void (*set_widgets_power_state)(struct hda_codec *codec);
165
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166#ifdef CONFIG_SND_HDA_POWER_SAVE
167 struct hda_loopback_check loopback;
168#endif
169};
170
Lydia Wang0341ccd2011-03-22 16:25:03 +0800171static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100172static struct via_spec * via_new_spec(struct hda_codec *codec)
173{
174 struct via_spec *spec;
175
176 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
177 if (spec == NULL)
178 return NULL;
179
180 codec->spec = spec;
181 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800182 spec->codec_type = get_codec_type(codec);
183 /* VT1708BCE & VT1708S are almost same */
184 if (spec->codec_type == VT1708BCE)
185 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100186 return spec;
187}
188
Lydia Wang744ff5f2009-10-10 19:07:26 +0800189static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800190{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800191 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800192 u16 ven_id = vendor_id >> 16;
193 u16 dev_id = vendor_id & 0xffff;
194 enum VIA_HDA_CODEC codec_type;
195
196 /* get codec type */
197 if (ven_id != 0x1106)
198 codec_type = UNKNOWN;
199 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
200 codec_type = VT1708;
201 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
202 codec_type = VT1709_10CH;
203 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
204 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800205 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800206 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800207 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
208 codec_type = VT1708BCE;
209 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800210 codec_type = VT1708B_4CH;
211 else if ((dev_id & 0xfff) == 0x397
212 && (dev_id >> 12) < 8)
213 codec_type = VT1708S;
214 else if ((dev_id & 0xfff) == 0x398
215 && (dev_id >> 12) < 8)
216 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800217 else if ((dev_id & 0xfff) == 0x428
218 && (dev_id >> 12) < 8)
219 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800220 else if (dev_id == 0x0433 || dev_id == 0xa721)
221 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800222 else if (dev_id == 0x0441 || dev_id == 0x4441)
223 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800224 else if (dev_id == 0x0438 || dev_id == 0x4438)
225 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800226 else if (dev_id == 0x0448)
227 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800228 else if (dev_id == 0x0440)
229 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800230 else if ((dev_id & 0xfff) == 0x446)
231 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800232 else
233 codec_type = UNKNOWN;
234 return codec_type;
235};
236
Harald Welte69e52a82008-09-09 15:57:32 +0800237#define VIA_HP_EVENT 0x01
238#define VIA_GPIO_EVENT 0x02
Lydia Wanga34df192009-10-10 19:08:01 +0800239#define VIA_JACK_EVENT 0x04
Lydia Wangf3db4232009-10-10 19:08:41 +0800240#define VIA_MONO_EVENT 0x08
Lydia Wang25eaba22009-10-10 19:08:43 +0800241#define VIA_SPEAKER_EVENT 0x10
242#define VIA_BIND_HP_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800243
Joseph Chanc577b8a2006-11-29 15:29:40 +0100244enum {
245 VIA_CTL_WIDGET_VOL,
246 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800247 VIA_CTL_WIDGET_ANALOG_MUTE,
Lydia Wang25eaba22009-10-10 19:08:43 +0800248 VIA_CTL_WIDGET_BIND_PIN_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100249};
250
251enum {
Harald Welteeb14a462008-09-09 15:40:38 +0800252 AUTO_SEQ_FRONT = 0,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100253 AUTO_SEQ_SURROUND,
254 AUTO_SEQ_CENLFE,
255 AUTO_SEQ_SIDE
256};
257
Lydia Wangf5271102009-10-10 19:07:35 +0800258static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800259static int is_aa_path_mute(struct hda_codec *codec);
260
261static void vt1708_start_hp_work(struct via_spec *spec)
262{
263 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
264 return;
265 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
266 !spec->vt1708_jack_detectect);
267 if (!delayed_work_pending(&spec->vt1708_hp_work))
268 schedule_delayed_work(&spec->vt1708_hp_work,
269 msecs_to_jiffies(100));
270}
271
272static void vt1708_stop_hp_work(struct via_spec *spec)
273{
274 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
275 return;
276 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
277 && !is_aa_path_mute(spec->codec))
278 return;
279 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
280 !spec->vt1708_jack_detectect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100281 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800282}
Lydia Wangf5271102009-10-10 19:07:35 +0800283
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800284static void set_widgets_power_state(struct hda_codec *codec)
285{
286 struct via_spec *spec = codec->spec;
287 if (spec->set_widgets_power_state)
288 spec->set_widgets_power_state(codec);
289}
Lydia Wang25eaba22009-10-10 19:08:43 +0800290
Lydia Wangf5271102009-10-10 19:07:35 +0800291static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
292 struct snd_ctl_elem_value *ucontrol)
293{
294 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
295 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
296
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800297 set_widgets_power_state(codec);
Lydia Wangf5271102009-10-10 19:07:35 +0800298 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800299 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
300 if (is_aa_path_mute(codec))
301 vt1708_start_hp_work(codec->spec);
302 else
303 vt1708_stop_hp_work(codec->spec);
304 }
Lydia Wangf5271102009-10-10 19:07:35 +0800305 return change;
306}
307
308/* modify .put = snd_hda_mixer_amp_switch_put */
309#define ANALOG_INPUT_MUTE \
310 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
311 .name = NULL, \
312 .index = 0, \
313 .info = snd_hda_mixer_amp_switch_info, \
314 .get = snd_hda_mixer_amp_switch_get, \
315 .put = analog_input_switch_put, \
316 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
317
Lydia Wang25eaba22009-10-10 19:08:43 +0800318static void via_hp_bind_automute(struct hda_codec *codec);
319
320static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
321 struct snd_ctl_elem_value *ucontrol)
322{
323 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
324 struct via_spec *spec = codec->spec;
325 int i;
326 int change = 0;
327
328 long *valp = ucontrol->value.integer.value;
329 int lmute, rmute;
330 if (strstr(kcontrol->id.name, "Switch") == NULL) {
331 snd_printd("Invalid control!\n");
332 return change;
333 }
334 change = snd_hda_mixer_amp_switch_put(kcontrol,
335 ucontrol);
336 /* Get mute value */
337 lmute = *valp ? 0 : HDA_AMP_MUTE;
338 valp++;
339 rmute = *valp ? 0 : HDA_AMP_MUTE;
340
341 /* Set hp pins */
342 if (!spec->hp_independent_mode) {
343 for (i = 0; i < spec->autocfg.hp_outs; i++) {
344 snd_hda_codec_amp_update(
345 codec, spec->autocfg.hp_pins[i],
346 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
347 lmute);
348 snd_hda_codec_amp_update(
349 codec, spec->autocfg.hp_pins[i],
350 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
351 rmute);
352 }
353 }
354
355 if (!lmute && !rmute) {
356 /* Line Outs */
357 for (i = 0; i < spec->autocfg.line_outs; i++)
358 snd_hda_codec_amp_stereo(
359 codec, spec->autocfg.line_out_pins[i],
360 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
361 /* Speakers */
362 for (i = 0; i < spec->autocfg.speaker_outs; i++)
363 snd_hda_codec_amp_stereo(
364 codec, spec->autocfg.speaker_pins[i],
365 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
366 /* unmute */
367 via_hp_bind_automute(codec);
368
369 } else {
370 if (lmute) {
371 /* Mute all left channels */
372 for (i = 1; i < spec->autocfg.line_outs; i++)
373 snd_hda_codec_amp_update(
374 codec,
375 spec->autocfg.line_out_pins[i],
376 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
377 lmute);
378 for (i = 0; i < spec->autocfg.speaker_outs; i++)
379 snd_hda_codec_amp_update(
380 codec,
381 spec->autocfg.speaker_pins[i],
382 0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
383 lmute);
384 }
385 if (rmute) {
386 /* mute all right channels */
387 for (i = 1; i < spec->autocfg.line_outs; i++)
388 snd_hda_codec_amp_update(
389 codec,
390 spec->autocfg.line_out_pins[i],
391 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
392 rmute);
393 for (i = 0; i < spec->autocfg.speaker_outs; i++)
394 snd_hda_codec_amp_update(
395 codec,
396 spec->autocfg.speaker_pins[i],
397 1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
398 rmute);
399 }
400 }
401 return change;
402}
403
404#define BIND_PIN_MUTE \
405 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
406 .name = NULL, \
407 .index = 0, \
408 .info = snd_hda_mixer_amp_switch_info, \
409 .get = snd_hda_mixer_amp_switch_get, \
410 .put = bind_pin_switch_put, \
411 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
412
Lydia Wang71eb7dc2009-10-10 19:08:49 +0800413static struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100414 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
415 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800416 ANALOG_INPUT_MUTE,
Lydia Wang25eaba22009-10-10 19:08:43 +0800417 BIND_PIN_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100418};
419
Joseph Chanc577b8a2006-11-29 15:29:40 +0100420static hda_nid_t vt1708_adc_nids[2] = {
421 /* ADC1-2 */
422 0x15, 0x27
423};
424
425static hda_nid_t vt1709_adc_nids[3] = {
426 /* ADC1-2 */
427 0x14, 0x15, 0x16
428};
429
Josepch Chanf7278fd2007-12-13 16:40:40 +0100430static hda_nid_t vt1708B_adc_nids[2] = {
431 /* ADC1-2 */
432 0x13, 0x14
433};
434
Harald Welted949cac2008-09-09 15:56:01 +0800435static hda_nid_t vt1708S_adc_nids[2] = {
436 /* ADC1-2 */
437 0x13, 0x14
438};
439
440static hda_nid_t vt1702_adc_nids[3] = {
441 /* ADC1-2 */
442 0x12, 0x20, 0x1F
443};
444
Lydia Wangeb7188c2009-10-10 19:08:34 +0800445static hda_nid_t vt1718S_adc_nids[2] = {
446 /* ADC1-2 */
447 0x10, 0x11
448};
449
Lydia Wangf3db4232009-10-10 19:08:41 +0800450static hda_nid_t vt1716S_adc_nids[2] = {
451 /* ADC1-2 */
452 0x13, 0x14
453};
454
Lydia Wang25eaba22009-10-10 19:08:43 +0800455static hda_nid_t vt2002P_adc_nids[2] = {
456 /* ADC1-2 */
457 0x10, 0x11
458};
459
Lydia Wangab6734e2009-10-10 19:08:46 +0800460static hda_nid_t vt1812_adc_nids[2] = {
461 /* ADC1-2 */
462 0x10, 0x11
463};
464
465
Joseph Chanc577b8a2006-11-29 15:29:40 +0100466/* add dynamic controls */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200467static int __via_add_control(struct via_spec *spec, int type, const char *name,
468 int idx, unsigned long val)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100469{
470 struct snd_kcontrol_new *knew;
471
Takashi Iwai603c4012008-07-30 15:01:44 +0200472 snd_array_init(&spec->kctls, sizeof(*knew), 32);
473 knew = snd_array_new(&spec->kctls);
474 if (!knew)
475 return -ENOMEM;
Lydia Wang71eb7dc2009-10-10 19:08:49 +0800476 *knew = via_control_templates[type];
Joseph Chanc577b8a2006-11-29 15:29:40 +0100477 knew->name = kstrdup(name, GFP_KERNEL);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100478 if (!knew->name)
479 return -ENOMEM;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100480 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100481 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100482 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100483 return 0;
484}
485
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200486#define via_add_control(spec, type, name, val) \
487 __via_add_control(spec, type, name, 0, val)
488
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100489static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
490 struct snd_kcontrol_new *tmpl)
491{
492 struct snd_kcontrol_new *knew;
493
494 snd_array_init(&spec->kctls, sizeof(*knew), 32);
495 knew = snd_array_new(&spec->kctls);
496 if (!knew)
497 return NULL;
498 *knew = *tmpl;
499 knew->name = kstrdup(tmpl->name, GFP_KERNEL);
500 if (!knew->name)
501 return NULL;
Takashi Iwaib3314392010-04-14 14:33:57 +0200502 return knew;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100503}
504
Takashi Iwai603c4012008-07-30 15:01:44 +0200505static void via_free_kctls(struct hda_codec *codec)
506{
507 struct via_spec *spec = codec->spec;
508
509 if (spec->kctls.list) {
510 struct snd_kcontrol_new *kctl = spec->kctls.list;
511 int i;
512 for (i = 0; i < spec->kctls.used; i++)
513 kfree(kctl[i].name);
514 }
515 snd_array_free(&spec->kctls);
516}
517
Joseph Chanc577b8a2006-11-29 15:29:40 +0100518/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800519static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200520 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100521{
522 char name[32];
523 int err;
524
525 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200526 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100527 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
528 if (err < 0)
529 return err;
530 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200531 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100532 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
533 if (err < 0)
534 return err;
535 return 0;
536}
537
538static void via_auto_set_output_and_unmute(struct hda_codec *codec,
539 hda_nid_t nid, int pin_type,
540 int dac_idx)
541{
542 /* set as output */
543 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
544 pin_type);
545 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
546 AMP_OUT_UNMUTE);
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200547 if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
Lydia Wang377ff312009-10-10 19:08:55 +0800548 snd_hda_codec_write(codec, nid, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200549 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100550}
551
552
553static void via_auto_init_multi_out(struct hda_codec *codec)
554{
555 struct via_spec *spec = codec->spec;
556 int i;
557
558 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
559 hda_nid_t nid = spec->autocfg.line_out_pins[i];
560 if (nid)
561 via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
562 }
563}
564
565static void via_auto_init_hp_out(struct hda_codec *codec)
566{
567 struct via_spec *spec = codec->spec;
568 hda_nid_t pin;
Lydia Wang25eaba22009-10-10 19:08:43 +0800569 int i;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570
Lydia Wang25eaba22009-10-10 19:08:43 +0800571 for (i = 0; i < spec->autocfg.hp_outs; i++) {
572 pin = spec->autocfg.hp_pins[i];
573 if (pin) /* connect to front */
574 via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
575 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100576}
577
Clemens Ladisch32e01912010-07-12 16:28:50 +0200578static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
579
Joseph Chanc577b8a2006-11-29 15:29:40 +0100580static void via_auto_init_analog_input(struct hda_codec *codec)
581{
582 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200583 const struct auto_pin_cfg *cfg = &spec->autocfg;
Clemens Ladisch32e01912010-07-12 16:28:50 +0200584 unsigned int ctl;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585 int i;
586
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200587 for (i = 0; i < cfg->num_inputs; i++) {
588 hda_nid_t nid = cfg->inputs[i].pin;
Clemens Ladisch32e01912010-07-12 16:28:50 +0200589 if (spec->smart51_enabled && is_smart51_pins(spec, nid))
590 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100591 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200592 ctl = PIN_VREF50;
593 else
594 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100595 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200596 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100597 }
598}
Lydia Wangf5271102009-10-10 19:07:35 +0800599
600static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
601 unsigned int *affected_parm)
602{
603 unsigned parm;
604 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
605 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
606 >> AC_DEFCFG_MISC_SHIFT
607 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Takashi Iwaid56757a2009-11-18 08:00:14 +0100608 unsigned present = snd_hda_jack_detect(codec, nid);
Lydia Wang1564b282009-10-10 19:07:52 +0800609 struct via_spec *spec = codec->spec;
610 if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
611 || ((no_presence || present)
612 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800613 *affected_parm = AC_PWRST_D0; /* if it's connected */
614 parm = AC_PWRST_D0;
615 } else
616 parm = AC_PWRST_D3;
617
618 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
619}
620
Joseph Chanc577b8a2006-11-29 15:29:40 +0100621/*
622 * input MUX handling
623 */
624static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
625 struct snd_ctl_elem_info *uinfo)
626{
627 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
628 struct via_spec *spec = codec->spec;
629 return snd_hda_input_mux_info(spec->input_mux, uinfo);
630}
631
632static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
633 struct snd_ctl_elem_value *ucontrol)
634{
635 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
636 struct via_spec *spec = codec->spec;
637 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
638
639 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
640 return 0;
641}
642
643static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
644 struct snd_ctl_elem_value *ucontrol)
645{
646 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
647 struct via_spec *spec = codec->spec;
648 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800649 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100650
Takashi Iwai337b9d02009-07-07 18:18:59 +0200651 if (!spec->mux_nids[adc_idx])
652 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800653 /* switch to D0 beofre change index */
654 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
655 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
656 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
657 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800658
659 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
660 spec->mux_nids[adc_idx],
661 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800662 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800663 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800664
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800665 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100666}
667
Harald Welte0aa62ae2008-09-09 15:58:27 +0800668static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
669 struct snd_ctl_elem_info *uinfo)
670{
671 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
672 struct via_spec *spec = codec->spec;
673 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
674}
675
676static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
677 struct snd_ctl_elem_value *ucontrol)
678{
679 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100680 hda_nid_t nid = kcontrol->private_value;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800681 unsigned int pinsel;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800682
Lydia Wangeb7188c2009-10-10 19:08:34 +0800683 /* use !! to translate conn sel 2 for VT1718S */
684 pinsel = !!snd_hda_codec_read(codec, nid, 0,
685 AC_VERB_GET_CONNECT_SEL,
686 0x00);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800687 ucontrol->value.enumerated.item[0] = pinsel;
688
689 return 0;
690}
691
Lydia Wang0713efe2009-10-10 19:07:43 +0800692static void activate_ctl(struct hda_codec *codec, const char *name, int active)
693{
694 struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
695 if (ctl) {
696 ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
697 ctl->vd[0].access |= active
698 ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
699 snd_ctl_notify(codec->bus->card,
700 SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
701 }
702}
703
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100704static hda_nid_t side_mute_channel(struct via_spec *spec)
705{
706 switch (spec->codec_type) {
707 case VT1708: return 0x1b;
708 case VT1709_10CH: return 0x29;
709 case VT1708B_8CH: /* fall thru */
710 case VT1708S: return 0x27;
Lydia Wange87885f2011-03-24 12:39:05 +0800711 case VT2002P: return 0x19;
712 case VT1802: return 0x15;
713 case VT1812: return 0x15;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100714 default: return 0;
715 }
716}
717
Lydia Wangcdc17842009-10-10 19:07:47 +0800718static int update_side_mute_status(struct hda_codec *codec)
719{
720 /* mute side channel */
721 struct via_spec *spec = codec->spec;
Lydia Wange87885f2011-03-24 12:39:05 +0800722 unsigned int parm;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100723 hda_nid_t sw3 = side_mute_channel(spec);
Lydia Wangcdc17842009-10-10 19:07:47 +0800724
Lydia Wange87885f2011-03-24 12:39:05 +0800725 if (sw3) {
726 if (VT2002P_COMPATIBLE(spec))
727 parm = spec->hp_independent_mode ?
728 AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
729 else
730 parm = spec->hp_independent_mode ?
731 AMP_OUT_MUTE : AMP_OUT_UNMUTE;
732 snd_hda_codec_write(codec, sw3, 0,
733 AC_VERB_SET_AMP_GAIN_MUTE, parm);
734 if (spec->codec_type == VT1812)
735 snd_hda_codec_write(codec, 0x1d, 0,
736 AC_VERB_SET_AMP_GAIN_MUTE, parm);
737 }
Lydia Wangcdc17842009-10-10 19:07:47 +0800738 return 0;
739}
740
Harald Welte0aa62ae2008-09-09 15:58:27 +0800741static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
742 struct snd_ctl_elem_value *ucontrol)
743{
744 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
745 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100746 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800747 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800748 /* Get Independent Mode index of headphone pin widget */
749 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
750 ? 1 : 0;
Lydia Wangce0e5a92011-03-22 16:22:37 +0800751 if (spec->codec_type == VT1718S)
752 snd_hda_codec_write(codec, nid, 0,
753 AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
754 else
755 snd_hda_codec_write(codec, nid, 0,
756 AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800757
Lydia Wangce0e5a92011-03-22 16:22:37 +0800758 if (spec->codec_type == VT1812)
759 snd_hda_codec_write(codec, 0x35, 0,
760 AC_VERB_SET_CONNECT_SEL, pinsel);
Lydia Wangcdc17842009-10-10 19:07:47 +0800761 if (spec->multiout.hp_nid && spec->multiout.hp_nid
762 != spec->multiout.dac_nids[HDA_FRONT])
763 snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
764 0, 0, 0);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800765
Lydia Wangcdc17842009-10-10 19:07:47 +0800766 update_side_mute_status(codec);
Lydia Wang0713efe2009-10-10 19:07:43 +0800767 /* update HP volume/swtich active state */
768 if (spec->codec_type == VT1708S
Lydia Wangeb7188c2009-10-10 19:08:34 +0800769 || spec->codec_type == VT1702
Lydia Wangf3db4232009-10-10 19:08:41 +0800770 || spec->codec_type == VT1718S
Lydia Wang25eaba22009-10-10 19:08:43 +0800771 || spec->codec_type == VT1716S
Lydia Wang118909562011-03-23 17:57:34 +0800772 || VT2002P_COMPATIBLE(spec)) {
Lydia Wang0713efe2009-10-10 19:07:43 +0800773 activate_ctl(codec, "Headphone Playback Volume",
774 spec->hp_independent_mode);
775 activate_ctl(codec, "Headphone Playback Switch",
776 spec->hp_independent_mode);
777 }
Lydia Wangce0e5a92011-03-22 16:22:37 +0800778 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800779 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800780 return 0;
781}
782
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100783static struct snd_kcontrol_new via_hp_mixer[2] = {
Harald Welte0aa62ae2008-09-09 15:58:27 +0800784 {
785 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
786 .name = "Independent HP",
Harald Welte0aa62ae2008-09-09 15:58:27 +0800787 .info = via_independent_hp_info,
788 .get = via_independent_hp_get,
789 .put = via_independent_hp_put,
790 },
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100791 {
792 .iface = NID_MAPPING,
793 .name = "Independent HP",
794 },
Harald Welte0aa62ae2008-09-09 15:58:27 +0800795};
796
Takashi Iwai3d83e572010-04-14 14:36:23 +0200797static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100798{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200799 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100800 struct snd_kcontrol_new *knew;
801 hda_nid_t nid;
Takashi Iwai3d83e572010-04-14 14:36:23 +0200802 int nums;
803 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100804
805 switch (spec->codec_type) {
806 case VT1718S:
807 nid = 0x34;
808 break;
809 case VT2002P:
Lydia Wang118909562011-03-23 17:57:34 +0800810 case VT1802:
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100811 nid = 0x35;
812 break;
813 case VT1812:
814 nid = 0x3d;
815 break;
816 default:
817 nid = spec->autocfg.hp_pins[0];
818 break;
819 }
820
Lydia Wangee3c35c2011-03-22 16:26:36 +0800821 if (spec->codec_type != VT1708) {
822 nums = snd_hda_get_connections(codec, nid,
823 conn, HDA_MAX_CONNECTIONS);
824 if (nums <= 1)
825 return 0;
826 }
Takashi Iwai3d83e572010-04-14 14:36:23 +0200827
828 knew = via_clone_control(spec, &via_hp_mixer[0]);
829 if (knew == NULL)
830 return -ENOMEM;
831
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100832 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
833 knew->private_value = nid;
834
835 knew = via_clone_control(spec, &via_hp_mixer[1]);
836 if (knew == NULL)
837 return -ENOMEM;
838 knew->subdevice = side_mute_channel(spec);
839
840 return 0;
841}
842
Lydia Wang1564b282009-10-10 19:07:52 +0800843static void notify_aa_path_ctls(struct hda_codec *codec)
844{
845 int i;
846 struct snd_ctl_elem_id id;
847 const char *labels[] = {"Mic", "Front Mic", "Line"};
848
849 memset(&id, 0, sizeof(id));
850 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
851 for (i = 0; i < ARRAY_SIZE(labels); i++) {
852 sprintf(id.name, "%s Playback Volume", labels[i]);
853 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
854 &id);
855 }
856}
857
858static void mute_aa_path(struct hda_codec *codec, int mute)
859{
860 struct via_spec *spec = codec->spec;
861 hda_nid_t nid_mixer;
862 int start_idx;
863 int end_idx;
864 int i;
865 /* get nid of MW0 and start & end index */
866 switch (spec->codec_type) {
867 case VT1708:
868 nid_mixer = 0x17;
869 start_idx = 2;
870 end_idx = 4;
871 break;
872 case VT1709_10CH:
873 case VT1709_6CH:
874 nid_mixer = 0x18;
875 start_idx = 2;
876 end_idx = 4;
877 break;
878 case VT1708B_8CH:
879 case VT1708B_4CH:
880 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800881 case VT1716S:
Lydia Wang1564b282009-10-10 19:07:52 +0800882 nid_mixer = 0x16;
883 start_idx = 2;
884 end_idx = 4;
885 break;
Lydia Wangab657e02011-03-22 16:23:23 +0800886 case VT1718S:
887 nid_mixer = 0x21;
888 start_idx = 1;
889 end_idx = 3;
890 break;
Lydia Wang1564b282009-10-10 19:07:52 +0800891 default:
892 return;
893 }
894 /* check AA path's mute status */
895 for (i = start_idx; i <= end_idx; i++) {
896 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
897 snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
898 HDA_AMP_MUTE, val);
899 }
900}
901static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
902{
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200903 const struct auto_pin_cfg *cfg = &spec->autocfg;
904 int i;
905
906 for (i = 0; i < cfg->num_inputs; i++) {
907 if (pin == cfg->inputs[i].pin)
Takashi Iwai86e29592010-09-09 14:50:17 +0200908 return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
Lydia Wang1564b282009-10-10 19:07:52 +0800909 }
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200910 return 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800911}
912
913static int via_smart51_info(struct snd_kcontrol *kcontrol,
914 struct snd_ctl_elem_info *uinfo)
915{
916 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
917 uinfo->count = 1;
918 uinfo->value.integer.min = 0;
919 uinfo->value.integer.max = 1;
920 return 0;
921}
922
923static int via_smart51_get(struct snd_kcontrol *kcontrol,
924 struct snd_ctl_elem_value *ucontrol)
925{
926 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
927 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200928 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang1564b282009-10-10 19:07:52 +0800929 int on = 1;
930 int i;
931
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200932 for (i = 0; i < cfg->num_inputs; i++) {
933 hda_nid_t nid = cfg->inputs[i].pin;
934 int ctl = snd_hda_codec_read(codec, nid, 0,
935 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai86e29592010-09-09 14:50:17 +0200936 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200937 continue;
Takashi Iwai86e29592010-09-09 14:50:17 +0200938 if (cfg->inputs[i].type == AUTO_PIN_MIC &&
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200939 spec->hp_independent_mode && spec->codec_type != VT1718S)
940 continue; /* ignore FMic for independent HP */
941 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
942 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800943 }
944 *ucontrol->value.integer.value = on;
945 return 0;
946}
947
948static int via_smart51_put(struct snd_kcontrol *kcontrol,
949 struct snd_ctl_elem_value *ucontrol)
950{
951 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
952 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200953 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang1564b282009-10-10 19:07:52 +0800954 int out_in = *ucontrol->value.integer.value
955 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800956 int i;
957
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200958 for (i = 0; i < cfg->num_inputs; i++) {
959 hda_nid_t nid = cfg->inputs[i].pin;
960 unsigned int parm;
961
Takashi Iwai86e29592010-09-09 14:50:17 +0200962 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200963 continue;
Takashi Iwai86e29592010-09-09 14:50:17 +0200964 if (cfg->inputs[i].type == AUTO_PIN_MIC &&
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200965 spec->hp_independent_mode && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +0800966 continue; /* don't retask FMic for independent HP */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200967
968 parm = snd_hda_codec_read(codec, nid, 0,
969 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
970 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
971 parm |= out_in;
972 snd_hda_codec_write(codec, nid, 0,
973 AC_VERB_SET_PIN_WIDGET_CONTROL,
974 parm);
975 if (out_in == AC_PINCTL_OUT_EN) {
976 mute_aa_path(codec, 1);
977 notify_aa_path_ctls(codec);
978 }
979 if (spec->codec_type == VT1718S) {
980 snd_hda_codec_amp_stereo(
Lydia Wangeb7188c2009-10-10 19:08:34 +0800981 codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
982 HDA_AMP_UNMUTE);
Lydia Wang1564b282009-10-10 19:07:52 +0800983 }
Takashi Iwai86e29592010-09-09 14:50:17 +0200984 if (cfg->inputs[i].type == AUTO_PIN_MIC) {
Lydia Wangf3db4232009-10-10 19:08:41 +0800985 if (spec->codec_type == VT1708S
986 || spec->codec_type == VT1716S) {
Lydia Wang1564b282009-10-10 19:07:52 +0800987 /* input = index 1 (AOW3) */
988 snd_hda_codec_write(
989 codec, nid, 0,
990 AC_VERB_SET_CONNECT_SEL, 1);
991 snd_hda_codec_amp_stereo(
992 codec, nid, HDA_OUTPUT,
993 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
994 }
995 }
996 }
997 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800998 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800999 return 1;
1000}
1001
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001002static struct snd_kcontrol_new via_smart51_mixer[2] = {
Lydia Wang1564b282009-10-10 19:07:52 +08001003 {
1004 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1005 .name = "Smart 5.1",
1006 .count = 1,
1007 .info = via_smart51_info,
1008 .get = via_smart51_get,
1009 .put = via_smart51_put,
1010 },
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001011 {
1012 .iface = NID_MAPPING,
1013 .name = "Smart 5.1",
1014 }
Lydia Wang1564b282009-10-10 19:07:52 +08001015};
1016
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001017static int via_smart51_build(struct via_spec *spec)
1018{
1019 struct snd_kcontrol_new *knew;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001020 const struct auto_pin_cfg *cfg = &spec->autocfg;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001021 hda_nid_t nid;
1022 int i;
1023
1024 knew = via_clone_control(spec, &via_smart51_mixer[0]);
1025 if (knew == NULL)
1026 return -ENOMEM;
1027
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001028 for (i = 0; i < cfg->num_inputs; i++) {
1029 nid = cfg->inputs[i].pin;
Takashi Iwai86e29592010-09-09 14:50:17 +02001030 if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001031 knew = via_clone_control(spec, &via_smart51_mixer[1]);
1032 if (knew == NULL)
1033 return -ENOMEM;
1034 knew->subdevice = nid;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001035 break;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001036 }
1037 }
1038
1039 return 0;
1040}
1041
Joseph Chanc577b8a2006-11-29 15:29:40 +01001042/* capture mixer elements */
1043static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1044 HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1045 HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1046 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1047 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1048 {
1049 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1050 /* The multiple "Capture Source" controls confuse alsamixer
1051 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01001052 */
1053 /* .name = "Capture Source", */
1054 .name = "Input Source",
1055 .count = 1,
1056 .info = via_mux_enum_info,
1057 .get = via_mux_enum_get,
1058 .put = via_mux_enum_put,
1059 },
1060 { } /* end */
1061};
Lydia Wangf5271102009-10-10 19:07:35 +08001062
1063/* check AA path's mute statue */
1064static int is_aa_path_mute(struct hda_codec *codec)
1065{
1066 int mute = 1;
1067 hda_nid_t nid_mixer;
1068 int start_idx;
1069 int end_idx;
1070 int i;
1071 struct via_spec *spec = codec->spec;
1072 /* get nid of MW0 and start & end index */
1073 switch (spec->codec_type) {
1074 case VT1708B_8CH:
1075 case VT1708B_4CH:
1076 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001077 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001078 nid_mixer = 0x16;
1079 start_idx = 2;
1080 end_idx = 4;
1081 break;
1082 case VT1702:
1083 nid_mixer = 0x1a;
1084 start_idx = 1;
1085 end_idx = 3;
1086 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +08001087 case VT1718S:
1088 nid_mixer = 0x21;
1089 start_idx = 1;
1090 end_idx = 3;
1091 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001092 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001093 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001094 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001095 nid_mixer = 0x21;
1096 start_idx = 0;
1097 end_idx = 2;
1098 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001099 default:
1100 return 0;
1101 }
1102 /* check AA path's mute status */
1103 for (i = start_idx; i <= end_idx; i++) {
1104 unsigned int con_list = snd_hda_codec_read(
1105 codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1106 int shift = 8 * (i % 4);
1107 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1108 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1109 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1110 /* check mute status while the pin is connected */
1111 int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1112 HDA_INPUT, i) >> 7;
1113 int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1114 HDA_INPUT, i) >> 7;
1115 if (!mute_l || !mute_r) {
1116 mute = 0;
1117 break;
1118 }
1119 }
1120 }
1121 return mute;
1122}
1123
1124/* enter/exit analog low-current mode */
1125static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1126{
1127 struct via_spec *spec = codec->spec;
1128 static int saved_stream_idle = 1; /* saved stream idle status */
1129 int enable = is_aa_path_mute(codec);
1130 unsigned int verb = 0;
1131 unsigned int parm = 0;
1132
1133 if (stream_idle == -1) /* stream status did not change */
1134 enable = enable && saved_stream_idle;
1135 else {
1136 enable = enable && stream_idle;
1137 saved_stream_idle = stream_idle;
1138 }
1139
1140 /* decide low current mode's verb & parameter */
1141 switch (spec->codec_type) {
1142 case VT1708B_8CH:
1143 case VT1708B_4CH:
1144 verb = 0xf70;
1145 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1146 break;
1147 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001148 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001149 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001150 verb = 0xf73;
1151 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1152 break;
1153 case VT1702:
1154 verb = 0xf73;
1155 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1156 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001157 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001158 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001159 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001160 verb = 0xf93;
1161 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1162 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001163 default:
1164 return; /* other codecs are not supported */
1165 }
1166 /* send verb */
1167 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1168}
1169
Joseph Chanc577b8a2006-11-29 15:29:40 +01001170/*
1171 * generic initialization of ADC, input mixers and output mixers
1172 */
1173static struct hda_verb vt1708_volume_init_verbs[] = {
1174 /*
1175 * Unmute ADC0-1 and set the default input to mic-in
1176 */
1177 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1178 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1179
1180
Josepch Chanf7278fd2007-12-13 16:40:40 +01001181 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01001182 * mixer widget
1183 */
1184 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01001185 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1186 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1187 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1188 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1189 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001190
1191 /*
1192 * Set up output mixers (0x19 - 0x1b)
1193 */
1194 /* set vol=0 to output mixers */
1195 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1196 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1197 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
Lydia Wang377ff312009-10-10 19:08:55 +08001198
Lydia Wangbfdc6752009-10-10 19:08:50 +08001199 /* Setup default input MW0 to PW4 */
1200 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01001201 /* PW9 Output enable */
1202 {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Lydia Wangaa266fc2011-03-24 12:41:01 +08001203 /* power down jack detect function */
1204 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001205 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001206};
1207
1208static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1209 struct hda_codec *codec,
1210 struct snd_pcm_substream *substream)
1211{
1212 struct via_spec *spec = codec->spec;
Lydia Wang17314372009-10-10 19:07:37 +08001213 int idle = substream->pstr->substream_opened == 1
1214 && substream->ref_count == 0;
Lydia Wang17314372009-10-10 19:07:37 +08001215 analog_low_current_mode(codec, idle);
Takashi Iwai9a081602008-02-12 18:37:26 +01001216 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1217 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001218}
1219
Harald Welte0aa62ae2008-09-09 15:58:27 +08001220static void playback_multi_pcm_prep_0(struct hda_codec *codec,
1221 unsigned int stream_tag,
1222 unsigned int format,
1223 struct snd_pcm_substream *substream)
1224{
1225 struct via_spec *spec = codec->spec;
1226 struct hda_multi_out *mout = &spec->multiout;
1227 hda_nid_t *nids = mout->dac_nids;
1228 int chs = substream->runtime->channels;
1229 int i;
1230
1231 mutex_lock(&codec->spdif_mutex);
1232 if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
1233 if (chs == 2 &&
1234 snd_hda_is_supported_format(codec, mout->dig_out_nid,
1235 format) &&
1236 !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
1237 mout->dig_out_used = HDA_DIG_ANALOG_DUP;
1238 /* turn off SPDIF once; otherwise the IEC958 bits won't
1239 * be updated */
1240 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1241 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1242 AC_VERB_SET_DIGI_CONVERT_1,
1243 codec->spdif_ctls &
1244 ~AC_DIG1_ENABLE & 0xff);
1245 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1246 stream_tag, 0, format);
1247 /* turn on again (if needed) */
1248 if (codec->spdif_ctls & AC_DIG1_ENABLE)
1249 snd_hda_codec_write(codec, mout->dig_out_nid, 0,
1250 AC_VERB_SET_DIGI_CONVERT_1,
1251 codec->spdif_ctls & 0xff);
1252 } else {
1253 mout->dig_out_used = 0;
1254 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1255 0, 0, 0);
1256 }
1257 }
1258 mutex_unlock(&codec->spdif_mutex);
1259
1260 /* front */
1261 snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
1262 0, format);
1263
Lydia Wangeb7188c2009-10-10 19:08:34 +08001264 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1265 && !spec->hp_independent_mode)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001266 /* headphone out will just decode front left/right (stereo) */
1267 snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
1268 0, format);
1269
1270 /* extra outputs copied from front */
1271 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1272 if (mout->extra_out_nid[i])
1273 snd_hda_codec_setup_stream(codec,
1274 mout->extra_out_nid[i],
1275 stream_tag, 0, format);
1276
1277 /* surrounds */
1278 for (i = 1; i < mout->num_dacs; i++) {
1279 if (chs >= (i + 1) * 2) /* independent out */
1280 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1281 i * 2, format);
1282 else /* copy front */
1283 snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
1284 0, format);
1285 }
1286}
1287
1288static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1289 struct hda_codec *codec,
1290 unsigned int stream_tag,
1291 unsigned int format,
1292 struct snd_pcm_substream *substream)
1293{
1294 struct via_spec *spec = codec->spec;
1295 struct hda_multi_out *mout = &spec->multiout;
1296 hda_nid_t *nids = mout->dac_nids;
1297
1298 if (substream->number == 0)
1299 playback_multi_pcm_prep_0(codec, stream_tag, format,
1300 substream);
1301 else {
1302 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1303 spec->hp_independent_mode)
1304 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1305 stream_tag, 0, format);
1306 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001307 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001308 return 0;
1309}
1310
1311static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1312 struct hda_codec *codec,
1313 struct snd_pcm_substream *substream)
1314{
1315 struct via_spec *spec = codec->spec;
1316 struct hda_multi_out *mout = &spec->multiout;
1317 hda_nid_t *nids = mout->dac_nids;
1318 int i;
1319
1320 if (substream->number == 0) {
1321 for (i = 0; i < mout->num_dacs; i++)
1322 snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
1323
1324 if (mout->hp_nid && !spec->hp_independent_mode)
1325 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1326 0, 0, 0);
1327
1328 for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
1329 if (mout->extra_out_nid[i])
1330 snd_hda_codec_setup_stream(codec,
1331 mout->extra_out_nid[i],
1332 0, 0, 0);
1333 mutex_lock(&codec->spdif_mutex);
1334 if (mout->dig_out_nid &&
1335 mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
1336 snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
1337 0, 0, 0);
1338 mout->dig_out_used = 0;
1339 }
1340 mutex_unlock(&codec->spdif_mutex);
1341 } else {
1342 if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
1343 spec->hp_independent_mode)
1344 snd_hda_codec_setup_stream(codec, mout->hp_nid,
1345 0, 0, 0);
1346 }
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001347 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001348 return 0;
1349}
1350
Joseph Chanc577b8a2006-11-29 15:29:40 +01001351/*
1352 * Digital out
1353 */
1354static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1355 struct hda_codec *codec,
1356 struct snd_pcm_substream *substream)
1357{
1358 struct via_spec *spec = codec->spec;
1359 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1360}
1361
1362static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1363 struct hda_codec *codec,
1364 struct snd_pcm_substream *substream)
1365{
1366 struct via_spec *spec = codec->spec;
1367 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1368}
1369
Harald Welte5691ec72008-09-15 22:42:26 +08001370static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001371 struct hda_codec *codec,
1372 unsigned int stream_tag,
1373 unsigned int format,
1374 struct snd_pcm_substream *substream)
1375{
1376 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001377 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1378 stream_tag, format, substream);
1379}
Harald Welte5691ec72008-09-15 22:42:26 +08001380
Takashi Iwai9da29272009-05-07 16:31:14 +02001381static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1382 struct hda_codec *codec,
1383 struct snd_pcm_substream *substream)
1384{
1385 struct via_spec *spec = codec->spec;
1386 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001387 return 0;
1388}
1389
Joseph Chanc577b8a2006-11-29 15:29:40 +01001390/*
1391 * Analog capture
1392 */
1393static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1394 struct hda_codec *codec,
1395 unsigned int stream_tag,
1396 unsigned int format,
1397 struct snd_pcm_substream *substream)
1398{
1399 struct via_spec *spec = codec->spec;
1400
1401 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1402 stream_tag, 0, format);
1403 return 0;
1404}
1405
1406static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1407 struct hda_codec *codec,
1408 struct snd_pcm_substream *substream)
1409{
1410 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001411 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 return 0;
1413}
1414
1415static struct hda_pcm_stream vt1708_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08001416 .substreams = 2,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 .channels_min = 2,
1418 .channels_max = 8,
1419 .nid = 0x10, /* NID to query formats and rates */
1420 .ops = {
1421 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001422 .prepare = via_playback_multi_pcm_prepare,
1423 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001424 },
1425};
1426
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001427static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Lydia Wangc873cc22009-10-10 19:08:21 +08001428 .substreams = 2,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001429 .channels_min = 2,
1430 .channels_max = 8,
1431 .nid = 0x10, /* NID to query formats and rates */
1432 /* We got noisy outputs on the right channel on VT1708 when
1433 * 24bit samples are used. Until any workaround is found,
1434 * disable the 24bit format, so far.
1435 */
1436 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1437 .ops = {
1438 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08001439 .prepare = via_playback_multi_pcm_prepare,
1440 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001441 },
1442};
1443
Joseph Chanc577b8a2006-11-29 15:29:40 +01001444static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1445 .substreams = 2,
1446 .channels_min = 2,
1447 .channels_max = 2,
1448 .nid = 0x15, /* NID to query formats and rates */
1449 .ops = {
1450 .prepare = via_capture_pcm_prepare,
1451 .cleanup = via_capture_pcm_cleanup
1452 },
1453};
1454
1455static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1456 .substreams = 1,
1457 .channels_min = 2,
1458 .channels_max = 2,
1459 /* NID is set in via_build_pcms */
1460 .ops = {
1461 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001462 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001463 .prepare = via_dig_playback_pcm_prepare,
1464 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001465 },
1466};
1467
1468static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1469 .substreams = 1,
1470 .channels_min = 2,
1471 .channels_max = 2,
1472};
1473
1474static int via_build_controls(struct hda_codec *codec)
1475{
1476 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001477 struct snd_kcontrol *kctl;
1478 struct snd_kcontrol_new *knew;
1479 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001480
1481 for (i = 0; i < spec->num_mixers; i++) {
1482 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1483 if (err < 0)
1484 return err;
1485 }
1486
1487 if (spec->multiout.dig_out_nid) {
1488 err = snd_hda_create_spdif_out_ctls(codec,
1489 spec->multiout.dig_out_nid);
1490 if (err < 0)
1491 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001492 err = snd_hda_create_spdif_share_sw(codec,
1493 &spec->multiout);
1494 if (err < 0)
1495 return err;
1496 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001497 }
1498 if (spec->dig_in_nid) {
1499 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1500 if (err < 0)
1501 return err;
1502 }
Lydia Wang17314372009-10-10 19:07:37 +08001503
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001504 /* assign Capture Source enums to NID */
1505 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1506 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001507 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001508 if (err < 0)
1509 return err;
1510 }
1511
1512 /* other nid->control mapping */
1513 for (i = 0; i < spec->num_mixers; i++) {
1514 for (knew = spec->mixers[i]; knew->name; knew++) {
1515 if (knew->iface != NID_MAPPING)
1516 continue;
1517 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1518 if (kctl == NULL)
1519 continue;
1520 err = snd_hda_add_nid(codec, kctl, 0,
1521 knew->subdevice);
1522 }
1523 }
1524
Lydia Wang17314372009-10-10 19:07:37 +08001525 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001526 set_widgets_power_state(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001527 analog_low_current_mode(codec, 1);
1528
Takashi Iwai603c4012008-07-30 15:01:44 +02001529 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001530 return 0;
1531}
1532
1533static int via_build_pcms(struct hda_codec *codec)
1534{
1535 struct via_spec *spec = codec->spec;
1536 struct hda_pcm *info = spec->pcm_rec;
1537
1538 codec->num_pcms = 1;
1539 codec->pcm_info = info;
1540
1541 info->name = spec->stream_name_analog;
Lydia Wang377ff312009-10-10 19:08:55 +08001542 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1543 *(spec->stream_analog_playback);
1544 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1545 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001546 info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1547 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1548
1549 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1550 spec->multiout.max_channels;
1551
1552 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1553 codec->num_pcms++;
1554 info++;
1555 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001556 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001557 if (spec->multiout.dig_out_nid) {
1558 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1559 *(spec->stream_digital_playback);
1560 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1561 spec->multiout.dig_out_nid;
1562 }
1563 if (spec->dig_in_nid) {
1564 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1565 *(spec->stream_digital_capture);
1566 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1567 spec->dig_in_nid;
1568 }
1569 }
1570
1571 return 0;
1572}
1573
1574static void via_free(struct hda_codec *codec)
1575{
1576 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577
1578 if (!spec)
1579 return;
1580
Takashi Iwai603c4012008-07-30 15:01:44 +02001581 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001582 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001583 kfree(codec->spec);
1584}
1585
Harald Welte69e52a82008-09-09 15:57:32 +08001586/* mute internal speaker if HP is plugged */
1587static void via_hp_automute(struct hda_codec *codec)
1588{
Lydia Wangdcf34c82009-10-10 19:08:15 +08001589 unsigned int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001590 struct via_spec *spec = codec->spec;
1591
Takashi Iwaid56757a2009-11-18 08:00:14 +01001592 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wangdcf34c82009-10-10 19:08:15 +08001593
1594 if (!spec->hp_independent_mode) {
1595 struct snd_ctl_elem_id id;
1596 /* auto mute */
1597 snd_hda_codec_amp_stereo(
1598 codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1599 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1600 /* notify change */
1601 memset(&id, 0, sizeof(id));
1602 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1603 strcpy(id.name, "Front Playback Switch");
1604 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1605 &id);
1606 }
Harald Welte69e52a82008-09-09 15:57:32 +08001607}
1608
Lydia Wangf3db4232009-10-10 19:08:41 +08001609/* mute mono out if HP or Line out is plugged */
1610static void via_mono_automute(struct hda_codec *codec)
1611{
1612 unsigned int hp_present, lineout_present;
1613 struct via_spec *spec = codec->spec;
1614
1615 if (spec->codec_type != VT1716S)
1616 return;
1617
Takashi Iwaid56757a2009-11-18 08:00:14 +01001618 lineout_present = snd_hda_jack_detect(codec,
1619 spec->autocfg.line_out_pins[0]);
Lydia Wangf3db4232009-10-10 19:08:41 +08001620
1621 /* Mute Mono Out if Line Out is plugged */
1622 if (lineout_present) {
1623 snd_hda_codec_amp_stereo(
1624 codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1625 return;
1626 }
1627
Takashi Iwaid56757a2009-11-18 08:00:14 +01001628 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wangf3db4232009-10-10 19:08:41 +08001629
1630 if (!spec->hp_independent_mode)
1631 snd_hda_codec_amp_stereo(
1632 codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1633 hp_present ? HDA_AMP_MUTE : 0);
1634}
1635
Harald Welte69e52a82008-09-09 15:57:32 +08001636static void via_gpio_control(struct hda_codec *codec)
1637{
1638 unsigned int gpio_data;
1639 unsigned int vol_counter;
1640 unsigned int vol;
1641 unsigned int master_vol;
1642
1643 struct via_spec *spec = codec->spec;
1644
1645 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1646 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1647
1648 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1649 0xF84, 0) & 0x3F0000) >> 16;
1650
1651 vol = vol_counter & 0x1F;
1652 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1653 AC_VERB_GET_AMP_GAIN_MUTE,
1654 AC_AMP_GET_INPUT);
1655
1656 if (gpio_data == 0x02) {
1657 /* unmute line out */
1658 snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
1659 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
1660
1661 if (vol_counter & 0x20) {
1662 /* decrease volume */
1663 if (vol > master_vol)
1664 vol = master_vol;
1665 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1666 0, HDA_AMP_VOLMASK,
1667 master_vol-vol);
1668 } else {
1669 /* increase volume */
1670 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1671 HDA_AMP_VOLMASK,
1672 ((master_vol+vol) > 0x2A) ? 0x2A :
1673 (master_vol+vol));
1674 }
1675 } else if (!(gpio_data & 0x02)) {
1676 /* mute line out */
1677 snd_hda_codec_amp_stereo(codec,
1678 spec->autocfg.line_out_pins[0],
1679 HDA_OUTPUT, 0, HDA_AMP_MUTE,
1680 HDA_AMP_MUTE);
1681 }
1682}
1683
Lydia Wang25eaba22009-10-10 19:08:43 +08001684/* mute Internal-Speaker if HP is plugged */
1685static void via_speaker_automute(struct hda_codec *codec)
1686{
1687 unsigned int hp_present;
1688 struct via_spec *spec = codec->spec;
1689
Lydia Wang27439ce2011-03-24 12:40:10 +08001690 if (!VT2002P_COMPATIBLE(spec))
Lydia Wang25eaba22009-10-10 19:08:43 +08001691 return;
1692
Takashi Iwaid56757a2009-11-18 08:00:14 +01001693 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08001694
1695 if (!spec->hp_independent_mode) {
1696 struct snd_ctl_elem_id id;
1697 snd_hda_codec_amp_stereo(
1698 codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
1699 HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
1700 /* notify change */
1701 memset(&id, 0, sizeof(id));
1702 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1703 strcpy(id.name, "Speaker Playback Switch");
1704 snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1705 &id);
1706 }
1707}
1708
1709/* mute line-out and internal speaker if HP is plugged */
1710static void via_hp_bind_automute(struct hda_codec *codec)
1711{
akpm@linux-foundation.org01a17962009-11-13 16:47:10 -08001712 /* use long instead of int below just to avoid an internal compiler
1713 * error with gcc 4.0.x
1714 */
1715 unsigned long hp_present, present = 0;
Lydia Wang25eaba22009-10-10 19:08:43 +08001716 struct via_spec *spec = codec->spec;
1717 int i;
1718
1719 if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
1720 return;
1721
Takashi Iwaid56757a2009-11-18 08:00:14 +01001722 hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08001723
Takashi Iwaid56757a2009-11-18 08:00:14 +01001724 present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
Lydia Wang25eaba22009-10-10 19:08:43 +08001725
1726 if (!spec->hp_independent_mode) {
1727 /* Mute Line-Outs */
1728 for (i = 0; i < spec->autocfg.line_outs; i++)
1729 snd_hda_codec_amp_stereo(
1730 codec, spec->autocfg.line_out_pins[i],
1731 HDA_OUTPUT, 0,
1732 HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
1733 if (hp_present)
1734 present = hp_present;
1735 }
1736 /* Speakers */
1737 for (i = 0; i < spec->autocfg.speaker_outs; i++)
1738 snd_hda_codec_amp_stereo(
1739 codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
1740 HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1741}
1742
1743
Harald Welte69e52a82008-09-09 15:57:32 +08001744/* unsolicited event for jack sensing */
1745static void via_unsol_event(struct hda_codec *codec,
1746 unsigned int res)
1747{
1748 res >>= 26;
Lydia Wanga34df192009-10-10 19:08:01 +08001749 if (res & VIA_HP_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001750 via_hp_automute(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001751 if (res & VIA_GPIO_EVENT)
Harald Welte69e52a82008-09-09 15:57:32 +08001752 via_gpio_control(codec);
Lydia Wanga34df192009-10-10 19:08:01 +08001753 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001754 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08001755 if (res & VIA_MONO_EVENT)
1756 via_mono_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08001757 if (res & VIA_SPEAKER_EVENT)
1758 via_speaker_automute(codec);
1759 if (res & VIA_BIND_HP_EVENT)
1760 via_hp_bind_automute(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001761}
1762
Joseph Chanc577b8a2006-11-29 15:29:40 +01001763static int via_init(struct hda_codec *codec)
1764{
1765 struct via_spec *spec = codec->spec;
Harald Welte69e52a82008-09-09 15:57:32 +08001766 int i;
1767 for (i = 0; i < spec->num_iverbs; i++)
1768 snd_hda_sequence_write(codec, spec->init_verbs[i]);
1769
Josepch Chanf7278fd2007-12-13 16:40:40 +01001770 /* Lydia Add for EAPD enable */
1771 if (!spec->dig_in_nid) { /* No Digital In connection */
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001772 if (spec->dig_in_pin) {
1773 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001774 AC_VERB_SET_PIN_WIDGET_CONTROL,
Takashi Iwai12b74c82008-01-15 12:39:38 +01001775 PIN_OUT);
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02001776 snd_hda_codec_write(codec, spec->dig_in_pin, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001777 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1778 }
Takashi Iwai12b74c82008-01-15 12:39:38 +01001779 } else /* enable SPDIF-input pin */
1780 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
1781 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
Josepch Chanf7278fd2007-12-13 16:40:40 +01001782
Takashi Iwai9da29272009-05-07 16:31:14 +02001783 /* assign slave outs */
1784 if (spec->slave_dig_outs[0])
1785 codec->slave_dig_outs = spec->slave_dig_outs;
Harald Welte5691ec72008-09-15 22:42:26 +08001786
Lydia Wang377ff312009-10-10 19:08:55 +08001787 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001788}
1789
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001790#ifdef SND_HDA_NEEDS_RESUME
1791static int via_suspend(struct hda_codec *codec, pm_message_t state)
1792{
1793 struct via_spec *spec = codec->spec;
1794 vt1708_stop_hp_work(spec);
1795 return 0;
1796}
1797#endif
1798
Takashi Iwaicb53c622007-08-10 17:21:45 +02001799#ifdef CONFIG_SND_HDA_POWER_SAVE
1800static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1801{
1802 struct via_spec *spec = codec->spec;
1803 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1804}
1805#endif
1806
Joseph Chanc577b8a2006-11-29 15:29:40 +01001807/*
1808 */
1809static struct hda_codec_ops via_patch_ops = {
1810 .build_controls = via_build_controls,
1811 .build_pcms = via_build_pcms,
1812 .init = via_init,
1813 .free = via_free,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001814#ifdef SND_HDA_NEEDS_RESUME
1815 .suspend = via_suspend,
1816#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001817#ifdef CONFIG_SND_HDA_POWER_SAVE
1818 .check_power_status = via_check_power_status,
1819#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001820};
1821
1822/* fill in the dac_nids table from the parsed pin configuration */
1823static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1824 const struct auto_pin_cfg *cfg)
1825{
1826 int i;
1827 hda_nid_t nid;
1828
1829 spec->multiout.num_dacs = cfg->line_outs;
1830
1831 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang377ff312009-10-10 19:08:55 +08001832
1833 for (i = 0; i < 4; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001834 nid = cfg->line_out_pins[i];
1835 if (nid) {
1836 /* config dac list */
1837 switch (i) {
1838 case AUTO_SEQ_FRONT:
1839 spec->multiout.dac_nids[i] = 0x10;
1840 break;
1841 case AUTO_SEQ_CENLFE:
1842 spec->multiout.dac_nids[i] = 0x12;
1843 break;
1844 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08001845 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001846 break;
1847 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08001848 spec->multiout.dac_nids[i] = 0x13;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849 break;
1850 }
1851 }
1852 }
1853
1854 return 0;
1855}
1856
1857/* add playback controls from the parsed DAC table */
1858static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1859 const struct auto_pin_cfg *cfg)
1860{
1861 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01001862 static const char * const chname[4] = {
1863 "Front", "Surround", "C/LFE", "Side"
1864 };
Lydia Wang9645c202009-10-10 19:08:27 +08001865 hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
Joseph Chanc577b8a2006-11-29 15:29:40 +01001866 int i, err;
1867
1868 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1869 nid = cfg->line_out_pins[i];
1870
1871 if (!nid)
1872 continue;
Lydia Wang377ff312009-10-10 19:08:55 +08001873
Lydia Wang9645c202009-10-10 19:08:27 +08001874 nid_vol = nid_vols[i];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001875
1876 if (i == AUTO_SEQ_CENLFE) {
1877 /* Center/LFE */
1878 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001879 "Center Playback Volume",
1880 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1881 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001882 if (err < 0)
1883 return err;
1884 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1885 "LFE Playback Volume",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001886 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1887 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001888 if (err < 0)
1889 return err;
1890 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1891 "Center Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001892 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1893 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001894 if (err < 0)
1895 return err;
1896 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1897 "LFE Playback Switch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01001898 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1899 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001900 if (err < 0)
1901 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08001902 } else if (i == AUTO_SEQ_FRONT) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001903 /* add control to mixer index 0 */
1904 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1905 "Master Front Playback Volume",
Lydia Wang9645c202009-10-10 19:08:27 +08001906 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001907 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001908 if (err < 0)
1909 return err;
1910 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1911 "Master Front Playback Switch",
Lydia Wang9645c202009-10-10 19:08:27 +08001912 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001913 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001914 if (err < 0)
1915 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08001916
Joseph Chanc577b8a2006-11-29 15:29:40 +01001917 /* add control to PW3 */
1918 sprintf(name, "%s Playback Volume", chname[i]);
1919 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001920 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1921 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001922 if (err < 0)
1923 return err;
1924 sprintf(name, "%s Playback Switch", chname[i]);
1925 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001926 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1927 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001928 if (err < 0)
1929 return err;
1930 } else {
1931 sprintf(name, "%s Playback Volume", chname[i]);
1932 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001933 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1934 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001935 if (err < 0)
1936 return err;
1937 sprintf(name, "%s Playback Switch", chname[i]);
1938 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01001939 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1940 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01001941 if (err < 0)
1942 return err;
1943 }
1944 }
1945
1946 return 0;
1947}
1948
Harald Welte0aa62ae2008-09-09 15:58:27 +08001949static void create_hp_imux(struct via_spec *spec)
1950{
1951 int i;
1952 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001953 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001954
1955 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001956 for (i = 0; texts[i]; i++)
1957 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001958
1959 spec->hp_mux = &spec->private_imux[1];
1960}
1961
Joseph Chanc577b8a2006-11-29 15:29:40 +01001962static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1963{
1964 int err;
1965
1966 if (!pin)
1967 return 0;
1968
1969 spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08001970 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001971
1972 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1973 "Headphone Playback Volume",
1974 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1975 if (err < 0)
1976 return err;
1977 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1978 "Headphone Playback Switch",
1979 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1980 if (err < 0)
1981 return err;
1982
Harald Welte0aa62ae2008-09-09 15:58:27 +08001983 create_hp_imux(spec);
1984
Joseph Chanc577b8a2006-11-29 15:29:40 +01001985 return 0;
1986}
1987
1988/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001989static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaif3268512010-08-30 11:00:19 +02001990 const struct auto_pin_cfg *cfg,
1991 hda_nid_t cap_nid,
1992 hda_nid_t pin_idxs[], int num_idxs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001993{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001994 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001995 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001996 int i, err, idx, type, type_idx = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001997
1998 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001999 for (idx = 0; idx < num_idxs; idx++) {
2000 if (pin_idxs[idx] == 0xff) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02002001 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02002002 break;
2003 }
2004 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002005
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002006 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02002007 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002008 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02002009 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002010 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02002011 break;
2012 if (idx >= num_idxs)
2013 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002014 if (i > 0 && type == cfg->inputs[i - 1].type)
2015 type_idx++;
2016 else
2017 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002018 label = hda_get_autocfg_input_label(codec, cfg, i);
Lydia Wang16922282011-03-22 16:24:10 +08002019 if (spec->codec_type == VT1708S ||
2020 spec->codec_type == VT1702 ||
2021 spec->codec_type == VT1716S)
2022 err = via_new_analog_input(spec, label, type_idx,
2023 idx+1, cap_nid);
2024 else
2025 err = via_new_analog_input(spec, label, type_idx,
2026 idx, cap_nid);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002027 if (err < 0)
2028 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002029 snd_hda_add_imux_item(imux, label, idx, NULL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002030 }
2031 return 0;
2032}
2033
Takashi Iwaif3268512010-08-30 11:00:19 +02002034/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02002035static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
Takashi Iwaif3268512010-08-30 11:00:19 +02002036 const struct auto_pin_cfg *cfg)
2037{
2038 static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
Takashi Iwai10a20af2010-09-09 16:28:02 +02002039 return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02002040 ARRAY_SIZE(pin_idxs));
2041}
2042
Takashi Iwaicb53c622007-08-10 17:21:45 +02002043#ifdef CONFIG_SND_HDA_POWER_SAVE
2044static struct hda_amp_list vt1708_loopbacks[] = {
2045 { 0x17, HDA_INPUT, 1 },
2046 { 0x17, HDA_INPUT, 2 },
2047 { 0x17, HDA_INPUT, 3 },
2048 { 0x17, HDA_INPUT, 4 },
2049 { } /* end */
2050};
2051#endif
2052
Harald Welte76d9b0d2008-09-09 15:50:37 +08002053static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2054{
2055 unsigned int def_conf;
2056 unsigned char seqassoc;
2057
Takashi Iwai2f334f92009-02-20 14:37:42 +01002058 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002059 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2060 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002061 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2062 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2063 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2064 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002065 }
2066
2067 return;
2068}
2069
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002070static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
2071 struct snd_ctl_elem_value *ucontrol)
2072{
2073 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2074 struct via_spec *spec = codec->spec;
2075
2076 if (spec->codec_type != VT1708)
2077 return 0;
2078 spec->vt1708_jack_detectect =
2079 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2080 ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
2081 return 0;
2082}
2083
2084static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
2085 struct snd_ctl_elem_value *ucontrol)
2086{
2087 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2088 struct via_spec *spec = codec->spec;
2089 int change;
2090
2091 if (spec->codec_type != VT1708)
2092 return 0;
2093 spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
2094 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2095 == !spec->vt1708_jack_detectect;
2096 if (spec->vt1708_jack_detectect) {
2097 mute_aa_path(codec, 1);
2098 notify_aa_path_ctls(codec);
2099 }
2100 return change;
2101}
2102
2103static struct snd_kcontrol_new vt1708_jack_detectect[] = {
2104 {
2105 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2106 .name = "Jack Detect",
2107 .count = 1,
2108 .info = snd_ctl_boolean_mono_info,
2109 .get = vt1708_jack_detectect_get,
2110 .put = vt1708_jack_detectect_put,
2111 },
2112 {} /* end */
2113};
2114
Joseph Chanc577b8a2006-11-29 15:29:40 +01002115static int vt1708_parse_auto_config(struct hda_codec *codec)
2116{
2117 struct via_spec *spec = codec->spec;
2118 int err;
2119
Harald Welte76d9b0d2008-09-09 15:50:37 +08002120 /* Add HP and CD pin config connect bit re-config action */
2121 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2122 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2123
Joseph Chanc577b8a2006-11-29 15:29:40 +01002124 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2125 if (err < 0)
2126 return err;
2127 err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2128 if (err < 0)
2129 return err;
2130 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2131 return 0; /* can't find valid BIOS pin config */
2132
2133 err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2134 if (err < 0)
2135 return err;
2136 err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2137 if (err < 0)
2138 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002139 err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002140 if (err < 0)
2141 return err;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002142 /* add jack detect on/off control */
2143 err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
2144 if (err < 0)
2145 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002146
2147 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2148
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002149 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002150 spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002151 spec->dig_in_pin = VT1708_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002152 if (spec->autocfg.dig_in_pin)
2153 spec->dig_in_nid = VT1708_DIGIN_NID;
2154
Takashi Iwai603c4012008-07-30 15:01:44 +02002155 if (spec->kctls.list)
2156 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002157
Harald Welte69e52a82008-09-09 15:57:32 +08002158 spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002159
Harald Welte0aa62ae2008-09-09 15:58:27 +08002160 spec->input_mux = &spec->private_imux[0];
2161
Harald Weltef8fdd492008-09-15 22:41:31 +08002162 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02002163 via_hp_build(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002164
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002165 via_smart51_build(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002166 return 1;
2167}
2168
2169/* init callback for auto-configuration model -- overriding the default init */
2170static int via_auto_init(struct hda_codec *codec)
2171{
Lydia Wang25eaba22009-10-10 19:08:43 +08002172 struct via_spec *spec = codec->spec;
2173
Joseph Chanc577b8a2006-11-29 15:29:40 +01002174 via_init(codec);
2175 via_auto_init_multi_out(codec);
2176 via_auto_init_hp_out(codec);
2177 via_auto_init_analog_input(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002178
2179 if (VT2002P_COMPATIBLE(spec)) {
Lydia Wang25eaba22009-10-10 19:08:43 +08002180 via_hp_bind_automute(codec);
2181 } else {
2182 via_hp_automute(codec);
2183 via_speaker_automute(codec);
2184 }
2185
Joseph Chanc577b8a2006-11-29 15:29:40 +01002186 return 0;
2187}
2188
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002189static void vt1708_update_hp_jack_state(struct work_struct *work)
2190{
2191 struct via_spec *spec = container_of(work, struct via_spec,
2192 vt1708_hp_work.work);
2193 if (spec->codec_type != VT1708)
2194 return;
2195 /* if jack state toggled */
2196 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002197 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002198 spec->vt1708_hp_present ^= 1;
2199 via_hp_automute(spec->codec);
2200 }
2201 vt1708_start_hp_work(spec);
2202}
2203
Takashi Iwai337b9d02009-07-07 18:18:59 +02002204static int get_mux_nids(struct hda_codec *codec)
2205{
2206 struct via_spec *spec = codec->spec;
2207 hda_nid_t nid, conn[8];
2208 unsigned int type;
2209 int i, n;
2210
2211 for (i = 0; i < spec->num_adc_nids; i++) {
2212 nid = spec->adc_nids[i];
2213 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002214 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002215 if (type == AC_WID_PIN)
2216 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002217 n = snd_hda_get_connections(codec, nid, conn,
2218 ARRAY_SIZE(conn));
2219 if (n <= 0)
2220 break;
2221 if (n > 1) {
2222 spec->mux_nids[i] = nid;
2223 break;
2224 }
2225 nid = conn[0];
2226 }
2227 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002228 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002229}
2230
Joseph Chanc577b8a2006-11-29 15:29:40 +01002231static int patch_vt1708(struct hda_codec *codec)
2232{
2233 struct via_spec *spec;
2234 int err;
2235
2236 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002237 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002238 if (spec == NULL)
2239 return -ENOMEM;
2240
Joseph Chanc577b8a2006-11-29 15:29:40 +01002241 /* automatic parse from the BIOS config */
2242 err = vt1708_parse_auto_config(codec);
2243 if (err < 0) {
2244 via_free(codec);
2245 return err;
2246 } else if (!err) {
2247 printk(KERN_INFO "hda_codec: Cannot set up configuration "
2248 "from BIOS. Using genenic mode...\n");
2249 }
2250
Lydia Wang377ff312009-10-10 19:08:55 +08002251
Joseph Chanc577b8a2006-11-29 15:29:40 +01002252 spec->stream_name_analog = "VT1708 Analog";
2253 spec->stream_analog_playback = &vt1708_pcm_analog_playback;
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002254 /* disable 32bit format on VT1708 */
2255 if (codec->vendor_id == 0x11061708)
2256 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257 spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2258
2259 spec->stream_name_digital = "VT1708 Digital";
2260 spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2261 spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2262
Lydia Wang377ff312009-10-10 19:08:55 +08002263
Joseph Chanc577b8a2006-11-29 15:29:40 +01002264 if (!spec->adc_nids && spec->input_mux) {
2265 spec->adc_nids = vt1708_adc_nids;
2266 spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
Takashi Iwai0f67a612009-08-31 08:12:29 +02002267 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002268 spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2269 spec->num_mixers++;
2270 }
2271
2272 codec->patch_ops = via_patch_ops;
2273
2274 codec->patch_ops.init = via_auto_init;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002275#ifdef CONFIG_SND_HDA_POWER_SAVE
2276 spec->loopback.amplist = vt1708_loopbacks;
2277#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002278 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002279 return 0;
2280}
2281
2282/* capture mixer elements */
2283static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2284 HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2285 HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2286 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2287 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2288 HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2289 HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2290 {
2291 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2292 /* The multiple "Capture Source" controls confuse alsamixer
2293 * So call somewhat different..
Joseph Chanc577b8a2006-11-29 15:29:40 +01002294 */
2295 /* .name = "Capture Source", */
2296 .name = "Input Source",
2297 .count = 1,
2298 .info = via_mux_enum_info,
2299 .get = via_mux_enum_get,
2300 .put = via_mux_enum_put,
2301 },
2302 { } /* end */
2303};
2304
Harald Welte69e52a82008-09-09 15:57:32 +08002305static struct hda_verb vt1709_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002306 {0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2307 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002308 { }
2309};
2310
Joseph Chanc577b8a2006-11-29 15:29:40 +01002311/*
2312 * generic initialization of ADC, input mixers and output mixers
2313 */
2314static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2315 /*
2316 * Unmute ADC0-2 and set the default input to mic-in
2317 */
2318 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2319 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2320 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2321
2322
Josepch Chanf7278fd2007-12-13 16:40:40 +01002323 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
Joseph Chanc577b8a2006-11-29 15:29:40 +01002324 * mixer widget
2325 */
2326 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002327 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2328 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2329 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2330 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2331 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002332
2333 /*
2334 * Set up output selector (0x1a, 0x1b, 0x29)
2335 */
2336 /* set vol=0 to output mixers */
2337 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2338 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2339 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2340
2341 /*
2342 * Unmute PW3 and PW4
2343 */
2344 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2345 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2346
Lydia Wangbfdc6752009-10-10 19:08:50 +08002347 /* Set input of PW4 as MW0 */
2348 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002349 /* PW9 Output enable */
2350 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2351 { }
2352};
2353
2354static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2355 .substreams = 1,
2356 .channels_min = 2,
2357 .channels_max = 10,
2358 .nid = 0x10, /* NID to query formats and rates */
2359 .ops = {
2360 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002361 .prepare = via_playback_multi_pcm_prepare,
2362 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002363 },
2364};
2365
2366static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2367 .substreams = 1,
2368 .channels_min = 2,
2369 .channels_max = 6,
2370 .nid = 0x10, /* NID to query formats and rates */
2371 .ops = {
2372 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08002373 .prepare = via_playback_multi_pcm_prepare,
2374 .cleanup = via_playback_multi_pcm_cleanup,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002375 },
2376};
2377
2378static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2379 .substreams = 2,
2380 .channels_min = 2,
2381 .channels_max = 2,
2382 .nid = 0x14, /* NID to query formats and rates */
2383 .ops = {
2384 .prepare = via_capture_pcm_prepare,
2385 .cleanup = via_capture_pcm_cleanup
2386 },
2387};
2388
2389static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2390 .substreams = 1,
2391 .channels_min = 2,
2392 .channels_max = 2,
2393 /* NID is set in via_build_pcms */
2394 .ops = {
2395 .open = via_dig_playback_pcm_open,
2396 .close = via_dig_playback_pcm_close
2397 },
2398};
2399
2400static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2401 .substreams = 1,
2402 .channels_min = 2,
2403 .channels_max = 2,
2404};
2405
2406static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2407 const struct auto_pin_cfg *cfg)
2408{
2409 int i;
2410 hda_nid_t nid;
2411
2412 if (cfg->line_outs == 4) /* 10 channels */
2413 spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2414 else if (cfg->line_outs == 3) /* 6 channels */
2415 spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2416
2417 spec->multiout.dac_nids = spec->private_dac_nids;
2418
2419 if (cfg->line_outs == 4) { /* 10 channels */
2420 for (i = 0; i < cfg->line_outs; i++) {
2421 nid = cfg->line_out_pins[i];
2422 if (nid) {
2423 /* config dac list */
2424 switch (i) {
2425 case AUTO_SEQ_FRONT:
2426 /* AOW0 */
2427 spec->multiout.dac_nids[i] = 0x10;
2428 break;
2429 case AUTO_SEQ_CENLFE:
2430 /* AOW2 */
2431 spec->multiout.dac_nids[i] = 0x12;
2432 break;
2433 case AUTO_SEQ_SURROUND:
2434 /* AOW3 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002435 spec->multiout.dac_nids[i] = 0x11;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002436 break;
2437 case AUTO_SEQ_SIDE:
2438 /* AOW1 */
Harald Weltefb4cb772008-09-09 15:53:36 +08002439 spec->multiout.dac_nids[i] = 0x27;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002440 break;
2441 default:
2442 break;
2443 }
2444 }
2445 }
2446 spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2447
2448 } else if (cfg->line_outs == 3) { /* 6 channels */
Lydia Wang377ff312009-10-10 19:08:55 +08002449 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002450 nid = cfg->line_out_pins[i];
2451 if (nid) {
2452 /* config dac list */
Lydia Wang377ff312009-10-10 19:08:55 +08002453 switch (i) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01002454 case AUTO_SEQ_FRONT:
2455 /* AOW0 */
2456 spec->multiout.dac_nids[i] = 0x10;
2457 break;
2458 case AUTO_SEQ_CENLFE:
2459 /* AOW2 */
2460 spec->multiout.dac_nids[i] = 0x12;
2461 break;
2462 case AUTO_SEQ_SURROUND:
2463 /* AOW1 */
2464 spec->multiout.dac_nids[i] = 0x11;
2465 break;
2466 default:
2467 break;
2468 }
2469 }
2470 }
2471 }
2472
2473 return 0;
2474}
2475
2476/* add playback controls from the parsed DAC table */
2477static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2478 const struct auto_pin_cfg *cfg)
2479{
2480 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01002481 static const char * const chname[4] = {
2482 "Front", "Surround", "C/LFE", "Side"
2483 };
Lydia Wang4483a2f2009-10-10 19:08:29 +08002484 hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
Joseph Chanc577b8a2006-11-29 15:29:40 +01002485 int i, err;
2486
2487 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2488 nid = cfg->line_out_pins[i];
2489
Lydia Wang377ff312009-10-10 19:08:55 +08002490 if (!nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002491 continue;
2492
Lydia Wang4483a2f2009-10-10 19:08:29 +08002493 nid_vol = nid_vols[i];
2494
Joseph Chanc577b8a2006-11-29 15:29:40 +01002495 if (i == AUTO_SEQ_CENLFE) {
2496 /* Center/LFE */
2497 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2498 "Center Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002499 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002500 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002501 if (err < 0)
2502 return err;
2503 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2504 "LFE Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002505 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002506 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002507 if (err < 0)
2508 return err;
2509 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2510 "Center Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002511 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002512 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002513 if (err < 0)
2514 return err;
2515 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2516 "LFE Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002517 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002518 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002519 if (err < 0)
2520 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002521 } else if (i == AUTO_SEQ_FRONT) {
Lydia Wang4483a2f2009-10-10 19:08:29 +08002522 /* ADD control to mixer index 0 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002523 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2524 "Master Front Playback Volume",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002525 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002526 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 if (err < 0)
2528 return err;
2529 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2530 "Master Front Playback Switch",
Lydia Wang4483a2f2009-10-10 19:08:29 +08002531 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002532 HDA_INPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002533 if (err < 0)
2534 return err;
Lydia Wang377ff312009-10-10 19:08:55 +08002535
Joseph Chanc577b8a2006-11-29 15:29:40 +01002536 /* add control to PW3 */
2537 sprintf(name, "%s Playback Volume", chname[i]);
2538 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002539 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2540 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002541 if (err < 0)
2542 return err;
2543 sprintf(name, "%s Playback Switch", chname[i]);
2544 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002545 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2546 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002547 if (err < 0)
2548 return err;
2549 } else if (i == AUTO_SEQ_SURROUND) {
2550 sprintf(name, "%s Playback Volume", chname[i]);
2551 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002552 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002553 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002554 if (err < 0)
2555 return err;
2556 sprintf(name, "%s Playback Switch", chname[i]);
2557 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002558 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002559 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002560 if (err < 0)
2561 return err;
2562 } else if (i == AUTO_SEQ_SIDE) {
2563 sprintf(name, "%s Playback Volume", chname[i]);
2564 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002565 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002566 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002567 if (err < 0)
2568 return err;
2569 sprintf(name, "%s Playback Switch", chname[i]);
2570 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Lydia Wang4483a2f2009-10-10 19:08:29 +08002571 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002572 HDA_OUTPUT));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002573 if (err < 0)
2574 return err;
2575 }
2576 }
2577
2578 return 0;
2579}
2580
2581static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2582{
2583 int err;
2584
2585 if (!pin)
2586 return 0;
2587
2588 if (spec->multiout.num_dacs == 5) /* 10 channels */
2589 spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2590 else if (spec->multiout.num_dacs == 3) /* 6 channels */
2591 spec->multiout.hp_nid = 0;
Lydia Wangcdc17842009-10-10 19:07:47 +08002592 spec->hp_independent_mode_index = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002593
2594 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2595 "Headphone Playback Volume",
2596 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2597 if (err < 0)
2598 return err;
2599 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2600 "Headphone Playback Switch",
2601 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2602 if (err < 0)
2603 return err;
2604
2605 return 0;
2606}
2607
2608/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02002609static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
Joseph Chanc577b8a2006-11-29 15:29:40 +01002610 const struct auto_pin_cfg *cfg)
2611{
Takashi Iwaif3268512010-08-30 11:00:19 +02002612 static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
Takashi Iwai10a20af2010-09-09 16:28:02 +02002613 return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02002614 ARRAY_SIZE(pin_idxs));
Joseph Chanc577b8a2006-11-29 15:29:40 +01002615}
2616
2617static int vt1709_parse_auto_config(struct hda_codec *codec)
2618{
2619 struct via_spec *spec = codec->spec;
2620 int err;
2621
2622 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2623 if (err < 0)
2624 return err;
2625 err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2626 if (err < 0)
2627 return err;
2628 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2629 return 0; /* can't find valid BIOS pin config */
2630
2631 err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2632 if (err < 0)
2633 return err;
2634 err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2635 if (err < 0)
2636 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02002637 err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002638 if (err < 0)
2639 return err;
2640
2641 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2642
Takashi Iwai0852d7a2009-02-11 11:35:15 +01002643 if (spec->autocfg.dig_outs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002644 spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02002645 spec->dig_in_pin = VT1709_DIGIN_PIN;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002646 if (spec->autocfg.dig_in_pin)
2647 spec->dig_in_nid = VT1709_DIGIN_NID;
2648
Takashi Iwai603c4012008-07-30 15:01:44 +02002649 if (spec->kctls.list)
2650 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002651
Harald Welte0aa62ae2008-09-09 15:58:27 +08002652 spec->input_mux = &spec->private_imux[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01002653
Harald Weltef8fdd492008-09-15 22:41:31 +08002654 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02002655 via_hp_build(codec);
Harald Weltef8fdd492008-09-15 22:41:31 +08002656
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002657 via_smart51_build(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002658 return 1;
2659}
2660
Takashi Iwaicb53c622007-08-10 17:21:45 +02002661#ifdef CONFIG_SND_HDA_POWER_SAVE
2662static struct hda_amp_list vt1709_loopbacks[] = {
2663 { 0x18, HDA_INPUT, 1 },
2664 { 0x18, HDA_INPUT, 2 },
2665 { 0x18, HDA_INPUT, 3 },
2666 { 0x18, HDA_INPUT, 4 },
2667 { } /* end */
2668};
2669#endif
2670
Joseph Chanc577b8a2006-11-29 15:29:40 +01002671static int patch_vt1709_10ch(struct hda_codec *codec)
2672{
2673 struct via_spec *spec;
2674 int err;
2675
2676 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002677 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002678 if (spec == NULL)
2679 return -ENOMEM;
2680
Joseph Chanc577b8a2006-11-29 15:29:40 +01002681 err = vt1709_parse_auto_config(codec);
2682 if (err < 0) {
2683 via_free(codec);
2684 return err;
2685 } else if (!err) {
2686 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2687 "Using genenic mode...\n");
2688 }
2689
Harald Welte69e52a82008-09-09 15:57:32 +08002690 spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
2691 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002692
2693 spec->stream_name_analog = "VT1709 Analog";
2694 spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2695 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2696
2697 spec->stream_name_digital = "VT1709 Digital";
2698 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2699 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2700
Lydia Wang377ff312009-10-10 19:08:55 +08002701
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702 if (!spec->adc_nids && spec->input_mux) {
2703 spec->adc_nids = vt1709_adc_nids;
2704 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002705 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002706 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2707 spec->num_mixers++;
2708 }
2709
2710 codec->patch_ops = via_patch_ops;
2711
2712 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002713 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002714#ifdef CONFIG_SND_HDA_POWER_SAVE
2715 spec->loopback.amplist = vt1709_loopbacks;
2716#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002717
2718 return 0;
2719}
2720/*
2721 * generic initialization of ADC, input mixers and output mixers
2722 */
2723static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2724 /*
2725 * Unmute ADC0-2 and set the default input to mic-in
2726 */
2727 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2728 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2729 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2730
2731
2732 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2733 * mixer widget
2734 */
2735 /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2736 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2737 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2738 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2739 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2740 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2741
2742 /*
2743 * Set up output selector (0x1a, 0x1b, 0x29)
2744 */
2745 /* set vol=0 to output mixers */
2746 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2747 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2748 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2749
2750 /*
2751 * Unmute PW3 and PW4
2752 */
2753 {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2754 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2755
2756 /* Set input of PW4 as MW0 */
2757 {0x20, AC_VERB_SET_CONNECT_SEL, 0},
Joseph Chanc577b8a2006-11-29 15:29:40 +01002758 /* PW9 Output enable */
2759 {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2760 { }
2761};
2762
2763static int patch_vt1709_6ch(struct hda_codec *codec)
2764{
2765 struct via_spec *spec;
2766 int err;
2767
2768 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002769 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002770 if (spec == NULL)
2771 return -ENOMEM;
2772
Joseph Chanc577b8a2006-11-29 15:29:40 +01002773 err = vt1709_parse_auto_config(codec);
2774 if (err < 0) {
2775 via_free(codec);
2776 return err;
2777 } else if (!err) {
2778 printk(KERN_INFO "hda_codec: Cannot set up configuration. "
2779 "Using genenic mode...\n");
2780 }
2781
Harald Welte69e52a82008-09-09 15:57:32 +08002782 spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
2783 spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002784
2785 spec->stream_name_analog = "VT1709 Analog";
2786 spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2787 spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2788
2789 spec->stream_name_digital = "VT1709 Digital";
2790 spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2791 spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2792
Lydia Wang377ff312009-10-10 19:08:55 +08002793
Joseph Chanc577b8a2006-11-29 15:29:40 +01002794 if (!spec->adc_nids && spec->input_mux) {
2795 spec->adc_nids = vt1709_adc_nids;
2796 spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02002797 get_mux_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002798 spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2799 spec->num_mixers++;
2800 }
2801
2802 codec->patch_ops = via_patch_ops;
2803
2804 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08002805 codec->patch_ops.unsol_event = via_unsol_event;
Takashi Iwaicb53c622007-08-10 17:21:45 +02002806#ifdef CONFIG_SND_HDA_POWER_SAVE
2807 spec->loopback.amplist = vt1709_loopbacks;
2808#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002809 return 0;
2810}
2811
2812/* capture mixer elements */
2813static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2814 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2815 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2816 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2817 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2818 {
2819 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2820 /* The multiple "Capture Source" controls confuse alsamixer
2821 * So call somewhat different..
Josepch Chanf7278fd2007-12-13 16:40:40 +01002822 */
2823 /* .name = "Capture Source", */
2824 .name = "Input Source",
2825 .count = 1,
2826 .info = via_mux_enum_info,
2827 .get = via_mux_enum_get,
2828 .put = via_mux_enum_put,
2829 },
2830 { } /* end */
2831};
2832/*
2833 * generic initialization of ADC, input mixers and output mixers
2834 */
2835static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2836 /*
2837 * Unmute ADC0-1 and set the default input to mic-in
2838 */
2839 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2840 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2841
2842
2843 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2844 * mixer widget
2845 */
2846 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2847 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2848 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2849 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2850 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2851 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2852
2853 /*
2854 * Set up output mixers
2855 */
2856 /* set vol=0 to output mixers */
2857 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2858 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2859 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2860
2861 /* Setup default input to PW4 */
Lydia Wangbfdc6752009-10-10 19:08:50 +08002862 {0x1d, AC_VERB_SET_CONNECT_SEL, 0},
Josepch Chanf7278fd2007-12-13 16:40:40 +01002863 /* PW9 Output enable */
2864 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2865 /* PW10 Input enable */
2866 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2867 { }
2868};
2869
2870static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2871 /*
2872 * Unmute ADC0-1 and set the default input to mic-in
2873 */
2874 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2875 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2876
2877
2878 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2879 * mixer widget
2880 */
2881 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2882 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2883 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2884 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2885 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2886 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2887
2888 /*
2889 * Set up output mixers
2890 */
2891 /* set vol=0 to output mixers */
2892 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2893 {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2894 {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2895
2896 /* Setup default input of PW4 to MW0 */
2897 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2898 /* PW9 Output enable */
2899 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2900 /* PW10 Input enable */
2901 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2902 { }
2903};
2904
Harald Welte69e52a82008-09-09 15:57:32 +08002905static struct hda_verb vt1708B_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08002906 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2907 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2908 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2909 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2910 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2911 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2912 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2913 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2914 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08002915 { }
2916};
2917
Lydia Wang17314372009-10-10 19:07:37 +08002918static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
2919 struct hda_codec *codec,
2920 struct snd_pcm_substream *substream)
2921{
2922 int idle = substream->pstr->substream_opened == 1
2923 && substream->ref_count == 0;
2924
2925 analog_low_current_mode(codec, idle);
2926 return 0;
2927}
2928
Josepch Chanf7278fd2007-12-13 16:40:40 +01002929static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002930 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002931 .channels_min = 2,
2932 .channels_max = 8,
2933 .nid = 0x10, /* NID to query formats and rates */
2934 .ops = {
2935 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002936 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002937 .cleanup = via_playback_multi_pcm_cleanup,
2938 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002939 },
2940};
2941
2942static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08002943 .substreams = 2,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002944 .channels_min = 2,
2945 .channels_max = 4,
2946 .nid = 0x10, /* NID to query formats and rates */
2947 .ops = {
2948 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08002949 .prepare = via_playback_multi_pcm_prepare,
2950 .cleanup = via_playback_multi_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002951 },
2952};
2953
2954static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2955 .substreams = 2,
2956 .channels_min = 2,
2957 .channels_max = 2,
2958 .nid = 0x13, /* NID to query formats and rates */
2959 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08002960 .open = via_pcm_open_close,
Josepch Chanf7278fd2007-12-13 16:40:40 +01002961 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08002962 .cleanup = via_capture_pcm_cleanup,
2963 .close = via_pcm_open_close
Josepch Chanf7278fd2007-12-13 16:40:40 +01002964 },
2965};
2966
2967static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2968 .substreams = 1,
2969 .channels_min = 2,
2970 .channels_max = 2,
2971 /* NID is set in via_build_pcms */
2972 .ops = {
2973 .open = via_dig_playback_pcm_open,
2974 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02002975 .prepare = via_dig_playback_pcm_prepare,
2976 .cleanup = via_dig_playback_pcm_cleanup
Josepch Chanf7278fd2007-12-13 16:40:40 +01002977 },
2978};
2979
2980static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2981 .substreams = 1,
2982 .channels_min = 2,
2983 .channels_max = 2,
2984};
2985
2986/* fill in the dac_nids table from the parsed pin configuration */
2987static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2988 const struct auto_pin_cfg *cfg)
2989{
2990 int i;
2991 hda_nid_t nid;
2992
2993 spec->multiout.num_dacs = cfg->line_outs;
2994
2995 spec->multiout.dac_nids = spec->private_dac_nids;
2996
2997 for (i = 0; i < 4; i++) {
2998 nid = cfg->line_out_pins[i];
2999 if (nid) {
3000 /* config dac list */
3001 switch (i) {
3002 case AUTO_SEQ_FRONT:
3003 spec->multiout.dac_nids[i] = 0x10;
3004 break;
3005 case AUTO_SEQ_CENLFE:
3006 spec->multiout.dac_nids[i] = 0x24;
3007 break;
3008 case AUTO_SEQ_SURROUND:
Harald Weltefb4cb772008-09-09 15:53:36 +08003009 spec->multiout.dac_nids[i] = 0x11;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003010 break;
3011 case AUTO_SEQ_SIDE:
Harald Weltefb4cb772008-09-09 15:53:36 +08003012 spec->multiout.dac_nids[i] = 0x25;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003013 break;
3014 }
3015 }
3016 }
3017
3018 return 0;
3019}
3020
3021/* add playback controls from the parsed DAC table */
3022static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3023 const struct auto_pin_cfg *cfg)
3024{
3025 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01003026 static const char * const chname[4] = {
3027 "Front", "Surround", "C/LFE", "Side"
3028 };
Harald Weltefb4cb772008-09-09 15:53:36 +08003029 hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
Josepch Chanf7278fd2007-12-13 16:40:40 +01003030 hda_nid_t nid, nid_vol = 0;
3031 int i, err;
3032
3033 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3034 nid = cfg->line_out_pins[i];
3035
3036 if (!nid)
3037 continue;
3038
3039 nid_vol = nid_vols[i];
3040
3041 if (i == AUTO_SEQ_CENLFE) {
3042 /* Center/LFE */
3043 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3044 "Center Playback Volume",
3045 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3046 HDA_OUTPUT));
3047 if (err < 0)
3048 return err;
3049 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3050 "LFE Playback Volume",
3051 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3052 HDA_OUTPUT));
3053 if (err < 0)
3054 return err;
3055 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3056 "Center Playback Switch",
3057 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3058 HDA_OUTPUT));
3059 if (err < 0)
3060 return err;
3061 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3062 "LFE Playback Switch",
3063 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3064 HDA_OUTPUT));
3065 if (err < 0)
3066 return err;
3067 } else if (i == AUTO_SEQ_FRONT) {
3068 /* add control to mixer index 0 */
3069 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3070 "Master Front Playback Volume",
3071 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3072 HDA_INPUT));
3073 if (err < 0)
3074 return err;
3075 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3076 "Master Front Playback Switch",
3077 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3078 HDA_INPUT));
3079 if (err < 0)
3080 return err;
3081
3082 /* add control to PW3 */
3083 sprintf(name, "%s Playback Volume", chname[i]);
3084 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3085 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3086 HDA_OUTPUT));
3087 if (err < 0)
3088 return err;
3089 sprintf(name, "%s Playback Switch", chname[i]);
3090 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3091 HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3092 HDA_OUTPUT));
3093 if (err < 0)
3094 return err;
3095 } else {
3096 sprintf(name, "%s Playback Volume", chname[i]);
3097 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3098 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3099 HDA_OUTPUT));
3100 if (err < 0)
3101 return err;
3102 sprintf(name, "%s Playback Switch", chname[i]);
3103 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3104 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3105 HDA_OUTPUT));
3106 if (err < 0)
3107 return err;
3108 }
3109 }
3110
3111 return 0;
3112}
3113
3114static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3115{
3116 int err;
3117
3118 if (!pin)
3119 return 0;
3120
3121 spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08003122 spec->hp_independent_mode_index = 1;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003123
3124 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3125 "Headphone Playback Volume",
3126 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3127 if (err < 0)
3128 return err;
3129 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3130 "Headphone Playback Switch",
3131 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3132 if (err < 0)
3133 return err;
3134
Harald Welte0aa62ae2008-09-09 15:58:27 +08003135 create_hp_imux(spec);
3136
Josepch Chanf7278fd2007-12-13 16:40:40 +01003137 return 0;
3138}
3139
3140/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02003141static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
Josepch Chanf7278fd2007-12-13 16:40:40 +01003142 const struct auto_pin_cfg *cfg)
3143{
Takashi Iwaif3268512010-08-30 11:00:19 +02003144 static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
Takashi Iwai10a20af2010-09-09 16:28:02 +02003145 return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02003146 ARRAY_SIZE(pin_idxs));
Josepch Chanf7278fd2007-12-13 16:40:40 +01003147}
3148
3149static int vt1708B_parse_auto_config(struct hda_codec *codec)
3150{
3151 struct via_spec *spec = codec->spec;
3152 int err;
3153
3154 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3155 if (err < 0)
3156 return err;
3157 err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3158 if (err < 0)
3159 return err;
3160 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3161 return 0; /* can't find valid BIOS pin config */
3162
3163 err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3164 if (err < 0)
3165 return err;
3166 err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3167 if (err < 0)
3168 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003169 err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003170 if (err < 0)
3171 return err;
3172
3173 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3174
Takashi Iwai0852d7a2009-02-11 11:35:15 +01003175 if (spec->autocfg.dig_outs)
Josepch Chanf7278fd2007-12-13 16:40:40 +01003176 spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
Takashi Iwai55d1d6c2009-07-07 13:39:03 +02003177 spec->dig_in_pin = VT1708B_DIGIN_PIN;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003178 if (spec->autocfg.dig_in_pin)
3179 spec->dig_in_nid = VT1708B_DIGIN_NID;
3180
Takashi Iwai603c4012008-07-30 15:01:44 +02003181 if (spec->kctls.list)
3182 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003183
Harald Welte0aa62ae2008-09-09 15:58:27 +08003184 spec->input_mux = &spec->private_imux[0];
3185
Harald Weltef8fdd492008-09-15 22:41:31 +08003186 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02003187 via_hp_build(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003188
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003189 via_smart51_build(spec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003190 return 1;
3191}
3192
3193#ifdef CONFIG_SND_HDA_POWER_SAVE
3194static struct hda_amp_list vt1708B_loopbacks[] = {
3195 { 0x16, HDA_INPUT, 1 },
3196 { 0x16, HDA_INPUT, 2 },
3197 { 0x16, HDA_INPUT, 3 },
3198 { 0x16, HDA_INPUT, 4 },
3199 { } /* end */
3200};
3201#endif
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003202
3203static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
3204{
3205 struct via_spec *spec = codec->spec;
3206 int imux_is_smixer;
3207 unsigned int parm;
3208 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08003209 if ((spec->codec_type != VT1708B_4CH) &&
3210 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003211 is_8ch = 1;
3212
3213 /* SW0 (17h) = stereo mixer */
3214 imux_is_smixer =
3215 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
3216 == ((spec->codec_type == VT1708S) ? 5 : 0));
3217 /* inputs */
3218 /* PW 1/2/5 (1ah/1bh/1eh) */
3219 parm = AC_PWRST_D3;
3220 set_pin_power_state(codec, 0x1a, &parm);
3221 set_pin_power_state(codec, 0x1b, &parm);
3222 set_pin_power_state(codec, 0x1e, &parm);
3223 if (imux_is_smixer)
3224 parm = AC_PWRST_D0;
3225 /* SW0 (17h), AIW 0/1 (13h/14h) */
3226 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3227 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3228 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3229
3230 /* outputs */
3231 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x19, &parm);
3234 if (spec->smart51_enabled)
3235 set_pin_power_state(codec, 0x1b, &parm);
3236 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3237 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3238
3239 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
3240 if (is_8ch) {
3241 parm = AC_PWRST_D3;
3242 set_pin_power_state(codec, 0x22, &parm);
3243 if (spec->smart51_enabled)
3244 set_pin_power_state(codec, 0x1a, &parm);
3245 snd_hda_codec_write(codec, 0x26, 0,
3246 AC_VERB_SET_POWER_STATE, parm);
3247 snd_hda_codec_write(codec, 0x24, 0,
3248 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08003249 } else if (codec->vendor_id == 0x11064397) {
3250 /* PW7(23h), SW2(27h), AOW2(25h) */
3251 parm = AC_PWRST_D3;
3252 set_pin_power_state(codec, 0x23, &parm);
3253 if (spec->smart51_enabled)
3254 set_pin_power_state(codec, 0x1a, &parm);
3255 snd_hda_codec_write(codec, 0x27, 0,
3256 AC_VERB_SET_POWER_STATE, parm);
3257 snd_hda_codec_write(codec, 0x25, 0,
3258 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003259 }
3260
3261 /* PW 3/4/7 (1ch/1dh/23h) */
3262 parm = AC_PWRST_D3;
3263 /* force to D0 for internal Speaker */
3264 set_pin_power_state(codec, 0x1c, &parm);
3265 set_pin_power_state(codec, 0x1d, &parm);
3266 if (is_8ch)
3267 set_pin_power_state(codec, 0x23, &parm);
3268
3269 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
3270 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3271 imux_is_smixer ? AC_PWRST_D0 : parm);
3272 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3273 if (is_8ch) {
3274 snd_hda_codec_write(codec, 0x25, 0,
3275 AC_VERB_SET_POWER_STATE, parm);
3276 snd_hda_codec_write(codec, 0x27, 0,
3277 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08003278 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
3279 snd_hda_codec_write(codec, 0x25, 0,
3280 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003281}
3282
Lydia Wang518bf3b2009-10-10 19:07:29 +08003283static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003284static int patch_vt1708B_8ch(struct hda_codec *codec)
3285{
3286 struct via_spec *spec;
3287 int err;
3288
Lydia Wang518bf3b2009-10-10 19:07:29 +08003289 if (get_codec_type(codec) == VT1708BCE)
3290 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003291 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003292 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003293 if (spec == NULL)
3294 return -ENOMEM;
3295
Josepch Chanf7278fd2007-12-13 16:40:40 +01003296 /* automatic parse from the BIOS config */
3297 err = vt1708B_parse_auto_config(codec);
3298 if (err < 0) {
3299 via_free(codec);
3300 return err;
3301 } else if (!err) {
3302 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3303 "from BIOS. Using genenic mode...\n");
3304 }
3305
Harald Welte69e52a82008-09-09 15:57:32 +08003306 spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
3307 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003308
3309 spec->stream_name_analog = "VT1708B Analog";
3310 spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3311 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3312
3313 spec->stream_name_digital = "VT1708B Digital";
3314 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3315 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3316
3317 if (!spec->adc_nids && spec->input_mux) {
3318 spec->adc_nids = vt1708B_adc_nids;
3319 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003320 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003321 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3322 spec->num_mixers++;
3323 }
3324
3325 codec->patch_ops = via_patch_ops;
3326
3327 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003328 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003329#ifdef CONFIG_SND_HDA_POWER_SAVE
3330 spec->loopback.amplist = vt1708B_loopbacks;
3331#endif
3332
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003333 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3334
Josepch Chanf7278fd2007-12-13 16:40:40 +01003335 return 0;
3336}
3337
3338static int patch_vt1708B_4ch(struct hda_codec *codec)
3339{
3340 struct via_spec *spec;
3341 int err;
3342
3343 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003344 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003345 if (spec == NULL)
3346 return -ENOMEM;
3347
Josepch Chanf7278fd2007-12-13 16:40:40 +01003348 /* automatic parse from the BIOS config */
3349 err = vt1708B_parse_auto_config(codec);
3350 if (err < 0) {
3351 via_free(codec);
3352 return err;
3353 } else if (!err) {
3354 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3355 "from BIOS. Using genenic mode...\n");
3356 }
3357
Harald Welte69e52a82008-09-09 15:57:32 +08003358 spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
3359 spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003360
3361 spec->stream_name_analog = "VT1708B Analog";
3362 spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3363 spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3364
3365 spec->stream_name_digital = "VT1708B Digital";
3366 spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3367 spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3368
3369 if (!spec->adc_nids && spec->input_mux) {
3370 spec->adc_nids = vt1708B_adc_nids;
3371 spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003372 get_mux_nids(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003373 spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3374 spec->num_mixers++;
3375 }
3376
3377 codec->patch_ops = via_patch_ops;
3378
3379 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003380 codec->patch_ops.unsol_event = via_unsol_event;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003381#ifdef CONFIG_SND_HDA_POWER_SAVE
3382 spec->loopback.amplist = vt1708B_loopbacks;
3383#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01003384
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003385 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3386
Joseph Chanc577b8a2006-11-29 15:29:40 +01003387 return 0;
3388}
3389
Harald Welted949cac2008-09-09 15:56:01 +08003390/* Patch for VT1708S */
3391
3392/* capture mixer elements */
3393static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3394 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3395 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3396 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3397 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
Lydia Wang6369bcf2009-10-10 19:08:31 +08003398 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3399 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3400 HDA_INPUT),
Harald Welted949cac2008-09-09 15:56:01 +08003401 {
3402 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3403 /* The multiple "Capture Source" controls confuse alsamixer
3404 * So call somewhat different..
3405 */
3406 /* .name = "Capture Source", */
3407 .name = "Input Source",
3408 .count = 1,
3409 .info = via_mux_enum_info,
3410 .get = via_mux_enum_get,
3411 .put = via_mux_enum_put,
3412 },
3413 { } /* end */
3414};
3415
3416static struct hda_verb vt1708S_volume_init_verbs[] = {
3417 /* Unmute ADC0-1 and set the default input to mic-in */
3418 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3419 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3420
3421 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3422 * analog-loopback mixer widget */
3423 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3424 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3425 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3426 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3427 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3428 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3429
3430 /* Setup default input of PW4 to MW0 */
3431 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
Harald Welte5691ec72008-09-15 22:42:26 +08003432 /* PW9, PW10 Output enable */
Harald Welted949cac2008-09-09 15:56:01 +08003433 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welte5691ec72008-09-15 22:42:26 +08003434 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Harald Welted7426322008-09-15 22:43:23 +08003435 /* Enable Mic Boost Volume backdoor */
3436 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003437 /* don't bybass mixer */
3438 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003439 { }
3440};
3441
Harald Welte69e52a82008-09-09 15:57:32 +08003442static struct hda_verb vt1708S_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003443 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3444 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3445 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3446 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3447 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3448 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3449 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3450 {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3451 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003452 { }
3453};
3454
Lydia Wangbc92df72011-03-23 17:56:05 +08003455static struct hda_verb vt1705_uniwill_init_verbs[] = {
3456 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3457 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3458 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3459 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3460 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3461 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3462 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3463 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3464 { }
3465};
3466
Harald Welted949cac2008-09-09 15:56:01 +08003467static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3468 .substreams = 2,
3469 .channels_min = 2,
3470 .channels_max = 8,
3471 .nid = 0x10, /* NID to query formats and rates */
3472 .ops = {
3473 .open = via_playback_pcm_open,
Lydia Wangc873cc22009-10-10 19:08:21 +08003474 .prepare = via_playback_multi_pcm_prepare,
3475 .cleanup = via_playback_multi_pcm_cleanup,
Lydia Wang17314372009-10-10 19:07:37 +08003476 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003477 },
3478};
3479
Lydia Wangbc92df72011-03-23 17:56:05 +08003480static struct hda_pcm_stream vt1705_pcm_analog_playback = {
3481 .substreams = 2,
3482 .channels_min = 2,
3483 .channels_max = 6,
3484 .nid = 0x10, /* NID to query formats and rates */
3485 .ops = {
3486 .open = via_playback_pcm_open,
3487 .prepare = via_playback_multi_pcm_prepare,
3488 .cleanup = via_playback_multi_pcm_cleanup,
3489 .close = via_pcm_open_close
3490 },
3491};
3492
Harald Welted949cac2008-09-09 15:56:01 +08003493static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3494 .substreams = 2,
3495 .channels_min = 2,
3496 .channels_max = 2,
3497 .nid = 0x13, /* NID to query formats and rates */
3498 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003499 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003500 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003501 .cleanup = via_capture_pcm_cleanup,
3502 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003503 },
3504};
3505
3506static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
Takashi Iwai9da29272009-05-07 16:31:14 +02003507 .substreams = 1,
Harald Welted949cac2008-09-09 15:56:01 +08003508 .channels_min = 2,
3509 .channels_max = 2,
3510 /* NID is set in via_build_pcms */
3511 .ops = {
3512 .open = via_dig_playback_pcm_open,
3513 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003514 .prepare = via_dig_playback_pcm_prepare,
3515 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003516 },
3517};
3518
3519/* fill in the dac_nids table from the parsed pin configuration */
3520static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3521 const struct auto_pin_cfg *cfg)
3522{
3523 int i;
3524 hda_nid_t nid;
3525
3526 spec->multiout.num_dacs = cfg->line_outs;
3527
3528 spec->multiout.dac_nids = spec->private_dac_nids;
3529
3530 for (i = 0; i < 4; i++) {
3531 nid = cfg->line_out_pins[i];
3532 if (nid) {
3533 /* config dac list */
3534 switch (i) {
3535 case AUTO_SEQ_FRONT:
3536 spec->multiout.dac_nids[i] = 0x10;
3537 break;
3538 case AUTO_SEQ_CENLFE:
Lydia Wangbc92df72011-03-23 17:56:05 +08003539 if (spec->codec->vendor_id == 0x11064397)
3540 spec->multiout.dac_nids[i] = 0x25;
3541 else
3542 spec->multiout.dac_nids[i] = 0x24;
Harald Welted949cac2008-09-09 15:56:01 +08003543 break;
3544 case AUTO_SEQ_SURROUND:
3545 spec->multiout.dac_nids[i] = 0x11;
3546 break;
3547 case AUTO_SEQ_SIDE:
3548 spec->multiout.dac_nids[i] = 0x25;
3549 break;
3550 }
3551 }
3552 }
3553
Clemens Ladisch32e01912010-07-12 16:28:50 +02003554 /* for Smart 5.1, line/mic inputs double as output pins */
3555 if (cfg->line_outs == 1) {
3556 spec->multiout.num_dacs = 3;
3557 spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
Lydia Wangbc92df72011-03-23 17:56:05 +08003558 if (spec->codec->vendor_id == 0x11064397)
3559 spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x25;
3560 else
3561 spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
Clemens Ladisch32e01912010-07-12 16:28:50 +02003562 }
3563
Harald Welted949cac2008-09-09 15:56:01 +08003564 return 0;
3565}
3566
3567/* add playback controls from the parsed DAC table */
Lydia Wangbc92df72011-03-23 17:56:05 +08003568static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec,
Harald Welted949cac2008-09-09 15:56:01 +08003569 const struct auto_pin_cfg *cfg)
3570{
Lydia Wangbc92df72011-03-23 17:56:05 +08003571 struct via_spec *spec = codec->spec;
Harald Welted949cac2008-09-09 15:56:01 +08003572 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01003573 static const char * const chname[4] = {
3574 "Front", "Surround", "C/LFE", "Side"
3575 };
Lydia Wangbc92df72011-03-23 17:56:05 +08003576 hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25},
3577 {0x10, 0x11, 0x25, 0} };
3578 hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27},
3579 {0x1C, 0x18, 0x27, 0} };
Harald Welted949cac2008-09-09 15:56:01 +08003580 hda_nid_t nid, nid_vol, nid_mute;
3581 int i, err;
3582
3583 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3584 nid = cfg->line_out_pins[i];
3585
Clemens Ladisch32e01912010-07-12 16:28:50 +02003586 /* for Smart 5.1, there are always at least six channels */
3587 if (!nid && i > AUTO_SEQ_CENLFE)
Harald Welted949cac2008-09-09 15:56:01 +08003588 continue;
3589
Lydia Wangbc92df72011-03-23 17:56:05 +08003590 if (codec->vendor_id == 0x11064397) {
3591 nid_vol = nid_vols[1][i];
3592 nid_mute = nid_mutes[1][i];
3593 } else {
3594 nid_vol = nid_vols[0][i];
3595 nid_mute = nid_mutes[0][i];
3596 }
3597 if (!nid_vol && !nid_mute)
3598 continue;
Harald Welted949cac2008-09-09 15:56:01 +08003599
3600 if (i == AUTO_SEQ_CENLFE) {
3601 /* Center/LFE */
3602 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3603 "Center Playback Volume",
3604 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3605 HDA_OUTPUT));
3606 if (err < 0)
3607 return err;
3608 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3609 "LFE Playback Volume",
3610 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3611 HDA_OUTPUT));
3612 if (err < 0)
3613 return err;
3614 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3615 "Center Playback Switch",
3616 HDA_COMPOSE_AMP_VAL(nid_mute,
3617 1, 0,
3618 HDA_OUTPUT));
3619 if (err < 0)
3620 return err;
3621 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3622 "LFE Playback Switch",
3623 HDA_COMPOSE_AMP_VAL(nid_mute,
3624 2, 0,
3625 HDA_OUTPUT));
3626 if (err < 0)
3627 return err;
3628 } else if (i == AUTO_SEQ_FRONT) {
3629 /* add control to mixer index 0 */
3630 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3631 "Master Front Playback Volume",
3632 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3633 HDA_INPUT));
3634 if (err < 0)
3635 return err;
3636 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3637 "Master Front Playback Switch",
3638 HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3639 HDA_INPUT));
3640 if (err < 0)
3641 return err;
3642
3643 /* Front */
3644 sprintf(name, "%s Playback Volume", chname[i]);
3645 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3646 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3647 HDA_OUTPUT));
3648 if (err < 0)
3649 return err;
3650 sprintf(name, "%s Playback Switch", chname[i]);
3651 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3652 HDA_COMPOSE_AMP_VAL(nid_mute,
3653 3, 0,
3654 HDA_OUTPUT));
3655 if (err < 0)
3656 return err;
3657 } else {
3658 sprintf(name, "%s Playback Volume", chname[i]);
3659 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3660 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3661 HDA_OUTPUT));
3662 if (err < 0)
3663 return err;
3664 sprintf(name, "%s Playback Switch", chname[i]);
3665 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3666 HDA_COMPOSE_AMP_VAL(nid_mute,
3667 3, 0,
3668 HDA_OUTPUT));
3669 if (err < 0)
3670 return err;
3671 }
3672 }
3673
3674 return 0;
3675}
3676
3677static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3678{
3679 int err;
3680
3681 if (!pin)
3682 return 0;
3683
3684 spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
Lydia Wangcdc17842009-10-10 19:07:47 +08003685 spec->hp_independent_mode_index = 1;
Harald Welted949cac2008-09-09 15:56:01 +08003686
3687 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3688 "Headphone Playback Volume",
3689 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3690 if (err < 0)
3691 return err;
3692
3693 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3694 "Headphone Playback Switch",
3695 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3696 if (err < 0)
3697 return err;
3698
Harald Welte0aa62ae2008-09-09 15:58:27 +08003699 create_hp_imux(spec);
3700
Harald Welted949cac2008-09-09 15:56:01 +08003701 return 0;
3702}
3703
3704/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02003705static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
Harald Welted949cac2008-09-09 15:56:01 +08003706 const struct auto_pin_cfg *cfg)
3707{
Takashi Iwaif3268512010-08-30 11:00:19 +02003708 static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
Takashi Iwai10a20af2010-09-09 16:28:02 +02003709 return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02003710 ARRAY_SIZE(pin_idxs));
Harald Welted949cac2008-09-09 15:56:01 +08003711}
3712
Takashi Iwai9da29272009-05-07 16:31:14 +02003713/* fill out digital output widgets; one for master and one for slave outputs */
3714static void fill_dig_outs(struct hda_codec *codec)
3715{
3716 struct via_spec *spec = codec->spec;
3717 int i;
3718
3719 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3720 hda_nid_t nid;
3721 int conn;
3722
3723 nid = spec->autocfg.dig_out_pins[i];
3724 if (!nid)
3725 continue;
3726 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3727 if (conn < 1)
3728 continue;
3729 if (!spec->multiout.dig_out_nid)
3730 spec->multiout.dig_out_nid = nid;
3731 else {
3732 spec->slave_dig_outs[0] = nid;
3733 break; /* at most two dig outs */
3734 }
3735 }
3736}
3737
Harald Welted949cac2008-09-09 15:56:01 +08003738static int vt1708S_parse_auto_config(struct hda_codec *codec)
3739{
3740 struct via_spec *spec = codec->spec;
3741 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003742
Takashi Iwai9da29272009-05-07 16:31:14 +02003743 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08003744 if (err < 0)
3745 return err;
3746 err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3747 if (err < 0)
3748 return err;
3749 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3750 return 0; /* can't find valid BIOS pin config */
3751
Lydia Wangbc92df72011-03-23 17:56:05 +08003752 err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg);
Harald Welted949cac2008-09-09 15:56:01 +08003753 if (err < 0)
3754 return err;
3755 err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3756 if (err < 0)
3757 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02003758 err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
Harald Welted949cac2008-09-09 15:56:01 +08003759 if (err < 0)
3760 return err;
3761
3762 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3763
Takashi Iwai9da29272009-05-07 16:31:14 +02003764 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08003765
Takashi Iwai603c4012008-07-30 15:01:44 +02003766 if (spec->kctls.list)
3767 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08003768
Harald Welte0aa62ae2008-09-09 15:58:27 +08003769 spec->input_mux = &spec->private_imux[0];
3770
Harald Weltef8fdd492008-09-15 22:41:31 +08003771 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02003772 via_hp_build(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003773
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003774 via_smart51_build(spec);
Harald Welted949cac2008-09-09 15:56:01 +08003775 return 1;
3776}
3777
3778#ifdef CONFIG_SND_HDA_POWER_SAVE
3779static struct hda_amp_list vt1708S_loopbacks[] = {
3780 { 0x16, HDA_INPUT, 1 },
3781 { 0x16, HDA_INPUT, 2 },
3782 { 0x16, HDA_INPUT, 3 },
3783 { 0x16, HDA_INPUT, 4 },
3784 { } /* end */
3785};
3786#endif
3787
Lydia Wang6369bcf2009-10-10 19:08:31 +08003788static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3789 int offset, int num_steps, int step_size)
3790{
3791 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3792 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3793 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3794 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3795 (0 << AC_AMPCAP_MUTE_SHIFT));
3796}
3797
Harald Welted949cac2008-09-09 15:56:01 +08003798static int patch_vt1708S(struct hda_codec *codec)
3799{
3800 struct via_spec *spec;
3801 int err;
3802
3803 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003804 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003805 if (spec == NULL)
3806 return -ENOMEM;
3807
Harald Welted949cac2008-09-09 15:56:01 +08003808 /* automatic parse from the BIOS config */
3809 err = vt1708S_parse_auto_config(codec);
3810 if (err < 0) {
3811 via_free(codec);
3812 return err;
3813 } else if (!err) {
3814 printk(KERN_INFO "hda_codec: Cannot set up configuration "
3815 "from BIOS. Using genenic mode...\n");
3816 }
3817
Harald Welte69e52a82008-09-09 15:57:32 +08003818 spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
Lydia Wangbc92df72011-03-23 17:56:05 +08003819 if (codec->vendor_id == 0x11064397)
3820 spec->init_verbs[spec->num_iverbs++] =
3821 vt1705_uniwill_init_verbs;
3822 else
3823 spec->init_verbs[spec->num_iverbs++] =
3824 vt1708S_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003825
Lydia Wang36dd5c42009-10-20 13:18:04 +08003826 if (codec->vendor_id == 0x11060440)
3827 spec->stream_name_analog = "VT1818S Analog";
Lydia Wangbc92df72011-03-23 17:56:05 +08003828 else if (codec->vendor_id == 0x11064397)
3829 spec->stream_name_analog = "VT1705 Analog";
Lydia Wang36dd5c42009-10-20 13:18:04 +08003830 else
3831 spec->stream_name_analog = "VT1708S Analog";
Lydia Wangbc92df72011-03-23 17:56:05 +08003832 if (codec->vendor_id == 0x11064397)
3833 spec->stream_analog_playback = &vt1705_pcm_analog_playback;
3834 else
3835 spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
Harald Welted949cac2008-09-09 15:56:01 +08003836 spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3837
Lydia Wang36dd5c42009-10-20 13:18:04 +08003838 if (codec->vendor_id == 0x11060440)
3839 spec->stream_name_digital = "VT1818S Digital";
Lydia Wangbc92df72011-03-23 17:56:05 +08003840 else if (codec->vendor_id == 0x11064397)
3841 spec->stream_name_digital = "VT1705 Digital";
Lydia Wang36dd5c42009-10-20 13:18:04 +08003842 else
3843 spec->stream_name_digital = "VT1708S Digital";
Harald Welted949cac2008-09-09 15:56:01 +08003844 spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3845
3846 if (!spec->adc_nids && spec->input_mux) {
3847 spec->adc_nids = vt1708S_adc_nids;
3848 spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02003849 get_mux_nids(codec);
Lydia Wang6369bcf2009-10-10 19:08:31 +08003850 override_mic_boost(codec, 0x1a, 0, 3, 40);
3851 override_mic_boost(codec, 0x1e, 0, 3, 40);
Harald Welted949cac2008-09-09 15:56:01 +08003852 spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3853 spec->num_mixers++;
3854 }
3855
3856 codec->patch_ops = via_patch_ops;
3857
3858 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08003859 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08003860#ifdef CONFIG_SND_HDA_POWER_SAVE
3861 spec->loopback.amplist = vt1708S_loopbacks;
3862#endif
3863
Lydia Wang518bf3b2009-10-10 19:07:29 +08003864 /* correct names for VT1708BCE */
3865 if (get_codec_type(codec) == VT1708BCE) {
3866 kfree(codec->chip_name);
3867 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3868 snprintf(codec->bus->card->mixername,
3869 sizeof(codec->bus->card->mixername),
3870 "%s %s", codec->vendor_name, codec->chip_name);
3871 spec->stream_name_analog = "VT1708BCE Analog";
3872 spec->stream_name_digital = "VT1708BCE Digital";
3873 }
Lydia Wang970f6302011-03-22 16:25:56 +08003874 /* correct names for VT1818S */
3875 if (codec->vendor_id == 0x11060440) {
3876 spec->stream_name_analog = "VT1818S Analog";
3877 spec->stream_name_digital = "VT1818S Digital";
3878 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003879 /* correct names for VT1705 */
3880 if (codec->vendor_id == 0x11064397) {
3881 kfree(codec->chip_name);
3882 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3883 snprintf(codec->bus->card->mixername,
3884 sizeof(codec->bus->card->mixername),
3885 "%s %s", codec->vendor_name, codec->chip_name);
3886 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003887 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003888 return 0;
3889}
3890
3891/* Patch for VT1702 */
3892
3893/* capture mixer elements */
3894static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3895 HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3896 HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3897 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3898 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3899 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3900 HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3901 HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3902 HDA_INPUT),
3903 {
3904 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3905 /* The multiple "Capture Source" controls confuse alsamixer
3906 * So call somewhat different..
3907 */
3908 /* .name = "Capture Source", */
3909 .name = "Input Source",
3910 .count = 1,
3911 .info = via_mux_enum_info,
3912 .get = via_mux_enum_get,
3913 .put = via_mux_enum_put,
3914 },
3915 { } /* end */
3916};
3917
3918static struct hda_verb vt1702_volume_init_verbs[] = {
3919 /*
3920 * Unmute ADC0-1 and set the default input to mic-in
3921 */
3922 {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3923 {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3924 {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3925
3926
3927 /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3928 * mixer widget
3929 */
3930 /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3931 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3932 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3933 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3934 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3935 {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3936
3937 /* Setup default input of PW4 to MW0 */
3938 {0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3939 /* PW6 PW7 Output enable */
3940 {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3941 {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003942 /* mixer enable */
3943 {0x1, 0xF88, 0x3},
3944 /* GPIO 0~2 */
3945 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003946 { }
3947};
3948
Harald Welte69e52a82008-09-09 15:57:32 +08003949static struct hda_verb vt1702_uniwill_init_verbs[] = {
Lydia Wanga34df192009-10-10 19:08:01 +08003950 {0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3951 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3952 {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3953 {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3954 {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3955 {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
Harald Welte69e52a82008-09-09 15:57:32 +08003956 { }
3957};
3958
Harald Welted949cac2008-09-09 15:56:01 +08003959static struct hda_pcm_stream vt1702_pcm_analog_playback = {
Harald Welte0aa62ae2008-09-09 15:58:27 +08003960 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003961 .channels_min = 2,
3962 .channels_max = 2,
3963 .nid = 0x10, /* NID to query formats and rates */
3964 .ops = {
3965 .open = via_playback_pcm_open,
Harald Welte0aa62ae2008-09-09 15:58:27 +08003966 .prepare = via_playback_multi_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003967 .cleanup = via_playback_multi_pcm_cleanup,
3968 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003969 },
3970};
3971
3972static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3973 .substreams = 3,
3974 .channels_min = 2,
3975 .channels_max = 2,
3976 .nid = 0x12, /* NID to query formats and rates */
3977 .ops = {
Lydia Wang17314372009-10-10 19:07:37 +08003978 .open = via_pcm_open_close,
Harald Welted949cac2008-09-09 15:56:01 +08003979 .prepare = via_capture_pcm_prepare,
Lydia Wang17314372009-10-10 19:07:37 +08003980 .cleanup = via_capture_pcm_cleanup,
3981 .close = via_pcm_open_close
Harald Welted949cac2008-09-09 15:56:01 +08003982 },
3983};
3984
3985static struct hda_pcm_stream vt1702_pcm_digital_playback = {
Harald Welte5691ec72008-09-15 22:42:26 +08003986 .substreams = 2,
Harald Welted949cac2008-09-09 15:56:01 +08003987 .channels_min = 2,
3988 .channels_max = 2,
3989 /* NID is set in via_build_pcms */
3990 .ops = {
3991 .open = via_dig_playback_pcm_open,
3992 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02003993 .prepare = via_dig_playback_pcm_prepare,
3994 .cleanup = via_dig_playback_pcm_cleanup
Harald Welted949cac2008-09-09 15:56:01 +08003995 },
3996};
3997
3998/* fill in the dac_nids table from the parsed pin configuration */
3999static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4000 const struct auto_pin_cfg *cfg)
4001{
4002 spec->multiout.num_dacs = 1;
4003 spec->multiout.dac_nids = spec->private_dac_nids;
4004
4005 if (cfg->line_out_pins[0]) {
4006 /* config dac list */
4007 spec->multiout.dac_nids[0] = 0x10;
4008 }
4009
4010 return 0;
4011}
4012
4013/* add playback controls from the parsed DAC table */
4014static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4015 const struct auto_pin_cfg *cfg)
4016{
4017 int err;
4018
4019 if (!cfg->line_out_pins[0])
4020 return -1;
4021
4022 /* add control to mixer index 0 */
4023 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4024 "Master Front Playback Volume",
4025 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4026 if (err < 0)
4027 return err;
4028 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4029 "Master Front Playback Switch",
4030 HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4031 if (err < 0)
4032 return err;
4033
4034 /* Front */
4035 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4036 "Front Playback Volume",
4037 HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4038 if (err < 0)
4039 return err;
4040 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4041 "Front Playback Switch",
4042 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4043 if (err < 0)
4044 return err;
4045
4046 return 0;
4047}
4048
4049static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4050{
Lydia Wang0713efe2009-10-10 19:07:43 +08004051 int err, i;
4052 struct hda_input_mux *imux;
Takashi Iwaiea734962011-01-17 11:29:34 +01004053 static const char * const texts[] = { "ON", "OFF", NULL};
Harald Welted949cac2008-09-09 15:56:01 +08004054 if (!pin)
4055 return 0;
Harald Welted949cac2008-09-09 15:56:01 +08004056 spec->multiout.hp_nid = 0x1D;
Lydia Wangcdc17842009-10-10 19:07:47 +08004057 spec->hp_independent_mode_index = 0;
Harald Welted949cac2008-09-09 15:56:01 +08004058
4059 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4060 "Headphone Playback Volume",
4061 HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4062 if (err < 0)
4063 return err;
4064
4065 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4066 "Headphone Playback Switch",
4067 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4068 if (err < 0)
4069 return err;
4070
Lydia Wang0713efe2009-10-10 19:07:43 +08004071 imux = &spec->private_imux[1];
Harald Welte0aa62ae2008-09-09 15:58:27 +08004072
Lydia Wang0713efe2009-10-10 19:07:43 +08004073 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02004074 for (i = 0; texts[i]; i++)
4075 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Lydia Wang0713efe2009-10-10 19:07:43 +08004076
4077 spec->hp_mux = &spec->private_imux[1];
Harald Welted949cac2008-09-09 15:56:01 +08004078 return 0;
4079}
4080
4081/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02004082static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
Harald Welted949cac2008-09-09 15:56:01 +08004083 const struct auto_pin_cfg *cfg)
4084{
Takashi Iwaif3268512010-08-30 11:00:19 +02004085 static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
Takashi Iwai10a20af2010-09-09 16:28:02 +02004086 return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02004087 ARRAY_SIZE(pin_idxs));
Harald Welted949cac2008-09-09 15:56:01 +08004088}
4089
4090static int vt1702_parse_auto_config(struct hda_codec *codec)
4091{
4092 struct via_spec *spec = codec->spec;
4093 int err;
Harald Welted949cac2008-09-09 15:56:01 +08004094
Takashi Iwai9da29272009-05-07 16:31:14 +02004095 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
Harald Welted949cac2008-09-09 15:56:01 +08004096 if (err < 0)
4097 return err;
4098 err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4099 if (err < 0)
4100 return err;
4101 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4102 return 0; /* can't find valid BIOS pin config */
4103
4104 err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4105 if (err < 0)
4106 return err;
4107 err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4108 if (err < 0)
4109 return err;
Lydia Wangc2c02ea2009-10-10 19:07:32 +08004110 /* limit AA path volume to 0 dB */
4111 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4112 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4113 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4114 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4115 (1 << AC_AMPCAP_MUTE_SHIFT));
Takashi Iwai10a20af2010-09-09 16:28:02 +02004116 err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
Harald Welted949cac2008-09-09 15:56:01 +08004117 if (err < 0)
4118 return err;
4119
4120 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4121
Takashi Iwai9da29272009-05-07 16:31:14 +02004122 fill_dig_outs(codec);
Harald Welte98aa34c2008-09-09 16:02:09 +08004123
Takashi Iwai603c4012008-07-30 15:01:44 +02004124 if (spec->kctls.list)
4125 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Harald Welted949cac2008-09-09 15:56:01 +08004126
Harald Welte0aa62ae2008-09-09 15:58:27 +08004127 spec->input_mux = &spec->private_imux[0];
4128
Harald Weltef8fdd492008-09-15 22:41:31 +08004129 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02004130 via_hp_build(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004131
4132 return 1;
4133}
4134
4135#ifdef CONFIG_SND_HDA_POWER_SAVE
4136static struct hda_amp_list vt1702_loopbacks[] = {
4137 { 0x1A, HDA_INPUT, 1 },
4138 { 0x1A, HDA_INPUT, 2 },
4139 { 0x1A, HDA_INPUT, 3 },
4140 { 0x1A, HDA_INPUT, 4 },
4141 { } /* end */
4142};
4143#endif
4144
Lydia Wang3e95b9a2011-03-23 15:13:28 +08004145static void set_widgets_power_state_vt1702(struct hda_codec *codec)
4146{
4147 int imux_is_smixer =
4148 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
4149 unsigned int parm;
4150 /* inputs */
4151 /* PW 1/2/5 (14h/15h/18h) */
4152 parm = AC_PWRST_D3;
4153 set_pin_power_state(codec, 0x14, &parm);
4154 set_pin_power_state(codec, 0x15, &parm);
4155 set_pin_power_state(codec, 0x18, &parm);
4156 if (imux_is_smixer)
4157 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
4158 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
4159 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
4160 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
4161 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
4162 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
4163
4164 /* outputs */
4165 /* PW 3/4 (16h/17h) */
4166 parm = AC_PWRST_D3;
4167 set_pin_power_state(codec, 0x17, &parm);
4168 set_pin_power_state(codec, 0x16, &parm);
4169 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
4170 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
4171 imux_is_smixer ? AC_PWRST_D0 : parm);
4172 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
4173 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
4174}
4175
Harald Welted949cac2008-09-09 15:56:01 +08004176static int patch_vt1702(struct hda_codec *codec)
4177{
4178 struct via_spec *spec;
4179 int err;
Harald Welted949cac2008-09-09 15:56:01 +08004180
4181 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004182 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004183 if (spec == NULL)
4184 return -ENOMEM;
4185
Harald Welted949cac2008-09-09 15:56:01 +08004186 /* automatic parse from the BIOS config */
4187 err = vt1702_parse_auto_config(codec);
4188 if (err < 0) {
4189 via_free(codec);
4190 return err;
4191 } else if (!err) {
4192 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4193 "from BIOS. Using genenic mode...\n");
4194 }
4195
Harald Welte69e52a82008-09-09 15:57:32 +08004196 spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
4197 spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08004198
4199 spec->stream_name_analog = "VT1702 Analog";
4200 spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4201 spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4202
4203 spec->stream_name_digital = "VT1702 Digital";
4204 spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4205
4206 if (!spec->adc_nids && spec->input_mux) {
4207 spec->adc_nids = vt1702_adc_nids;
4208 spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
Takashi Iwai337b9d02009-07-07 18:18:59 +02004209 get_mux_nids(codec);
Harald Welted949cac2008-09-09 15:56:01 +08004210 spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4211 spec->num_mixers++;
4212 }
4213
4214 codec->patch_ops = via_patch_ops;
4215
4216 codec->patch_ops.init = via_auto_init;
Harald Welte69e52a82008-09-09 15:57:32 +08004217 codec->patch_ops.unsol_event = via_unsol_event;
Harald Welted949cac2008-09-09 15:56:01 +08004218#ifdef CONFIG_SND_HDA_POWER_SAVE
4219 spec->loopback.amplist = vt1702_loopbacks;
4220#endif
4221
Lydia Wang3e95b9a2011-03-23 15:13:28 +08004222 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08004223 return 0;
4224}
4225
Lydia Wangeb7188c2009-10-10 19:08:34 +08004226/* Patch for VT1718S */
4227
4228/* capture mixer elements */
4229static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4230 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4231 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4232 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4233 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4234 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4235 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4236 HDA_INPUT),
4237 {
4238 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4239 /* The multiple "Capture Source" controls confuse alsamixer
4240 * So call somewhat different..
4241 */
4242 .name = "Input Source",
4243 .count = 2,
4244 .info = via_mux_enum_info,
4245 .get = via_mux_enum_get,
4246 .put = via_mux_enum_put,
4247 },
4248 { } /* end */
4249};
4250
4251static struct hda_verb vt1718S_volume_init_verbs[] = {
4252 /*
4253 * Unmute ADC0-1 and set the default input to mic-in
4254 */
4255 {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4256 {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4257
Lydia Wang4ab2d532011-03-24 12:42:03 +08004258 /* Enable MW0 adjust Gain 5 */
4259 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08004260 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4261 * mixer widget
4262 */
4263 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4264 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4265 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4266 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4267 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
Lydia Wang4ab2d532011-03-24 12:42:03 +08004268 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
Lydia Wangeb7188c2009-10-10 19:08:34 +08004269
4270 /* Setup default input of Front HP to MW9 */
4271 {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4272 /* PW9 PW10 Output enable */
4273 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4274 {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4275 /* PW11 Input enable */
4276 {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4277 /* Enable Boost Volume backdoor */
4278 {0x1, 0xf88, 0x8},
4279 /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4280 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4281 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4282 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4283 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4284 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4285 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4286 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4287 {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4288 {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4289 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4290 /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4291 {0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4292 {0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4293 /* Unmute MW4's index 0 */
4294 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4295 { }
4296};
4297
4298
4299static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4300 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4301 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4302 {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4303 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4304 {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4305 {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4306 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4307 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4308 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4309 { }
4310};
4311
4312static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4313 .substreams = 2,
4314 .channels_min = 2,
4315 .channels_max = 10,
4316 .nid = 0x8, /* NID to query formats and rates */
4317 .ops = {
4318 .open = via_playback_pcm_open,
4319 .prepare = via_playback_multi_pcm_prepare,
4320 .cleanup = via_playback_multi_pcm_cleanup,
4321 .close = via_pcm_open_close,
4322 },
4323};
4324
4325static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4326 .substreams = 2,
4327 .channels_min = 2,
4328 .channels_max = 2,
4329 .nid = 0x10, /* NID to query formats and rates */
4330 .ops = {
4331 .open = via_pcm_open_close,
4332 .prepare = via_capture_pcm_prepare,
4333 .cleanup = via_capture_pcm_cleanup,
4334 .close = via_pcm_open_close,
4335 },
4336};
4337
4338static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4339 .substreams = 2,
4340 .channels_min = 2,
4341 .channels_max = 2,
Lydia Wangeb7188c2009-10-10 19:08:34 +08004342 /* NID is set in via_build_pcms */
4343 .ops = {
4344 .open = via_dig_playback_pcm_open,
4345 .close = via_dig_playback_pcm_close,
4346 .prepare = via_dig_playback_pcm_prepare,
4347 .cleanup = via_dig_playback_pcm_cleanup
4348 },
4349};
4350
4351static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4352 .substreams = 1,
4353 .channels_min = 2,
4354 .channels_max = 2,
4355};
4356
4357/* fill in the dac_nids table from the parsed pin configuration */
4358static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4359 const struct auto_pin_cfg *cfg)
4360{
4361 int i;
4362 hda_nid_t nid;
4363
4364 spec->multiout.num_dacs = cfg->line_outs;
4365
4366 spec->multiout.dac_nids = spec->private_dac_nids;
4367
4368 for (i = 0; i < 4; i++) {
4369 nid = cfg->line_out_pins[i];
4370 if (nid) {
4371 /* config dac list */
4372 switch (i) {
4373 case AUTO_SEQ_FRONT:
4374 spec->multiout.dac_nids[i] = 0x8;
4375 break;
4376 case AUTO_SEQ_CENLFE:
4377 spec->multiout.dac_nids[i] = 0xa;
4378 break;
4379 case AUTO_SEQ_SURROUND:
4380 spec->multiout.dac_nids[i] = 0x9;
4381 break;
4382 case AUTO_SEQ_SIDE:
4383 spec->multiout.dac_nids[i] = 0xb;
4384 break;
4385 }
4386 }
4387 }
4388
4389 return 0;
4390}
4391
4392/* add playback controls from the parsed DAC table */
4393static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4394 const struct auto_pin_cfg *cfg)
4395{
4396 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01004397 static const char * const chname[4] = {
4398 "Front", "Surround", "C/LFE", "Side"
4399 };
Lydia Wangeb7188c2009-10-10 19:08:34 +08004400 hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4401 hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4402 hda_nid_t nid, nid_vol, nid_mute = 0;
4403 int i, err;
4404
4405 for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4406 nid = cfg->line_out_pins[i];
4407
4408 if (!nid)
4409 continue;
4410 nid_vol = nid_vols[i];
4411 nid_mute = nid_mutes[i];
4412
4413 if (i == AUTO_SEQ_CENLFE) {
4414 /* Center/LFE */
4415 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4416 "Center Playback Volume",
4417 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4418 HDA_OUTPUT));
4419 if (err < 0)
4420 return err;
4421 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4422 "LFE Playback Volume",
4423 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4424 HDA_OUTPUT));
4425 if (err < 0)
4426 return err;
4427 err = via_add_control(
4428 spec, VIA_CTL_WIDGET_MUTE,
4429 "Center Playback Switch",
4430 HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4431 HDA_OUTPUT));
4432 if (err < 0)
4433 return err;
4434 err = via_add_control(
4435 spec, VIA_CTL_WIDGET_MUTE,
4436 "LFE Playback Switch",
4437 HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4438 HDA_OUTPUT));
4439 if (err < 0)
4440 return err;
4441 } else if (i == AUTO_SEQ_FRONT) {
4442 /* Front */
4443 sprintf(name, "%s Playback Volume", chname[i]);
4444 err = via_add_control(
4445 spec, VIA_CTL_WIDGET_VOL, name,
4446 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4447 if (err < 0)
4448 return err;
4449 sprintf(name, "%s Playback Switch", chname[i]);
4450 err = via_add_control(
4451 spec, VIA_CTL_WIDGET_MUTE, name,
4452 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4453 HDA_OUTPUT));
4454 if (err < 0)
4455 return err;
4456 } else {
4457 sprintf(name, "%s Playback Volume", chname[i]);
4458 err = via_add_control(
4459 spec, VIA_CTL_WIDGET_VOL, name,
4460 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4461 if (err < 0)
4462 return err;
4463 sprintf(name, "%s Playback Switch", chname[i]);
4464 err = via_add_control(
4465 spec, VIA_CTL_WIDGET_MUTE, name,
4466 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4467 HDA_OUTPUT));
4468 if (err < 0)
4469 return err;
4470 }
4471 }
4472 return 0;
4473}
4474
4475static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4476{
4477 int err;
4478
4479 if (!pin)
4480 return 0;
4481
4482 spec->multiout.hp_nid = 0xc; /* AOW4 */
4483 spec->hp_independent_mode_index = 1;
4484
4485 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4486 "Headphone Playback Volume",
4487 HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4488 if (err < 0)
4489 return err;
4490
4491 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4492 "Headphone Playback Switch",
4493 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4494 if (err < 0)
4495 return err;
4496
4497 create_hp_imux(spec);
4498 return 0;
4499}
4500
4501/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02004502static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
Lydia Wangeb7188c2009-10-10 19:08:34 +08004503 const struct auto_pin_cfg *cfg)
4504{
Takashi Iwaif3268512010-08-30 11:00:19 +02004505 static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
Takashi Iwai10a20af2010-09-09 16:28:02 +02004506 return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02004507 ARRAY_SIZE(pin_idxs));
Lydia Wangeb7188c2009-10-10 19:08:34 +08004508}
4509
4510static int vt1718S_parse_auto_config(struct hda_codec *codec)
4511{
4512 struct via_spec *spec = codec->spec;
4513 int err;
4514
4515 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4516
4517 if (err < 0)
4518 return err;
4519 err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4520 if (err < 0)
4521 return err;
4522 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4523 return 0; /* can't find valid BIOS pin config */
4524
4525 err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4526 if (err < 0)
4527 return err;
4528 err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4529 if (err < 0)
4530 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02004531 err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004532 if (err < 0)
4533 return err;
4534
4535 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4536
4537 fill_dig_outs(codec);
4538
4539 if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4540 spec->dig_in_nid = 0x13;
4541
4542 if (spec->kctls.list)
4543 spec->mixers[spec->num_mixers++] = spec->kctls.list;
4544
4545 spec->input_mux = &spec->private_imux[0];
4546
4547 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02004548 via_hp_build(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004549
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004550 via_smart51_build(spec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004551
4552 return 1;
4553}
4554
4555#ifdef CONFIG_SND_HDA_POWER_SAVE
4556static struct hda_amp_list vt1718S_loopbacks[] = {
4557 { 0x21, HDA_INPUT, 1 },
4558 { 0x21, HDA_INPUT, 2 },
4559 { 0x21, HDA_INPUT, 3 },
4560 { 0x21, HDA_INPUT, 4 },
4561 { } /* end */
4562};
4563#endif
4564
Lydia Wang3e95b9a2011-03-23 15:13:28 +08004565static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
4566{
4567 struct via_spec *spec = codec->spec;
4568 int imux_is_smixer;
4569 unsigned int parm;
4570 /* MUX6 (1eh) = stereo mixer */
4571 imux_is_smixer =
4572 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
4573 /* inputs */
4574 /* PW 5/6/7 (29h/2ah/2bh) */
4575 parm = AC_PWRST_D3;
4576 set_pin_power_state(codec, 0x29, &parm);
4577 set_pin_power_state(codec, 0x2a, &parm);
4578 set_pin_power_state(codec, 0x2b, &parm);
4579 if (imux_is_smixer)
4580 parm = AC_PWRST_D0;
4581 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
4582 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
4583 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
4584 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
4585 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
4586
4587 /* outputs */
4588 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
4589 parm = AC_PWRST_D3;
4590 set_pin_power_state(codec, 0x27, &parm);
4591 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
4592 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
4593
4594 /* PW2 (26h), AOW2 (ah) */
4595 parm = AC_PWRST_D3;
4596 set_pin_power_state(codec, 0x26, &parm);
4597 if (spec->smart51_enabled)
4598 set_pin_power_state(codec, 0x2b, &parm);
4599 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
4600
4601 /* PW0 (24h), AOW0 (8h) */
4602 parm = AC_PWRST_D3;
4603 set_pin_power_state(codec, 0x24, &parm);
4604 if (!spec->hp_independent_mode) /* check for redirected HP */
4605 set_pin_power_state(codec, 0x28, &parm);
4606 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
4607 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
4608 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
4609 imux_is_smixer ? AC_PWRST_D0 : parm);
4610
4611 /* PW1 (25h), AOW1 (9h) */
4612 parm = AC_PWRST_D3;
4613 set_pin_power_state(codec, 0x25, &parm);
4614 if (spec->smart51_enabled)
4615 set_pin_power_state(codec, 0x2a, &parm);
4616 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
4617
4618 if (spec->hp_independent_mode) {
4619 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
4620 parm = AC_PWRST_D3;
4621 set_pin_power_state(codec, 0x28, &parm);
4622 snd_hda_codec_write(codec, 0x1b, 0,
4623 AC_VERB_SET_POWER_STATE, parm);
4624 snd_hda_codec_write(codec, 0x34, 0,
4625 AC_VERB_SET_POWER_STATE, parm);
4626 snd_hda_codec_write(codec, 0xc, 0,
4627 AC_VERB_SET_POWER_STATE, parm);
4628 }
4629}
4630
Lydia Wangeb7188c2009-10-10 19:08:34 +08004631static int patch_vt1718S(struct hda_codec *codec)
4632{
4633 struct via_spec *spec;
4634 int err;
4635
4636 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004637 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004638 if (spec == NULL)
4639 return -ENOMEM;
4640
Lydia Wangeb7188c2009-10-10 19:08:34 +08004641 /* automatic parse from the BIOS config */
4642 err = vt1718S_parse_auto_config(codec);
4643 if (err < 0) {
4644 via_free(codec);
4645 return err;
4646 } else if (!err) {
4647 printk(KERN_INFO "hda_codec: Cannot set up configuration "
4648 "from BIOS. Using genenic mode...\n");
4649 }
4650
4651 spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4652 spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4653
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004654 if (codec->vendor_id == 0x11060441)
4655 spec->stream_name_analog = "VT2020 Analog";
4656 else if (codec->vendor_id == 0x11064441)
4657 spec->stream_name_analog = "VT1828S Analog";
4658 else
4659 spec->stream_name_analog = "VT1718S Analog";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004660 spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4661 spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4662
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004663 if (codec->vendor_id == 0x11060441)
4664 spec->stream_name_digital = "VT2020 Digital";
4665 else if (codec->vendor_id == 0x11064441)
4666 spec->stream_name_digital = "VT1828S Digital";
4667 else
4668 spec->stream_name_digital = "VT1718S Digital";
Lydia Wangeb7188c2009-10-10 19:08:34 +08004669 spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004670 if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
Lydia Wangeb7188c2009-10-10 19:08:34 +08004671 spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4672
4673 if (!spec->adc_nids && spec->input_mux) {
4674 spec->adc_nids = vt1718S_adc_nids;
4675 spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4676 get_mux_nids(codec);
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08004677 override_mic_boost(codec, 0x2b, 0, 3, 40);
4678 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangeb7188c2009-10-10 19:08:34 +08004679 spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4680 spec->num_mixers++;
4681 }
4682
4683 codec->patch_ops = via_patch_ops;
4684
4685 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11004686 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangeb7188c2009-10-10 19:08:34 +08004687
4688#ifdef CONFIG_SND_HDA_POWER_SAVE
4689 spec->loopback.amplist = vt1718S_loopbacks;
4690#endif
4691
Lydia Wang3e95b9a2011-03-23 15:13:28 +08004692 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
4693
Lydia Wangeb7188c2009-10-10 19:08:34 +08004694 return 0;
4695}
Lydia Wangf3db4232009-10-10 19:08:41 +08004696
4697/* Patch for VT1716S */
4698
4699static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4700 struct snd_ctl_elem_info *uinfo)
4701{
4702 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4703 uinfo->count = 1;
4704 uinfo->value.integer.min = 0;
4705 uinfo->value.integer.max = 1;
4706 return 0;
4707}
4708
4709static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4710 struct snd_ctl_elem_value *ucontrol)
4711{
4712 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4713 int index = 0;
4714
4715 index = snd_hda_codec_read(codec, 0x26, 0,
4716 AC_VERB_GET_CONNECT_SEL, 0);
4717 if (index != -1)
4718 *ucontrol->value.integer.value = index;
4719
4720 return 0;
4721}
4722
4723static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4724 struct snd_ctl_elem_value *ucontrol)
4725{
4726 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4727 struct via_spec *spec = codec->spec;
4728 int index = *ucontrol->value.integer.value;
4729
4730 snd_hda_codec_write(codec, 0x26, 0,
4731 AC_VERB_SET_CONNECT_SEL, index);
4732 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08004733 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08004734 return 1;
4735}
4736
4737/* capture mixer elements */
4738static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4739 HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4740 HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4741 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4742 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4743 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4744 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4745 HDA_INPUT),
4746 {
4747 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4748 .name = "Input Source",
4749 .count = 1,
4750 .info = via_mux_enum_info,
4751 .get = via_mux_enum_get,
4752 .put = via_mux_enum_put,
4753 },
4754 { } /* end */
4755};
4756
4757static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4758 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4759 {
4760 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4761 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01004762 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08004763 .count = 1,
4764 .info = vt1716s_dmic_info,
4765 .get = vt1716s_dmic_get,
4766 .put = vt1716s_dmic_put,
4767 },
4768 {} /* end */
4769};
4770
4771
4772/* mono-out mixer elements */
4773static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4774 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4775 { } /* end */
4776};
4777
4778static struct hda_verb vt1716S_volume_init_verbs[] = {
4779 /*
4780 * Unmute ADC0-1 and set the default input to mic-in
4781 */
4782 {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4783 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4784
4785
4786 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4787 * mixer widget
4788 */
4789 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4790 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4791 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4792 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4793 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4794 {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4795
4796 /* MUX Indices: Stereo Mixer = 5 */
4797 {0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4798
4799 /* Setup default input of PW4 to MW0 */
4800 {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4801
4802 /* Setup default input of SW1 as MW0 */
4803 {0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4804
4805 /* Setup default input of SW4 as AOW0 */
4806 {0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4807
4808 /* PW9 PW10 Output enable */
4809 {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4810 {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4811
4812 /* Unmute SW1, PW12 */
4813 {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4814 {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4815 /* PW12 Output enable */
4816 {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4817 /* Enable Boost Volume backdoor */
4818 {0x1, 0xf8a, 0x80},
4819 /* don't bybass mixer */
4820 {0x1, 0xf88, 0xc0},
4821 /* Enable mono output */
4822 {0x1, 0xf90, 0x08},
4823 { }
4824};
4825
4826
4827static struct hda_verb vt1716S_uniwill_init_verbs[] = {
4828 {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4829 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4830 {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4831 {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4832 {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4833 {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4834 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4835 {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4836 {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4837 { }
4838};
4839
4840static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4841 .substreams = 2,
4842 .channels_min = 2,
4843 .channels_max = 6,
4844 .nid = 0x10, /* NID to query formats and rates */
4845 .ops = {
4846 .open = via_playback_pcm_open,
4847 .prepare = via_playback_multi_pcm_prepare,
4848 .cleanup = via_playback_multi_pcm_cleanup,
4849 .close = via_pcm_open_close,
4850 },
4851};
4852
4853static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4854 .substreams = 2,
4855 .channels_min = 2,
4856 .channels_max = 2,
4857 .nid = 0x13, /* NID to query formats and rates */
4858 .ops = {
4859 .open = via_pcm_open_close,
4860 .prepare = via_capture_pcm_prepare,
4861 .cleanup = via_capture_pcm_cleanup,
4862 .close = via_pcm_open_close,
4863 },
4864};
4865
4866static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4867 .substreams = 2,
4868 .channels_min = 2,
4869 .channels_max = 2,
Lydia Wangf3db4232009-10-10 19:08:41 +08004870 /* NID is set in via_build_pcms */
4871 .ops = {
4872 .open = via_dig_playback_pcm_open,
4873 .close = via_dig_playback_pcm_close,
4874 .prepare = via_dig_playback_pcm_prepare,
4875 .cleanup = via_dig_playback_pcm_cleanup
4876 },
4877};
4878
4879/* fill in the dac_nids table from the parsed pin configuration */
4880static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
4881 const struct auto_pin_cfg *cfg)
4882{ int i;
4883 hda_nid_t nid;
4884
4885 spec->multiout.num_dacs = cfg->line_outs;
4886
4887 spec->multiout.dac_nids = spec->private_dac_nids;
4888
4889 for (i = 0; i < 3; i++) {
4890 nid = cfg->line_out_pins[i];
4891 if (nid) {
4892 /* config dac list */
4893 switch (i) {
4894 case AUTO_SEQ_FRONT:
4895 spec->multiout.dac_nids[i] = 0x10;
4896 break;
4897 case AUTO_SEQ_CENLFE:
4898 spec->multiout.dac_nids[i] = 0x25;
4899 break;
4900 case AUTO_SEQ_SURROUND:
4901 spec->multiout.dac_nids[i] = 0x11;
4902 break;
4903 }
4904 }
4905 }
4906
4907 return 0;
4908}
4909
4910/* add playback controls from the parsed DAC table */
4911static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
4912 const struct auto_pin_cfg *cfg)
4913{
4914 char name[32];
Takashi Iwaiea734962011-01-17 11:29:34 +01004915 static const char * const chname[3] = {
4916 "Front", "Surround", "C/LFE"
4917 };
Lydia Wangf3db4232009-10-10 19:08:41 +08004918 hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
4919 hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
4920 hda_nid_t nid, nid_vol, nid_mute;
4921 int i, err;
4922
4923 for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
4924 nid = cfg->line_out_pins[i];
4925
4926 if (!nid)
4927 continue;
4928
4929 nid_vol = nid_vols[i];
4930 nid_mute = nid_mutes[i];
4931
4932 if (i == AUTO_SEQ_CENLFE) {
4933 err = via_add_control(
4934 spec, VIA_CTL_WIDGET_VOL,
4935 "Center Playback Volume",
4936 HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
4937 if (err < 0)
4938 return err;
4939 err = via_add_control(
4940 spec, VIA_CTL_WIDGET_VOL,
4941 "LFE Playback Volume",
4942 HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
4943 if (err < 0)
4944 return err;
4945 err = via_add_control(
4946 spec, VIA_CTL_WIDGET_MUTE,
4947 "Center Playback Switch",
4948 HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4949 HDA_OUTPUT));
4950 if (err < 0)
4951 return err;
4952 err = via_add_control(
4953 spec, VIA_CTL_WIDGET_MUTE,
4954 "LFE Playback Switch",
4955 HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4956 HDA_OUTPUT));
4957 if (err < 0)
4958 return err;
4959 } else if (i == AUTO_SEQ_FRONT) {
4960
4961 err = via_add_control(
4962 spec, VIA_CTL_WIDGET_VOL,
4963 "Master Front Playback Volume",
4964 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4965 if (err < 0)
4966 return err;
4967 err = via_add_control(
4968 spec, VIA_CTL_WIDGET_MUTE,
4969 "Master Front Playback Switch",
4970 HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4971 if (err < 0)
4972 return err;
4973
4974 sprintf(name, "%s Playback Volume", chname[i]);
4975 err = via_add_control(
4976 spec, VIA_CTL_WIDGET_VOL, name,
4977 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4978 if (err < 0)
4979 return err;
4980 sprintf(name, "%s Playback Switch", chname[i]);
4981 err = via_add_control(
4982 spec, VIA_CTL_WIDGET_MUTE, name,
4983 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4984 HDA_OUTPUT));
4985 if (err < 0)
4986 return err;
4987 } else {
4988 sprintf(name, "%s Playback Volume", chname[i]);
4989 err = via_add_control(
4990 spec, VIA_CTL_WIDGET_VOL, name,
4991 HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4992 if (err < 0)
4993 return err;
4994 sprintf(name, "%s Playback Switch", chname[i]);
4995 err = via_add_control(
4996 spec, VIA_CTL_WIDGET_MUTE, name,
4997 HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4998 HDA_OUTPUT));
4999 if (err < 0)
5000 return err;
5001 }
5002 }
5003 return 0;
5004}
5005
5006static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5007{
5008 int err;
5009
5010 if (!pin)
5011 return 0;
5012
5013 spec->multiout.hp_nid = 0x25; /* AOW3 */
5014 spec->hp_independent_mode_index = 1;
5015
5016 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5017 "Headphone Playback Volume",
5018 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5019 if (err < 0)
5020 return err;
5021
5022 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5023 "Headphone Playback Switch",
5024 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5025 if (err < 0)
5026 return err;
5027
5028 create_hp_imux(spec);
5029 return 0;
5030}
5031
5032/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02005033static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
Lydia Wangf3db4232009-10-10 19:08:41 +08005034 const struct auto_pin_cfg *cfg)
5035{
Takashi Iwaif3268512010-08-30 11:00:19 +02005036 static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
Takashi Iwai10a20af2010-09-09 16:28:02 +02005037 return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02005038 ARRAY_SIZE(pin_idxs));
Lydia Wangf3db4232009-10-10 19:08:41 +08005039}
5040
5041static int vt1716S_parse_auto_config(struct hda_codec *codec)
5042{
5043 struct via_spec *spec = codec->spec;
5044 int err;
5045
5046 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5047 if (err < 0)
5048 return err;
5049 err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5050 if (err < 0)
5051 return err;
5052 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5053 return 0; /* can't find valid BIOS pin config */
5054
5055 err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5056 if (err < 0)
5057 return err;
5058 err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5059 if (err < 0)
5060 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02005061 err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
Lydia Wangf3db4232009-10-10 19:08:41 +08005062 if (err < 0)
5063 return err;
5064
5065 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5066
5067 fill_dig_outs(codec);
5068
5069 if (spec->kctls.list)
5070 spec->mixers[spec->num_mixers++] = spec->kctls.list;
5071
5072 spec->input_mux = &spec->private_imux[0];
5073
5074 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02005075 via_hp_build(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005076
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005077 via_smart51_build(spec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005078
5079 return 1;
5080}
5081
5082#ifdef CONFIG_SND_HDA_POWER_SAVE
5083static struct hda_amp_list vt1716S_loopbacks[] = {
5084 { 0x16, HDA_INPUT, 1 },
5085 { 0x16, HDA_INPUT, 2 },
5086 { 0x16, HDA_INPUT, 3 },
5087 { 0x16, HDA_INPUT, 4 },
5088 { } /* end */
5089};
5090#endif
5091
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005092static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
5093{
5094 struct via_spec *spec = codec->spec;
5095 int imux_is_smixer;
5096 unsigned int parm;
5097 unsigned int mono_out, present;
5098 /* SW0 (17h) = stereo mixer */
5099 imux_is_smixer =
5100 (snd_hda_codec_read(codec, 0x17, 0,
5101 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
5102 /* inputs */
5103 /* PW 1/2/5 (1ah/1bh/1eh) */
5104 parm = AC_PWRST_D3;
5105 set_pin_power_state(codec, 0x1a, &parm);
5106 set_pin_power_state(codec, 0x1b, &parm);
5107 set_pin_power_state(codec, 0x1e, &parm);
5108 if (imux_is_smixer)
5109 parm = AC_PWRST_D0;
5110 /* SW0 (17h), AIW0(13h) */
5111 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
5112 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
5113
5114 parm = AC_PWRST_D3;
5115 set_pin_power_state(codec, 0x1e, &parm);
5116 /* PW11 (22h) */
5117 if (spec->dmic_enabled)
5118 set_pin_power_state(codec, 0x22, &parm);
5119 else
5120 snd_hda_codec_write(codec, 0x22, 0,
5121 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
5122
5123 /* SW2(26h), AIW1(14h) */
5124 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
5125 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
5126
5127 /* outputs */
5128 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
5129 parm = AC_PWRST_D3;
5130 set_pin_power_state(codec, 0x19, &parm);
5131 /* Smart 5.1 PW2(1bh) */
5132 if (spec->smart51_enabled)
5133 set_pin_power_state(codec, 0x1b, &parm);
5134 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
5135 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
5136
5137 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
5138 parm = AC_PWRST_D3;
5139 set_pin_power_state(codec, 0x23, &parm);
5140 /* Smart 5.1 PW1(1ah) */
5141 if (spec->smart51_enabled)
5142 set_pin_power_state(codec, 0x1a, &parm);
5143 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
5144
5145 /* Smart 5.1 PW5(1eh) */
5146 if (spec->smart51_enabled)
5147 set_pin_power_state(codec, 0x1e, &parm);
5148 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
5149
5150 /* Mono out */
5151 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
5152 present = snd_hda_jack_detect(codec, 0x1c);
5153
5154 if (present)
5155 mono_out = 0;
5156 else {
5157 present = snd_hda_jack_detect(codec, 0x1d);
5158 if (!spec->hp_independent_mode && present)
5159 mono_out = 0;
5160 else
5161 mono_out = 1;
5162 }
5163 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
5164 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
5165 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
5166 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
5167
5168 /* PW 3/4 (1ch/1dh) */
5169 parm = AC_PWRST_D3;
5170 set_pin_power_state(codec, 0x1c, &parm);
5171 set_pin_power_state(codec, 0x1d, &parm);
5172 /* HP Independent Mode, power on AOW3 */
5173 if (spec->hp_independent_mode)
5174 snd_hda_codec_write(codec, 0x25, 0,
5175 AC_VERB_SET_POWER_STATE, parm);
5176
5177 /* force to D0 for internal Speaker */
5178 /* MW0 (16h), AOW0 (10h) */
5179 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
5180 imux_is_smixer ? AC_PWRST_D0 : parm);
5181 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
5182 mono_out ? AC_PWRST_D0 : parm);
5183}
5184
Lydia Wangf3db4232009-10-10 19:08:41 +08005185static int patch_vt1716S(struct hda_codec *codec)
5186{
5187 struct via_spec *spec;
5188 int err;
5189
5190 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005191 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08005192 if (spec == NULL)
5193 return -ENOMEM;
5194
Lydia Wangf3db4232009-10-10 19:08:41 +08005195 /* automatic parse from the BIOS config */
5196 err = vt1716S_parse_auto_config(codec);
5197 if (err < 0) {
5198 via_free(codec);
5199 return err;
5200 } else if (!err) {
5201 printk(KERN_INFO "hda_codec: Cannot set up configuration "
5202 "from BIOS. Using genenic mode...\n");
5203 }
5204
5205 spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs;
5206 spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5207
5208 spec->stream_name_analog = "VT1716S Analog";
5209 spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5210 spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5211
5212 spec->stream_name_digital = "VT1716S Digital";
5213 spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5214
5215 if (!spec->adc_nids && spec->input_mux) {
5216 spec->adc_nids = vt1716S_adc_nids;
5217 spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5218 get_mux_nids(codec);
5219 override_mic_boost(codec, 0x1a, 0, 3, 40);
5220 override_mic_boost(codec, 0x1e, 0, 3, 40);
5221 spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5222 spec->num_mixers++;
5223 }
5224
5225 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5226 spec->num_mixers++;
5227
5228 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5229
5230 codec->patch_ops = via_patch_ops;
5231
5232 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11005233 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangf3db4232009-10-10 19:08:41 +08005234
5235#ifdef CONFIG_SND_HDA_POWER_SAVE
5236 spec->loopback.amplist = vt1716S_loopbacks;
5237#endif
5238
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005239 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08005240 return 0;
5241}
Lydia Wang25eaba22009-10-10 19:08:43 +08005242
5243/* for vt2002P */
5244
5245/* capture mixer elements */
5246static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
5247 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5248 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5249 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5250 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5251 HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5252 HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
5253 HDA_INPUT),
5254 {
5255 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5256 /* The multiple "Capture Source" controls confuse alsamixer
5257 * So call somewhat different..
5258 */
5259 /* .name = "Capture Source", */
5260 .name = "Input Source",
5261 .count = 2,
5262 .info = via_mux_enum_info,
5263 .get = via_mux_enum_get,
5264 .put = via_mux_enum_put,
5265 },
5266 { } /* end */
5267};
5268
5269static struct hda_verb vt2002P_volume_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08005270 /* Class-D speaker related verbs */
5271 {0x1, 0xfe0, 0x4},
5272 {0x1, 0xfe9, 0x80},
5273 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08005274 /*
5275 * Unmute ADC0-1 and set the default input to mic-in
5276 */
5277 {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5278 {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5279
5280
5281 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5282 * mixer widget
5283 */
5284 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5285 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5286 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5287 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5288 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5289 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5290
5291 /* MUX Indices: Mic = 0 */
5292 {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5293 {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5294
5295 /* PW9 Output enable */
5296 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5297
5298 /* Enable Boost Volume backdoor */
5299 {0x1, 0xfb9, 0x24},
5300
5301 /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5302 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5303 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5304 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5305 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5306 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5307 {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5308 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5309 {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5310
5311 /* set MUX0/1/4/8 = 0 (AOW0) */
5312 {0x34, AC_VERB_SET_CONNECT_SEL, 0},
5313 {0x35, AC_VERB_SET_CONNECT_SEL, 0},
5314 {0x37, AC_VERB_SET_CONNECT_SEL, 0},
5315 {0x3b, AC_VERB_SET_CONNECT_SEL, 0},
5316
5317 /* set PW0 index=0 (MW0) */
5318 {0x24, AC_VERB_SET_CONNECT_SEL, 0},
5319
5320 /* Enable AOW0 to MW9 */
5321 {0x1, 0xfb8, 0x88},
5322 { }
5323};
Lydia Wang118909562011-03-23 17:57:34 +08005324static struct hda_verb vt1802_volume_init_verbs[] = {
5325 /*
5326 * Unmute ADC0-1 and set the default input to mic-in
5327 */
5328 {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5329 {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5330
5331
5332 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5333 * mixer widget
5334 */
5335 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5336 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5337 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5338 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5339 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5340 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5341
5342 /* MUX Indices: Mic = 0 */
5343 {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5344 {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5345
5346 /* PW9 Output enable */
5347 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5348
5349 /* Enable Boost Volume backdoor */
5350 {0x1, 0xfb9, 0x24},
5351
5352 /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5353 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5354 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5355 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5356 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5357 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5358 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5359 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5360 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5361
5362 /* set MUX0/1/4/8 = 0 (AOW0) */
5363 {0x34, AC_VERB_SET_CONNECT_SEL, 0},
5364 {0x35, AC_VERB_SET_CONNECT_SEL, 0},
5365 {0x38, AC_VERB_SET_CONNECT_SEL, 0},
5366 {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5367
5368 /* set PW0 index=0 (MW0) */
5369 {0x24, AC_VERB_SET_CONNECT_SEL, 0},
5370
5371 /* Enable AOW0 to MW9 */
5372 {0x1, 0xfb8, 0x88},
5373 { }
5374};
Lydia Wang25eaba22009-10-10 19:08:43 +08005375
5376
5377static struct hda_verb vt2002P_uniwill_init_verbs[] = {
5378 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
5379 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5380 {0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
5381 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5382 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5383 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5384 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5385 { }
5386};
Lydia Wang118909562011-03-23 17:57:34 +08005387static struct hda_verb vt1802_uniwill_init_verbs[] = {
5388 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
5389 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5390 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5391 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5392 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5393 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5394 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5395 { }
5396};
Lydia Wang25eaba22009-10-10 19:08:43 +08005397
5398static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
5399 .substreams = 2,
5400 .channels_min = 2,
5401 .channels_max = 2,
5402 .nid = 0x8, /* NID to query formats and rates */
5403 .ops = {
5404 .open = via_playback_pcm_open,
5405 .prepare = via_playback_multi_pcm_prepare,
5406 .cleanup = via_playback_multi_pcm_cleanup,
5407 .close = via_pcm_open_close,
5408 },
5409};
5410
5411static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
5412 .substreams = 2,
5413 .channels_min = 2,
5414 .channels_max = 2,
5415 .nid = 0x10, /* NID to query formats and rates */
5416 .ops = {
5417 .open = via_pcm_open_close,
5418 .prepare = via_capture_pcm_prepare,
5419 .cleanup = via_capture_pcm_cleanup,
5420 .close = via_pcm_open_close,
5421 },
5422};
5423
5424static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
5425 .substreams = 1,
5426 .channels_min = 2,
5427 .channels_max = 2,
Lydia Wang25eaba22009-10-10 19:08:43 +08005428 /* NID is set in via_build_pcms */
5429 .ops = {
5430 .open = via_dig_playback_pcm_open,
5431 .close = via_dig_playback_pcm_close,
5432 .prepare = via_dig_playback_pcm_prepare,
5433 .cleanup = via_dig_playback_pcm_cleanup
5434 },
5435};
5436
5437/* fill in the dac_nids table from the parsed pin configuration */
5438static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
5439 const struct auto_pin_cfg *cfg)
5440{
5441 spec->multiout.num_dacs = 1;
5442 spec->multiout.dac_nids = spec->private_dac_nids;
5443 if (cfg->line_out_pins[0])
5444 spec->multiout.dac_nids[0] = 0x8;
5445 return 0;
5446}
5447
5448/* add playback controls from the parsed DAC table */
5449static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
5450 const struct auto_pin_cfg *cfg)
5451{
5452 int err;
Lydia Wang118909562011-03-23 17:57:34 +08005453 hda_nid_t sw_nid;
Lydia Wang25eaba22009-10-10 19:08:43 +08005454
5455 if (!cfg->line_out_pins[0])
5456 return -1;
5457
Lydia Wang118909562011-03-23 17:57:34 +08005458 if (spec->codec_type == VT1802)
5459 sw_nid = 0x28;
5460 else
5461 sw_nid = 0x26;
Lydia Wang25eaba22009-10-10 19:08:43 +08005462
5463 /* Line-Out: PortE */
5464 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5465 "Master Front Playback Volume",
5466 HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5467 if (err < 0)
5468 return err;
5469 err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
5470 "Master Front Playback Switch",
Lydia Wang118909562011-03-23 17:57:34 +08005471 HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT));
Lydia Wang25eaba22009-10-10 19:08:43 +08005472 if (err < 0)
5473 return err;
5474
5475 return 0;
5476}
5477
5478static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5479{
5480 int err;
5481
5482 if (!pin)
5483 return 0;
5484
5485 spec->multiout.hp_nid = 0x9;
5486 spec->hp_independent_mode_index = 1;
5487
5488 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5489 "Headphone Playback Volume",
5490 HDA_COMPOSE_AMP_VAL(
5491 spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5492 if (err < 0)
5493 return err;
5494
5495 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5496 "Headphone Playback Switch",
5497 HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5498 if (err < 0)
5499 return err;
5500
5501 create_hp_imux(spec);
5502 return 0;
5503}
5504
5505/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02005506static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
Lydia Wang25eaba22009-10-10 19:08:43 +08005507 const struct auto_pin_cfg *cfg)
5508{
Takashi Iwai10a20af2010-09-09 16:28:02 +02005509 struct via_spec *spec = codec->spec;
Lydia Wang25eaba22009-10-10 19:08:43 +08005510 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaif3268512010-08-30 11:00:19 +02005511 static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
5512 int err;
Lydia Wang25eaba22009-10-10 19:08:43 +08005513
Takashi Iwai10a20af2010-09-09 16:28:02 +02005514 err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02005515 ARRAY_SIZE(pin_idxs));
5516 if (err < 0)
5517 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08005518 /* build volume/mute control of loopback */
Takashi Iwai7b315bb2010-08-30 13:06:30 +02005519 err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
Lydia Wang25eaba22009-10-10 19:08:43 +08005520 if (err < 0)
5521 return err;
5522
Lydia Wang25eaba22009-10-10 19:08:43 +08005523 /* for digital mic select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02005524 snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
Lydia Wang25eaba22009-10-10 19:08:43 +08005525
5526 return 0;
5527}
5528
5529static int vt2002P_parse_auto_config(struct hda_codec *codec)
5530{
5531 struct via_spec *spec = codec->spec;
5532 int err;
5533
5534
5535 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5536 if (err < 0)
5537 return err;
5538
5539 err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
5540 if (err < 0)
5541 return err;
5542
5543 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5544 return 0; /* can't find valid BIOS pin config */
5545
5546 err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
5547 if (err < 0)
5548 return err;
5549 err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5550 if (err < 0)
5551 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02005552 err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
Lydia Wang25eaba22009-10-10 19:08:43 +08005553 if (err < 0)
5554 return err;
5555
5556 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5557
5558 fill_dig_outs(codec);
5559
5560 if (spec->kctls.list)
5561 spec->mixers[spec->num_mixers++] = spec->kctls.list;
5562
5563 spec->input_mux = &spec->private_imux[0];
5564
5565 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02005566 via_hp_build(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08005567
5568 return 1;
5569}
5570
5571#ifdef CONFIG_SND_HDA_POWER_SAVE
5572static struct hda_amp_list vt2002P_loopbacks[] = {
5573 { 0x21, HDA_INPUT, 0 },
5574 { 0x21, HDA_INPUT, 1 },
5575 { 0x21, HDA_INPUT, 2 },
5576 { } /* end */
5577};
5578#endif
5579
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005580static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
5581{
5582 struct via_spec *spec = codec->spec;
5583 int imux_is_smixer;
5584 unsigned int parm;
5585 unsigned int present;
5586 /* MUX9 (1eh) = stereo mixer */
5587 imux_is_smixer =
5588 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
5589 /* inputs */
5590 /* PW 5/6/7 (29h/2ah/2bh) */
5591 parm = AC_PWRST_D3;
5592 set_pin_power_state(codec, 0x29, &parm);
5593 set_pin_power_state(codec, 0x2a, &parm);
5594 set_pin_power_state(codec, 0x2b, &parm);
5595 parm = AC_PWRST_D0;
5596 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
5597 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
5598 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
5599 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
5600 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
5601
5602 /* outputs */
5603 /* AOW0 (8h)*/
5604 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
5605
Lydia Wang118909562011-03-23 17:57:34 +08005606 if (spec->codec_type == VT1802) {
5607 /* PW4 (28h), MW4 (18h), MUX4(38h) */
5608 parm = AC_PWRST_D3;
5609 set_pin_power_state(codec, 0x28, &parm);
5610 snd_hda_codec_write(codec, 0x18, 0,
5611 AC_VERB_SET_POWER_STATE, parm);
5612 snd_hda_codec_write(codec, 0x38, 0,
5613 AC_VERB_SET_POWER_STATE, parm);
5614 } else {
5615 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
5616 parm = AC_PWRST_D3;
5617 set_pin_power_state(codec, 0x26, &parm);
5618 snd_hda_codec_write(codec, 0x1c, 0,
5619 AC_VERB_SET_POWER_STATE, parm);
5620 snd_hda_codec_write(codec, 0x37, 0,
5621 AC_VERB_SET_POWER_STATE, parm);
5622 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005623
Lydia Wang118909562011-03-23 17:57:34 +08005624 if (spec->codec_type == VT1802) {
5625 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
5626 parm = AC_PWRST_D3;
5627 set_pin_power_state(codec, 0x25, &parm);
5628 snd_hda_codec_write(codec, 0x15, 0,
5629 AC_VERB_SET_POWER_STATE, parm);
5630 snd_hda_codec_write(codec, 0x35, 0,
5631 AC_VERB_SET_POWER_STATE, parm);
5632 } else {
5633 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
5634 parm = AC_PWRST_D3;
5635 set_pin_power_state(codec, 0x25, &parm);
5636 snd_hda_codec_write(codec, 0x19, 0,
5637 AC_VERB_SET_POWER_STATE, parm);
5638 snd_hda_codec_write(codec, 0x35, 0,
5639 AC_VERB_SET_POWER_STATE, parm);
5640 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005641
5642 if (spec->hp_independent_mode)
5643 snd_hda_codec_write(codec, 0x9, 0,
5644 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
5645
5646 /* Class-D */
5647 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
5648 present = snd_hda_jack_detect(codec, 0x25);
5649
5650 parm = AC_PWRST_D3;
5651 set_pin_power_state(codec, 0x24, &parm);
5652 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08005653 if (spec->codec_type == VT1802)
5654 snd_hda_codec_write(codec, 0x14, 0,
5655 AC_VERB_SET_POWER_STATE, parm);
5656 else
5657 snd_hda_codec_write(codec, 0x18, 0,
5658 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005659 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
5660
5661 /* Mono Out */
5662 present = snd_hda_jack_detect(codec, 0x26);
5663
5664 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08005665 if (spec->codec_type == VT1802) {
5666 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
5667 snd_hda_codec_write(codec, 0x33, 0,
5668 AC_VERB_SET_POWER_STATE, parm);
5669 snd_hda_codec_write(codec, 0x1c, 0,
5670 AC_VERB_SET_POWER_STATE, parm);
5671 snd_hda_codec_write(codec, 0x3c, 0,
5672 AC_VERB_SET_POWER_STATE, parm);
5673 } else {
5674 /* PW15 (31h), MW8(17h), MUX8(3bh) */
5675 snd_hda_codec_write(codec, 0x31, 0,
5676 AC_VERB_SET_POWER_STATE, parm);
5677 snd_hda_codec_write(codec, 0x17, 0,
5678 AC_VERB_SET_POWER_STATE, parm);
5679 snd_hda_codec_write(codec, 0x3b, 0,
5680 AC_VERB_SET_POWER_STATE, parm);
5681 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005682 /* MW9 (21h) */
5683 if (imux_is_smixer || !is_aa_path_mute(codec))
5684 snd_hda_codec_write(codec, 0x21, 0,
5685 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
5686 else
5687 snd_hda_codec_write(codec, 0x21, 0,
5688 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
5689}
Lydia Wang25eaba22009-10-10 19:08:43 +08005690
5691/* patch for vt2002P */
5692static int patch_vt2002P(struct hda_codec *codec)
5693{
5694 struct via_spec *spec;
5695 int err;
5696
5697 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01005698 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08005699 if (spec == NULL)
5700 return -ENOMEM;
5701
Lydia Wang25eaba22009-10-10 19:08:43 +08005702 /* automatic parse from the BIOS config */
5703 err = vt2002P_parse_auto_config(codec);
5704 if (err < 0) {
5705 via_free(codec);
5706 return err;
5707 } else if (!err) {
5708 printk(KERN_INFO "hda_codec: Cannot set up configuration "
5709 "from BIOS. Using genenic mode...\n");
5710 }
5711
Lydia Wang118909562011-03-23 17:57:34 +08005712 if (spec->codec_type == VT1802)
5713 spec->init_verbs[spec->num_iverbs++] =
5714 vt1802_volume_init_verbs;
5715 else
5716 spec->init_verbs[spec->num_iverbs++] =
5717 vt2002P_volume_init_verbs;
Lydia Wang25eaba22009-10-10 19:08:43 +08005718
Lydia Wang118909562011-03-23 17:57:34 +08005719 if (spec->codec_type == VT1802)
5720 spec->init_verbs[spec->num_iverbs++] =
5721 vt1802_uniwill_init_verbs;
5722 else
5723 spec->init_verbs[spec->num_iverbs++] =
5724 vt2002P_uniwill_init_verbs;
5725
5726 if (spec->codec_type == VT1802)
5727 spec->stream_name_analog = "VT1802 Analog";
5728 else
5729 spec->stream_name_analog = "VT2002P Analog";
Lydia Wang25eaba22009-10-10 19:08:43 +08005730 spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
5731 spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
5732
Lydia Wang118909562011-03-23 17:57:34 +08005733 if (spec->codec_type == VT1802)
5734 spec->stream_name_digital = "VT1802 Digital";
5735 else
5736 spec->stream_name_digital = "VT2002P Digital";
Lydia Wang25eaba22009-10-10 19:08:43 +08005737 spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
5738
5739 if (!spec->adc_nids && spec->input_mux) {
5740 spec->adc_nids = vt2002P_adc_nids;
5741 spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
5742 get_mux_nids(codec);
5743 override_mic_boost(codec, 0x2b, 0, 3, 40);
5744 override_mic_boost(codec, 0x29, 0, 3, 40);
5745 spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
5746 spec->num_mixers++;
5747 }
5748
5749 codec->patch_ops = via_patch_ops;
5750
5751 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11005752 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wang25eaba22009-10-10 19:08:43 +08005753
5754#ifdef CONFIG_SND_HDA_POWER_SAVE
5755 spec->loopback.amplist = vt2002P_loopbacks;
5756#endif
5757
Lydia Wang3e95b9a2011-03-23 15:13:28 +08005758 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08005759 return 0;
5760}
Lydia Wangab6734e2009-10-10 19:08:46 +08005761
5762/* for vt1812 */
5763
5764/* capture mixer elements */
5765static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5766 HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5767 HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5768 HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5769 HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5770 HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5771 HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5772 HDA_INPUT),
5773 {
5774 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5775 /* The multiple "Capture Source" controls confuse alsamixer
5776 * So call somewhat different..
5777 */
5778 .name = "Input Source",
5779 .count = 2,
5780 .info = via_mux_enum_info,
5781 .get = via_mux_enum_get,
5782 .put = via_mux_enum_put,
5783 },
5784 { } /* end */
5785};
5786
5787static struct hda_verb vt1812_volume_init_verbs[] = {
5788 /*
5789 * Unmute ADC0-1 and set the default input to mic-in
5790 */
5791 {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5792 {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5793
5794
5795 /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5796 * mixer widget
5797 */
5798 /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5799 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5800 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5801 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5802 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5803 {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5804
5805 /* MUX Indices: Mic = 0 */
5806 {0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5807 {0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5808
5809 /* PW9 Output enable */
5810 {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5811
5812 /* Enable Boost Volume backdoor */
5813 {0x1, 0xfb9, 0x24},
5814
5815 /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5816 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5817 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5818 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5819 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5820 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5821 {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5822 {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5823 {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5824 {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5825 {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5826
5827 /* set MUX0/1/4/13/15 = 0 (AOW0) */
5828 {0x34, AC_VERB_SET_CONNECT_SEL, 0},
5829 {0x35, AC_VERB_SET_CONNECT_SEL, 0},
5830 {0x38, AC_VERB_SET_CONNECT_SEL, 0},
5831 {0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5832 {0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5833
5834 /* Enable AOW0 to MW9 */
5835 {0x1, 0xfb8, 0xa8},
5836 { }
5837};
5838
5839
5840static struct hda_verb vt1812_uniwill_init_verbs[] = {
5841 {0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5842 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5843 {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5844 {0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5845 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5846 {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5847 {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5848 {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5849 { }
5850};
5851
5852static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5853 .substreams = 2,
5854 .channels_min = 2,
5855 .channels_max = 2,
5856 .nid = 0x8, /* NID to query formats and rates */
5857 .ops = {
5858 .open = via_playback_pcm_open,
5859 .prepare = via_playback_multi_pcm_prepare,
5860 .cleanup = via_playback_multi_pcm_cleanup,
5861 .close = via_pcm_open_close,
5862 },
5863};
5864
5865static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5866 .substreams = 2,
5867 .channels_min = 2,
5868 .channels_max = 2,
5869 .nid = 0x10, /* NID to query formats and rates */
5870 .ops = {
5871 .open = via_pcm_open_close,
5872 .prepare = via_capture_pcm_prepare,
5873 .cleanup = via_capture_pcm_cleanup,
5874 .close = via_pcm_open_close,
5875 },
5876};
5877
5878static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5879 .substreams = 1,
5880 .channels_min = 2,
5881 .channels_max = 2,
Lydia Wangab6734e2009-10-10 19:08:46 +08005882 /* NID is set in via_build_pcms */
5883 .ops = {
5884 .open = via_dig_playback_pcm_open,
5885 .close = via_dig_playback_pcm_close,
5886 .prepare = via_dig_playback_pcm_prepare,
5887 .cleanup = via_dig_playback_pcm_cleanup
5888 },
5889};
5890/* fill in the dac_nids table from the parsed pin configuration */
5891static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5892 const struct auto_pin_cfg *cfg)
5893{
5894 spec->multiout.num_dacs = 1;
5895 spec->multiout.dac_nids = spec->private_dac_nids;
5896 if (cfg->line_out_pins[0])
5897 spec->multiout.dac_nids[0] = 0x8;
5898 return 0;
5899}
5900
5901
5902/* add playback controls from the parsed DAC table */
5903static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5904 const struct auto_pin_cfg *cfg)
5905{
5906 int err;
5907
5908 if (!cfg->line_out_pins[0])
5909 return -1;
5910
5911 /* Line-Out: PortE */
5912 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
Takashi Iwai3d83e572010-04-14 14:36:23 +02005913 "Front Playback Volume",
Lydia Wangab6734e2009-10-10 19:08:46 +08005914 HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5915 if (err < 0)
5916 return err;
5917 err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
Takashi Iwai3d83e572010-04-14 14:36:23 +02005918 "Front Playback Switch",
Lydia Wangab6734e2009-10-10 19:08:46 +08005919 HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5920 if (err < 0)
5921 return err;
5922
5923 return 0;
5924}
5925
5926static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5927{
5928 int err;
5929
5930 if (!pin)
5931 return 0;
5932
5933 spec->multiout.hp_nid = 0x9;
5934 spec->hp_independent_mode_index = 1;
5935
5936
5937 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5938 "Headphone Playback Volume",
5939 HDA_COMPOSE_AMP_VAL(
5940 spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5941 if (err < 0)
5942 return err;
5943
5944 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5945 "Headphone Playback Switch",
5946 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5947 if (err < 0)
5948 return err;
5949
5950 create_hp_imux(spec);
5951 return 0;
5952}
5953
5954/* create playback/capture controls for input pins */
Takashi Iwai10a20af2010-09-09 16:28:02 +02005955static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
Lydia Wangab6734e2009-10-10 19:08:46 +08005956 const struct auto_pin_cfg *cfg)
5957{
Takashi Iwai10a20af2010-09-09 16:28:02 +02005958 struct via_spec *spec = codec->spec;
Lydia Wangab6734e2009-10-10 19:08:46 +08005959 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaif3268512010-08-30 11:00:19 +02005960 static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
5961 int err;
Lydia Wangab6734e2009-10-10 19:08:46 +08005962
Takashi Iwai10a20af2010-09-09 16:28:02 +02005963 err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
Takashi Iwaif3268512010-08-30 11:00:19 +02005964 ARRAY_SIZE(pin_idxs));
5965 if (err < 0)
5966 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08005967
Lydia Wangab6734e2009-10-10 19:08:46 +08005968 /* build volume/mute control of loopback */
Takashi Iwai7b315bb2010-08-30 13:06:30 +02005969 err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
Lydia Wangab6734e2009-10-10 19:08:46 +08005970 if (err < 0)
5971 return err;
5972
Lydia Wangab6734e2009-10-10 19:08:46 +08005973 /* for digital mic select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02005974 snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
Lydia Wangab6734e2009-10-10 19:08:46 +08005975
5976 return 0;
5977}
5978
5979static int vt1812_parse_auto_config(struct hda_codec *codec)
5980{
5981 struct via_spec *spec = codec->spec;
5982 int err;
5983
5984
5985 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5986 if (err < 0)
5987 return err;
5988 fill_dig_outs(codec);
5989 err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
5990 if (err < 0)
5991 return err;
5992
5993 if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
5994 return 0; /* can't find valid BIOS pin config */
5995
5996 err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
5997 if (err < 0)
5998 return err;
5999 err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
6000 if (err < 0)
6001 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02006002 err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
Lydia Wangab6734e2009-10-10 19:08:46 +08006003 if (err < 0)
6004 return err;
6005
6006 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
6007
6008 fill_dig_outs(codec);
6009
6010 if (spec->kctls.list)
6011 spec->mixers[spec->num_mixers++] = spec->kctls.list;
6012
6013 spec->input_mux = &spec->private_imux[0];
6014
6015 if (spec->hp_mux)
Takashi Iwai3d83e572010-04-14 14:36:23 +02006016 via_hp_build(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08006017
6018 return 1;
6019}
6020
6021#ifdef CONFIG_SND_HDA_POWER_SAVE
6022static struct hda_amp_list vt1812_loopbacks[] = {
6023 { 0x21, HDA_INPUT, 0 },
6024 { 0x21, HDA_INPUT, 1 },
6025 { 0x21, HDA_INPUT, 2 },
6026 { } /* end */
6027};
6028#endif
6029
Lydia Wang3e95b9a2011-03-23 15:13:28 +08006030static void set_widgets_power_state_vt1812(struct hda_codec *codec)
6031{
6032 struct via_spec *spec = codec->spec;
6033 int imux_is_smixer =
6034 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
6035 unsigned int parm;
6036 unsigned int present;
6037 /* MUX10 (1eh) = stereo mixer */
6038 imux_is_smixer =
6039 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
6040 /* inputs */
6041 /* PW 5/6/7 (29h/2ah/2bh) */
6042 parm = AC_PWRST_D3;
6043 set_pin_power_state(codec, 0x29, &parm);
6044 set_pin_power_state(codec, 0x2a, &parm);
6045 set_pin_power_state(codec, 0x2b, &parm);
6046 parm = AC_PWRST_D0;
6047 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
6048 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
6049 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
6050 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
6051 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
6052
6053 /* outputs */
6054 /* AOW0 (8h)*/
6055 snd_hda_codec_write(codec, 0x8, 0,
6056 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6057
6058 /* PW4 (28h), MW4 (18h), MUX4(38h) */
6059 parm = AC_PWRST_D3;
6060 set_pin_power_state(codec, 0x28, &parm);
6061 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
6062 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
6063
6064 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
6065 parm = AC_PWRST_D3;
6066 set_pin_power_state(codec, 0x25, &parm);
6067 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
6068 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
6069 if (spec->hp_independent_mode)
6070 snd_hda_codec_write(codec, 0x9, 0,
6071 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6072
6073 /* Internal Speaker */
6074 /* PW0 (24h), MW0(14h), MUX0(34h) */
6075 present = snd_hda_jack_detect(codec, 0x25);
6076
6077 parm = AC_PWRST_D3;
6078 set_pin_power_state(codec, 0x24, &parm);
6079 if (present) {
6080 snd_hda_codec_write(codec, 0x14, 0,
6081 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
6082 snd_hda_codec_write(codec, 0x34, 0,
6083 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
6084 } else {
6085 snd_hda_codec_write(codec, 0x14, 0,
6086 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6087 snd_hda_codec_write(codec, 0x34, 0,
6088 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6089 }
6090
6091
6092 /* Mono Out */
6093 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
6094 present = snd_hda_jack_detect(codec, 0x28);
6095
6096 parm = AC_PWRST_D3;
6097 set_pin_power_state(codec, 0x31, &parm);
6098 if (present) {
6099 snd_hda_codec_write(codec, 0x1c, 0,
6100 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
6101 snd_hda_codec_write(codec, 0x3c, 0,
6102 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
6103 snd_hda_codec_write(codec, 0x3e, 0,
6104 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
6105 } else {
6106 snd_hda_codec_write(codec, 0x1c, 0,
6107 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6108 snd_hda_codec_write(codec, 0x3c, 0,
6109 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6110 snd_hda_codec_write(codec, 0x3e, 0,
6111 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
6112 }
6113
6114 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
6115 parm = AC_PWRST_D3;
6116 set_pin_power_state(codec, 0x33, &parm);
6117 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
6118 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
6119
6120}
Lydia Wangab6734e2009-10-10 19:08:46 +08006121
6122/* patch for vt1812 */
6123static int patch_vt1812(struct hda_codec *codec)
6124{
6125 struct via_spec *spec;
6126 int err;
6127
6128 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01006129 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08006130 if (spec == NULL)
6131 return -ENOMEM;
6132
Lydia Wangab6734e2009-10-10 19:08:46 +08006133 /* automatic parse from the BIOS config */
6134 err = vt1812_parse_auto_config(codec);
6135 if (err < 0) {
6136 via_free(codec);
6137 return err;
6138 } else if (!err) {
6139 printk(KERN_INFO "hda_codec: Cannot set up configuration "
6140 "from BIOS. Using genenic mode...\n");
6141 }
6142
6143
6144 spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs;
6145 spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6146
6147 spec->stream_name_analog = "VT1812 Analog";
6148 spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6149 spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6150
6151 spec->stream_name_digital = "VT1812 Digital";
6152 spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6153
6154
6155 if (!spec->adc_nids && spec->input_mux) {
6156 spec->adc_nids = vt1812_adc_nids;
6157 spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6158 get_mux_nids(codec);
6159 override_mic_boost(codec, 0x2b, 0, 3, 40);
6160 override_mic_boost(codec, 0x29, 0, 3, 40);
6161 spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6162 spec->num_mixers++;
6163 }
6164
6165 codec->patch_ops = via_patch_ops;
6166
6167 codec->patch_ops.init = via_auto_init;
Stephen Rothwell0f483272009-10-12 15:56:17 +11006168 codec->patch_ops.unsol_event = via_unsol_event;
Lydia Wangab6734e2009-10-10 19:08:46 +08006169
6170#ifdef CONFIG_SND_HDA_POWER_SAVE
6171 spec->loopback.amplist = vt1812_loopbacks;
6172#endif
6173
Lydia Wang3e95b9a2011-03-23 15:13:28 +08006174 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08006175 return 0;
6176}
6177
Joseph Chanc577b8a2006-11-29 15:29:40 +01006178/*
6179 * patch entries
6180 */
Takashi Iwai1289e9e2008-11-27 15:47:11 +01006181static struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01006182 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
6183 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
6184 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
6185 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
6186 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006187 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006188 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006189 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006190 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006191 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006192 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006193 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006194 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006195 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006196 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006197 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006198 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006199 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006200 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006201 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006202 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006203 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006204 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006205 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006206 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006207 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006208 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006209 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006210 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006211 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006212 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006213 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006214 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006215 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006216 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01006217 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01006218 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006219 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006220 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006221 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006222 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006223 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006224 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006225 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08006226 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08006227 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006228 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006229 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006230 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006231 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006232 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08006233 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01006234 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006235 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006236 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006237 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006238 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006239 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006240 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006241 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006242 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006243 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006244 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006245 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006246 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006247 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01006248 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08006249 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08006250 { .id = 0x11060428, .name = "VT1718S",
6251 .patch = patch_vt1718S},
6252 { .id = 0x11064428, .name = "VT1718S",
6253 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08006254 { .id = 0x11060441, .name = "VT2020",
6255 .patch = patch_vt1718S},
6256 { .id = 0x11064441, .name = "VT1828S",
6257 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08006258 { .id = 0x11060433, .name = "VT1716S",
6259 .patch = patch_vt1716S},
6260 { .id = 0x1106a721, .name = "VT1716S",
6261 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08006262 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
6263 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08006264 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08006265 { .id = 0x11060440, .name = "VT1818S",
6266 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08006267 { .id = 0x11060446, .name = "VT1802",
6268 .patch = patch_vt2002P},
6269 { .id = 0x11068446, .name = "VT1802",
6270 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01006271 {} /* terminator */
6272};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01006273
6274MODULE_ALIAS("snd-hda-codec-id:1106*");
6275
6276static struct hda_codec_preset_list via_list = {
6277 .preset = snd_hda_preset_via,
6278 .owner = THIS_MODULE,
6279};
6280
6281MODULE_LICENSE("GPL");
6282MODULE_DESCRIPTION("VIA HD-audio codec");
6283
6284static int __init patch_via_init(void)
6285{
6286 return snd_hda_add_codec_preset(&via_list);
6287}
6288
6289static void __exit patch_via_exit(void)
6290{
6291 snd_hda_delete_codec_preset(&via_list);
6292}
6293
6294module_init(patch_via_init)
6295module_exit(patch_via_exit)