xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 25eaba2f8a6877ba6f58197c4723c2433a316e09)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
4d949cac1SHarald Welte  * HD audio interface patch for VIA VT1702/VT1708/VT1709 codec
5c577b8a1SJoseph Chan  *
676d9b0ddSHarald Welte  * Copyright (c) 2006-2008 Lydia Wang <lydiawang@viatech.com>
7c577b8a1SJoseph Chan  *			   Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*                                                                           */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid          */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec                       */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization      */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support                        */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support        */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin             */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature                        */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li    Add VT1708S Mic Boost workaround/backdoor	     */
39c577b8a1SJoseph Chan /*                                                                           */
40c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
41c577b8a1SJoseph Chan 
42c577b8a1SJoseph Chan 
43c577b8a1SJoseph Chan #include <linux/init.h>
44c577b8a1SJoseph Chan #include <linux/delay.h>
45c577b8a1SJoseph Chan #include <linux/slab.h>
46c577b8a1SJoseph Chan #include <sound/core.h>
470aa62aefSHarald Welte #include <sound/asoundef.h>
48c577b8a1SJoseph Chan #include "hda_codec.h"
49c577b8a1SJoseph Chan #include "hda_local.h"
50c577b8a1SJoseph Chan 
51c577b8a1SJoseph Chan /* amp values */
52c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
53c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
54c577b8a1SJoseph Chan 
55c577b8a1SJoseph Chan /* Pin Widget NID */
56c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
57c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
58c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
59f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6076d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6176d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
64c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
65c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
66f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
67f7278fd0SJosepch Chan 
68f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
69f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
70f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
71f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
72c577b8a1SJoseph Chan 
73d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
74d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
75d949cac1SHarald Welte 
76d949cac1SHarald Welte #define VT1702_HP_NID		0x17
77d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
78d949cac1SHarald Welte 
79d7426329SHarald Welte enum VIA_HDA_CODEC {
80d7426329SHarald Welte 	UNKNOWN = -1,
81d7426329SHarald Welte 	VT1708,
82d7426329SHarald Welte 	VT1709_10CH,
83d7426329SHarald Welte 	VT1709_6CH,
84d7426329SHarald Welte 	VT1708B_8CH,
85d7426329SHarald Welte 	VT1708B_4CH,
86d7426329SHarald Welte 	VT1708S,
87518bf3baSLydia Wang 	VT1708BCE,
88d7426329SHarald Welte 	VT1702,
89eb7188caSLydia Wang 	VT1718S,
90f3db423dSLydia Wang 	VT1716S,
91*25eaba2fSLydia Wang 	VT2002P,
92d7426329SHarald Welte 	CODEC_TYPES,
93d7426329SHarald Welte };
94d7426329SHarald Welte 
951f2e99feSLydia Wang struct via_spec {
961f2e99feSLydia Wang 	/* codec parameterization */
97f3db423dSLydia Wang 	struct snd_kcontrol_new *mixers[6];
981f2e99feSLydia Wang 	unsigned int num_mixers;
991f2e99feSLydia Wang 
1001f2e99feSLydia Wang 	struct hda_verb *init_verbs[5];
1011f2e99feSLydia Wang 	unsigned int num_iverbs;
1021f2e99feSLydia Wang 
1031f2e99feSLydia Wang 	char *stream_name_analog;
1041f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_playback;
1051f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_capture;
1061f2e99feSLydia Wang 
1071f2e99feSLydia Wang 	char *stream_name_digital;
1081f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_playback;
1091f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_capture;
1101f2e99feSLydia Wang 
1111f2e99feSLydia Wang 	/* playback */
1121f2e99feSLydia Wang 	struct hda_multi_out multiout;
1131f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1141f2e99feSLydia Wang 
1151f2e99feSLydia Wang 	/* capture */
1161f2e99feSLydia Wang 	unsigned int num_adc_nids;
1171f2e99feSLydia Wang 	hda_nid_t *adc_nids;
1181f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
1191f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1201f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1211f2e99feSLydia Wang 
1221f2e99feSLydia Wang 	/* capture source */
1231f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1241f2e99feSLydia Wang 	unsigned int cur_mux[3];
1251f2e99feSLydia Wang 
1261f2e99feSLydia Wang 	/* PCM information */
1271f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1281f2e99feSLydia Wang 
1291f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1301f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1311f2e99feSLydia Wang 	struct snd_array kctls;
1321f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1331f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1341f2e99feSLydia Wang 
1351f2e99feSLydia Wang 	/* HP mode source */
1361f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1371f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1381f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
1391f2e99feSLydia Wang 	unsigned int smart51_enabled;
140f3db423dSLydia Wang 	unsigned int dmic_enabled;
1411f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1421f2e99feSLydia Wang 
1431f2e99feSLydia Wang 	/* work to check hp jack state */
1441f2e99feSLydia Wang 	struct hda_codec *codec;
1451f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
1461f2e99feSLydia Wang 	int vt1708_jack_detectect;
1471f2e99feSLydia Wang 	int vt1708_hp_present;
1481f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1491f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1501f2e99feSLydia Wang #endif
1511f2e99feSLydia Wang };
1521f2e99feSLydia Wang 
153744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
154d7426329SHarald Welte {
155744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
156d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
157d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
158d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
159d7426329SHarald Welte 
160d7426329SHarald Welte 	/* get codec type */
161d7426329SHarald Welte 	if (ven_id != 0x1106)
162d7426329SHarald Welte 		codec_type = UNKNOWN;
163d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
164d7426329SHarald Welte 		codec_type = VT1708;
165d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
166d7426329SHarald Welte 		codec_type = VT1709_10CH;
167d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
168d7426329SHarald Welte 		codec_type = VT1709_6CH;
169518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
170d7426329SHarald Welte 		codec_type = VT1708B_8CH;
171518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
172518bf3baSLydia Wang 			codec_type = VT1708BCE;
173518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
174d7426329SHarald Welte 		codec_type = VT1708B_4CH;
175d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
176d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
177d7426329SHarald Welte 		codec_type = VT1708S;
178d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
179d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
180d7426329SHarald Welte 		codec_type = VT1702;
181eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
182eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
183eb7188caSLydia Wang 		codec_type = VT1718S;
184f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
185f3db423dSLydia Wang 		codec_type = VT1716S;
186bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
187bb3c6bfcSLydia Wang 		codec_type = VT1718S;
188*25eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
189*25eaba2fSLydia Wang 		codec_type = VT2002P;
190d7426329SHarald Welte 	else
191d7426329SHarald Welte 		codec_type = UNKNOWN;
192d7426329SHarald Welte 	return codec_type;
193d7426329SHarald Welte };
194d7426329SHarald Welte 
19569e52a80SHarald Welte #define VIA_HP_EVENT		0x01
19669e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
197a34df19aSLydia Wang #define VIA_JACK_EVENT		0x04
198f3db423dSLydia Wang #define VIA_MONO_EVENT		0x08
199*25eaba2fSLydia Wang #define VIA_SPEAKER_EVENT	0x10
200*25eaba2fSLydia Wang #define VIA_BIND_HP_EVENT	0x20
20169e52a80SHarald Welte 
202c577b8a1SJoseph Chan enum {
203c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
204c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
205f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
206*25eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
207c577b8a1SJoseph Chan };
208c577b8a1SJoseph Chan 
209c577b8a1SJoseph Chan enum {
210eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
211c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
212c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
213c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
214c577b8a1SJoseph Chan };
215c577b8a1SJoseph Chan 
216f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
217f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec);
2181f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2191f2e99feSLydia Wang 
2201f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2211f2e99feSLydia Wang {
2221f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2231f2e99feSLydia Wang 		return;
2241f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2251f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2261f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2271f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2281f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2291f2e99feSLydia Wang }
2301f2e99feSLydia Wang 
2311f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2321f2e99feSLydia Wang {
2331f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2341f2e99feSLydia Wang 		return;
2351f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2361f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2371f2e99feSLydia Wang 		return;
2381f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2391f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2401f2e99feSLydia Wang 	cancel_delayed_work(&spec->vt1708_hp_work);
2411f2e99feSLydia Wang 	flush_scheduled_work();
2421f2e99feSLydia Wang }
243f5271101SLydia Wang 
244*25eaba2fSLydia Wang 
245f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
246f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
247f5271101SLydia Wang {
248f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
249f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
250f5271101SLydia Wang 
251f5271101SLydia Wang 	set_jack_power_state(codec);
252f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2531f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2541f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2551f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2561f2e99feSLydia Wang 		else
2571f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2581f2e99feSLydia Wang 	}
259f5271101SLydia Wang 	return change;
260f5271101SLydia Wang }
261f5271101SLydia Wang 
262f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
263f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
264f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
265f5271101SLydia Wang 			.name = NULL,					\
266f5271101SLydia Wang 			.index = 0,					\
267f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
268f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
269f5271101SLydia Wang 			.put = analog_input_switch_put,			\
270f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
271f5271101SLydia Wang 
272*25eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
273*25eaba2fSLydia Wang 
274*25eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
275*25eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
276*25eaba2fSLydia Wang {
277*25eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
278*25eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
279*25eaba2fSLydia Wang 	int i;
280*25eaba2fSLydia Wang 	int change = 0;
281*25eaba2fSLydia Wang 
282*25eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
283*25eaba2fSLydia Wang 	int lmute, rmute;
284*25eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
285*25eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
286*25eaba2fSLydia Wang 		return change;
287*25eaba2fSLydia Wang 	}
288*25eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
289*25eaba2fSLydia Wang 					      ucontrol);
290*25eaba2fSLydia Wang 	/* Get mute value */
291*25eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
292*25eaba2fSLydia Wang 	valp++;
293*25eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
294*25eaba2fSLydia Wang 
295*25eaba2fSLydia Wang 	/* Set hp pins */
296*25eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
297*25eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
298*25eaba2fSLydia Wang 			snd_hda_codec_amp_update(
299*25eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
300*25eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
301*25eaba2fSLydia Wang 				lmute);
302*25eaba2fSLydia Wang 			snd_hda_codec_amp_update(
303*25eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
304*25eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
305*25eaba2fSLydia Wang 				rmute);
306*25eaba2fSLydia Wang 		}
307*25eaba2fSLydia Wang 	}
308*25eaba2fSLydia Wang 
309*25eaba2fSLydia Wang 	if (!lmute && !rmute) {
310*25eaba2fSLydia Wang 		/* Line Outs */
311*25eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
312*25eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
313*25eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
314*25eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
315*25eaba2fSLydia Wang 		/* Speakers */
316*25eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
317*25eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
318*25eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
319*25eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
320*25eaba2fSLydia Wang 		/* unmute */
321*25eaba2fSLydia Wang 		via_hp_bind_automute(codec);
322*25eaba2fSLydia Wang 
323*25eaba2fSLydia Wang 	} else {
324*25eaba2fSLydia Wang 		if (lmute) {
325*25eaba2fSLydia Wang 			/* Mute all left channels */
326*25eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
327*25eaba2fSLydia Wang 				snd_hda_codec_amp_update(
328*25eaba2fSLydia Wang 					codec,
329*25eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
330*25eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
331*25eaba2fSLydia Wang 					lmute);
332*25eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
333*25eaba2fSLydia Wang 				snd_hda_codec_amp_update(
334*25eaba2fSLydia Wang 					codec,
335*25eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
336*25eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
337*25eaba2fSLydia Wang 					lmute);
338*25eaba2fSLydia Wang 		}
339*25eaba2fSLydia Wang 		if (rmute) {
340*25eaba2fSLydia Wang 			/* mute all right channels */
341*25eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
342*25eaba2fSLydia Wang 				snd_hda_codec_amp_update(
343*25eaba2fSLydia Wang 					codec,
344*25eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
345*25eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
346*25eaba2fSLydia Wang 					rmute);
347*25eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
348*25eaba2fSLydia Wang 				snd_hda_codec_amp_update(
349*25eaba2fSLydia Wang 					codec,
350*25eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
351*25eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
352*25eaba2fSLydia Wang 					rmute);
353*25eaba2fSLydia Wang 		}
354*25eaba2fSLydia Wang 	}
355*25eaba2fSLydia Wang 	return change;
356*25eaba2fSLydia Wang }
357*25eaba2fSLydia Wang 
358*25eaba2fSLydia Wang #define BIND_PIN_MUTE							\
359*25eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
360*25eaba2fSLydia Wang 			.name = NULL,					\
361*25eaba2fSLydia Wang 			.index = 0,					\
362*25eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
363*25eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
364*25eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
365*25eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
366*25eaba2fSLydia Wang 
367c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_control_templates[] = {
368c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
369c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
370f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
371*25eaba2fSLydia Wang 	BIND_PIN_MUTE,
372c577b8a1SJoseph Chan };
373c577b8a1SJoseph Chan 
374c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
375c577b8a1SJoseph Chan 	/* ADC1-2 */
376c577b8a1SJoseph Chan 	0x15, 0x27
377c577b8a1SJoseph Chan };
378c577b8a1SJoseph Chan 
379c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
380c577b8a1SJoseph Chan 	/* ADC1-2 */
381c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
382c577b8a1SJoseph Chan };
383c577b8a1SJoseph Chan 
384f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
385f7278fd0SJosepch Chan 	/* ADC1-2 */
386f7278fd0SJosepch Chan 	0x13, 0x14
387f7278fd0SJosepch Chan };
388f7278fd0SJosepch Chan 
389d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
390d949cac1SHarald Welte 	/* ADC1-2 */
391d949cac1SHarald Welte 	0x13, 0x14
392d949cac1SHarald Welte };
393d949cac1SHarald Welte 
394d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
395d949cac1SHarald Welte 	/* ADC1-2 */
396d949cac1SHarald Welte 	0x12, 0x20, 0x1F
397d949cac1SHarald Welte };
398d949cac1SHarald Welte 
399eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
400eb7188caSLydia Wang 	/* ADC1-2 */
401eb7188caSLydia Wang 	0x10, 0x11
402eb7188caSLydia Wang };
403eb7188caSLydia Wang 
404f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
405f3db423dSLydia Wang 	/* ADC1-2 */
406f3db423dSLydia Wang 	0x13, 0x14
407f3db423dSLydia Wang };
408f3db423dSLydia Wang 
409*25eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
410*25eaba2fSLydia Wang 	/* ADC1-2 */
411*25eaba2fSLydia Wang 	0x10, 0x11
412*25eaba2fSLydia Wang };
413*25eaba2fSLydia Wang 
414c577b8a1SJoseph Chan /* add dynamic controls */
415c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name,
416c577b8a1SJoseph Chan 			   unsigned long val)
417c577b8a1SJoseph Chan {
418c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
419c577b8a1SJoseph Chan 
420603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
421603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
422c577b8a1SJoseph Chan 	if (!knew)
423c577b8a1SJoseph Chan 		return -ENOMEM;
424c577b8a1SJoseph Chan 	*knew = vt1708_control_templates[type];
425c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
426c577b8a1SJoseph Chan 	if (!knew->name)
427c577b8a1SJoseph Chan 		return -ENOMEM;
428c577b8a1SJoseph Chan 	knew->private_value = val;
429c577b8a1SJoseph Chan 	return 0;
430c577b8a1SJoseph Chan }
431c577b8a1SJoseph Chan 
432603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
433603c4019STakashi Iwai {
434603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
435603c4019STakashi Iwai 
436603c4019STakashi Iwai 	if (spec->kctls.list) {
437603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
438603c4019STakashi Iwai 		int i;
439603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
440603c4019STakashi Iwai 			kfree(kctl[i].name);
441603c4019STakashi Iwai 	}
442603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
443603c4019STakashi Iwai }
444603c4019STakashi Iwai 
445c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4469510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4479510e8ddSLydia Wang 				int idx, int mix_nid)
448c577b8a1SJoseph Chan {
449c577b8a1SJoseph Chan 	char name[32];
450c577b8a1SJoseph Chan 	int err;
451c577b8a1SJoseph Chan 
452c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
453c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
454c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
455c577b8a1SJoseph Chan 	if (err < 0)
456c577b8a1SJoseph Chan 		return err;
457c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
458f5271101SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
459c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
460c577b8a1SJoseph Chan 	if (err < 0)
461c577b8a1SJoseph Chan 		return err;
462c577b8a1SJoseph Chan 	return 0;
463c577b8a1SJoseph Chan }
464c577b8a1SJoseph Chan 
465c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
466c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
467c577b8a1SJoseph Chan 					   int dac_idx)
468c577b8a1SJoseph Chan {
469c577b8a1SJoseph Chan 	/* set as output */
470c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
471c577b8a1SJoseph Chan 			    pin_type);
472c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
473c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
474d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
475d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
476d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
477c577b8a1SJoseph Chan }
478c577b8a1SJoseph Chan 
479c577b8a1SJoseph Chan 
480c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
481c577b8a1SJoseph Chan {
482c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
483c577b8a1SJoseph Chan 	int i;
484c577b8a1SJoseph Chan 
485c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
486c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
487c577b8a1SJoseph Chan 		if (nid)
488c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
489c577b8a1SJoseph Chan 	}
490c577b8a1SJoseph Chan }
491c577b8a1SJoseph Chan 
492c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
493c577b8a1SJoseph Chan {
494c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
495c577b8a1SJoseph Chan 	hda_nid_t pin;
496*25eaba2fSLydia Wang 	int i;
497c577b8a1SJoseph Chan 
498*25eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
499*25eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
500c577b8a1SJoseph Chan 		if (pin) /* connect to front */
501c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
502c577b8a1SJoseph Chan 	}
503*25eaba2fSLydia Wang }
504c577b8a1SJoseph Chan 
505c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
506c577b8a1SJoseph Chan {
507c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
508c577b8a1SJoseph Chan 	int i;
509c577b8a1SJoseph Chan 
510c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
511c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.input_pins[i];
512c577b8a1SJoseph Chan 
513c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
514c577b8a1SJoseph Chan 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
515c577b8a1SJoseph Chan 				    (i <= AUTO_PIN_FRONT_MIC ?
516c577b8a1SJoseph Chan 				     PIN_VREF50 : PIN_IN));
517c577b8a1SJoseph Chan 
518c577b8a1SJoseph Chan 	}
519c577b8a1SJoseph Chan }
520f5271101SLydia Wang 
5211564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
5221564b287SLydia Wang 
523f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
524f5271101SLydia Wang 				unsigned int *affected_parm)
525f5271101SLydia Wang {
526f5271101SLydia Wang 	unsigned parm;
527f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
528f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
529f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
530f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
531f5271101SLydia Wang 	unsigned present = snd_hda_codec_read(codec, nid, 0,
532f5271101SLydia Wang 					      AC_VERB_GET_PIN_SENSE, 0) >> 31;
5331564b287SLydia Wang 	struct via_spec *spec = codec->spec;
5341564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5351564b287SLydia Wang 	    || ((no_presence || present)
5361564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
537f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
538f5271101SLydia Wang 		parm = AC_PWRST_D0;
539f5271101SLydia Wang 	} else
540f5271101SLydia Wang 		parm = AC_PWRST_D3;
541f5271101SLydia Wang 
542f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
543f5271101SLydia Wang }
544f5271101SLydia Wang 
545f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec)
546f5271101SLydia Wang {
547f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
548f5271101SLydia Wang 	int imux_is_smixer;
549f5271101SLydia Wang 	unsigned int parm;
550f5271101SLydia Wang 
551f5271101SLydia Wang 	if (spec->codec_type == VT1702) {
552f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
553f5271101SLydia Wang 			codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
554f5271101SLydia Wang 		/* inputs */
555f5271101SLydia Wang 		/* PW 1/2/5 (14h/15h/18h) */
556f5271101SLydia Wang 		parm = AC_PWRST_D3;
557f5271101SLydia Wang 		set_pin_power_state(codec, 0x14, &parm);
558f5271101SLydia Wang 		set_pin_power_state(codec, 0x15, &parm);
559f5271101SLydia Wang 		set_pin_power_state(codec, 0x18, &parm);
560f5271101SLydia Wang 		if (imux_is_smixer)
561f5271101SLydia Wang 			parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
562f5271101SLydia Wang 		/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
563f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
564f5271101SLydia Wang 				    parm);
565f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
566f5271101SLydia Wang 				    parm);
567f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
568f5271101SLydia Wang 				    parm);
569f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
570f5271101SLydia Wang 				    parm);
571f5271101SLydia Wang 
572f5271101SLydia Wang 		/* outputs */
573f5271101SLydia Wang 		/* PW 3/4 (16h/17h) */
574f5271101SLydia Wang 		parm = AC_PWRST_D3;
575f5271101SLydia Wang 		set_pin_power_state(codec, 0x16, &parm);
576f5271101SLydia Wang 		set_pin_power_state(codec, 0x17, &parm);
577f5271101SLydia Wang 		/* MW0 (1ah), AOW 0/1 (10h/1dh) */
578f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
579f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
580f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
581f5271101SLydia Wang 				    parm);
582f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
583f5271101SLydia Wang 				    parm);
584f5271101SLydia Wang 	} else if (spec->codec_type == VT1708B_8CH
585f5271101SLydia Wang 		   || spec->codec_type == VT1708B_4CH
586f5271101SLydia Wang 		   || spec->codec_type == VT1708S) {
587f5271101SLydia Wang 		/* SW0 (17h) = stereo mixer */
588f5271101SLydia Wang 		int is_8ch = spec->codec_type != VT1708B_4CH;
589f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
590f5271101SLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
591f5271101SLydia Wang 			== ((spec->codec_type == VT1708S)  ? 5 : 0);
592f5271101SLydia Wang 		/* inputs */
593f5271101SLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
594f5271101SLydia Wang 		parm = AC_PWRST_D3;
595f5271101SLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
596f5271101SLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
597f5271101SLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
598f5271101SLydia Wang 		if (imux_is_smixer)
599f5271101SLydia Wang 			parm = AC_PWRST_D0;
600f5271101SLydia Wang 		/* SW0 (17h), AIW 0/1 (13h/14h) */
601f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
602f5271101SLydia Wang 				    parm);
603f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
604f5271101SLydia Wang 				    parm);
605f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
606f5271101SLydia Wang 				    parm);
607f5271101SLydia Wang 
608f5271101SLydia Wang 		/* outputs */
609f5271101SLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
610f5271101SLydia Wang 		parm = AC_PWRST_D3;
611f5271101SLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
612f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
613f5271101SLydia Wang 				    parm);
614f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
615f5271101SLydia Wang 				    parm);
616f5271101SLydia Wang 
617f5271101SLydia Wang 		/* PW6 (22h), SW2 (26h), AOW2 (24h) */
618f5271101SLydia Wang 		if (is_8ch) {
619f5271101SLydia Wang 			parm = AC_PWRST_D3;
620f5271101SLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
621f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x26, 0,
622f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
623f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x24, 0,
624f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
625f5271101SLydia Wang 		}
626f5271101SLydia Wang 
627f5271101SLydia Wang 		/* PW 3/4/7 (1ch/1dh/23h) */
628f5271101SLydia Wang 		parm = AC_PWRST_D3;
629f5271101SLydia Wang 		/* force to D0 for internal Speaker */
630f5271101SLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
631f5271101SLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
632f5271101SLydia Wang 		if (is_8ch)
633f5271101SLydia Wang 			set_pin_power_state(codec, 0x23, &parm);
634f5271101SLydia Wang 		/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
635f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
636f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
637f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
638f5271101SLydia Wang 				    parm);
639f5271101SLydia Wang 		if (is_8ch) {
640f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
641f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
642f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x27, 0,
643f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
644f5271101SLydia Wang 		}
645eb7188caSLydia Wang 	}  else if (spec->codec_type == VT1718S) {
646eb7188caSLydia Wang 		/* MUX6 (1eh) = stereo mixer */
647eb7188caSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
648eb7188caSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
649eb7188caSLydia Wang 		/* inputs */
650eb7188caSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
651eb7188caSLydia Wang 		parm = AC_PWRST_D3;
652eb7188caSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
653eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
654eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
655eb7188caSLydia Wang 		if (imux_is_smixer)
656eb7188caSLydia Wang 			parm = AC_PWRST_D0;
657eb7188caSLydia Wang 		/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
658eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
659eb7188caSLydia Wang 				    parm);
660eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
661eb7188caSLydia Wang 				    parm);
662eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
663eb7188caSLydia Wang 				    parm);
664eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
665eb7188caSLydia Wang 				    parm);
666eb7188caSLydia Wang 
667eb7188caSLydia Wang 		/* outputs */
668eb7188caSLydia Wang 		/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
669eb7188caSLydia Wang 		parm = AC_PWRST_D3;
670eb7188caSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
671eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
672eb7188caSLydia Wang 				    parm);
673eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
674eb7188caSLydia Wang 				    parm);
675eb7188caSLydia Wang 
676eb7188caSLydia Wang 		/* PW2 (26h), AOW2 (ah) */
677eb7188caSLydia Wang 		parm = AC_PWRST_D3;
678eb7188caSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
679eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
680eb7188caSLydia Wang 				    parm);
681eb7188caSLydia Wang 
682eb7188caSLydia Wang 		/* PW0/1 (24h/25h) */
683eb7188caSLydia Wang 		parm = AC_PWRST_D3;
684eb7188caSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
685eb7188caSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
686eb7188caSLydia Wang 		if (!spec->hp_independent_mode) /* check for redirected HP */
687eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
688eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
689eb7188caSLydia Wang 				    parm);
690eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
691eb7188caSLydia Wang 				    parm);
692eb7188caSLydia Wang 		/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
693eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
694eb7188caSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
695eb7188caSLydia Wang 		if (spec->hp_independent_mode) {
696eb7188caSLydia Wang 			/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
697eb7188caSLydia Wang 			parm = AC_PWRST_D3;
698eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
699eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x1b, 0,
700eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
701eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
702eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
703eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0xc, 0,
704eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
705eb7188caSLydia Wang 		}
706f3db423dSLydia Wang 	} else if (spec->codec_type == VT1716S) {
707f3db423dSLydia Wang 		unsigned int mono_out, present;
708f3db423dSLydia Wang 		/* SW0 (17h) = stereo mixer */
709f3db423dSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
710f3db423dSLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) ==  5;
711f3db423dSLydia Wang 		/* inputs */
712f3db423dSLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
713f3db423dSLydia Wang 		parm = AC_PWRST_D3;
714f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
715f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
716f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
717f3db423dSLydia Wang 		if (imux_is_smixer)
718f3db423dSLydia Wang 			parm = AC_PWRST_D0;
719f3db423dSLydia Wang 		/* SW0 (17h), AIW0(13h) */
720f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
721f3db423dSLydia Wang 				    parm);
722f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
723f3db423dSLydia Wang 				    parm);
724f3db423dSLydia Wang 
725f3db423dSLydia Wang 		parm = AC_PWRST_D3;
726f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
727f3db423dSLydia Wang 		/* PW11 (22h) */
728f3db423dSLydia Wang 		if (spec->dmic_enabled)
729f3db423dSLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
730f3db423dSLydia Wang 		else
731f3db423dSLydia Wang 			snd_hda_codec_write(
732f3db423dSLydia Wang 				codec, 0x22, 0,
733f3db423dSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
734f3db423dSLydia Wang 
735f3db423dSLydia Wang 		/* SW2(26h), AIW1(14h) */
736f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
737f3db423dSLydia Wang 				    parm);
738f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
739f3db423dSLydia Wang 				    parm);
740f3db423dSLydia Wang 
741f3db423dSLydia Wang 		/* outputs */
742f3db423dSLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
743f3db423dSLydia Wang 		parm = AC_PWRST_D3;
744f3db423dSLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
745f3db423dSLydia Wang 		/* Smart 5.1 PW2(1bh) */
746f3db423dSLydia Wang 		if (spec->smart51_enabled)
747f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1b, &parm);
748f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
749f3db423dSLydia Wang 				    parm);
750f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
751f3db423dSLydia Wang 				    parm);
752f3db423dSLydia Wang 
753f3db423dSLydia Wang 		/* PW7 (23h), SW3 (27h), AOW3 (25h) */
754f3db423dSLydia Wang 		parm = AC_PWRST_D3;
755f3db423dSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
756f3db423dSLydia Wang 		/* Smart 5.1 PW1(1ah) */
757f3db423dSLydia Wang 		if (spec->smart51_enabled)
758f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
759f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
760f3db423dSLydia Wang 				    parm);
761f3db423dSLydia Wang 
762f3db423dSLydia Wang 		/* Smart 5.1 PW5(1eh) */
763f3db423dSLydia Wang 		if (spec->smart51_enabled)
764f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1e, &parm);
765f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
766f3db423dSLydia Wang 				    parm);
767f3db423dSLydia Wang 
768f3db423dSLydia Wang 		/* Mono out */
769f3db423dSLydia Wang 		/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
770f3db423dSLydia Wang 		present = snd_hda_codec_read(
771f3db423dSLydia Wang 			codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
772f3db423dSLydia Wang 		if (present)
773f3db423dSLydia Wang 			mono_out = 0;
774f3db423dSLydia Wang 		else {
775f3db423dSLydia Wang 			present = snd_hda_codec_read(
776f3db423dSLydia Wang 				codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0)
777f3db423dSLydia Wang 				& 0x80000000;
778f3db423dSLydia Wang 			if (!spec->hp_independent_mode && present)
779f3db423dSLydia Wang 				mono_out = 0;
780f3db423dSLydia Wang 			else
781f3db423dSLydia Wang 				mono_out = 1;
782f3db423dSLydia Wang 		}
783f3db423dSLydia Wang 		parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
784f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
785f3db423dSLydia Wang 				    parm);
786f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
787f3db423dSLydia Wang 				    parm);
788f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
789f3db423dSLydia Wang 				    parm);
790f3db423dSLydia Wang 
791f3db423dSLydia Wang 		/* PW 3/4 (1ch/1dh) */
792f3db423dSLydia Wang 		parm = AC_PWRST_D3;
793f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
794f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
795f3db423dSLydia Wang 		/* HP Independent Mode, power on AOW3 */
796f3db423dSLydia Wang 		if (spec->hp_independent_mode)
797f3db423dSLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
798f3db423dSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
799f3db423dSLydia Wang 
800f3db423dSLydia Wang 		/* force to D0 for internal Speaker */
801f3db423dSLydia Wang 		/* MW0 (16h), AOW0 (10h) */
802f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
803f3db423dSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
804f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
805f3db423dSLydia Wang 				    mono_out ? AC_PWRST_D0 : parm);
806*25eaba2fSLydia Wang 	} else if (spec->codec_type == VT2002P) {
807*25eaba2fSLydia Wang 		unsigned int present;
808*25eaba2fSLydia Wang 		/* MUX9 (1eh) = stereo mixer */
809*25eaba2fSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
810*25eaba2fSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
811*25eaba2fSLydia Wang 		/* inputs */
812*25eaba2fSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
813*25eaba2fSLydia Wang 		parm = AC_PWRST_D3;
814*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
815*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
816*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
817*25eaba2fSLydia Wang 		if (imux_is_smixer)
818*25eaba2fSLydia Wang 			parm = AC_PWRST_D0;
819*25eaba2fSLydia Wang 		/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
820*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
821*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
822*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
823*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
824*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
825*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
826*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
827*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
828*25eaba2fSLydia Wang 
829*25eaba2fSLydia Wang 		/* outputs */
830*25eaba2fSLydia Wang 		/* AOW0 (8h)*/
831*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
832*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
833*25eaba2fSLydia Wang 
834*25eaba2fSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
835*25eaba2fSLydia Wang 		parm = AC_PWRST_D3;
836*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
837*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
838*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
839*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x37,
840*25eaba2fSLydia Wang 				    0, AC_VERB_SET_POWER_STATE, parm);
841*25eaba2fSLydia Wang 
842*25eaba2fSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
843*25eaba2fSLydia Wang 		parm = AC_PWRST_D3;
844*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
845*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
846*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
847*25eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
848*25eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
849*25eaba2fSLydia Wang 		if (spec->hp_independent_mode)	{
850*25eaba2fSLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
851*25eaba2fSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
852*25eaba2fSLydia Wang 		}
853*25eaba2fSLydia Wang 
854*25eaba2fSLydia Wang 		/* Class-D */
855*25eaba2fSLydia Wang 		/* PW0 (24h), MW0(18h), MUX0(34h) */
856*25eaba2fSLydia Wang 		present = snd_hda_codec_read(
857*25eaba2fSLydia Wang 			codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
858*25eaba2fSLydia Wang 		parm = AC_PWRST_D3;
859*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
860*25eaba2fSLydia Wang 		if (present) {
861*25eaba2fSLydia Wang 			snd_hda_codec_write(
862*25eaba2fSLydia Wang 				codec, 0x18, 0,
863*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
864*25eaba2fSLydia Wang 			snd_hda_codec_write(
865*25eaba2fSLydia Wang 				codec, 0x34, 0,
866*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
867*25eaba2fSLydia Wang 		} else {
868*25eaba2fSLydia Wang 			snd_hda_codec_write(
869*25eaba2fSLydia Wang 				codec, 0x18, 0,
870*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
871*25eaba2fSLydia Wang 			snd_hda_codec_write(
872*25eaba2fSLydia Wang 				codec, 0x34, 0,
873*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
874*25eaba2fSLydia Wang 		}
875*25eaba2fSLydia Wang 
876*25eaba2fSLydia Wang 		/* Mono Out */
877*25eaba2fSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
878*25eaba2fSLydia Wang 		present = snd_hda_codec_read(
879*25eaba2fSLydia Wang 			codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
880*25eaba2fSLydia Wang 		parm = AC_PWRST_D3;
881*25eaba2fSLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
882*25eaba2fSLydia Wang 		if (present) {
883*25eaba2fSLydia Wang 			snd_hda_codec_write(
884*25eaba2fSLydia Wang 				codec, 0x17, 0,
885*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
886*25eaba2fSLydia Wang 			snd_hda_codec_write(
887*25eaba2fSLydia Wang 				codec, 0x3b, 0,
888*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
889*25eaba2fSLydia Wang 		} else {
890*25eaba2fSLydia Wang 			snd_hda_codec_write(
891*25eaba2fSLydia Wang 				codec, 0x17, 0,
892*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
893*25eaba2fSLydia Wang 			snd_hda_codec_write(
894*25eaba2fSLydia Wang 				codec, 0x3b, 0,
895*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
896*25eaba2fSLydia Wang 		}
897*25eaba2fSLydia Wang 
898*25eaba2fSLydia Wang 		/* MW9 (21h) */
899*25eaba2fSLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
900*25eaba2fSLydia Wang 			snd_hda_codec_write(
901*25eaba2fSLydia Wang 				codec, 0x21, 0,
902*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
903*25eaba2fSLydia Wang 		else
904*25eaba2fSLydia Wang 			snd_hda_codec_write(
905*25eaba2fSLydia Wang 				codec, 0x21, 0,
906*25eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
907f5271101SLydia Wang 	}
908f5271101SLydia Wang }
909f5271101SLydia Wang 
910c577b8a1SJoseph Chan /*
911c577b8a1SJoseph Chan  * input MUX handling
912c577b8a1SJoseph Chan  */
913c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
914c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
915c577b8a1SJoseph Chan {
916c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
917c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
918c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
919c577b8a1SJoseph Chan }
920c577b8a1SJoseph Chan 
921c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
922c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
923c577b8a1SJoseph Chan {
924c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
925c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
926c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
927c577b8a1SJoseph Chan 
928c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
929c577b8a1SJoseph Chan 	return 0;
930c577b8a1SJoseph Chan }
931c577b8a1SJoseph Chan 
932c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
933c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
934c577b8a1SJoseph Chan {
935c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
936c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
937c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
938c577b8a1SJoseph Chan 
939337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
940337b9d02STakashi Iwai 		return -EINVAL;
941a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
942a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
943a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
944a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
945a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
946a80e6e3cSLydia Wang 	/* update jack power state */
947a80e6e3cSLydia Wang 	set_jack_power_state(codec);
948a80e6e3cSLydia Wang 
949c577b8a1SJoseph Chan 	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
950337b9d02STakashi Iwai 				     spec->mux_nids[adc_idx],
951c577b8a1SJoseph Chan 				     &spec->cur_mux[adc_idx]);
952c577b8a1SJoseph Chan }
953c577b8a1SJoseph Chan 
9540aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
9550aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
9560aa62aefSHarald Welte {
9570aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9580aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
9590aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
9600aa62aefSHarald Welte }
9610aa62aefSHarald Welte 
9620aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
9630aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
9640aa62aefSHarald Welte {
9650aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9660aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
967eb7188caSLydia Wang 	hda_nid_t nid;
968eb7188caSLydia Wang 	unsigned int pinsel;
969eb7188caSLydia Wang 
970eb7188caSLydia Wang 	switch (spec->codec_type) {
971eb7188caSLydia Wang 	case VT1718S:
972eb7188caSLydia Wang 		nid = 0x34;
973eb7188caSLydia Wang 		break;
974*25eaba2fSLydia Wang 	case VT2002P:
975*25eaba2fSLydia Wang 		nid = 0x35;
976*25eaba2fSLydia Wang 		break;
977eb7188caSLydia Wang 	default:
978eb7188caSLydia Wang 		nid = spec->autocfg.hp_pins[0];
979eb7188caSLydia Wang 		break;
980eb7188caSLydia Wang 	}
981eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
982eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
9830aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
9840aa62aefSHarald Welte 				      0x00);
9850aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
9860aa62aefSHarald Welte 
9870aa62aefSHarald Welte 	return 0;
9880aa62aefSHarald Welte }
9890aa62aefSHarald Welte 
9900713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
9910713efebSLydia Wang {
9920713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
9930713efebSLydia Wang 	if (ctl) {
9940713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
9950713efebSLydia Wang 		ctl->vd[0].access |= active
9960713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
9970713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
9980713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
9990713efebSLydia Wang 	}
10000713efebSLydia Wang }
10010713efebSLydia Wang 
1002cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
1003cdc1784dSLydia Wang {
1004cdc1784dSLydia Wang 	/* mute side channel */
1005cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
1006cdc1784dSLydia Wang 	unsigned int parm = spec->hp_independent_mode
1007cdc1784dSLydia Wang 		? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
1008cdc1784dSLydia Wang 	hda_nid_t sw3;
1009cdc1784dSLydia Wang 
1010cdc1784dSLydia Wang 	switch (spec->codec_type) {
1011cdc1784dSLydia Wang 	case VT1708:
1012cdc1784dSLydia Wang 		sw3 = 0x1b;
1013cdc1784dSLydia Wang 		break;
1014cdc1784dSLydia Wang 	case VT1709_10CH:
1015cdc1784dSLydia Wang 		sw3 = 0x29;
1016cdc1784dSLydia Wang 		break;
1017cdc1784dSLydia Wang 	case VT1708B_8CH:
1018cdc1784dSLydia Wang 	case VT1708S:
1019cdc1784dSLydia Wang 		sw3 = 0x27;
1020cdc1784dSLydia Wang 		break;
1021cdc1784dSLydia Wang 	default:
1022cdc1784dSLydia Wang 		sw3 = 0;
1023cdc1784dSLydia Wang 		break;
1024cdc1784dSLydia Wang 	}
1025cdc1784dSLydia Wang 
1026cdc1784dSLydia Wang 	if (sw3)
1027cdc1784dSLydia Wang 		snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1028cdc1784dSLydia Wang 				    parm);
1029cdc1784dSLydia Wang 	return 0;
1030cdc1784dSLydia Wang }
1031cdc1784dSLydia Wang 
10320aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
10330aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
10340aa62aefSHarald Welte {
10350aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
10360aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10370aa62aefSHarald Welte 	hda_nid_t nid = spec->autocfg.hp_pins[0];
10380aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
1039cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
1040cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
1041cdc1784dSLydia Wang 		? 1 : 0;
10420aa62aefSHarald Welte 
1043eb7188caSLydia Wang 	switch (spec->codec_type) {
1044eb7188caSLydia Wang 	case VT1718S:
1045eb7188caSLydia Wang 		nid = 0x34;
1046eb7188caSLydia Wang 		pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
1047eb7188caSLydia Wang 		spec->multiout.num_dacs = 4;
1048eb7188caSLydia Wang 		break;
1049*25eaba2fSLydia Wang 	case VT2002P:
1050*25eaba2fSLydia Wang 		nid = 0x35;
1051*25eaba2fSLydia Wang 		break;
1052eb7188caSLydia Wang 	default:
1053eb7188caSLydia Wang 		nid = spec->autocfg.hp_pins[0];
1054eb7188caSLydia Wang 		break;
1055eb7188caSLydia Wang 	}
1056cdc1784dSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
10570aa62aefSHarald Welte 
1058cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
1059cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
1060cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
10610aa62aefSHarald Welte 					   0, 0, 0);
10620aa62aefSHarald Welte 
1063cdc1784dSLydia Wang 	update_side_mute_status(codec);
10640713efebSLydia Wang 	/* update HP volume/swtich active state */
10650713efebSLydia Wang 	if (spec->codec_type == VT1708S
1066eb7188caSLydia Wang 	    || spec->codec_type == VT1702
1067f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
1068*25eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
1069*25eaba2fSLydia Wang 	    || spec->codec_type == VT2002P) {
10700713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
10710713efebSLydia Wang 			     spec->hp_independent_mode);
10720713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
10730713efebSLydia Wang 			     spec->hp_independent_mode);
10740713efebSLydia Wang 	}
10750aa62aefSHarald Welte 	return 0;
10760aa62aefSHarald Welte }
10770aa62aefSHarald Welte 
10780aa62aefSHarald Welte static struct snd_kcontrol_new via_hp_mixer[] = {
10790aa62aefSHarald Welte 	{
10800aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10810aa62aefSHarald Welte 		.name = "Independent HP",
10820aa62aefSHarald Welte 		.count = 1,
10830aa62aefSHarald Welte 		.info = via_independent_hp_info,
10840aa62aefSHarald Welte 		.get = via_independent_hp_get,
10850aa62aefSHarald Welte 		.put = via_independent_hp_put,
10860aa62aefSHarald Welte 	},
10870aa62aefSHarald Welte 	{ } /* end */
10880aa62aefSHarald Welte };
10890aa62aefSHarald Welte 
10901564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
10911564b287SLydia Wang {
10921564b287SLydia Wang 	int i;
10931564b287SLydia Wang 	struct snd_ctl_elem_id id;
10941564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
10951564b287SLydia Wang 
10961564b287SLydia Wang 	memset(&id, 0, sizeof(id));
10971564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
10981564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
10991564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
11001564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
11011564b287SLydia Wang 			       &id);
11021564b287SLydia Wang 	}
11031564b287SLydia Wang }
11041564b287SLydia Wang 
11051564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
11061564b287SLydia Wang {
11071564b287SLydia Wang 	struct via_spec *spec = codec->spec;
11081564b287SLydia Wang 	hda_nid_t  nid_mixer;
11091564b287SLydia Wang 	int start_idx;
11101564b287SLydia Wang 	int end_idx;
11111564b287SLydia Wang 	int i;
11121564b287SLydia Wang 	/* get nid of MW0 and start & end index */
11131564b287SLydia Wang 	switch (spec->codec_type) {
11141564b287SLydia Wang 	case VT1708:
11151564b287SLydia Wang 		nid_mixer = 0x17;
11161564b287SLydia Wang 		start_idx = 2;
11171564b287SLydia Wang 		end_idx = 4;
11181564b287SLydia Wang 		break;
11191564b287SLydia Wang 	case VT1709_10CH:
11201564b287SLydia Wang 	case VT1709_6CH:
11211564b287SLydia Wang 		nid_mixer = 0x18;
11221564b287SLydia Wang 		start_idx = 2;
11231564b287SLydia Wang 		end_idx = 4;
11241564b287SLydia Wang 		break;
11251564b287SLydia Wang 	case VT1708B_8CH:
11261564b287SLydia Wang 	case VT1708B_4CH:
11271564b287SLydia Wang 	case VT1708S:
1128f3db423dSLydia Wang 	case VT1716S:
11291564b287SLydia Wang 		nid_mixer = 0x16;
11301564b287SLydia Wang 		start_idx = 2;
11311564b287SLydia Wang 		end_idx = 4;
11321564b287SLydia Wang 		break;
11331564b287SLydia Wang 	default:
11341564b287SLydia Wang 		return;
11351564b287SLydia Wang 	}
11361564b287SLydia Wang 	/* check AA path's mute status */
11371564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
11381564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
11391564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
11401564b287SLydia Wang 					 HDA_AMP_MUTE, val);
11411564b287SLydia Wang 	}
11421564b287SLydia Wang }
11431564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
11441564b287SLydia Wang {
11451564b287SLydia Wang 	int res = 0;
11461564b287SLydia Wang 	int index;
11471564b287SLydia Wang 	for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
11481564b287SLydia Wang 		if (pin == spec->autocfg.input_pins[index]) {
11491564b287SLydia Wang 			res = 1;
11501564b287SLydia Wang 			break;
11511564b287SLydia Wang 		}
11521564b287SLydia Wang 	}
11531564b287SLydia Wang 	return res;
11541564b287SLydia Wang }
11551564b287SLydia Wang 
11561564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
11571564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
11581564b287SLydia Wang {
11591564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
11601564b287SLydia Wang 	uinfo->count = 1;
11611564b287SLydia Wang 	uinfo->value.integer.min = 0;
11621564b287SLydia Wang 	uinfo->value.integer.max = 1;
11631564b287SLydia Wang 	return 0;
11641564b287SLydia Wang }
11651564b287SLydia Wang 
11661564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
11671564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
11681564b287SLydia Wang {
11691564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11701564b287SLydia Wang 	struct via_spec *spec = codec->spec;
11711564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
11721564b287SLydia Wang 	int on = 1;
11731564b287SLydia Wang 	int i;
11741564b287SLydia Wang 
11751564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
11761564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
11771564b287SLydia Wang 		if (nid) {
11781564b287SLydia Wang 			int ctl =
11791564b287SLydia Wang 			    snd_hda_codec_read(codec, nid, 0,
11801564b287SLydia Wang 					       AC_VERB_GET_PIN_WIDGET_CONTROL,
11811564b287SLydia Wang 					       0);
11821564b287SLydia Wang 			if (i == AUTO_PIN_FRONT_MIC
1183eb7188caSLydia Wang 			    && spec->hp_independent_mode
1184eb7188caSLydia Wang 			    && spec->codec_type != VT1718S)
11851564b287SLydia Wang 				continue; /* ignore FMic for independent HP */
11861564b287SLydia Wang 			if (ctl & AC_PINCTL_IN_EN
11871564b287SLydia Wang 			    && !(ctl & AC_PINCTL_OUT_EN))
11881564b287SLydia Wang 				on = 0;
11891564b287SLydia Wang 		}
11901564b287SLydia Wang 	}
11911564b287SLydia Wang 	*ucontrol->value.integer.value = on;
11921564b287SLydia Wang 	return 0;
11931564b287SLydia Wang }
11941564b287SLydia Wang 
11951564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
11961564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
11971564b287SLydia Wang {
11981564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11991564b287SLydia Wang 	struct via_spec *spec = codec->spec;
12001564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
12011564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
12021564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
12031564b287SLydia Wang 	int i;
12041564b287SLydia Wang 
12051564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
12061564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
12071564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC
1208eb7188caSLydia Wang 		    && spec->hp_independent_mode
1209eb7188caSLydia Wang 		    && spec->codec_type != VT1718S)
12101564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
12111564b287SLydia Wang 		if (nid) {
12121564b287SLydia Wang 			unsigned int parm = snd_hda_codec_read(
12131564b287SLydia Wang 				codec, nid, 0,
12141564b287SLydia Wang 				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
12151564b287SLydia Wang 			parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
12161564b287SLydia Wang 			parm |= out_in;
12171564b287SLydia Wang 			snd_hda_codec_write(codec, nid, 0,
12181564b287SLydia Wang 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
12191564b287SLydia Wang 					    parm);
12201564b287SLydia Wang 			if (out_in == AC_PINCTL_OUT_EN) {
12211564b287SLydia Wang 				mute_aa_path(codec, 1);
12221564b287SLydia Wang 				notify_aa_path_ctls(codec);
12231564b287SLydia Wang 			}
1224eb7188caSLydia Wang 			if (spec->codec_type == VT1718S)
1225eb7188caSLydia Wang 				snd_hda_codec_amp_stereo(
1226eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1227eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
12281564b287SLydia Wang 		}
12291564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC) {
1230f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1231f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
12321564b287SLydia Wang 				/* input = index 1 (AOW3) */
12331564b287SLydia Wang 				snd_hda_codec_write(
12341564b287SLydia Wang 					codec, nid, 0,
12351564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
12361564b287SLydia Wang 				snd_hda_codec_amp_stereo(
12371564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
12381564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
12391564b287SLydia Wang 			}
12401564b287SLydia Wang 		}
12411564b287SLydia Wang 	}
12421564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
12431564b287SLydia Wang 	set_jack_power_state(codec);
12441564b287SLydia Wang 	return 1;
12451564b287SLydia Wang }
12461564b287SLydia Wang 
12471564b287SLydia Wang static struct snd_kcontrol_new via_smart51_mixer[] = {
12481564b287SLydia Wang 	{
12491564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
12501564b287SLydia Wang 	 .name = "Smart 5.1",
12511564b287SLydia Wang 	 .count = 1,
12521564b287SLydia Wang 	 .info = via_smart51_info,
12531564b287SLydia Wang 	 .get = via_smart51_get,
12541564b287SLydia Wang 	 .put = via_smart51_put,
12551564b287SLydia Wang 	 },
12561564b287SLydia Wang 	{}			/* end */
12571564b287SLydia Wang };
12581564b287SLydia Wang 
1259c577b8a1SJoseph Chan /* capture mixer elements */
1260c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1261c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1262c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1263c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1264c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1265c577b8a1SJoseph Chan 	{
1266c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1267c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1268c577b8a1SJoseph Chan 		 * So call somewhat different..
1269c577b8a1SJoseph Chan 		 */
1270c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1271c577b8a1SJoseph Chan 		.name = "Input Source",
1272c577b8a1SJoseph Chan 		.count = 1,
1273c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1274c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1275c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1276c577b8a1SJoseph Chan 	},
1277c577b8a1SJoseph Chan 	{ } /* end */
1278c577b8a1SJoseph Chan };
1279f5271101SLydia Wang 
1280f5271101SLydia Wang /* check AA path's mute statue */
1281f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1282f5271101SLydia Wang {
1283f5271101SLydia Wang 	int mute = 1;
1284f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1285f5271101SLydia Wang 	int start_idx;
1286f5271101SLydia Wang 	int end_idx;
1287f5271101SLydia Wang 	int i;
1288f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1289f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1290f5271101SLydia Wang 	switch (spec->codec_type) {
1291f5271101SLydia Wang 	case VT1708B_8CH:
1292f5271101SLydia Wang 	case VT1708B_4CH:
1293f5271101SLydia Wang 	case VT1708S:
1294f3db423dSLydia Wang 	case VT1716S:
1295f5271101SLydia Wang 		nid_mixer = 0x16;
1296f5271101SLydia Wang 		start_idx = 2;
1297f5271101SLydia Wang 		end_idx = 4;
1298f5271101SLydia Wang 		break;
1299f5271101SLydia Wang 	case VT1702:
1300f5271101SLydia Wang 		nid_mixer = 0x1a;
1301f5271101SLydia Wang 		start_idx = 1;
1302f5271101SLydia Wang 		end_idx = 3;
1303f5271101SLydia Wang 		break;
1304eb7188caSLydia Wang 	case VT1718S:
1305eb7188caSLydia Wang 		nid_mixer = 0x21;
1306eb7188caSLydia Wang 		start_idx = 1;
1307eb7188caSLydia Wang 		end_idx = 3;
1308eb7188caSLydia Wang 		break;
1309*25eaba2fSLydia Wang 	case VT2002P:
1310*25eaba2fSLydia Wang 		nid_mixer = 0x21;
1311*25eaba2fSLydia Wang 		start_idx = 0;
1312*25eaba2fSLydia Wang 		end_idx = 2;
1313*25eaba2fSLydia Wang 		break;
1314f5271101SLydia Wang 	default:
1315f5271101SLydia Wang 		return 0;
1316f5271101SLydia Wang 	}
1317f5271101SLydia Wang 	/* check AA path's mute status */
1318f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1319f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1320f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1321f5271101SLydia Wang 		int shift = 8 * (i % 4);
1322f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1323f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1324f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1325f5271101SLydia Wang 			/* check mute status while the pin is connected */
1326f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1327f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1328f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1329f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1330f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1331f5271101SLydia Wang 				mute = 0;
1332f5271101SLydia Wang 				break;
1333f5271101SLydia Wang 			}
1334f5271101SLydia Wang 		}
1335f5271101SLydia Wang 	}
1336f5271101SLydia Wang 	return mute;
1337f5271101SLydia Wang }
1338f5271101SLydia Wang 
1339f5271101SLydia Wang /* enter/exit analog low-current mode */
1340f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1341f5271101SLydia Wang {
1342f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1343f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1344f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1345f5271101SLydia Wang 	unsigned int verb = 0;
1346f5271101SLydia Wang 	unsigned int parm = 0;
1347f5271101SLydia Wang 
1348f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1349f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1350f5271101SLydia Wang 	else {
1351f5271101SLydia Wang 		enable = enable && stream_idle;
1352f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1353f5271101SLydia Wang 	}
1354f5271101SLydia Wang 
1355f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1356f5271101SLydia Wang 	switch (spec->codec_type) {
1357f5271101SLydia Wang 	case VT1708B_8CH:
1358f5271101SLydia Wang 	case VT1708B_4CH:
1359f5271101SLydia Wang 		verb = 0xf70;
1360f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1361f5271101SLydia Wang 		break;
1362f5271101SLydia Wang 	case VT1708S:
1363eb7188caSLydia Wang 	case VT1718S:
1364f3db423dSLydia Wang 	case VT1716S:
1365f5271101SLydia Wang 		verb = 0xf73;
1366f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1367f5271101SLydia Wang 		break;
1368f5271101SLydia Wang 	case VT1702:
1369f5271101SLydia Wang 		verb = 0xf73;
1370f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1371f5271101SLydia Wang 		break;
1372*25eaba2fSLydia Wang 	case VT2002P:
1373*25eaba2fSLydia Wang 		verb = 0xf93;
1374*25eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1375*25eaba2fSLydia Wang 		break;
1376f5271101SLydia Wang 	default:
1377f5271101SLydia Wang 		return;		/* other codecs are not supported */
1378f5271101SLydia Wang 	}
1379f5271101SLydia Wang 	/* send verb */
1380f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1381f5271101SLydia Wang }
1382f5271101SLydia Wang 
1383c577b8a1SJoseph Chan /*
1384c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1385c577b8a1SJoseph Chan  */
1386c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1387c577b8a1SJoseph Chan 	/*
1388c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1389c577b8a1SJoseph Chan 	 */
1390c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1391c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1392c577b8a1SJoseph Chan 
1393c577b8a1SJoseph Chan 
1394f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1395c577b8a1SJoseph Chan 	 * mixer widget
1396c577b8a1SJoseph Chan 	 */
1397c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1398f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1399f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1400f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1401f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1402f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1403c577b8a1SJoseph Chan 
1404c577b8a1SJoseph Chan 	/*
1405c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1406c577b8a1SJoseph Chan 	 */
1407c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1408c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1409c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1410c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1411c577b8a1SJoseph Chan 
1412c577b8a1SJoseph Chan 	/* Setup default input to PW4 */
1413c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
1414c577b8a1SJoseph Chan 	/* PW9 Output enable */
1415c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1416f7278fd0SJosepch Chan 	{ }
1417c577b8a1SJoseph Chan };
1418c577b8a1SJoseph Chan 
1419c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1420c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1421c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1422c577b8a1SJoseph Chan {
1423c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
142417314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
142517314379SLydia Wang 		&& substream->ref_count == 0;
142617314379SLydia Wang 	analog_low_current_mode(codec, idle);
14279a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
14289a08160bSTakashi Iwai 					     hinfo);
1429c577b8a1SJoseph Chan }
1430c577b8a1SJoseph Chan 
14310aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
14320aa62aefSHarald Welte 				      unsigned int stream_tag,
14330aa62aefSHarald Welte 				      unsigned int format,
14340aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
14350aa62aefSHarald Welte {
14360aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
14370aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
14380aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
14390aa62aefSHarald Welte 	int chs = substream->runtime->channels;
14400aa62aefSHarald Welte 	int i;
14410aa62aefSHarald Welte 
14420aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
14430aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
14440aa62aefSHarald Welte 		if (chs == 2 &&
14450aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
14460aa62aefSHarald Welte 						format) &&
14470aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
14480aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
14490aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
14500aa62aefSHarald Welte 			 * be updated */
14510aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
14520aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
14530aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
14540aa62aefSHarald Welte 						    codec->spdif_ctls &
14550aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
14560aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
14570aa62aefSHarald Welte 						   stream_tag, 0, format);
14580aa62aefSHarald Welte 			/* turn on again (if needed) */
14590aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
14600aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
14610aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
14620aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
14630aa62aefSHarald Welte 		} else {
14640aa62aefSHarald Welte 			mout->dig_out_used = 0;
14650aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
14660aa62aefSHarald Welte 						   0, 0, 0);
14670aa62aefSHarald Welte 		}
14680aa62aefSHarald Welte 	}
14690aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
14700aa62aefSHarald Welte 
14710aa62aefSHarald Welte 	/* front */
14720aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
14730aa62aefSHarald Welte 				   0, format);
14740aa62aefSHarald Welte 
1475eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1476eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
14770aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
14780aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
14790aa62aefSHarald Welte 					   0, format);
14800aa62aefSHarald Welte 
14810aa62aefSHarald Welte 	/* extra outputs copied from front */
14820aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
14830aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
14840aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
14850aa62aefSHarald Welte 						   mout->extra_out_nid[i],
14860aa62aefSHarald Welte 						   stream_tag, 0, format);
14870aa62aefSHarald Welte 
14880aa62aefSHarald Welte 	/* surrounds */
14890aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
14900aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
14910aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
14920aa62aefSHarald Welte 						   i * 2, format);
14930aa62aefSHarald Welte 		else /* copy front */
14940aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
14950aa62aefSHarald Welte 						   0, format);
14960aa62aefSHarald Welte 	}
14970aa62aefSHarald Welte }
14980aa62aefSHarald Welte 
14990aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
15000aa62aefSHarald Welte 					  struct hda_codec *codec,
15010aa62aefSHarald Welte 					  unsigned int stream_tag,
15020aa62aefSHarald Welte 					  unsigned int format,
15030aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
15040aa62aefSHarald Welte {
15050aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
15060aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
15070aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
15080aa62aefSHarald Welte 
15090aa62aefSHarald Welte 	if (substream->number == 0)
15100aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
15110aa62aefSHarald Welte 					  substream);
15120aa62aefSHarald Welte 	else {
15130aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
15140aa62aefSHarald Welte 		    spec->hp_independent_mode)
15150aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
15160aa62aefSHarald Welte 						   stream_tag, 0, format);
15170aa62aefSHarald Welte 	}
15181f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
15190aa62aefSHarald Welte 	return 0;
15200aa62aefSHarald Welte }
15210aa62aefSHarald Welte 
15220aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
15230aa62aefSHarald Welte 				    struct hda_codec *codec,
15240aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
15250aa62aefSHarald Welte {
15260aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
15270aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
15280aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
15290aa62aefSHarald Welte 	int i;
15300aa62aefSHarald Welte 
15310aa62aefSHarald Welte 	if (substream->number == 0) {
15320aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
15330aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
15340aa62aefSHarald Welte 
15350aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
15360aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
15370aa62aefSHarald Welte 						   0, 0, 0);
15380aa62aefSHarald Welte 
15390aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
15400aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
15410aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
15420aa62aefSHarald Welte 							mout->extra_out_nid[i],
15430aa62aefSHarald Welte 							0, 0, 0);
15440aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
15450aa62aefSHarald Welte 		if (mout->dig_out_nid &&
15460aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
15470aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
15480aa62aefSHarald Welte 						   0, 0, 0);
15490aa62aefSHarald Welte 			mout->dig_out_used = 0;
15500aa62aefSHarald Welte 		}
15510aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
15520aa62aefSHarald Welte 	} else {
15530aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
15540aa62aefSHarald Welte 		    spec->hp_independent_mode)
15550aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
15560aa62aefSHarald Welte 						   0, 0, 0);
15570aa62aefSHarald Welte 	}
15581f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
15590aa62aefSHarald Welte 	return 0;
15600aa62aefSHarald Welte }
15610aa62aefSHarald Welte 
1562c577b8a1SJoseph Chan /*
1563c577b8a1SJoseph Chan  * Digital out
1564c577b8a1SJoseph Chan  */
1565c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1566c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1567c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1568c577b8a1SJoseph Chan {
1569c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1570c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1571c577b8a1SJoseph Chan }
1572c577b8a1SJoseph Chan 
1573c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1574c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1575c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1576c577b8a1SJoseph Chan {
1577c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1578c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1579c577b8a1SJoseph Chan }
1580c577b8a1SJoseph Chan 
15815691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
158298aa34c0SHarald Welte 					struct hda_codec *codec,
158398aa34c0SHarald Welte 					unsigned int stream_tag,
158498aa34c0SHarald Welte 					unsigned int format,
158598aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
158698aa34c0SHarald Welte {
158798aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
15889da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
15899da29271STakashi Iwai 					     stream_tag, format, substream);
15909da29271STakashi Iwai }
15915691ec7fSHarald Welte 
15929da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
15939da29271STakashi Iwai 					struct hda_codec *codec,
15949da29271STakashi Iwai 					struct snd_pcm_substream *substream)
15959da29271STakashi Iwai {
15969da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
15979da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
159898aa34c0SHarald Welte 	return 0;
159998aa34c0SHarald Welte }
160098aa34c0SHarald Welte 
1601c577b8a1SJoseph Chan /*
1602c577b8a1SJoseph Chan  * Analog capture
1603c577b8a1SJoseph Chan  */
1604c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1605c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1606c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1607c577b8a1SJoseph Chan 				   unsigned int format,
1608c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1609c577b8a1SJoseph Chan {
1610c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1611c577b8a1SJoseph Chan 
1612c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1613c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1614c577b8a1SJoseph Chan 	return 0;
1615c577b8a1SJoseph Chan }
1616c577b8a1SJoseph Chan 
1617c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1618c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1619c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1620c577b8a1SJoseph Chan {
1621c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1622888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1623c577b8a1SJoseph Chan 	return 0;
1624c577b8a1SJoseph Chan }
1625c577b8a1SJoseph Chan 
1626c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
16270aa62aefSHarald Welte 	.substreams = 2,
1628c577b8a1SJoseph Chan 	.channels_min = 2,
1629c577b8a1SJoseph Chan 	.channels_max = 8,
1630c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1631c577b8a1SJoseph Chan 	.ops = {
1632c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
16330aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
16340aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1635c577b8a1SJoseph Chan 	},
1636c577b8a1SJoseph Chan };
1637c577b8a1SJoseph Chan 
1638bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1639c873cc25SLydia Wang 	.substreams = 2,
1640bc9b5623STakashi Iwai 	.channels_min = 2,
1641bc9b5623STakashi Iwai 	.channels_max = 8,
1642bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1643bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1644bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1645bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1646bc9b5623STakashi Iwai 	 */
1647bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1648bc9b5623STakashi Iwai 	.ops = {
1649bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1650c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1651c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1652bc9b5623STakashi Iwai 	},
1653bc9b5623STakashi Iwai };
1654bc9b5623STakashi Iwai 
1655c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1656c577b8a1SJoseph Chan 	.substreams = 2,
1657c577b8a1SJoseph Chan 	.channels_min = 2,
1658c577b8a1SJoseph Chan 	.channels_max = 2,
1659c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1660c577b8a1SJoseph Chan 	.ops = {
1661c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1662c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1663c577b8a1SJoseph Chan 	},
1664c577b8a1SJoseph Chan };
1665c577b8a1SJoseph Chan 
1666c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1667c577b8a1SJoseph Chan 	.substreams = 1,
1668c577b8a1SJoseph Chan 	.channels_min = 2,
1669c577b8a1SJoseph Chan 	.channels_max = 2,
1670c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1671c577b8a1SJoseph Chan 	.ops = {
1672c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
16736b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
16749da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
16759da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1676c577b8a1SJoseph Chan 	},
1677c577b8a1SJoseph Chan };
1678c577b8a1SJoseph Chan 
1679c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1680c577b8a1SJoseph Chan 	.substreams = 1,
1681c577b8a1SJoseph Chan 	.channels_min = 2,
1682c577b8a1SJoseph Chan 	.channels_max = 2,
1683c577b8a1SJoseph Chan };
1684c577b8a1SJoseph Chan 
1685c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1686c577b8a1SJoseph Chan {
1687c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1688c577b8a1SJoseph Chan 	int err;
1689c577b8a1SJoseph Chan 	int i;
1690c577b8a1SJoseph Chan 
1691c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1692c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1693c577b8a1SJoseph Chan 		if (err < 0)
1694c577b8a1SJoseph Chan 			return err;
1695c577b8a1SJoseph Chan 	}
1696c577b8a1SJoseph Chan 
1697c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1698c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1699c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1700c577b8a1SJoseph Chan 		if (err < 0)
1701c577b8a1SJoseph Chan 			return err;
17029a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
17039a08160bSTakashi Iwai 						    &spec->multiout);
17049a08160bSTakashi Iwai 		if (err < 0)
17059a08160bSTakashi Iwai 			return err;
17069a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1707c577b8a1SJoseph Chan 	}
1708c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1709c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1710c577b8a1SJoseph Chan 		if (err < 0)
1711c577b8a1SJoseph Chan 			return err;
1712c577b8a1SJoseph Chan 	}
171317314379SLydia Wang 
171417314379SLydia Wang 	/* init power states */
171517314379SLydia Wang 	set_jack_power_state(codec);
171617314379SLydia Wang 	analog_low_current_mode(codec, 1);
171717314379SLydia Wang 
1718603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1719c577b8a1SJoseph Chan 	return 0;
1720c577b8a1SJoseph Chan }
1721c577b8a1SJoseph Chan 
1722c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1723c577b8a1SJoseph Chan {
1724c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1725c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1726c577b8a1SJoseph Chan 
1727c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1728c577b8a1SJoseph Chan 	codec->pcm_info = info;
1729c577b8a1SJoseph Chan 
1730c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1731c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] = *(spec->stream_analog_playback);
1732c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
1733c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1734c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1735c577b8a1SJoseph Chan 
1736c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1737c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1738c577b8a1SJoseph Chan 
1739c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1740c577b8a1SJoseph Chan 		codec->num_pcms++;
1741c577b8a1SJoseph Chan 		info++;
1742c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
17437ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1744c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1745c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1746c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1747c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1748c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1749c577b8a1SJoseph Chan 		}
1750c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1751c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1752c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1753c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1754c577b8a1SJoseph Chan 				spec->dig_in_nid;
1755c577b8a1SJoseph Chan 		}
1756c577b8a1SJoseph Chan 	}
1757c577b8a1SJoseph Chan 
1758c577b8a1SJoseph Chan 	return 0;
1759c577b8a1SJoseph Chan }
1760c577b8a1SJoseph Chan 
1761c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1762c577b8a1SJoseph Chan {
1763c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1764c577b8a1SJoseph Chan 
1765c577b8a1SJoseph Chan 	if (!spec)
1766c577b8a1SJoseph Chan 		return;
1767c577b8a1SJoseph Chan 
1768603c4019STakashi Iwai 	via_free_kctls(codec);
17691f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1770c577b8a1SJoseph Chan 	kfree(codec->spec);
1771c577b8a1SJoseph Chan }
1772c577b8a1SJoseph Chan 
177369e52a80SHarald Welte /* mute internal speaker if HP is plugged */
177469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
177569e52a80SHarald Welte {
1776dcf34c8cSLydia Wang 	unsigned int present = 0;
177769e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
177869e52a80SHarald Welte 
177969e52a80SHarald Welte 	present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
178069e52a80SHarald Welte 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1781dcf34c8cSLydia Wang 
1782dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1783dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
1784dcf34c8cSLydia Wang 		/* auto mute */
1785dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
1786dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1787dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1788dcf34c8cSLydia Wang 		/* notify change */
1789dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
1790dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1791dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
1792dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1793dcf34c8cSLydia Wang 			       &id);
1794dcf34c8cSLydia Wang 	}
179569e52a80SHarald Welte }
179669e52a80SHarald Welte 
1797f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1798f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1799f3db423dSLydia Wang {
1800f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1801f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1802f3db423dSLydia Wang 
1803f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1804f3db423dSLydia Wang 		return;
1805f3db423dSLydia Wang 
1806f3db423dSLydia Wang 	lineout_present = snd_hda_codec_read(
1807f3db423dSLydia Wang 		codec, spec->autocfg.line_out_pins[0], 0,
1808f3db423dSLydia Wang 		AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1809f3db423dSLydia Wang 
1810f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1811f3db423dSLydia Wang 	if (lineout_present) {
1812f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1813f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1814f3db423dSLydia Wang 		return;
1815f3db423dSLydia Wang 	}
1816f3db423dSLydia Wang 
1817f3db423dSLydia Wang 	hp_present = snd_hda_codec_read(
1818f3db423dSLydia Wang 		codec, spec->autocfg.hp_pins[0], 0,
1819f3db423dSLydia Wang 		AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1820f3db423dSLydia Wang 
1821f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
1822f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1823f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1824f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
1825f3db423dSLydia Wang }
1826f3db423dSLydia Wang 
182769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
182869e52a80SHarald Welte {
182969e52a80SHarald Welte 	unsigned int gpio_data;
183069e52a80SHarald Welte 	unsigned int vol_counter;
183169e52a80SHarald Welte 	unsigned int vol;
183269e52a80SHarald Welte 	unsigned int master_vol;
183369e52a80SHarald Welte 
183469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
183569e52a80SHarald Welte 
183669e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
183769e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
183869e52a80SHarald Welte 
183969e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
184069e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
184169e52a80SHarald Welte 
184269e52a80SHarald Welte 	vol = vol_counter & 0x1F;
184369e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
184469e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
184569e52a80SHarald Welte 					AC_AMP_GET_INPUT);
184669e52a80SHarald Welte 
184769e52a80SHarald Welte 	if (gpio_data == 0x02) {
184869e52a80SHarald Welte 		/* unmute line out */
184969e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
185069e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
185169e52a80SHarald Welte 
185269e52a80SHarald Welte 		if (vol_counter & 0x20) {
185369e52a80SHarald Welte 			/* decrease volume */
185469e52a80SHarald Welte 			if (vol > master_vol)
185569e52a80SHarald Welte 				vol = master_vol;
185669e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
185769e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
185869e52a80SHarald Welte 						 master_vol-vol);
185969e52a80SHarald Welte 		} else {
186069e52a80SHarald Welte 			/* increase volume */
186169e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
186269e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
186369e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
186469e52a80SHarald Welte 					  (master_vol+vol));
186569e52a80SHarald Welte 		}
186669e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
186769e52a80SHarald Welte 		/* mute line out */
186869e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
186969e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
187069e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
187169e52a80SHarald Welte 					 HDA_AMP_MUTE);
187269e52a80SHarald Welte 	}
187369e52a80SHarald Welte }
187469e52a80SHarald Welte 
1875*25eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
1876*25eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
1877*25eaba2fSLydia Wang {
1878*25eaba2fSLydia Wang 	unsigned int hp_present;
1879*25eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
1880*25eaba2fSLydia Wang 
1881*25eaba2fSLydia Wang 	if (spec->codec_type != VT2002P)
1882*25eaba2fSLydia Wang 		return;
1883*25eaba2fSLydia Wang 
1884*25eaba2fSLydia Wang 	hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
1885*25eaba2fSLydia Wang 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1886*25eaba2fSLydia Wang 
1887*25eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
1888*25eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
1889*25eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
1890*25eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
1891*25eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
1892*25eaba2fSLydia Wang 		/* notify change */
1893*25eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
1894*25eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1895*25eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
1896*25eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1897*25eaba2fSLydia Wang 			       &id);
1898*25eaba2fSLydia Wang 	}
1899*25eaba2fSLydia Wang }
1900*25eaba2fSLydia Wang 
1901*25eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
1902*25eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
1903*25eaba2fSLydia Wang {
1904*25eaba2fSLydia Wang 	unsigned int hp_present, present = 0;
1905*25eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
1906*25eaba2fSLydia Wang 	int i;
1907*25eaba2fSLydia Wang 
1908*25eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
1909*25eaba2fSLydia Wang 		return;
1910*25eaba2fSLydia Wang 
1911*25eaba2fSLydia Wang 	hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
1912*25eaba2fSLydia Wang 					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1913*25eaba2fSLydia Wang 
1914*25eaba2fSLydia Wang 	present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0,
1915*25eaba2fSLydia Wang 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1916*25eaba2fSLydia Wang 
1917*25eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
1918*25eaba2fSLydia Wang 		/* Mute Line-Outs */
1919*25eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
1920*25eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
1921*25eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
1922*25eaba2fSLydia Wang 				HDA_OUTPUT, 0,
1923*25eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
1924*25eaba2fSLydia Wang 		if (hp_present)
1925*25eaba2fSLydia Wang 			present = hp_present;
1926*25eaba2fSLydia Wang 	}
1927*25eaba2fSLydia Wang 	/* Speakers */
1928*25eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
1929*25eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
1930*25eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
1931*25eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1932*25eaba2fSLydia Wang }
1933*25eaba2fSLydia Wang 
1934*25eaba2fSLydia Wang 
193569e52a80SHarald Welte /* unsolicited event for jack sensing */
193669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
193769e52a80SHarald Welte 				  unsigned int res)
193869e52a80SHarald Welte {
193969e52a80SHarald Welte 	res >>= 26;
1940a34df19aSLydia Wang 	if (res & VIA_HP_EVENT)
194169e52a80SHarald Welte 		via_hp_automute(codec);
1942a34df19aSLydia Wang 	if (res & VIA_GPIO_EVENT)
194369e52a80SHarald Welte 		via_gpio_control(codec);
1944a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
1945a34df19aSLydia Wang 		set_jack_power_state(codec);
1946f3db423dSLydia Wang 	if (res & VIA_MONO_EVENT)
1947f3db423dSLydia Wang 		via_mono_automute(codec);
1948*25eaba2fSLydia Wang 	if (res & VIA_SPEAKER_EVENT)
1949*25eaba2fSLydia Wang 		via_speaker_automute(codec);
1950*25eaba2fSLydia Wang 	if (res & VIA_BIND_HP_EVENT)
1951*25eaba2fSLydia Wang 		via_hp_bind_automute(codec);
195269e52a80SHarald Welte }
195369e52a80SHarald Welte 
1954c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1955c577b8a1SJoseph Chan {
1956c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
195769e52a80SHarald Welte 	int i;
195869e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
195969e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
196069e52a80SHarald Welte 
1961518bf3baSLydia Wang 	spec->codec_type = get_codec_type(codec);
1962518bf3baSLydia Wang 	if (spec->codec_type == VT1708BCE)
1963518bf3baSLydia Wang 		spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
1964518bf3baSLydia Wang 					       same */
1965f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1966f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
196755d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
196855d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1969f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
197012b74c80STakashi Iwai 					    PIN_OUT);
197155d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1972f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1973f7278fd0SJosepch Chan 		}
197412b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
197512b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
197612b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1977f7278fd0SJosepch Chan 
19789da29271STakashi Iwai 	/* assign slave outs */
19799da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
19809da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
19815691ec7fSHarald Welte 
1982c577b8a1SJoseph Chan  	return 0;
1983c577b8a1SJoseph Chan }
1984c577b8a1SJoseph Chan 
19851f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
19861f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
19871f2e99feSLydia Wang {
19881f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
19891f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
19901f2e99feSLydia Wang 	return 0;
19911f2e99feSLydia Wang }
19921f2e99feSLydia Wang #endif
19931f2e99feSLydia Wang 
1994cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1995cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1996cb53c626STakashi Iwai {
1997cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1998cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1999cb53c626STakashi Iwai }
2000cb53c626STakashi Iwai #endif
2001cb53c626STakashi Iwai 
2002c577b8a1SJoseph Chan /*
2003c577b8a1SJoseph Chan  */
2004c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
2005c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
2006c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
2007c577b8a1SJoseph Chan 	.init = via_init,
2008c577b8a1SJoseph Chan 	.free = via_free,
20091f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
20101f2e99feSLydia Wang 	.suspend = via_suspend,
20111f2e99feSLydia Wang #endif
2012cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2013cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
2014cb53c626STakashi Iwai #endif
2015c577b8a1SJoseph Chan };
2016c577b8a1SJoseph Chan 
2017c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
2018c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
2019c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2020c577b8a1SJoseph Chan {
2021c577b8a1SJoseph Chan 	int i;
2022c577b8a1SJoseph Chan 	hda_nid_t nid;
2023c577b8a1SJoseph Chan 
2024c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
2025c577b8a1SJoseph Chan 
2026c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2027c577b8a1SJoseph Chan 
2028c577b8a1SJoseph Chan 	for(i = 0; i < 4; i++) {
2029c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2030c577b8a1SJoseph Chan 		if (nid) {
2031c577b8a1SJoseph Chan 			/* config dac list */
2032c577b8a1SJoseph Chan 			switch (i) {
2033c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
2034c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
2035c577b8a1SJoseph Chan 				break;
2036c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
2037c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
2038c577b8a1SJoseph Chan 				break;
2039c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
2040fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
2041c577b8a1SJoseph Chan 				break;
2042c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
2043fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
2044c577b8a1SJoseph Chan 				break;
2045c577b8a1SJoseph Chan 			}
2046c577b8a1SJoseph Chan 		}
2047c577b8a1SJoseph Chan 	}
2048c577b8a1SJoseph Chan 
2049c577b8a1SJoseph Chan 	return 0;
2050c577b8a1SJoseph Chan }
2051c577b8a1SJoseph Chan 
2052c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2053c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
2054c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2055c577b8a1SJoseph Chan {
2056c577b8a1SJoseph Chan 	char name[32];
2057c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
20589645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
2059c577b8a1SJoseph Chan 	int i, err;
2060c577b8a1SJoseph Chan 
2061c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2062c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2063c577b8a1SJoseph Chan 
2064c577b8a1SJoseph Chan 		if (!nid)
2065c577b8a1SJoseph Chan 			continue;
2066c577b8a1SJoseph Chan 
20679645c203SLydia Wang 		nid_vol = nid_vols[i];
2068c577b8a1SJoseph Chan 
2069c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2070c577b8a1SJoseph Chan 			/* Center/LFE */
2071c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2072c577b8a1SJoseph Chan 					"Center Playback Volume",
2073f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2074f7278fd0SJosepch Chan 							    HDA_OUTPUT));
2075c577b8a1SJoseph Chan 			if (err < 0)
2076c577b8a1SJoseph Chan 				return err;
2077c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2078c577b8a1SJoseph Chan 					      "LFE Playback Volume",
2079f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2080f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2081c577b8a1SJoseph Chan 			if (err < 0)
2082c577b8a1SJoseph Chan 				return err;
2083c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2084c577b8a1SJoseph Chan 					      "Center Playback Switch",
2085f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2086f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2087c577b8a1SJoseph Chan 			if (err < 0)
2088c577b8a1SJoseph Chan 				return err;
2089c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2090c577b8a1SJoseph Chan 					      "LFE Playback Switch",
2091f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2092f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2093c577b8a1SJoseph Chan 			if (err < 0)
2094c577b8a1SJoseph Chan 				return err;
2095c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT){
2096c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
2097c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2098c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
20999645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2100f7278fd0SJosepch Chan 								  HDA_INPUT));
2101c577b8a1SJoseph Chan 			if (err < 0)
2102c577b8a1SJoseph Chan 				return err;
2103c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2104c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
21059645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2106f7278fd0SJosepch Chan 								  HDA_INPUT));
2107c577b8a1SJoseph Chan 			if (err < 0)
2108c577b8a1SJoseph Chan 				return err;
2109c577b8a1SJoseph Chan 
2110c577b8a1SJoseph Chan 			/* add control to PW3 */
2111c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2112c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2113f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2114f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2115c577b8a1SJoseph Chan 			if (err < 0)
2116c577b8a1SJoseph Chan 				return err;
2117c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2118c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2119f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2120f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2121c577b8a1SJoseph Chan 			if (err < 0)
2122c577b8a1SJoseph Chan 				return err;
2123c577b8a1SJoseph Chan 		} else {
2124c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2125c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2126f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2127f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2128c577b8a1SJoseph Chan 			if (err < 0)
2129c577b8a1SJoseph Chan 				return err;
2130c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2131c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2132f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2133f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2134c577b8a1SJoseph Chan 			if (err < 0)
2135c577b8a1SJoseph Chan 				return err;
2136c577b8a1SJoseph Chan 		}
2137c577b8a1SJoseph Chan 	}
2138c577b8a1SJoseph Chan 
2139c577b8a1SJoseph Chan 	return 0;
2140c577b8a1SJoseph Chan }
2141c577b8a1SJoseph Chan 
21420aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
21430aa62aefSHarald Welte {
21440aa62aefSHarald Welte 	int i;
21450aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
21460aa62aefSHarald Welte 	static const char *texts[] = { "OFF", "ON", NULL};
21470aa62aefSHarald Welte 
21480aa62aefSHarald Welte 	/* for hp mode select */
21490aa62aefSHarald Welte 	i = 0;
21500aa62aefSHarald Welte 	while (texts[i] != NULL) {
21510aa62aefSHarald Welte 		imux->items[imux->num_items].label =  texts[i];
21520aa62aefSHarald Welte 		imux->items[imux->num_items].index = i;
21530aa62aefSHarald Welte 		imux->num_items++;
21540aa62aefSHarald Welte 		i++;
21550aa62aefSHarald Welte 	}
21560aa62aefSHarald Welte 
21570aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
21580aa62aefSHarald Welte }
21590aa62aefSHarald Welte 
2160c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2161c577b8a1SJoseph Chan {
2162c577b8a1SJoseph Chan 	int err;
2163c577b8a1SJoseph Chan 
2164c577b8a1SJoseph Chan 	if (!pin)
2165c577b8a1SJoseph Chan 		return 0;
2166c577b8a1SJoseph Chan 
2167c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
2168cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2169c577b8a1SJoseph Chan 
2170c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2171c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2172c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2173c577b8a1SJoseph Chan 	if (err < 0)
2174c577b8a1SJoseph Chan 		return err;
2175c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2176c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2177c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2178c577b8a1SJoseph Chan 	if (err < 0)
2179c577b8a1SJoseph Chan 		return err;
2180c577b8a1SJoseph Chan 
21810aa62aefSHarald Welte 	create_hp_imux(spec);
21820aa62aefSHarald Welte 
2183c577b8a1SJoseph Chan 	return 0;
2184c577b8a1SJoseph Chan }
2185c577b8a1SJoseph Chan 
2186c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2187c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
2188c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2189c577b8a1SJoseph Chan {
2190c577b8a1SJoseph Chan 	static char *labels[] = {
2191c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2192c577b8a1SJoseph Chan 	};
21930aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2194c577b8a1SJoseph Chan 	int i, err, idx = 0;
2195c577b8a1SJoseph Chan 
2196c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2197c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
2198c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
2199c577b8a1SJoseph Chan 	imux->num_items++;
2200c577b8a1SJoseph Chan 
2201c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
2202c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
2203c577b8a1SJoseph Chan 			continue;
2204c577b8a1SJoseph Chan 
2205c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
2206c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
2207c577b8a1SJoseph Chan 			idx = 2;
2208c577b8a1SJoseph Chan 			break;
2209c577b8a1SJoseph Chan 
2210c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
2211c577b8a1SJoseph Chan 			idx = 3;
2212c577b8a1SJoseph Chan 			break;
2213c577b8a1SJoseph Chan 
2214c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
2215c577b8a1SJoseph Chan 			idx = 4;
2216c577b8a1SJoseph Chan 			break;
2217c577b8a1SJoseph Chan 
2218c577b8a1SJoseph Chan 		case 0x24: /* CD */
2219c577b8a1SJoseph Chan 			idx = 1;
2220c577b8a1SJoseph Chan 			break;
2221c577b8a1SJoseph Chan 		}
22229510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x17);
2223c577b8a1SJoseph Chan 		if (err < 0)
2224c577b8a1SJoseph Chan 			return err;
2225c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
2226c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
2227c577b8a1SJoseph Chan 		imux->num_items++;
2228c577b8a1SJoseph Chan 	}
2229c577b8a1SJoseph Chan 	return 0;
2230c577b8a1SJoseph Chan }
2231c577b8a1SJoseph Chan 
2232cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2233cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2234cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2235cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2236cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2237cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2238cb53c626STakashi Iwai 	{ } /* end */
2239cb53c626STakashi Iwai };
2240cb53c626STakashi Iwai #endif
2241cb53c626STakashi Iwai 
224276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
224376d9b0ddSHarald Welte {
224476d9b0ddSHarald Welte 	unsigned int def_conf;
224576d9b0ddSHarald Welte 	unsigned char seqassoc;
224676d9b0ddSHarald Welte 
22472f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
224876d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
224976d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
225082ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
225182ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
225276d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
22532f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
225476d9b0ddSHarald Welte 	}
225576d9b0ddSHarald Welte 
225676d9b0ddSHarald Welte 	return;
225776d9b0ddSHarald Welte }
225876d9b0ddSHarald Welte 
22591f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
22601f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22611f2e99feSLydia Wang {
22621f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22631f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22641f2e99feSLydia Wang 
22651f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22661f2e99feSLydia Wang 		return 0;
22671f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
22681f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
22691f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
22701f2e99feSLydia Wang 	return 0;
22711f2e99feSLydia Wang }
22721f2e99feSLydia Wang 
22731f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
22741f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22751f2e99feSLydia Wang {
22761f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22771f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22781f2e99feSLydia Wang 	int change;
22791f2e99feSLydia Wang 
22801f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22811f2e99feSLydia Wang 		return 0;
22821f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
22831f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
22841f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
22851f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
22861f2e99feSLydia Wang 		mute_aa_path(codec, 1);
22871f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
22881f2e99feSLydia Wang 	}
22891f2e99feSLydia Wang 	return change;
22901f2e99feSLydia Wang }
22911f2e99feSLydia Wang 
22921f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
22931f2e99feSLydia Wang 	{
22941f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
22951f2e99feSLydia Wang 		.name = "Jack Detect",
22961f2e99feSLydia Wang 		.count = 1,
22971f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
22981f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
22991f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
23001f2e99feSLydia Wang 	},
23011f2e99feSLydia Wang 	{} /* end */
23021f2e99feSLydia Wang };
23031f2e99feSLydia Wang 
2304c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2305c577b8a1SJoseph Chan {
2306c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2307c577b8a1SJoseph Chan 	int err;
2308c577b8a1SJoseph Chan 
230976d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
231076d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
231176d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
231276d9b0ddSHarald Welte 
2313c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2314c577b8a1SJoseph Chan 	if (err < 0)
2315c577b8a1SJoseph Chan 		return err;
2316c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2317c577b8a1SJoseph Chan 	if (err < 0)
2318c577b8a1SJoseph Chan 		return err;
2319c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2320c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2321c577b8a1SJoseph Chan 
2322c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2323c577b8a1SJoseph Chan 	if (err < 0)
2324c577b8a1SJoseph Chan 		return err;
2325c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2326c577b8a1SJoseph Chan 	if (err < 0)
2327c577b8a1SJoseph Chan 		return err;
2328c577b8a1SJoseph Chan 	err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
2329c577b8a1SJoseph Chan 	if (err < 0)
2330c577b8a1SJoseph Chan 		return err;
23311f2e99feSLydia Wang 	/* add jack detect on/off control */
23321f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
23331f2e99feSLydia Wang 	if (err < 0)
23341f2e99feSLydia Wang 		return err;
2335c577b8a1SJoseph Chan 
2336c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2337c577b8a1SJoseph Chan 
23380852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2339c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
234055d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2341c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2342c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2343c577b8a1SJoseph Chan 
2344603c4019STakashi Iwai 	if (spec->kctls.list)
2345603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2346c577b8a1SJoseph Chan 
234769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2348c577b8a1SJoseph Chan 
23490aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
23500aa62aefSHarald Welte 
2351f8fdd495SHarald Welte 	if (spec->hp_mux)
23520aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
2353c577b8a1SJoseph Chan 
23541564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
2355c577b8a1SJoseph Chan 	return 1;
2356c577b8a1SJoseph Chan }
2357c577b8a1SJoseph Chan 
2358c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2359c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2360c577b8a1SJoseph Chan {
2361*25eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
2362*25eaba2fSLydia Wang 
2363c577b8a1SJoseph Chan 	via_init(codec);
2364c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2365c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2366c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
2367*25eaba2fSLydia Wang 	if (spec->codec_type == VT2002P) {
2368*25eaba2fSLydia Wang 		via_hp_bind_automute(codec);
2369*25eaba2fSLydia Wang 	} else {
2370*25eaba2fSLydia Wang 		via_hp_automute(codec);
2371*25eaba2fSLydia Wang 		via_speaker_automute(codec);
2372*25eaba2fSLydia Wang 	}
2373*25eaba2fSLydia Wang 
2374c577b8a1SJoseph Chan 	return 0;
2375c577b8a1SJoseph Chan }
2376c577b8a1SJoseph Chan 
23771f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
23781f2e99feSLydia Wang {
23791f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
23801f2e99feSLydia Wang 					     vt1708_hp_work.work);
23811f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
23821f2e99feSLydia Wang 		return;
23831f2e99feSLydia Wang 	/* if jack state toggled */
23841f2e99feSLydia Wang 	if (spec->vt1708_hp_present
23851f2e99feSLydia Wang 	    != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0,
23861f2e99feSLydia Wang 				   AC_VERB_GET_PIN_SENSE, 0) >> 31)) {
23871f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
23881f2e99feSLydia Wang 		via_hp_automute(spec->codec);
23891f2e99feSLydia Wang 	}
23901f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
23911f2e99feSLydia Wang }
23921f2e99feSLydia Wang 
2393337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2394337b9d02STakashi Iwai {
2395337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2396337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2397337b9d02STakashi Iwai 	unsigned int type;
2398337b9d02STakashi Iwai 	int i, n;
2399337b9d02STakashi Iwai 
2400337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2401337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2402337b9d02STakashi Iwai 		while (nid) {
2403a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
24041c55d521STakashi Iwai 			if (type == AC_WID_PIN)
24051c55d521STakashi Iwai 				break;
2406337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2407337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2408337b9d02STakashi Iwai 			if (n <= 0)
2409337b9d02STakashi Iwai 				break;
2410337b9d02STakashi Iwai 			if (n > 1) {
2411337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2412337b9d02STakashi Iwai 				break;
2413337b9d02STakashi Iwai 			}
2414337b9d02STakashi Iwai 			nid = conn[0];
2415337b9d02STakashi Iwai 		}
2416337b9d02STakashi Iwai 	}
24171c55d521STakashi Iwai 	return 0;
2418337b9d02STakashi Iwai }
2419337b9d02STakashi Iwai 
2420c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2421c577b8a1SJoseph Chan {
2422c577b8a1SJoseph Chan 	struct via_spec *spec;
2423c577b8a1SJoseph Chan 	int err;
2424c577b8a1SJoseph Chan 
2425c577b8a1SJoseph Chan 	/* create a codec specific record */
2426eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2427c577b8a1SJoseph Chan 	if (spec == NULL)
2428c577b8a1SJoseph Chan 		return -ENOMEM;
2429c577b8a1SJoseph Chan 
2430c577b8a1SJoseph Chan 	codec->spec = spec;
2431c577b8a1SJoseph Chan 
2432c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2433c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2434c577b8a1SJoseph Chan 	if (err < 0) {
2435c577b8a1SJoseph Chan 		via_free(codec);
2436c577b8a1SJoseph Chan 		return err;
2437c577b8a1SJoseph Chan 	} else if (!err) {
2438c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2439c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2440c577b8a1SJoseph Chan 	}
2441c577b8a1SJoseph Chan 
2442c577b8a1SJoseph Chan 
2443c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2444c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2445bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2446bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2447bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2448c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2449c577b8a1SJoseph Chan 
2450c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2451c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2452c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2453c577b8a1SJoseph Chan 
2454c577b8a1SJoseph Chan 
2455c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2456c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2457c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
24580f67a611STakashi Iwai 		get_mux_nids(codec);
2459c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2460c577b8a1SJoseph Chan 		spec->num_mixers++;
2461c577b8a1SJoseph Chan 	}
2462c577b8a1SJoseph Chan 
2463c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2464c577b8a1SJoseph Chan 
2465c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2466cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2467cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2468cb53c626STakashi Iwai #endif
24691f2e99feSLydia Wang 	spec->codec = codec;
24701f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2471c577b8a1SJoseph Chan 	return 0;
2472c577b8a1SJoseph Chan }
2473c577b8a1SJoseph Chan 
2474c577b8a1SJoseph Chan /* capture mixer elements */
2475c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2476c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2477c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2478c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2479c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2480c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2481c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2482c577b8a1SJoseph Chan 	{
2483c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2484c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2485c577b8a1SJoseph Chan 		 * So call somewhat different..
2486c577b8a1SJoseph Chan 		 */
2487c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2488c577b8a1SJoseph Chan 		.name = "Input Source",
2489c577b8a1SJoseph Chan 		.count = 1,
2490c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2491c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2492c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2493c577b8a1SJoseph Chan 	},
2494c577b8a1SJoseph Chan 	{ } /* end */
2495c577b8a1SJoseph Chan };
2496c577b8a1SJoseph Chan 
249769e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2498a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2499a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
250069e52a80SHarald Welte 	{ }
250169e52a80SHarald Welte };
250269e52a80SHarald Welte 
2503c577b8a1SJoseph Chan /*
2504c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2505c577b8a1SJoseph Chan  */
2506c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2507c577b8a1SJoseph Chan 	/*
2508c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2509c577b8a1SJoseph Chan 	 */
2510c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2511c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2512c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2513c577b8a1SJoseph Chan 
2514c577b8a1SJoseph Chan 
2515f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2516c577b8a1SJoseph Chan 	 * mixer widget
2517c577b8a1SJoseph Chan 	 */
2518c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2519f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2520f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2521f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2522f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2523f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2524c577b8a1SJoseph Chan 
2525c577b8a1SJoseph Chan 	/*
2526c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2527c577b8a1SJoseph Chan 	 */
2528c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2529c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2530c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2531c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2532c577b8a1SJoseph Chan 
2533c577b8a1SJoseph Chan 	/*
2534c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2535c577b8a1SJoseph Chan 	 */
2536c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2537c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2538c577b8a1SJoseph Chan 
2539c577b8a1SJoseph Chan 	/* Set input of PW4 as AOW4 */
2540c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0x1},
2541c577b8a1SJoseph Chan 	/* PW9 Output enable */
2542c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2543c577b8a1SJoseph Chan 	{ }
2544c577b8a1SJoseph Chan };
2545c577b8a1SJoseph Chan 
2546c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2547c577b8a1SJoseph Chan 	.substreams = 1,
2548c577b8a1SJoseph Chan 	.channels_min = 2,
2549c577b8a1SJoseph Chan 	.channels_max = 10,
2550c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2551c577b8a1SJoseph Chan 	.ops = {
2552c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2553c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2554c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2555c577b8a1SJoseph Chan 	},
2556c577b8a1SJoseph Chan };
2557c577b8a1SJoseph Chan 
2558c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2559c577b8a1SJoseph Chan 	.substreams = 1,
2560c577b8a1SJoseph Chan 	.channels_min = 2,
2561c577b8a1SJoseph Chan 	.channels_max = 6,
2562c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2563c577b8a1SJoseph Chan 	.ops = {
2564c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2565c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2566c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2567c577b8a1SJoseph Chan 	},
2568c577b8a1SJoseph Chan };
2569c577b8a1SJoseph Chan 
2570c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2571c577b8a1SJoseph Chan 	.substreams = 2,
2572c577b8a1SJoseph Chan 	.channels_min = 2,
2573c577b8a1SJoseph Chan 	.channels_max = 2,
2574c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2575c577b8a1SJoseph Chan 	.ops = {
2576c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2577c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2578c577b8a1SJoseph Chan 	},
2579c577b8a1SJoseph Chan };
2580c577b8a1SJoseph Chan 
2581c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2582c577b8a1SJoseph Chan 	.substreams = 1,
2583c577b8a1SJoseph Chan 	.channels_min = 2,
2584c577b8a1SJoseph Chan 	.channels_max = 2,
2585c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2586c577b8a1SJoseph Chan 	.ops = {
2587c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2588c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2589c577b8a1SJoseph Chan 	},
2590c577b8a1SJoseph Chan };
2591c577b8a1SJoseph Chan 
2592c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2593c577b8a1SJoseph Chan 	.substreams = 1,
2594c577b8a1SJoseph Chan 	.channels_min = 2,
2595c577b8a1SJoseph Chan 	.channels_max = 2,
2596c577b8a1SJoseph Chan };
2597c577b8a1SJoseph Chan 
2598c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2599c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2600c577b8a1SJoseph Chan {
2601c577b8a1SJoseph Chan 	int i;
2602c577b8a1SJoseph Chan 	hda_nid_t nid;
2603c577b8a1SJoseph Chan 
2604c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2605c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2606c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2607c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2608c577b8a1SJoseph Chan 
2609c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2610c577b8a1SJoseph Chan 
2611c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2612c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2613c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2614c577b8a1SJoseph Chan 			if (nid) {
2615c577b8a1SJoseph Chan 				/* config dac list */
2616c577b8a1SJoseph Chan 				switch (i) {
2617c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2618c577b8a1SJoseph Chan 					/* AOW0 */
2619c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2620c577b8a1SJoseph Chan 					break;
2621c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2622c577b8a1SJoseph Chan 					/* AOW2 */
2623c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2624c577b8a1SJoseph Chan 					break;
2625c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2626c577b8a1SJoseph Chan 					/* AOW3 */
2627fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2628c577b8a1SJoseph Chan 					break;
2629c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2630c577b8a1SJoseph Chan 					/* AOW1 */
2631fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2632c577b8a1SJoseph Chan 					break;
2633c577b8a1SJoseph Chan 				default:
2634c577b8a1SJoseph Chan 					break;
2635c577b8a1SJoseph Chan 				}
2636c577b8a1SJoseph Chan 			}
2637c577b8a1SJoseph Chan 		}
2638c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2639c577b8a1SJoseph Chan 
2640c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2641c577b8a1SJoseph Chan 		for(i = 0; i < cfg->line_outs; i++) {
2642c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2643c577b8a1SJoseph Chan 			if (nid) {
2644c577b8a1SJoseph Chan 				/* config dac list */
2645c577b8a1SJoseph Chan 				switch(i) {
2646c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2647c577b8a1SJoseph Chan 					/* AOW0 */
2648c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2649c577b8a1SJoseph Chan 					break;
2650c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2651c577b8a1SJoseph Chan 					/* AOW2 */
2652c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2653c577b8a1SJoseph Chan 					break;
2654c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2655c577b8a1SJoseph Chan 					/* AOW1 */
2656c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2657c577b8a1SJoseph Chan 					break;
2658c577b8a1SJoseph Chan 				default:
2659c577b8a1SJoseph Chan 					break;
2660c577b8a1SJoseph Chan 				}
2661c577b8a1SJoseph Chan 			}
2662c577b8a1SJoseph Chan 		}
2663c577b8a1SJoseph Chan 	}
2664c577b8a1SJoseph Chan 
2665c577b8a1SJoseph Chan 	return 0;
2666c577b8a1SJoseph Chan }
2667c577b8a1SJoseph Chan 
2668c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2669c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2670c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2671c577b8a1SJoseph Chan {
2672c577b8a1SJoseph Chan 	char name[32];
2673c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
26744483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2675c577b8a1SJoseph Chan 	int i, err;
2676c577b8a1SJoseph Chan 
2677c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2678c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2679c577b8a1SJoseph Chan 
2680c577b8a1SJoseph Chan 		if (!nid)
2681c577b8a1SJoseph Chan 			continue;
2682c577b8a1SJoseph Chan 
26834483a2f5SLydia Wang 		nid_vol = nid_vols[i];
26844483a2f5SLydia Wang 
2685c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2686c577b8a1SJoseph Chan 			/* Center/LFE */
2687c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2688c577b8a1SJoseph Chan 					      "Center Playback Volume",
26894483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2690f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2691c577b8a1SJoseph Chan 			if (err < 0)
2692c577b8a1SJoseph Chan 				return err;
2693c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2694c577b8a1SJoseph Chan 					      "LFE Playback Volume",
26954483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2696f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2697c577b8a1SJoseph Chan 			if (err < 0)
2698c577b8a1SJoseph Chan 				return err;
2699c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2700c577b8a1SJoseph Chan 					      "Center Playback Switch",
27014483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2702f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2703c577b8a1SJoseph Chan 			if (err < 0)
2704c577b8a1SJoseph Chan 				return err;
2705c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2706c577b8a1SJoseph Chan 					      "LFE Playback Switch",
27074483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2708f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2709c577b8a1SJoseph Chan 			if (err < 0)
2710c577b8a1SJoseph Chan 				return err;
2711c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT){
27124483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2713c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2714c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
27154483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2716f7278fd0SJosepch Chan 								  HDA_INPUT));
2717c577b8a1SJoseph Chan 			if (err < 0)
2718c577b8a1SJoseph Chan 				return err;
2719c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2720c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
27214483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2722f7278fd0SJosepch Chan 								  HDA_INPUT));
2723c577b8a1SJoseph Chan 			if (err < 0)
2724c577b8a1SJoseph Chan 				return err;
2725c577b8a1SJoseph Chan 
2726c577b8a1SJoseph Chan 			/* add control to PW3 */
2727c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2728c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2729f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2730f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2731c577b8a1SJoseph Chan 			if (err < 0)
2732c577b8a1SJoseph Chan 				return err;
2733c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2734c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2735f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2736f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2737c577b8a1SJoseph Chan 			if (err < 0)
2738c577b8a1SJoseph Chan 				return err;
2739c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2740c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2741c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
27424483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2743f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2744c577b8a1SJoseph Chan 			if (err < 0)
2745c577b8a1SJoseph Chan 				return err;
2746c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2747c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
27484483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2749f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2750c577b8a1SJoseph Chan 			if (err < 0)
2751c577b8a1SJoseph Chan 				return err;
2752c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2753c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2754c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
27554483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2756f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2757c577b8a1SJoseph Chan 			if (err < 0)
2758c577b8a1SJoseph Chan 				return err;
2759c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2760c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
27614483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2762f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2763c577b8a1SJoseph Chan 			if (err < 0)
2764c577b8a1SJoseph Chan 				return err;
2765c577b8a1SJoseph Chan 		}
2766c577b8a1SJoseph Chan 	}
2767c577b8a1SJoseph Chan 
2768c577b8a1SJoseph Chan 	return 0;
2769c577b8a1SJoseph Chan }
2770c577b8a1SJoseph Chan 
2771c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2772c577b8a1SJoseph Chan {
2773c577b8a1SJoseph Chan 	int err;
2774c577b8a1SJoseph Chan 
2775c577b8a1SJoseph Chan 	if (!pin)
2776c577b8a1SJoseph Chan 		return 0;
2777c577b8a1SJoseph Chan 
2778c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2779c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2780c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2781c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2782cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2783c577b8a1SJoseph Chan 
2784c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2785c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2786c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2787c577b8a1SJoseph Chan 	if (err < 0)
2788c577b8a1SJoseph Chan 		return err;
2789c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2790c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2791c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2792c577b8a1SJoseph Chan 	if (err < 0)
2793c577b8a1SJoseph Chan 		return err;
2794c577b8a1SJoseph Chan 
2795c577b8a1SJoseph Chan 	return 0;
2796c577b8a1SJoseph Chan }
2797c577b8a1SJoseph Chan 
2798c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2799c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
2800c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2801c577b8a1SJoseph Chan {
2802c577b8a1SJoseph Chan 	static char *labels[] = {
2803c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2804c577b8a1SJoseph Chan 	};
28050aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2806c577b8a1SJoseph Chan 	int i, err, idx = 0;
2807c577b8a1SJoseph Chan 
2808c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2809c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
2810c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
2811c577b8a1SJoseph Chan 	imux->num_items++;
2812c577b8a1SJoseph Chan 
2813c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
2814c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
2815c577b8a1SJoseph Chan 			continue;
2816c577b8a1SJoseph Chan 
2817c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
2818c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
2819c577b8a1SJoseph Chan 			idx = 2;
2820c577b8a1SJoseph Chan 			break;
2821c577b8a1SJoseph Chan 
2822c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
2823c577b8a1SJoseph Chan 			idx = 3;
2824c577b8a1SJoseph Chan 			break;
2825c577b8a1SJoseph Chan 
2826c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
2827c577b8a1SJoseph Chan 			idx = 4;
2828c577b8a1SJoseph Chan 			break;
2829c577b8a1SJoseph Chan 
2830c577b8a1SJoseph Chan 		case 0x23: /* CD */
2831c577b8a1SJoseph Chan 			idx = 1;
2832c577b8a1SJoseph Chan 			break;
2833c577b8a1SJoseph Chan 		}
28349510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x18);
2835c577b8a1SJoseph Chan 		if (err < 0)
2836c577b8a1SJoseph Chan 			return err;
2837c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
2838c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
2839c577b8a1SJoseph Chan 		imux->num_items++;
2840c577b8a1SJoseph Chan 	}
2841c577b8a1SJoseph Chan 	return 0;
2842c577b8a1SJoseph Chan }
2843c577b8a1SJoseph Chan 
2844c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2845c577b8a1SJoseph Chan {
2846c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2847c577b8a1SJoseph Chan 	int err;
2848c577b8a1SJoseph Chan 
2849c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2850c577b8a1SJoseph Chan 	if (err < 0)
2851c577b8a1SJoseph Chan 		return err;
2852c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2853c577b8a1SJoseph Chan 	if (err < 0)
2854c577b8a1SJoseph Chan 		return err;
2855c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2856c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2857c577b8a1SJoseph Chan 
2858c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2859c577b8a1SJoseph Chan 	if (err < 0)
2860c577b8a1SJoseph Chan 		return err;
2861c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2862c577b8a1SJoseph Chan 	if (err < 0)
2863c577b8a1SJoseph Chan 		return err;
2864c577b8a1SJoseph Chan 	err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
2865c577b8a1SJoseph Chan 	if (err < 0)
2866c577b8a1SJoseph Chan 		return err;
2867c577b8a1SJoseph Chan 
2868c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2869c577b8a1SJoseph Chan 
28700852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2871c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
287255d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2873c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2874c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2875c577b8a1SJoseph Chan 
2876603c4019STakashi Iwai 	if (spec->kctls.list)
2877603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2878c577b8a1SJoseph Chan 
28790aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2880c577b8a1SJoseph Chan 
2881f8fdd495SHarald Welte 	if (spec->hp_mux)
2882f8fdd495SHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
2883f8fdd495SHarald Welte 
28841564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
2885c577b8a1SJoseph Chan 	return 1;
2886c577b8a1SJoseph Chan }
2887c577b8a1SJoseph Chan 
2888cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2889cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
2890cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2891cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2892cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2893cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2894cb53c626STakashi Iwai 	{ } /* end */
2895cb53c626STakashi Iwai };
2896cb53c626STakashi Iwai #endif
2897cb53c626STakashi Iwai 
2898c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2899c577b8a1SJoseph Chan {
2900c577b8a1SJoseph Chan 	struct via_spec *spec;
2901c577b8a1SJoseph Chan 	int err;
2902c577b8a1SJoseph Chan 
2903c577b8a1SJoseph Chan 	/* create a codec specific record */
2904eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2905c577b8a1SJoseph Chan 	if (spec == NULL)
2906c577b8a1SJoseph Chan 		return -ENOMEM;
2907c577b8a1SJoseph Chan 
2908c577b8a1SJoseph Chan 	codec->spec = spec;
2909c577b8a1SJoseph Chan 
2910c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2911c577b8a1SJoseph Chan 	if (err < 0) {
2912c577b8a1SJoseph Chan 		via_free(codec);
2913c577b8a1SJoseph Chan 		return err;
2914c577b8a1SJoseph Chan 	} else if (!err) {
2915c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2916c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2917c577b8a1SJoseph Chan 	}
2918c577b8a1SJoseph Chan 
291969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
292069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2921c577b8a1SJoseph Chan 
2922c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
2923c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2924c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2925c577b8a1SJoseph Chan 
2926c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
2927c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2928c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2929c577b8a1SJoseph Chan 
2930c577b8a1SJoseph Chan 
2931c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2932c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
2933c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
2934337b9d02STakashi Iwai 		get_mux_nids(codec);
2935c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2936c577b8a1SJoseph Chan 		spec->num_mixers++;
2937c577b8a1SJoseph Chan 	}
2938c577b8a1SJoseph Chan 
2939c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2940c577b8a1SJoseph Chan 
2941c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
294269e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2943cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2944cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2945cb53c626STakashi Iwai #endif
2946c577b8a1SJoseph Chan 
2947c577b8a1SJoseph Chan 	return 0;
2948c577b8a1SJoseph Chan }
2949c577b8a1SJoseph Chan /*
2950c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2951c577b8a1SJoseph Chan  */
2952c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2953c577b8a1SJoseph Chan 	/*
2954c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2955c577b8a1SJoseph Chan 	 */
2956c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2957c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2958c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2959c577b8a1SJoseph Chan 
2960c577b8a1SJoseph Chan 
2961c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2962c577b8a1SJoseph Chan 	 * mixer widget
2963c577b8a1SJoseph Chan 	 */
2964c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2965c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2966c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2967c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2968c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2969c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2970c577b8a1SJoseph Chan 
2971c577b8a1SJoseph Chan 	/*
2972c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2973c577b8a1SJoseph Chan 	 */
2974c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2975c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2976c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2977c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2978c577b8a1SJoseph Chan 
2979c577b8a1SJoseph Chan 	/*
2980c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2981c577b8a1SJoseph Chan 	 */
2982c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2983c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2984c577b8a1SJoseph Chan 
2985c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2986c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2987c577b8a1SJoseph Chan 	/* PW9 Output enable */
2988c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2989c577b8a1SJoseph Chan 	{ }
2990c577b8a1SJoseph Chan };
2991c577b8a1SJoseph Chan 
2992c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2993c577b8a1SJoseph Chan {
2994c577b8a1SJoseph Chan 	struct via_spec *spec;
2995c577b8a1SJoseph Chan 	int err;
2996c577b8a1SJoseph Chan 
2997c577b8a1SJoseph Chan 	/* create a codec specific record */
2998eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2999c577b8a1SJoseph Chan 	if (spec == NULL)
3000c577b8a1SJoseph Chan 		return -ENOMEM;
3001c577b8a1SJoseph Chan 
3002c577b8a1SJoseph Chan 	codec->spec = spec;
3003c577b8a1SJoseph Chan 
3004c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3005c577b8a1SJoseph Chan 	if (err < 0) {
3006c577b8a1SJoseph Chan 		via_free(codec);
3007c577b8a1SJoseph Chan 		return err;
3008c577b8a1SJoseph Chan 	} else if (!err) {
3009c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3010c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3011c577b8a1SJoseph Chan 	}
3012c577b8a1SJoseph Chan 
301369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
301469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3015c577b8a1SJoseph Chan 
3016c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3017c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
3018c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3019c577b8a1SJoseph Chan 
3020c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3021c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3022c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3023c577b8a1SJoseph Chan 
3024c577b8a1SJoseph Chan 
3025c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3026c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3027c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3028337b9d02STakashi Iwai 		get_mux_nids(codec);
3029c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3030c577b8a1SJoseph Chan 		spec->num_mixers++;
3031c577b8a1SJoseph Chan 	}
3032c577b8a1SJoseph Chan 
3033c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3034c577b8a1SJoseph Chan 
3035c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
303669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3037cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3038cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3039cb53c626STakashi Iwai #endif
3040f7278fd0SJosepch Chan 	return 0;
3041f7278fd0SJosepch Chan }
3042f7278fd0SJosepch Chan 
3043f7278fd0SJosepch Chan /* capture mixer elements */
3044f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
3045f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3046f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3047f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3048f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3049f7278fd0SJosepch Chan 	{
3050f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3051f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
3052f7278fd0SJosepch Chan 		 * So call somewhat different..
3053f7278fd0SJosepch Chan 		 */
3054f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
3055f7278fd0SJosepch Chan 		.name = "Input Source",
3056f7278fd0SJosepch Chan 		.count = 1,
3057f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
3058f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
3059f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
3060f7278fd0SJosepch Chan 	},
3061f7278fd0SJosepch Chan 	{ } /* end */
3062f7278fd0SJosepch Chan };
3063f7278fd0SJosepch Chan /*
3064f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
3065f7278fd0SJosepch Chan  */
3066f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
3067f7278fd0SJosepch Chan 	/*
3068f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3069f7278fd0SJosepch Chan 	 */
3070f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3071f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3072f7278fd0SJosepch Chan 
3073f7278fd0SJosepch Chan 
3074f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3075f7278fd0SJosepch Chan 	 * mixer widget
3076f7278fd0SJosepch Chan 	 */
3077f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3078f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3079f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3080f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3081f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3082f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3083f7278fd0SJosepch Chan 
3084f7278fd0SJosepch Chan 	/*
3085f7278fd0SJosepch Chan 	 * Set up output mixers
3086f7278fd0SJosepch Chan 	 */
3087f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3088f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3089f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3090f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3091f7278fd0SJosepch Chan 
3092f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
3093f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x1},
3094f7278fd0SJosepch Chan 	/* PW9 Output enable */
3095f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3096f7278fd0SJosepch Chan 	/* PW10 Input enable */
3097f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3098f7278fd0SJosepch Chan 	{ }
3099f7278fd0SJosepch Chan };
3100f7278fd0SJosepch Chan 
3101f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
3102f7278fd0SJosepch Chan 	/*
3103f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3104f7278fd0SJosepch Chan 	 */
3105f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3106f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3107f7278fd0SJosepch Chan 
3108f7278fd0SJosepch Chan 
3109f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3110f7278fd0SJosepch Chan 	 * mixer widget
3111f7278fd0SJosepch Chan 	 */
3112f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3113f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3114f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3115f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3116f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3117f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3118f7278fd0SJosepch Chan 
3119f7278fd0SJosepch Chan 	/*
3120f7278fd0SJosepch Chan 	 * Set up output mixers
3121f7278fd0SJosepch Chan 	 */
3122f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3123f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3124f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3125f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3126f7278fd0SJosepch Chan 
3127f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
3128f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3129f7278fd0SJosepch Chan 	/* PW9 Output enable */
3130f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3131f7278fd0SJosepch Chan 	/* PW10 Input enable */
3132f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3133f7278fd0SJosepch Chan 	{ }
3134f7278fd0SJosepch Chan };
3135f7278fd0SJosepch Chan 
313669e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
3137a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3138a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3139a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3140a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3141a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3142a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3143a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3144a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3145a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
314669e52a80SHarald Welte 	{ }
314769e52a80SHarald Welte };
314869e52a80SHarald Welte 
314917314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
315017314379SLydia Wang 			      struct hda_codec *codec,
315117314379SLydia Wang 			      struct snd_pcm_substream *substream)
315217314379SLydia Wang {
315317314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
315417314379SLydia Wang 		&& substream->ref_count == 0;
315517314379SLydia Wang 
315617314379SLydia Wang 	analog_low_current_mode(codec, idle);
315717314379SLydia Wang 	return 0;
315817314379SLydia Wang }
315917314379SLydia Wang 
3160f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
31610aa62aefSHarald Welte 	.substreams = 2,
3162f7278fd0SJosepch Chan 	.channels_min = 2,
3163f7278fd0SJosepch Chan 	.channels_max = 8,
3164f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3165f7278fd0SJosepch Chan 	.ops = {
3166f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
31670aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
316817314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
316917314379SLydia Wang 		.close = via_pcm_open_close
3170f7278fd0SJosepch Chan 	},
3171f7278fd0SJosepch Chan };
3172f7278fd0SJosepch Chan 
3173f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
31740aa62aefSHarald Welte 	.substreams = 2,
3175f7278fd0SJosepch Chan 	.channels_min = 2,
3176f7278fd0SJosepch Chan 	.channels_max = 4,
3177f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3178f7278fd0SJosepch Chan 	.ops = {
3179f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
31800aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
31810aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
3182f7278fd0SJosepch Chan 	},
3183f7278fd0SJosepch Chan };
3184f7278fd0SJosepch Chan 
3185f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3186f7278fd0SJosepch Chan 	.substreams = 2,
3187f7278fd0SJosepch Chan 	.channels_min = 2,
3188f7278fd0SJosepch Chan 	.channels_max = 2,
3189f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
3190f7278fd0SJosepch Chan 	.ops = {
319117314379SLydia Wang 		.open = via_pcm_open_close,
3192f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
319317314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
319417314379SLydia Wang 		.close = via_pcm_open_close
3195f7278fd0SJosepch Chan 	},
3196f7278fd0SJosepch Chan };
3197f7278fd0SJosepch Chan 
3198f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3199f7278fd0SJosepch Chan 	.substreams = 1,
3200f7278fd0SJosepch Chan 	.channels_min = 2,
3201f7278fd0SJosepch Chan 	.channels_max = 2,
3202f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
3203f7278fd0SJosepch Chan 	.ops = {
3204f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
3205f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
32069da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
32079da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3208f7278fd0SJosepch Chan 	},
3209f7278fd0SJosepch Chan };
3210f7278fd0SJosepch Chan 
3211f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3212f7278fd0SJosepch Chan 	.substreams = 1,
3213f7278fd0SJosepch Chan 	.channels_min = 2,
3214f7278fd0SJosepch Chan 	.channels_max = 2,
3215f7278fd0SJosepch Chan };
3216f7278fd0SJosepch Chan 
3217f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
3218f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3219f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
3220f7278fd0SJosepch Chan {
3221f7278fd0SJosepch Chan 	int i;
3222f7278fd0SJosepch Chan 	hda_nid_t nid;
3223f7278fd0SJosepch Chan 
3224f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
3225f7278fd0SJosepch Chan 
3226f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3227f7278fd0SJosepch Chan 
3228f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3229f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3230f7278fd0SJosepch Chan 		if (nid) {
3231f7278fd0SJosepch Chan 			/* config dac list */
3232f7278fd0SJosepch Chan 			switch (i) {
3233f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3234f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
3235f7278fd0SJosepch Chan 				break;
3236f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3237f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
3238f7278fd0SJosepch Chan 				break;
3239f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3240fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3241f7278fd0SJosepch Chan 				break;
3242f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3243fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3244f7278fd0SJosepch Chan 				break;
3245f7278fd0SJosepch Chan 			}
3246f7278fd0SJosepch Chan 		}
3247f7278fd0SJosepch Chan 	}
3248f7278fd0SJosepch Chan 
3249f7278fd0SJosepch Chan 	return 0;
3250f7278fd0SJosepch Chan }
3251f7278fd0SJosepch Chan 
3252f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3253f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3254f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3255f7278fd0SJosepch Chan {
3256f7278fd0SJosepch Chan 	char name[32];
3257f7278fd0SJosepch Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3258fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3259f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3260f7278fd0SJosepch Chan 	int i, err;
3261f7278fd0SJosepch Chan 
3262f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3263f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3264f7278fd0SJosepch Chan 
3265f7278fd0SJosepch Chan 		if (!nid)
3266f7278fd0SJosepch Chan 			continue;
3267f7278fd0SJosepch Chan 
3268f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3269f7278fd0SJosepch Chan 
3270f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3271f7278fd0SJosepch Chan 			/* Center/LFE */
3272f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3273f7278fd0SJosepch Chan 					      "Center Playback Volume",
3274f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3275f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3276f7278fd0SJosepch Chan 			if (err < 0)
3277f7278fd0SJosepch Chan 				return err;
3278f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3279f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3280f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3281f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3282f7278fd0SJosepch Chan 			if (err < 0)
3283f7278fd0SJosepch Chan 				return err;
3284f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3285f7278fd0SJosepch Chan 					      "Center Playback Switch",
3286f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3287f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3288f7278fd0SJosepch Chan 			if (err < 0)
3289f7278fd0SJosepch Chan 				return err;
3290f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3291f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3292f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3293f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3294f7278fd0SJosepch Chan 			if (err < 0)
3295f7278fd0SJosepch Chan 				return err;
3296f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3297f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3298f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3299f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3300f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3301f7278fd0SJosepch Chan 								  HDA_INPUT));
3302f7278fd0SJosepch Chan 			if (err < 0)
3303f7278fd0SJosepch Chan 				return err;
3304f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3305f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3306f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3307f7278fd0SJosepch Chan 								  HDA_INPUT));
3308f7278fd0SJosepch Chan 			if (err < 0)
3309f7278fd0SJosepch Chan 				return err;
3310f7278fd0SJosepch Chan 
3311f7278fd0SJosepch Chan 			/* add control to PW3 */
3312f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3313f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3314f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3315f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3316f7278fd0SJosepch Chan 			if (err < 0)
3317f7278fd0SJosepch Chan 				return err;
3318f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3319f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3320f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3321f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3322f7278fd0SJosepch Chan 			if (err < 0)
3323f7278fd0SJosepch Chan 				return err;
3324f7278fd0SJosepch Chan 		} else {
3325f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3326f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3327f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3328f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3329f7278fd0SJosepch Chan 			if (err < 0)
3330f7278fd0SJosepch Chan 				return err;
3331f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3332f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3333f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3334f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3335f7278fd0SJosepch Chan 			if (err < 0)
3336f7278fd0SJosepch Chan 				return err;
3337f7278fd0SJosepch Chan 		}
3338f7278fd0SJosepch Chan 	}
3339f7278fd0SJosepch Chan 
3340f7278fd0SJosepch Chan 	return 0;
3341f7278fd0SJosepch Chan }
3342f7278fd0SJosepch Chan 
3343f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3344f7278fd0SJosepch Chan {
3345f7278fd0SJosepch Chan 	int err;
3346f7278fd0SJosepch Chan 
3347f7278fd0SJosepch Chan 	if (!pin)
3348f7278fd0SJosepch Chan 		return 0;
3349f7278fd0SJosepch Chan 
3350f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3351cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3352f7278fd0SJosepch Chan 
3353f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3354f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3355f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3356f7278fd0SJosepch Chan 	if (err < 0)
3357f7278fd0SJosepch Chan 		return err;
3358f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3359f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3360f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3361f7278fd0SJosepch Chan 	if (err < 0)
3362f7278fd0SJosepch Chan 		return err;
3363f7278fd0SJosepch Chan 
33640aa62aefSHarald Welte 	create_hp_imux(spec);
33650aa62aefSHarald Welte 
3366f7278fd0SJosepch Chan 	return 0;
3367f7278fd0SJosepch Chan }
3368f7278fd0SJosepch Chan 
3369f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
3370f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
3371f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3372f7278fd0SJosepch Chan {
3373f7278fd0SJosepch Chan 	static char *labels[] = {
3374f7278fd0SJosepch Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3375f7278fd0SJosepch Chan 	};
33760aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3377f7278fd0SJosepch Chan 	int i, err, idx = 0;
3378f7278fd0SJosepch Chan 
3379f7278fd0SJosepch Chan 	/* for internal loopback recording select */
3380f7278fd0SJosepch Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
3381f7278fd0SJosepch Chan 	imux->items[imux->num_items].index = idx;
3382f7278fd0SJosepch Chan 	imux->num_items++;
3383f7278fd0SJosepch Chan 
3384f7278fd0SJosepch Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3385f7278fd0SJosepch Chan 		if (!cfg->input_pins[i])
3386f7278fd0SJosepch Chan 			continue;
3387f7278fd0SJosepch Chan 
3388f7278fd0SJosepch Chan 		switch (cfg->input_pins[i]) {
3389f7278fd0SJosepch Chan 		case 0x1a: /* Mic */
3390f7278fd0SJosepch Chan 			idx = 2;
3391f7278fd0SJosepch Chan 			break;
3392f7278fd0SJosepch Chan 
3393f7278fd0SJosepch Chan 		case 0x1b: /* Line In */
3394f7278fd0SJosepch Chan 			idx = 3;
3395f7278fd0SJosepch Chan 			break;
3396f7278fd0SJosepch Chan 
3397f7278fd0SJosepch Chan 		case 0x1e: /* Front Mic */
3398f7278fd0SJosepch Chan 			idx = 4;
3399f7278fd0SJosepch Chan 			break;
3400f7278fd0SJosepch Chan 
3401f7278fd0SJosepch Chan 		case 0x1f: /* CD */
3402f7278fd0SJosepch Chan 			idx = 1;
3403f7278fd0SJosepch Chan 			break;
3404f7278fd0SJosepch Chan 		}
34059510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
3406f7278fd0SJosepch Chan 		if (err < 0)
3407f7278fd0SJosepch Chan 			return err;
3408f7278fd0SJosepch Chan 		imux->items[imux->num_items].label = labels[i];
3409f7278fd0SJosepch Chan 		imux->items[imux->num_items].index = idx;
3410f7278fd0SJosepch Chan 		imux->num_items++;
3411f7278fd0SJosepch Chan 	}
3412f7278fd0SJosepch Chan 	return 0;
3413f7278fd0SJosepch Chan }
3414f7278fd0SJosepch Chan 
3415f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3416f7278fd0SJosepch Chan {
3417f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3418f7278fd0SJosepch Chan 	int err;
3419f7278fd0SJosepch Chan 
3420f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3421f7278fd0SJosepch Chan 	if (err < 0)
3422f7278fd0SJosepch Chan 		return err;
3423f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3424f7278fd0SJosepch Chan 	if (err < 0)
3425f7278fd0SJosepch Chan 		return err;
3426f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3427f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3428f7278fd0SJosepch Chan 
3429f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3430f7278fd0SJosepch Chan 	if (err < 0)
3431f7278fd0SJosepch Chan 		return err;
3432f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3433f7278fd0SJosepch Chan 	if (err < 0)
3434f7278fd0SJosepch Chan 		return err;
3435f7278fd0SJosepch Chan 	err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
3436f7278fd0SJosepch Chan 	if (err < 0)
3437f7278fd0SJosepch Chan 		return err;
3438f7278fd0SJosepch Chan 
3439f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3440f7278fd0SJosepch Chan 
34410852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3442f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
344355d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3444f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3445f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3446f7278fd0SJosepch Chan 
3447603c4019STakashi Iwai 	if (spec->kctls.list)
3448603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3449f7278fd0SJosepch Chan 
34500aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
34510aa62aefSHarald Welte 
3452f8fdd495SHarald Welte 	if (spec->hp_mux)
34530aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
3454f7278fd0SJosepch Chan 
34551564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
3456f7278fd0SJosepch Chan 	return 1;
3457f7278fd0SJosepch Chan }
3458f7278fd0SJosepch Chan 
3459f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3460f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3461f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3462f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3463f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3464f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3465f7278fd0SJosepch Chan 	{ } /* end */
3466f7278fd0SJosepch Chan };
3467f7278fd0SJosepch Chan #endif
3468518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3469f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3470f7278fd0SJosepch Chan {
3471f7278fd0SJosepch Chan 	struct via_spec *spec;
3472f7278fd0SJosepch Chan 	int err;
3473f7278fd0SJosepch Chan 
3474518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3475518bf3baSLydia Wang 		return patch_vt1708S(codec);
3476f7278fd0SJosepch Chan 	/* create a codec specific record */
3477eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3478f7278fd0SJosepch Chan 	if (spec == NULL)
3479f7278fd0SJosepch Chan 		return -ENOMEM;
3480f7278fd0SJosepch Chan 
3481f7278fd0SJosepch Chan 	codec->spec = spec;
3482f7278fd0SJosepch Chan 
3483f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3484f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3485f7278fd0SJosepch Chan 	if (err < 0) {
3486f7278fd0SJosepch Chan 		via_free(codec);
3487f7278fd0SJosepch Chan 		return err;
3488f7278fd0SJosepch Chan 	} else if (!err) {
3489f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3490f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3491f7278fd0SJosepch Chan 	}
3492f7278fd0SJosepch Chan 
349369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
349469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3495f7278fd0SJosepch Chan 
3496f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3497f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3498f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3499f7278fd0SJosepch Chan 
3500f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3501f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3502f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3503f7278fd0SJosepch Chan 
3504f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3505f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3506f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3507337b9d02STakashi Iwai 		get_mux_nids(codec);
3508f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3509f7278fd0SJosepch Chan 		spec->num_mixers++;
3510f7278fd0SJosepch Chan 	}
3511f7278fd0SJosepch Chan 
3512f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3513f7278fd0SJosepch Chan 
3514f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
351569e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3516f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3517f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3518f7278fd0SJosepch Chan #endif
3519f7278fd0SJosepch Chan 
3520f7278fd0SJosepch Chan 	return 0;
3521f7278fd0SJosepch Chan }
3522f7278fd0SJosepch Chan 
3523f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3524f7278fd0SJosepch Chan {
3525f7278fd0SJosepch Chan 	struct via_spec *spec;
3526f7278fd0SJosepch Chan 	int err;
3527f7278fd0SJosepch Chan 
3528f7278fd0SJosepch Chan 	/* create a codec specific record */
3529eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3530f7278fd0SJosepch Chan 	if (spec == NULL)
3531f7278fd0SJosepch Chan 		return -ENOMEM;
3532f7278fd0SJosepch Chan 
3533f7278fd0SJosepch Chan 	codec->spec = spec;
3534f7278fd0SJosepch Chan 
3535f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3536f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3537f7278fd0SJosepch Chan 	if (err < 0) {
3538f7278fd0SJosepch Chan 		via_free(codec);
3539f7278fd0SJosepch Chan 		return err;
3540f7278fd0SJosepch Chan 	} else if (!err) {
3541f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3542f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3543f7278fd0SJosepch Chan 	}
3544f7278fd0SJosepch Chan 
354569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
354669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3547f7278fd0SJosepch Chan 
3548f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3549f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3550f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3551f7278fd0SJosepch Chan 
3552f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3553f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3554f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3555f7278fd0SJosepch Chan 
3556f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3557f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3558f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3559337b9d02STakashi Iwai 		get_mux_nids(codec);
3560f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3561f7278fd0SJosepch Chan 		spec->num_mixers++;
3562f7278fd0SJosepch Chan 	}
3563f7278fd0SJosepch Chan 
3564f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3565f7278fd0SJosepch Chan 
3566f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
356769e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3568f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3569f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3570f7278fd0SJosepch Chan #endif
3571c577b8a1SJoseph Chan 
3572c577b8a1SJoseph Chan 	return 0;
3573c577b8a1SJoseph Chan }
3574c577b8a1SJoseph Chan 
3575d949cac1SHarald Welte /* Patch for VT1708S */
3576d949cac1SHarald Welte 
3577d949cac1SHarald Welte /* capture mixer elements */
3578d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3579d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3580d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3581d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3582d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
35836369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
35846369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
35856369bcfcSLydia Wang 			 HDA_INPUT),
3586d949cac1SHarald Welte 	{
3587d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3588d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3589d949cac1SHarald Welte 		 * So call somewhat different..
3590d949cac1SHarald Welte 		 */
3591d949cac1SHarald Welte 		/* .name = "Capture Source", */
3592d949cac1SHarald Welte 		.name = "Input Source",
3593d949cac1SHarald Welte 		.count = 1,
3594d949cac1SHarald Welte 		.info = via_mux_enum_info,
3595d949cac1SHarald Welte 		.get = via_mux_enum_get,
3596d949cac1SHarald Welte 		.put = via_mux_enum_put,
3597d949cac1SHarald Welte 	},
3598d949cac1SHarald Welte 	{ } /* end */
3599d949cac1SHarald Welte };
3600d949cac1SHarald Welte 
3601d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3602d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3603d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3604d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3605d949cac1SHarald Welte 
3606d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3607d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3608d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3609d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3610d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3611d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3612d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3613d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3614d949cac1SHarald Welte 
3615d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3616d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
36175691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3618d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
36195691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3620d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3621d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3622bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3623bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3624d949cac1SHarald Welte 	{ }
3625d949cac1SHarald Welte };
3626d949cac1SHarald Welte 
362769e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3628a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3629a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3630a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3631a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3632a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3633a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3634a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3635a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3636a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
363769e52a80SHarald Welte 	{ }
363869e52a80SHarald Welte };
363969e52a80SHarald Welte 
3640d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3641d949cac1SHarald Welte 	.substreams = 2,
3642d949cac1SHarald Welte 	.channels_min = 2,
3643d949cac1SHarald Welte 	.channels_max = 8,
3644d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3645d949cac1SHarald Welte 	.ops = {
3646d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3647c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3648c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
364917314379SLydia Wang 		.close = via_pcm_open_close
3650d949cac1SHarald Welte 	},
3651d949cac1SHarald Welte };
3652d949cac1SHarald Welte 
3653d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3654d949cac1SHarald Welte 	.substreams = 2,
3655d949cac1SHarald Welte 	.channels_min = 2,
3656d949cac1SHarald Welte 	.channels_max = 2,
3657d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3658d949cac1SHarald Welte 	.ops = {
365917314379SLydia Wang 		.open = via_pcm_open_close,
3660d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
366117314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
366217314379SLydia Wang 		.close = via_pcm_open_close
3663d949cac1SHarald Welte 	},
3664d949cac1SHarald Welte };
3665d949cac1SHarald Welte 
3666d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
36679da29271STakashi Iwai 	.substreams = 1,
3668d949cac1SHarald Welte 	.channels_min = 2,
3669d949cac1SHarald Welte 	.channels_max = 2,
3670d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3671d949cac1SHarald Welte 	.ops = {
3672d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3673d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
36749da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
36759da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3676d949cac1SHarald Welte 	},
3677d949cac1SHarald Welte };
3678d949cac1SHarald Welte 
3679d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3680d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3681d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3682d949cac1SHarald Welte {
3683d949cac1SHarald Welte 	int i;
3684d949cac1SHarald Welte 	hda_nid_t nid;
3685d949cac1SHarald Welte 
3686d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3687d949cac1SHarald Welte 
3688d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3689d949cac1SHarald Welte 
3690d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3691d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3692d949cac1SHarald Welte 		if (nid) {
3693d949cac1SHarald Welte 			/* config dac list */
3694d949cac1SHarald Welte 			switch (i) {
3695d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3696d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3697d949cac1SHarald Welte 				break;
3698d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3699d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x24;
3700d949cac1SHarald Welte 				break;
3701d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3702d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3703d949cac1SHarald Welte 				break;
3704d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3705d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3706d949cac1SHarald Welte 				break;
3707d949cac1SHarald Welte 			}
3708d949cac1SHarald Welte 		}
3709d949cac1SHarald Welte 	}
3710d949cac1SHarald Welte 
3711d949cac1SHarald Welte 	return 0;
3712d949cac1SHarald Welte }
3713d949cac1SHarald Welte 
3714d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3715d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3716d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3717d949cac1SHarald Welte {
3718d949cac1SHarald Welte 	char name[32];
3719d949cac1SHarald Welte 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3720d949cac1SHarald Welte 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3721d949cac1SHarald Welte 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3722d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3723d949cac1SHarald Welte 	int i, err;
3724d949cac1SHarald Welte 
3725d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3726d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3727d949cac1SHarald Welte 
3728d949cac1SHarald Welte 		if (!nid)
3729d949cac1SHarald Welte 			continue;
3730d949cac1SHarald Welte 
3731d949cac1SHarald Welte 		nid_vol = nid_vols[i];
3732d949cac1SHarald Welte 		nid_mute = nid_mutes[i];
3733d949cac1SHarald Welte 
3734d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3735d949cac1SHarald Welte 			/* Center/LFE */
3736d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3737d949cac1SHarald Welte 					      "Center Playback Volume",
3738d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3739d949cac1SHarald Welte 								  HDA_OUTPUT));
3740d949cac1SHarald Welte 			if (err < 0)
3741d949cac1SHarald Welte 				return err;
3742d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3743d949cac1SHarald Welte 					      "LFE Playback Volume",
3744d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3745d949cac1SHarald Welte 								  HDA_OUTPUT));
3746d949cac1SHarald Welte 			if (err < 0)
3747d949cac1SHarald Welte 				return err;
3748d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3749d949cac1SHarald Welte 					      "Center Playback Switch",
3750d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3751d949cac1SHarald Welte 								  1, 0,
3752d949cac1SHarald Welte 								  HDA_OUTPUT));
3753d949cac1SHarald Welte 			if (err < 0)
3754d949cac1SHarald Welte 				return err;
3755d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3756d949cac1SHarald Welte 					      "LFE Playback Switch",
3757d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3758d949cac1SHarald Welte 								  2, 0,
3759d949cac1SHarald Welte 								  HDA_OUTPUT));
3760d949cac1SHarald Welte 			if (err < 0)
3761d949cac1SHarald Welte 				return err;
3762d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3763d949cac1SHarald Welte 			/* add control to mixer index 0 */
3764d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3765d949cac1SHarald Welte 					      "Master Front Playback Volume",
3766d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3767d949cac1SHarald Welte 								  HDA_INPUT));
3768d949cac1SHarald Welte 			if (err < 0)
3769d949cac1SHarald Welte 				return err;
3770d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3771d949cac1SHarald Welte 					      "Master Front Playback Switch",
3772d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3773d949cac1SHarald Welte 								  HDA_INPUT));
3774d949cac1SHarald Welte 			if (err < 0)
3775d949cac1SHarald Welte 				return err;
3776d949cac1SHarald Welte 
3777d949cac1SHarald Welte 			/* Front */
3778d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3779d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3780d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3781d949cac1SHarald Welte 								  HDA_OUTPUT));
3782d949cac1SHarald Welte 			if (err < 0)
3783d949cac1SHarald Welte 				return err;
3784d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3785d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3786d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3787d949cac1SHarald Welte 								  3, 0,
3788d949cac1SHarald Welte 								  HDA_OUTPUT));
3789d949cac1SHarald Welte 			if (err < 0)
3790d949cac1SHarald Welte 				return err;
3791d949cac1SHarald Welte 		} else {
3792d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3793d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3794d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3795d949cac1SHarald Welte 								  HDA_OUTPUT));
3796d949cac1SHarald Welte 			if (err < 0)
3797d949cac1SHarald Welte 				return err;
3798d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3799d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3800d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3801d949cac1SHarald Welte 								  3, 0,
3802d949cac1SHarald Welte 								  HDA_OUTPUT));
3803d949cac1SHarald Welte 			if (err < 0)
3804d949cac1SHarald Welte 				return err;
3805d949cac1SHarald Welte 		}
3806d949cac1SHarald Welte 	}
3807d949cac1SHarald Welte 
3808d949cac1SHarald Welte 	return 0;
3809d949cac1SHarald Welte }
3810d949cac1SHarald Welte 
3811d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3812d949cac1SHarald Welte {
3813d949cac1SHarald Welte 	int err;
3814d949cac1SHarald Welte 
3815d949cac1SHarald Welte 	if (!pin)
3816d949cac1SHarald Welte 		return 0;
3817d949cac1SHarald Welte 
3818d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3819cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3820d949cac1SHarald Welte 
3821d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3822d949cac1SHarald Welte 			      "Headphone Playback Volume",
3823d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3824d949cac1SHarald Welte 	if (err < 0)
3825d949cac1SHarald Welte 		return err;
3826d949cac1SHarald Welte 
3827d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3828d949cac1SHarald Welte 			      "Headphone Playback Switch",
3829d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3830d949cac1SHarald Welte 	if (err < 0)
3831d949cac1SHarald Welte 		return err;
3832d949cac1SHarald Welte 
38330aa62aefSHarald Welte 	create_hp_imux(spec);
38340aa62aefSHarald Welte 
3835d949cac1SHarald Welte 	return 0;
3836d949cac1SHarald Welte }
3837d949cac1SHarald Welte 
3838d949cac1SHarald Welte /* create playback/capture controls for input pins */
3839d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
3840d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
3841d949cac1SHarald Welte {
3842d949cac1SHarald Welte 	static char *labels[] = {
3843d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3844d949cac1SHarald Welte 	};
38450aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3846d949cac1SHarald Welte 	int i, err, idx = 0;
3847d949cac1SHarald Welte 
3848d949cac1SHarald Welte 	/* for internal loopback recording select */
3849d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
3850d949cac1SHarald Welte 	imux->items[imux->num_items].index = 5;
3851d949cac1SHarald Welte 	imux->num_items++;
3852d949cac1SHarald Welte 
3853d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3854d949cac1SHarald Welte 		if (!cfg->input_pins[i])
3855d949cac1SHarald Welte 			continue;
3856d949cac1SHarald Welte 
3857d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
3858d949cac1SHarald Welte 		case 0x1a: /* Mic */
3859d949cac1SHarald Welte 			idx = 2;
3860d949cac1SHarald Welte 			break;
3861d949cac1SHarald Welte 
3862d949cac1SHarald Welte 		case 0x1b: /* Line In */
3863d949cac1SHarald Welte 			idx = 3;
3864d949cac1SHarald Welte 			break;
3865d949cac1SHarald Welte 
3866d949cac1SHarald Welte 		case 0x1e: /* Front Mic */
3867d949cac1SHarald Welte 			idx = 4;
3868d949cac1SHarald Welte 			break;
3869d949cac1SHarald Welte 
3870d949cac1SHarald Welte 		case 0x1f: /* CD */
3871d949cac1SHarald Welte 			idx = 1;
3872d949cac1SHarald Welte 			break;
3873d949cac1SHarald Welte 		}
38749510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
3875d949cac1SHarald Welte 		if (err < 0)
3876d949cac1SHarald Welte 			return err;
3877d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
3878d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
3879d949cac1SHarald Welte 		imux->num_items++;
3880d949cac1SHarald Welte 	}
3881d949cac1SHarald Welte 	return 0;
3882d949cac1SHarald Welte }
3883d949cac1SHarald Welte 
38849da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
38859da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
38869da29271STakashi Iwai {
38879da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
38889da29271STakashi Iwai 	int i;
38899da29271STakashi Iwai 
38909da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
38919da29271STakashi Iwai 		hda_nid_t nid;
38929da29271STakashi Iwai 		int conn;
38939da29271STakashi Iwai 
38949da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
38959da29271STakashi Iwai 		if (!nid)
38969da29271STakashi Iwai 			continue;
38979da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
38989da29271STakashi Iwai 		if (conn < 1)
38999da29271STakashi Iwai 			continue;
39009da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
39019da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
39029da29271STakashi Iwai 		else {
39039da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
39049da29271STakashi Iwai 			break; /* at most two dig outs */
39059da29271STakashi Iwai 		}
39069da29271STakashi Iwai 	}
39079da29271STakashi Iwai }
39089da29271STakashi Iwai 
3909d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3910d949cac1SHarald Welte {
3911d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3912d949cac1SHarald Welte 	int err;
3913d949cac1SHarald Welte 
39149da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3915d949cac1SHarald Welte 	if (err < 0)
3916d949cac1SHarald Welte 		return err;
3917d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3918d949cac1SHarald Welte 	if (err < 0)
3919d949cac1SHarald Welte 		return err;
3920d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3921d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3922d949cac1SHarald Welte 
3923d949cac1SHarald Welte 	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
3924d949cac1SHarald Welte 	if (err < 0)
3925d949cac1SHarald Welte 		return err;
3926d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3927d949cac1SHarald Welte 	if (err < 0)
3928d949cac1SHarald Welte 		return err;
3929d949cac1SHarald Welte 	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
3930d949cac1SHarald Welte 	if (err < 0)
3931d949cac1SHarald Welte 		return err;
3932d949cac1SHarald Welte 
3933d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3934d949cac1SHarald Welte 
39359da29271STakashi Iwai 	fill_dig_outs(codec);
393698aa34c0SHarald Welte 
3937603c4019STakashi Iwai 	if (spec->kctls.list)
3938603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3939d949cac1SHarald Welte 
39400aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
39410aa62aefSHarald Welte 
3942f8fdd495SHarald Welte 	if (spec->hp_mux)
39430aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
3944d949cac1SHarald Welte 
39451564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
3946d949cac1SHarald Welte 	return 1;
3947d949cac1SHarald Welte }
3948d949cac1SHarald Welte 
3949d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3950d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
3951d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3952d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3953d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3954d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3955d949cac1SHarald Welte 	{ } /* end */
3956d949cac1SHarald Welte };
3957d949cac1SHarald Welte #endif
3958d949cac1SHarald Welte 
39596369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
39606369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
39616369bcfcSLydia Wang {
39626369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
39636369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
39646369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
39656369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
39666369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
39676369bcfcSLydia Wang }
39686369bcfcSLydia Wang 
3969d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3970d949cac1SHarald Welte {
3971d949cac1SHarald Welte 	struct via_spec *spec;
3972d949cac1SHarald Welte 	int err;
3973d949cac1SHarald Welte 
3974d949cac1SHarald Welte 	/* create a codec specific record */
3975d949cac1SHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3976d949cac1SHarald Welte 	if (spec == NULL)
3977d949cac1SHarald Welte 		return -ENOMEM;
3978d949cac1SHarald Welte 
3979d949cac1SHarald Welte 	codec->spec = spec;
3980d949cac1SHarald Welte 
3981d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3982d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3983d949cac1SHarald Welte 	if (err < 0) {
3984d949cac1SHarald Welte 		via_free(codec);
3985d949cac1SHarald Welte 		return err;
3986d949cac1SHarald Welte 	} else if (!err) {
3987d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3988d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3989d949cac1SHarald Welte 	}
3990d949cac1SHarald Welte 
399169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
399269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
3993d949cac1SHarald Welte 
3994d949cac1SHarald Welte 	spec->stream_name_analog = "VT1708S Analog";
3995d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3996d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3997d949cac1SHarald Welte 
3998d949cac1SHarald Welte 	spec->stream_name_digital = "VT1708S Digital";
3999d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
4000d949cac1SHarald Welte 
4001d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4002d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
4003d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
4004337b9d02STakashi Iwai 		get_mux_nids(codec);
40056369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
40066369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
4007d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
4008d949cac1SHarald Welte 		spec->num_mixers++;
4009d949cac1SHarald Welte 	}
4010d949cac1SHarald Welte 
4011d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4012d949cac1SHarald Welte 
4013d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
401469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4015d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4016d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
4017d949cac1SHarald Welte #endif
4018d949cac1SHarald Welte 
4019518bf3baSLydia Wang 	/* correct names for VT1708BCE */
4020518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
4021518bf3baSLydia Wang 		kfree(codec->chip_name);
4022518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
4023518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
4024518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
4025518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
4026518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
4027518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
4028518bf3baSLydia Wang 	}
4029d949cac1SHarald Welte 	return 0;
4030d949cac1SHarald Welte }
4031d949cac1SHarald Welte 
4032d949cac1SHarald Welte /* Patch for VT1702 */
4033d949cac1SHarald Welte 
4034d949cac1SHarald Welte /* capture mixer elements */
4035d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
4036d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
4037d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
4038d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
4039d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
4040d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
4041d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
4042d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
4043d949cac1SHarald Welte 			 HDA_INPUT),
4044d949cac1SHarald Welte 	{
4045d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4046d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
4047d949cac1SHarald Welte 		 * So call somewhat different..
4048d949cac1SHarald Welte 		 */
4049d949cac1SHarald Welte 		/* .name = "Capture Source", */
4050d949cac1SHarald Welte 		.name = "Input Source",
4051d949cac1SHarald Welte 		.count = 1,
4052d949cac1SHarald Welte 		.info = via_mux_enum_info,
4053d949cac1SHarald Welte 		.get = via_mux_enum_get,
4054d949cac1SHarald Welte 		.put = via_mux_enum_put,
4055d949cac1SHarald Welte 	},
4056d949cac1SHarald Welte 	{ } /* end */
4057d949cac1SHarald Welte };
4058d949cac1SHarald Welte 
4059d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
4060d949cac1SHarald Welte 	/*
4061d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
4062d949cac1SHarald Welte 	 */
4063d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4064d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4065d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4066d949cac1SHarald Welte 
4067d949cac1SHarald Welte 
4068d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4069d949cac1SHarald Welte 	 * mixer widget
4070d949cac1SHarald Welte 	 */
4071d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
4072d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4073d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4074d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4075d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4076d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4077d949cac1SHarald Welte 
4078d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
4079d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
4080d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
4081d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4082d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4083bc7e7e5cSLydia Wang 	/* mixer enable */
4084bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
4085bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
4086bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
4087d949cac1SHarald Welte 	{ }
4088d949cac1SHarald Welte };
4089d949cac1SHarald Welte 
409069e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
4091a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
4092a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4093a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4094a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4095a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4096a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
409769e52a80SHarald Welte 	{ }
409869e52a80SHarald Welte };
409969e52a80SHarald Welte 
4100d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
41010aa62aefSHarald Welte 	.substreams = 2,
4102d949cac1SHarald Welte 	.channels_min = 2,
4103d949cac1SHarald Welte 	.channels_max = 2,
4104d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
4105d949cac1SHarald Welte 	.ops = {
4106d949cac1SHarald Welte 		.open = via_playback_pcm_open,
41070aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
410817314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
410917314379SLydia Wang 		.close = via_pcm_open_close
4110d949cac1SHarald Welte 	},
4111d949cac1SHarald Welte };
4112d949cac1SHarald Welte 
4113d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
4114d949cac1SHarald Welte 	.substreams = 3,
4115d949cac1SHarald Welte 	.channels_min = 2,
4116d949cac1SHarald Welte 	.channels_max = 2,
4117d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
4118d949cac1SHarald Welte 	.ops = {
411917314379SLydia Wang 		.open = via_pcm_open_close,
4120d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
412117314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
412217314379SLydia Wang 		.close = via_pcm_open_close
4123d949cac1SHarald Welte 	},
4124d949cac1SHarald Welte };
4125d949cac1SHarald Welte 
4126d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
41275691ec7fSHarald Welte 	.substreams = 2,
4128d949cac1SHarald Welte 	.channels_min = 2,
4129d949cac1SHarald Welte 	.channels_max = 2,
4130d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
4131d949cac1SHarald Welte 	.ops = {
4132d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
4133d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
41349da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
41359da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
4136d949cac1SHarald Welte 	},
4137d949cac1SHarald Welte };
4138d949cac1SHarald Welte 
4139d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4140d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4141d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4142d949cac1SHarald Welte {
4143d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4144d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4145d949cac1SHarald Welte 
4146d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4147d949cac1SHarald Welte 		/* config dac list */
4148d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
4149d949cac1SHarald Welte 	}
4150d949cac1SHarald Welte 
4151d949cac1SHarald Welte 	return 0;
4152d949cac1SHarald Welte }
4153d949cac1SHarald Welte 
4154d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4155d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4156d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4157d949cac1SHarald Welte {
4158d949cac1SHarald Welte 	int err;
4159d949cac1SHarald Welte 
4160d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4161d949cac1SHarald Welte 		return -1;
4162d949cac1SHarald Welte 
4163d949cac1SHarald Welte 	/* add control to mixer index 0 */
4164d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4165d949cac1SHarald Welte 			      "Master Front Playback Volume",
4166d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4167d949cac1SHarald Welte 	if (err < 0)
4168d949cac1SHarald Welte 		return err;
4169d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4170d949cac1SHarald Welte 			      "Master Front Playback Switch",
4171d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4172d949cac1SHarald Welte 	if (err < 0)
4173d949cac1SHarald Welte 		return err;
4174d949cac1SHarald Welte 
4175d949cac1SHarald Welte 	/* Front */
4176d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4177d949cac1SHarald Welte 			      "Front Playback Volume",
4178d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4179d949cac1SHarald Welte 	if (err < 0)
4180d949cac1SHarald Welte 		return err;
4181d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4182d949cac1SHarald Welte 			      "Front Playback Switch",
4183d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4184d949cac1SHarald Welte 	if (err < 0)
4185d949cac1SHarald Welte 		return err;
4186d949cac1SHarald Welte 
4187d949cac1SHarald Welte 	return 0;
4188d949cac1SHarald Welte }
4189d949cac1SHarald Welte 
4190d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4191d949cac1SHarald Welte {
41920713efebSLydia Wang 	int err, i;
41930713efebSLydia Wang 	struct hda_input_mux *imux;
41940713efebSLydia Wang 	static const char *texts[] = { "ON", "OFF", NULL};
4195d949cac1SHarald Welte 	if (!pin)
4196d949cac1SHarald Welte 		return 0;
4197d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4198cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4199d949cac1SHarald Welte 
4200d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4201d949cac1SHarald Welte 			      "Headphone Playback Volume",
4202d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4203d949cac1SHarald Welte 	if (err < 0)
4204d949cac1SHarald Welte 		return err;
4205d949cac1SHarald Welte 
4206d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4207d949cac1SHarald Welte 			      "Headphone Playback Switch",
4208d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4209d949cac1SHarald Welte 	if (err < 0)
4210d949cac1SHarald Welte 		return err;
4211d949cac1SHarald Welte 
42120713efebSLydia Wang 	imux = &spec->private_imux[1];
42130aa62aefSHarald Welte 
42140713efebSLydia Wang 	/* for hp mode select */
42150713efebSLydia Wang 	i = 0;
42160713efebSLydia Wang 	while (texts[i] != NULL)	{
42170713efebSLydia Wang 		imux->items[imux->num_items].label =  texts[i];
42180713efebSLydia Wang 		imux->items[imux->num_items].index = i;
42190713efebSLydia Wang 		imux->num_items++;
42200713efebSLydia Wang 		i++;
42210713efebSLydia Wang 	}
42220713efebSLydia Wang 
42230713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4224d949cac1SHarald Welte 	return 0;
4225d949cac1SHarald Welte }
4226d949cac1SHarald Welte 
4227d949cac1SHarald Welte /* create playback/capture controls for input pins */
4228d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
4229d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4230d949cac1SHarald Welte {
4231d949cac1SHarald Welte 	static char *labels[] = {
4232d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4233d949cac1SHarald Welte 	};
42340aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
4235d949cac1SHarald Welte 	int i, err, idx = 0;
4236d949cac1SHarald Welte 
4237d949cac1SHarald Welte 	/* for internal loopback recording select */
4238d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
4239d949cac1SHarald Welte 	imux->items[imux->num_items].index = 3;
4240d949cac1SHarald Welte 	imux->num_items++;
4241d949cac1SHarald Welte 
4242d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4243d949cac1SHarald Welte 		if (!cfg->input_pins[i])
4244d949cac1SHarald Welte 			continue;
4245d949cac1SHarald Welte 
4246d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
4247d949cac1SHarald Welte 		case 0x14: /* Mic */
4248d949cac1SHarald Welte 			idx = 1;
4249d949cac1SHarald Welte 			break;
4250d949cac1SHarald Welte 
4251d949cac1SHarald Welte 		case 0x15: /* Line In */
4252d949cac1SHarald Welte 			idx = 2;
4253d949cac1SHarald Welte 			break;
4254d949cac1SHarald Welte 
4255d949cac1SHarald Welte 		case 0x18: /* Front Mic */
4256d949cac1SHarald Welte 			idx = 3;
4257d949cac1SHarald Welte 			break;
4258d949cac1SHarald Welte 		}
42599510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x1A);
4260d949cac1SHarald Welte 		if (err < 0)
4261d949cac1SHarald Welte 			return err;
4262d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
4263d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
4264d949cac1SHarald Welte 		imux->num_items++;
4265d949cac1SHarald Welte 	}
4266d949cac1SHarald Welte 	return 0;
4267d949cac1SHarald Welte }
4268d949cac1SHarald Welte 
4269d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4270d949cac1SHarald Welte {
4271d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4272d949cac1SHarald Welte 	int err;
4273d949cac1SHarald Welte 
42749da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4275d949cac1SHarald Welte 	if (err < 0)
4276d949cac1SHarald Welte 		return err;
4277d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4278d949cac1SHarald Welte 	if (err < 0)
4279d949cac1SHarald Welte 		return err;
4280d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4281d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4282d949cac1SHarald Welte 
4283d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4284d949cac1SHarald Welte 	if (err < 0)
4285d949cac1SHarald Welte 		return err;
4286d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4287d949cac1SHarald Welte 	if (err < 0)
4288d949cac1SHarald Welte 		return err;
4289c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4290c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4291c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4292c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4293c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4294c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
4295d949cac1SHarald Welte 	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
4296d949cac1SHarald Welte 	if (err < 0)
4297d949cac1SHarald Welte 		return err;
4298d949cac1SHarald Welte 
4299d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4300d949cac1SHarald Welte 
43019da29271STakashi Iwai 	fill_dig_outs(codec);
430298aa34c0SHarald Welte 
4303603c4019STakashi Iwai 	if (spec->kctls.list)
4304603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4305d949cac1SHarald Welte 
43060aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
43070aa62aefSHarald Welte 
4308f8fdd495SHarald Welte 	if (spec->hp_mux)
43090aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
4310d949cac1SHarald Welte 
4311d949cac1SHarald Welte 	return 1;
4312d949cac1SHarald Welte }
4313d949cac1SHarald Welte 
4314d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4315d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4316d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4317d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4318d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4319d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4320d949cac1SHarald Welte 	{ } /* end */
4321d949cac1SHarald Welte };
4322d949cac1SHarald Welte #endif
4323d949cac1SHarald Welte 
4324d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4325d949cac1SHarald Welte {
4326d949cac1SHarald Welte 	struct via_spec *spec;
4327d949cac1SHarald Welte 	int err;
4328d949cac1SHarald Welte 
4329d949cac1SHarald Welte 	/* create a codec specific record */
4330d949cac1SHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4331d949cac1SHarald Welte 	if (spec == NULL)
4332d949cac1SHarald Welte 		return -ENOMEM;
4333d949cac1SHarald Welte 
4334d949cac1SHarald Welte 	codec->spec = spec;
4335d949cac1SHarald Welte 
4336d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4337d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4338d949cac1SHarald Welte 	if (err < 0) {
4339d949cac1SHarald Welte 		via_free(codec);
4340d949cac1SHarald Welte 		return err;
4341d949cac1SHarald Welte 	} else if (!err) {
4342d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4343d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4344d949cac1SHarald Welte 	}
4345d949cac1SHarald Welte 
434669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
434769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4348d949cac1SHarald Welte 
4349d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4350d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4351d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4352d949cac1SHarald Welte 
4353d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4354d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4355d949cac1SHarald Welte 
4356d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4357d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4358d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4359337b9d02STakashi Iwai 		get_mux_nids(codec);
4360d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4361d949cac1SHarald Welte 		spec->num_mixers++;
4362d949cac1SHarald Welte 	}
4363d949cac1SHarald Welte 
4364d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4365d949cac1SHarald Welte 
4366d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
436769e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4368d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4369d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4370d949cac1SHarald Welte #endif
4371d949cac1SHarald Welte 
4372d949cac1SHarald Welte 	return 0;
4373d949cac1SHarald Welte }
4374d949cac1SHarald Welte 
4375eb7188caSLydia Wang /* Patch for VT1718S */
4376eb7188caSLydia Wang 
4377eb7188caSLydia Wang /* capture mixer elements */
4378eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4379eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4380eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4381eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4382eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4383eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4384eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4385eb7188caSLydia Wang 			 HDA_INPUT),
4386eb7188caSLydia Wang 	{
4387eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4388eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4389eb7188caSLydia Wang 		 * So call somewhat different..
4390eb7188caSLydia Wang 		 */
4391eb7188caSLydia Wang 		.name = "Input Source",
4392eb7188caSLydia Wang 		.count = 2,
4393eb7188caSLydia Wang 		.info = via_mux_enum_info,
4394eb7188caSLydia Wang 		.get = via_mux_enum_get,
4395eb7188caSLydia Wang 		.put = via_mux_enum_put,
4396eb7188caSLydia Wang 	},
4397eb7188caSLydia Wang 	{ } /* end */
4398eb7188caSLydia Wang };
4399eb7188caSLydia Wang 
4400eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4401eb7188caSLydia Wang 	/*
4402eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4403eb7188caSLydia Wang 	 */
4404eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4405eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4406eb7188caSLydia Wang 
4407eb7188caSLydia Wang 
4408eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4409eb7188caSLydia Wang 	 * mixer widget
4410eb7188caSLydia Wang 	 */
4411eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4412eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4413eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4414eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4415eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4416eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4417eb7188caSLydia Wang 
4418eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4419eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4420eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4421eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4422eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4423eb7188caSLydia Wang 	/* PW11 Input enable */
4424eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4425eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4426eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4427eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4428eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4429eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4430eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4431eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4432eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4433eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4434eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4435eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4436eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4437eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4438eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4439eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4440eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4441eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4442eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4443eb7188caSLydia Wang 	{ }
4444eb7188caSLydia Wang };
4445eb7188caSLydia Wang 
4446eb7188caSLydia Wang 
4447eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4448eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4449eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4450eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4451eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4452eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4453eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4454eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4455eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4456eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4457eb7188caSLydia Wang 	{ }
4458eb7188caSLydia Wang };
4459eb7188caSLydia Wang 
4460eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4461eb7188caSLydia Wang 	.substreams = 2,
4462eb7188caSLydia Wang 	.channels_min = 2,
4463eb7188caSLydia Wang 	.channels_max = 10,
4464eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4465eb7188caSLydia Wang 	.ops = {
4466eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4467eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4468eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4469eb7188caSLydia Wang 		.close = via_pcm_open_close,
4470eb7188caSLydia Wang 	},
4471eb7188caSLydia Wang };
4472eb7188caSLydia Wang 
4473eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4474eb7188caSLydia Wang 	.substreams = 2,
4475eb7188caSLydia Wang 	.channels_min = 2,
4476eb7188caSLydia Wang 	.channels_max = 2,
4477eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4478eb7188caSLydia Wang 	.ops = {
4479eb7188caSLydia Wang 		.open = via_pcm_open_close,
4480eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4481eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4482eb7188caSLydia Wang 		.close = via_pcm_open_close,
4483eb7188caSLydia Wang 	},
4484eb7188caSLydia Wang };
4485eb7188caSLydia Wang 
4486eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4487eb7188caSLydia Wang 	.substreams = 2,
4488eb7188caSLydia Wang 	.channels_min = 2,
4489eb7188caSLydia Wang 	.channels_max = 2,
4490eb7188caSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
4491eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4492eb7188caSLydia Wang 	.ops = {
4493eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4494eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4495eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4496eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4497eb7188caSLydia Wang 	},
4498eb7188caSLydia Wang };
4499eb7188caSLydia Wang 
4500eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4501eb7188caSLydia Wang 	.substreams = 1,
4502eb7188caSLydia Wang 	.channels_min = 2,
4503eb7188caSLydia Wang 	.channels_max = 2,
4504eb7188caSLydia Wang };
4505eb7188caSLydia Wang 
4506eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4507eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4508eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4509eb7188caSLydia Wang {
4510eb7188caSLydia Wang 	int i;
4511eb7188caSLydia Wang 	hda_nid_t nid;
4512eb7188caSLydia Wang 
4513eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4514eb7188caSLydia Wang 
4515eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4516eb7188caSLydia Wang 
4517eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4518eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4519eb7188caSLydia Wang 		if (nid) {
4520eb7188caSLydia Wang 			/* config dac list */
4521eb7188caSLydia Wang 			switch (i) {
4522eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4523eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4524eb7188caSLydia Wang 				break;
4525eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4526eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4527eb7188caSLydia Wang 				break;
4528eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4529eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4530eb7188caSLydia Wang 				break;
4531eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4532eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4533eb7188caSLydia Wang 				break;
4534eb7188caSLydia Wang 			}
4535eb7188caSLydia Wang 		}
4536eb7188caSLydia Wang 	}
4537eb7188caSLydia Wang 
4538eb7188caSLydia Wang 	return 0;
4539eb7188caSLydia Wang }
4540eb7188caSLydia Wang 
4541eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4542eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4543eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4544eb7188caSLydia Wang {
4545eb7188caSLydia Wang 	char name[32];
4546eb7188caSLydia Wang 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
4547eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4548eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4549eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4550eb7188caSLydia Wang 	int i, err;
4551eb7188caSLydia Wang 
4552eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4553eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4554eb7188caSLydia Wang 
4555eb7188caSLydia Wang 		if (!nid)
4556eb7188caSLydia Wang 			continue;
4557eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4558eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4559eb7188caSLydia Wang 
4560eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4561eb7188caSLydia Wang 			/* Center/LFE */
4562eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4563eb7188caSLydia Wang 					      "Center Playback Volume",
4564eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4565eb7188caSLydia Wang 								  HDA_OUTPUT));
4566eb7188caSLydia Wang 			if (err < 0)
4567eb7188caSLydia Wang 				return err;
4568eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4569eb7188caSLydia Wang 					      "LFE Playback Volume",
4570eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4571eb7188caSLydia Wang 								  HDA_OUTPUT));
4572eb7188caSLydia Wang 			if (err < 0)
4573eb7188caSLydia Wang 				return err;
4574eb7188caSLydia Wang 			err = via_add_control(
4575eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4576eb7188caSLydia Wang 				"Center Playback Switch",
4577eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4578eb7188caSLydia Wang 						    HDA_OUTPUT));
4579eb7188caSLydia Wang 			if (err < 0)
4580eb7188caSLydia Wang 				return err;
4581eb7188caSLydia Wang 			err = via_add_control(
4582eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4583eb7188caSLydia Wang 				"LFE Playback Switch",
4584eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4585eb7188caSLydia Wang 						    HDA_OUTPUT));
4586eb7188caSLydia Wang 			if (err < 0)
4587eb7188caSLydia Wang 				return err;
4588eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4589eb7188caSLydia Wang 			/* Front */
4590eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4591eb7188caSLydia Wang 			err = via_add_control(
4592eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4593eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4594eb7188caSLydia Wang 			if (err < 0)
4595eb7188caSLydia Wang 				return err;
4596eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4597eb7188caSLydia Wang 			err = via_add_control(
4598eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4599eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4600eb7188caSLydia Wang 						    HDA_OUTPUT));
4601eb7188caSLydia Wang 			if (err < 0)
4602eb7188caSLydia Wang 				return err;
4603eb7188caSLydia Wang 		} else {
4604eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4605eb7188caSLydia Wang 			err = via_add_control(
4606eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4607eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4608eb7188caSLydia Wang 			if (err < 0)
4609eb7188caSLydia Wang 				return err;
4610eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4611eb7188caSLydia Wang 			err = via_add_control(
4612eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4613eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4614eb7188caSLydia Wang 						    HDA_OUTPUT));
4615eb7188caSLydia Wang 			if (err < 0)
4616eb7188caSLydia Wang 				return err;
4617eb7188caSLydia Wang 		}
4618eb7188caSLydia Wang 	}
4619eb7188caSLydia Wang 	return 0;
4620eb7188caSLydia Wang }
4621eb7188caSLydia Wang 
4622eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4623eb7188caSLydia Wang {
4624eb7188caSLydia Wang 	int err;
4625eb7188caSLydia Wang 
4626eb7188caSLydia Wang 	if (!pin)
4627eb7188caSLydia Wang 		return 0;
4628eb7188caSLydia Wang 
4629eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4630eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4631eb7188caSLydia Wang 
4632eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4633eb7188caSLydia Wang 			      "Headphone Playback Volume",
4634eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4635eb7188caSLydia Wang 	if (err < 0)
4636eb7188caSLydia Wang 		return err;
4637eb7188caSLydia Wang 
4638eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4639eb7188caSLydia Wang 			      "Headphone Playback Switch",
4640eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4641eb7188caSLydia Wang 	if (err < 0)
4642eb7188caSLydia Wang 		return err;
4643eb7188caSLydia Wang 
4644eb7188caSLydia Wang 	create_hp_imux(spec);
4645eb7188caSLydia Wang 	return 0;
4646eb7188caSLydia Wang }
4647eb7188caSLydia Wang 
4648eb7188caSLydia Wang /* create playback/capture controls for input pins */
4649eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
4650eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4651eb7188caSLydia Wang {
4652eb7188caSLydia Wang 	static char *labels[] = {
4653eb7188caSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4654eb7188caSLydia Wang 	};
4655eb7188caSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
4656eb7188caSLydia Wang 	int i, err, idx = 0;
4657eb7188caSLydia Wang 
4658eb7188caSLydia Wang 	/* for internal loopback recording select */
4659eb7188caSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
4660eb7188caSLydia Wang 	imux->items[imux->num_items].index = 5;
4661eb7188caSLydia Wang 	imux->num_items++;
4662eb7188caSLydia Wang 
4663eb7188caSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4664eb7188caSLydia Wang 		if (!cfg->input_pins[i])
4665eb7188caSLydia Wang 			continue;
4666eb7188caSLydia Wang 
4667eb7188caSLydia Wang 		switch (cfg->input_pins[i]) {
4668eb7188caSLydia Wang 		case 0x2b: /* Mic */
4669eb7188caSLydia Wang 			idx = 1;
4670eb7188caSLydia Wang 			break;
4671eb7188caSLydia Wang 
4672eb7188caSLydia Wang 		case 0x2a: /* Line In */
4673eb7188caSLydia Wang 			idx = 2;
4674eb7188caSLydia Wang 			break;
4675eb7188caSLydia Wang 
4676eb7188caSLydia Wang 		case 0x29: /* Front Mic */
4677eb7188caSLydia Wang 			idx = 3;
4678eb7188caSLydia Wang 			break;
4679eb7188caSLydia Wang 
4680eb7188caSLydia Wang 		case 0x2c: /* CD */
4681eb7188caSLydia Wang 			idx = 0;
4682eb7188caSLydia Wang 			break;
4683eb7188caSLydia Wang 		}
4684eb7188caSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
4685eb7188caSLydia Wang 		if (err < 0)
4686eb7188caSLydia Wang 			return err;
4687eb7188caSLydia Wang 		imux->items[imux->num_items].label = labels[i];
4688eb7188caSLydia Wang 		imux->items[imux->num_items].index = idx;
4689eb7188caSLydia Wang 		imux->num_items++;
4690eb7188caSLydia Wang 	}
4691eb7188caSLydia Wang 	return 0;
4692eb7188caSLydia Wang }
4693eb7188caSLydia Wang 
4694eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4695eb7188caSLydia Wang {
4696eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4697eb7188caSLydia Wang 	int err;
4698eb7188caSLydia Wang 
4699eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4700eb7188caSLydia Wang 
4701eb7188caSLydia Wang 	if (err < 0)
4702eb7188caSLydia Wang 		return err;
4703eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4704eb7188caSLydia Wang 	if (err < 0)
4705eb7188caSLydia Wang 		return err;
4706eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4707eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4708eb7188caSLydia Wang 
4709eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4710eb7188caSLydia Wang 	if (err < 0)
4711eb7188caSLydia Wang 		return err;
4712eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4713eb7188caSLydia Wang 	if (err < 0)
4714eb7188caSLydia Wang 		return err;
4715eb7188caSLydia Wang 	err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4716eb7188caSLydia Wang 	if (err < 0)
4717eb7188caSLydia Wang 		return err;
4718eb7188caSLydia Wang 
4719eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4720eb7188caSLydia Wang 
4721eb7188caSLydia Wang 	fill_dig_outs(codec);
4722eb7188caSLydia Wang 
4723eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4724eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4725eb7188caSLydia Wang 
4726eb7188caSLydia Wang 	if (spec->kctls.list)
4727eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4728eb7188caSLydia Wang 
4729eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4730eb7188caSLydia Wang 
4731eb7188caSLydia Wang 	if (spec->hp_mux)
4732eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
4733eb7188caSLydia Wang 
4734eb7188caSLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
4735eb7188caSLydia Wang 
4736eb7188caSLydia Wang 	return 1;
4737eb7188caSLydia Wang }
4738eb7188caSLydia Wang 
4739eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4740eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = {
4741eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4742eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4743eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4744eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4745eb7188caSLydia Wang 	{ } /* end */
4746eb7188caSLydia Wang };
4747eb7188caSLydia Wang #endif
4748eb7188caSLydia Wang 
4749eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4750eb7188caSLydia Wang {
4751eb7188caSLydia Wang 	struct via_spec *spec;
4752eb7188caSLydia Wang 	int err;
4753eb7188caSLydia Wang 
4754eb7188caSLydia Wang 	/* create a codec specific record */
4755eb7188caSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4756eb7188caSLydia Wang 	if (spec == NULL)
4757eb7188caSLydia Wang 		return -ENOMEM;
4758eb7188caSLydia Wang 
4759eb7188caSLydia Wang 	codec->spec = spec;
4760eb7188caSLydia Wang 
4761eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4762eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4763eb7188caSLydia Wang 	if (err < 0) {
4764eb7188caSLydia Wang 		via_free(codec);
4765eb7188caSLydia Wang 		return err;
4766eb7188caSLydia Wang 	} else if (!err) {
4767eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4768eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4769eb7188caSLydia Wang 	}
4770eb7188caSLydia Wang 
4771eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4772eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4773eb7188caSLydia Wang 
4774bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4775bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4776bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4777bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4778bb3c6bfcSLydia Wang 	else
4779eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4780eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4781eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4782eb7188caSLydia Wang 
4783bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4784bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4785bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4786bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4787bb3c6bfcSLydia Wang 	else
4788eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4789eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4790bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4791eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4792eb7188caSLydia Wang 
4793eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4794eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4795eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4796eb7188caSLydia Wang 		get_mux_nids(codec);
4797bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4798bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4799eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4800eb7188caSLydia Wang 		spec->num_mixers++;
4801eb7188caSLydia Wang 	}
4802eb7188caSLydia Wang 
4803eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4804eb7188caSLydia Wang 
4805eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
4806eb7188caSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
4807eb7188caSLydia Wang 
4808eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4809eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4810eb7188caSLydia Wang #endif
4811eb7188caSLydia Wang 
4812eb7188caSLydia Wang 	return 0;
4813eb7188caSLydia Wang }
4814f3db423dSLydia Wang 
4815f3db423dSLydia Wang /* Patch for VT1716S */
4816f3db423dSLydia Wang 
4817f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4818f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4819f3db423dSLydia Wang {
4820f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4821f3db423dSLydia Wang 	uinfo->count = 1;
4822f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4823f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4824f3db423dSLydia Wang 	return 0;
4825f3db423dSLydia Wang }
4826f3db423dSLydia Wang 
4827f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4828f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4829f3db423dSLydia Wang {
4830f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4831f3db423dSLydia Wang 	int index = 0;
4832f3db423dSLydia Wang 
4833f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4834f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4835f3db423dSLydia Wang 	if (index != -1)
4836f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4837f3db423dSLydia Wang 
4838f3db423dSLydia Wang 	return 0;
4839f3db423dSLydia Wang }
4840f3db423dSLydia Wang 
4841f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4842f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4843f3db423dSLydia Wang {
4844f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4845f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4846f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4847f3db423dSLydia Wang 
4848f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4849f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4850f3db423dSLydia Wang 	spec->dmic_enabled = index;
4851f3db423dSLydia Wang 	set_jack_power_state(codec);
4852f3db423dSLydia Wang 
4853f3db423dSLydia Wang 	return 1;
4854f3db423dSLydia Wang }
4855f3db423dSLydia Wang 
4856f3db423dSLydia Wang /* capture mixer elements */
4857f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4858f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4859f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4860f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4861f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4862f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4863f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4864f3db423dSLydia Wang 			 HDA_INPUT),
4865f3db423dSLydia Wang 	{
4866f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4867f3db423dSLydia Wang 		.name = "Input Source",
4868f3db423dSLydia Wang 		.count = 1,
4869f3db423dSLydia Wang 		.info = via_mux_enum_info,
4870f3db423dSLydia Wang 		.get = via_mux_enum_get,
4871f3db423dSLydia Wang 		.put = via_mux_enum_put,
4872f3db423dSLydia Wang 	},
4873f3db423dSLydia Wang 	{ } /* end */
4874f3db423dSLydia Wang };
4875f3db423dSLydia Wang 
4876f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4877f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4878f3db423dSLydia Wang 	{
4879f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4880f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
4881f3db423dSLydia Wang 	 .count = 1,
4882f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
4883f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
4884f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
4885f3db423dSLydia Wang 	 },
4886f3db423dSLydia Wang 	{}			/* end */
4887f3db423dSLydia Wang };
4888f3db423dSLydia Wang 
4889f3db423dSLydia Wang 
4890f3db423dSLydia Wang /* mono-out mixer elements */
4891f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4892f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4893f3db423dSLydia Wang 	{ } /* end */
4894f3db423dSLydia Wang };
4895f3db423dSLydia Wang 
4896f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
4897f3db423dSLydia Wang 	/*
4898f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4899f3db423dSLydia Wang 	 */
4900f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4901f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4902f3db423dSLydia Wang 
4903f3db423dSLydia Wang 
4904f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4905f3db423dSLydia Wang 	 * mixer widget
4906f3db423dSLydia Wang 	 */
4907f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4908f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4909f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4910f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4911f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4912f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4913f3db423dSLydia Wang 
4914f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4915f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4916f3db423dSLydia Wang 
4917f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4918f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4919f3db423dSLydia Wang 
4920f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4921f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4922f3db423dSLydia Wang 
4923f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4924f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4925f3db423dSLydia Wang 
4926f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4927f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4928f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4929f3db423dSLydia Wang 
4930f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4931f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4932f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4933f3db423dSLydia Wang 	/* PW12 Output enable */
4934f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4935f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4936f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4937f3db423dSLydia Wang 	/* don't bybass mixer */
4938f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4939f3db423dSLydia Wang 	/* Enable mono output */
4940f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4941f3db423dSLydia Wang 	{ }
4942f3db423dSLydia Wang };
4943f3db423dSLydia Wang 
4944f3db423dSLydia Wang 
4945f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
4946f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4947f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4948f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4949f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4950f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4951f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4952f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4953f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4954f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4955f3db423dSLydia Wang 	{ }
4956f3db423dSLydia Wang };
4957f3db423dSLydia Wang 
4958f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4959f3db423dSLydia Wang 	.substreams = 2,
4960f3db423dSLydia Wang 	.channels_min = 2,
4961f3db423dSLydia Wang 	.channels_max = 6,
4962f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4963f3db423dSLydia Wang 	.ops = {
4964f3db423dSLydia Wang 		.open = via_playback_pcm_open,
4965f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4966f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4967f3db423dSLydia Wang 		.close = via_pcm_open_close,
4968f3db423dSLydia Wang 	},
4969f3db423dSLydia Wang };
4970f3db423dSLydia Wang 
4971f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4972f3db423dSLydia Wang 	.substreams = 2,
4973f3db423dSLydia Wang 	.channels_min = 2,
4974f3db423dSLydia Wang 	.channels_max = 2,
4975f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
4976f3db423dSLydia Wang 	.ops = {
4977f3db423dSLydia Wang 		.open = via_pcm_open_close,
4978f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
4979f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4980f3db423dSLydia Wang 		.close = via_pcm_open_close,
4981f3db423dSLydia Wang 	},
4982f3db423dSLydia Wang };
4983f3db423dSLydia Wang 
4984f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4985f3db423dSLydia Wang 	.substreams = 2,
4986f3db423dSLydia Wang 	.channels_min = 2,
4987f3db423dSLydia Wang 	.channels_max = 2,
4988f3db423dSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
4989f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
4990f3db423dSLydia Wang 	.ops = {
4991f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
4992f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
4993f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4994f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4995f3db423dSLydia Wang 	},
4996f3db423dSLydia Wang };
4997f3db423dSLydia Wang 
4998f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4999f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
5000f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
5001f3db423dSLydia Wang {	int i;
5002f3db423dSLydia Wang 	hda_nid_t nid;
5003f3db423dSLydia Wang 
5004f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
5005f3db423dSLydia Wang 
5006f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5007f3db423dSLydia Wang 
5008f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
5009f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5010f3db423dSLydia Wang 		if (nid) {
5011f3db423dSLydia Wang 			/* config dac list */
5012f3db423dSLydia Wang 			switch (i) {
5013f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
5014f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
5015f3db423dSLydia Wang 				break;
5016f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
5017f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
5018f3db423dSLydia Wang 				break;
5019f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
5020f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
5021f3db423dSLydia Wang 				break;
5022f3db423dSLydia Wang 			}
5023f3db423dSLydia Wang 		}
5024f3db423dSLydia Wang 	}
5025f3db423dSLydia Wang 
5026f3db423dSLydia Wang 	return 0;
5027f3db423dSLydia Wang }
5028f3db423dSLydia Wang 
5029f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
5030f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
5031f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
5032f3db423dSLydia Wang {
5033f3db423dSLydia Wang 	char name[32];
5034f3db423dSLydia Wang 	static const char *chname[3] = { "Front", "Surround", "C/LFE" };
5035f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
5036f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
5037f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
5038f3db423dSLydia Wang 	int i, err;
5039f3db423dSLydia Wang 
5040f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
5041f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5042f3db423dSLydia Wang 
5043f3db423dSLydia Wang 		if (!nid)
5044f3db423dSLydia Wang 			continue;
5045f3db423dSLydia Wang 
5046f3db423dSLydia Wang 		nid_vol = nid_vols[i];
5047f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
5048f3db423dSLydia Wang 
5049f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
5050f3db423dSLydia Wang 			err = via_add_control(
5051f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5052f3db423dSLydia Wang 				"Center Playback Volume",
5053f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
5054f3db423dSLydia Wang 			if (err < 0)
5055f3db423dSLydia Wang 				return err;
5056f3db423dSLydia Wang 			err = via_add_control(
5057f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5058f3db423dSLydia Wang 				"LFE Playback Volume",
5059f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
5060f3db423dSLydia Wang 			if (err < 0)
5061f3db423dSLydia Wang 				return err;
5062f3db423dSLydia Wang 			err = via_add_control(
5063f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5064f3db423dSLydia Wang 				"Center Playback Switch",
5065f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
5066f3db423dSLydia Wang 						    HDA_OUTPUT));
5067f3db423dSLydia Wang 			if (err < 0)
5068f3db423dSLydia Wang 				return err;
5069f3db423dSLydia Wang 			err = via_add_control(
5070f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5071f3db423dSLydia Wang 				"LFE Playback Switch",
5072f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
5073f3db423dSLydia Wang 						    HDA_OUTPUT));
5074f3db423dSLydia Wang 			if (err < 0)
5075f3db423dSLydia Wang 				return err;
5076f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
5077f3db423dSLydia Wang 
5078f3db423dSLydia Wang 			err = via_add_control(
5079f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5080f3db423dSLydia Wang 				"Master Front Playback Volume",
5081f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5082f3db423dSLydia Wang 			if (err < 0)
5083f3db423dSLydia Wang 				return err;
5084f3db423dSLydia Wang 			err = via_add_control(
5085f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5086f3db423dSLydia Wang 				"Master Front Playback Switch",
5087f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5088f3db423dSLydia Wang 			if (err < 0)
5089f3db423dSLydia Wang 				return err;
5090f3db423dSLydia Wang 
5091f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5092f3db423dSLydia Wang 			err = via_add_control(
5093f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5094f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5095f3db423dSLydia Wang 			if (err < 0)
5096f3db423dSLydia Wang 				return err;
5097f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5098f3db423dSLydia Wang 			err = via_add_control(
5099f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5100f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5101f3db423dSLydia Wang 						    HDA_OUTPUT));
5102f3db423dSLydia Wang 			if (err < 0)
5103f3db423dSLydia Wang 				return err;
5104f3db423dSLydia Wang 		} else {
5105f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5106f3db423dSLydia Wang 			err = via_add_control(
5107f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5108f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5109f3db423dSLydia Wang 			if (err < 0)
5110f3db423dSLydia Wang 				return err;
5111f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5112f3db423dSLydia Wang 			err = via_add_control(
5113f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5114f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5115f3db423dSLydia Wang 						    HDA_OUTPUT));
5116f3db423dSLydia Wang 			if (err < 0)
5117f3db423dSLydia Wang 				return err;
5118f3db423dSLydia Wang 		}
5119f3db423dSLydia Wang 	}
5120f3db423dSLydia Wang 	return 0;
5121f3db423dSLydia Wang }
5122f3db423dSLydia Wang 
5123f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5124f3db423dSLydia Wang {
5125f3db423dSLydia Wang 	int err;
5126f3db423dSLydia Wang 
5127f3db423dSLydia Wang 	if (!pin)
5128f3db423dSLydia Wang 		return 0;
5129f3db423dSLydia Wang 
5130f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5131f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5132f3db423dSLydia Wang 
5133f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5134f3db423dSLydia Wang 			      "Headphone Playback Volume",
5135f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5136f3db423dSLydia Wang 	if (err < 0)
5137f3db423dSLydia Wang 		return err;
5138f3db423dSLydia Wang 
5139f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5140f3db423dSLydia Wang 			      "Headphone Playback Switch",
5141f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5142f3db423dSLydia Wang 	if (err < 0)
5143f3db423dSLydia Wang 		return err;
5144f3db423dSLydia Wang 
5145f3db423dSLydia Wang 	create_hp_imux(spec);
5146f3db423dSLydia Wang 	return 0;
5147f3db423dSLydia Wang }
5148f3db423dSLydia Wang 
5149f3db423dSLydia Wang /* create playback/capture controls for input pins */
5150f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
5151f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5152f3db423dSLydia Wang {
5153f3db423dSLydia Wang 	static char *labels[] = {
5154f3db423dSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5155f3db423dSLydia Wang 	};
5156f3db423dSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5157f3db423dSLydia Wang 	int i, err, idx = 0;
5158f3db423dSLydia Wang 
5159f3db423dSLydia Wang 	/* for internal loopback recording select */
5160f3db423dSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
5161f3db423dSLydia Wang 	imux->items[imux->num_items].index = 5;
5162f3db423dSLydia Wang 	imux->num_items++;
5163f3db423dSLydia Wang 
5164f3db423dSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
5165f3db423dSLydia Wang 		if (!cfg->input_pins[i])
5166f3db423dSLydia Wang 			continue;
5167f3db423dSLydia Wang 
5168f3db423dSLydia Wang 		switch (cfg->input_pins[i]) {
5169f3db423dSLydia Wang 		case 0x1a: /* Mic */
5170f3db423dSLydia Wang 			idx = 2;
5171f3db423dSLydia Wang 			break;
5172f3db423dSLydia Wang 
5173f3db423dSLydia Wang 		case 0x1b: /* Line In */
5174f3db423dSLydia Wang 			idx = 3;
5175f3db423dSLydia Wang 			break;
5176f3db423dSLydia Wang 
5177f3db423dSLydia Wang 		case 0x1e: /* Front Mic */
5178f3db423dSLydia Wang 			idx = 4;
5179f3db423dSLydia Wang 			break;
5180f3db423dSLydia Wang 
5181f3db423dSLydia Wang 		case 0x1f: /* CD */
5182f3db423dSLydia Wang 			idx = 1;
5183f3db423dSLydia Wang 			break;
5184f3db423dSLydia Wang 		}
5185f3db423dSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
5186f3db423dSLydia Wang 		if (err < 0)
5187f3db423dSLydia Wang 			return err;
5188f3db423dSLydia Wang 		imux->items[imux->num_items].label = labels[i];
5189f3db423dSLydia Wang 		imux->items[imux->num_items].index = idx-1;
5190f3db423dSLydia Wang 		imux->num_items++;
5191f3db423dSLydia Wang 	}
5192f3db423dSLydia Wang 	return 0;
5193f3db423dSLydia Wang }
5194f3db423dSLydia Wang 
5195f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5196f3db423dSLydia Wang {
5197f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5198f3db423dSLydia Wang 	int err;
5199f3db423dSLydia Wang 
5200f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5201f3db423dSLydia Wang 	if (err < 0)
5202f3db423dSLydia Wang 		return err;
5203f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5204f3db423dSLydia Wang 	if (err < 0)
5205f3db423dSLydia Wang 		return err;
5206f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5207f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5208f3db423dSLydia Wang 
5209f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5210f3db423dSLydia Wang 	if (err < 0)
5211f3db423dSLydia Wang 		return err;
5212f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5213f3db423dSLydia Wang 	if (err < 0)
5214f3db423dSLydia Wang 		return err;
5215f3db423dSLydia Wang 	err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
5216f3db423dSLydia Wang 	if (err < 0)
5217f3db423dSLydia Wang 		return err;
5218f3db423dSLydia Wang 
5219f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5220f3db423dSLydia Wang 
5221f3db423dSLydia Wang 	fill_dig_outs(codec);
5222f3db423dSLydia Wang 
5223f3db423dSLydia Wang 	if (spec->kctls.list)
5224f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5225f3db423dSLydia Wang 
5226f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5227f3db423dSLydia Wang 
5228f3db423dSLydia Wang 	if (spec->hp_mux)
5229f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
5230f3db423dSLydia Wang 
5231f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
5232f3db423dSLydia Wang 
5233f3db423dSLydia Wang 	return 1;
5234f3db423dSLydia Wang }
5235f3db423dSLydia Wang 
5236f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5237f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5238f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5239f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5240f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5241f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5242f3db423dSLydia Wang 	{ } /* end */
5243f3db423dSLydia Wang };
5244f3db423dSLydia Wang #endif
5245f3db423dSLydia Wang 
5246f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5247f3db423dSLydia Wang {
5248f3db423dSLydia Wang 	struct via_spec *spec;
5249f3db423dSLydia Wang 	int err;
5250f3db423dSLydia Wang 
5251f3db423dSLydia Wang 	/* create a codec specific record */
5252f3db423dSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
5253f3db423dSLydia Wang 	if (spec == NULL)
5254f3db423dSLydia Wang 		return -ENOMEM;
5255f3db423dSLydia Wang 
5256f3db423dSLydia Wang 	codec->spec = spec;
5257f3db423dSLydia Wang 
5258f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5259f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5260f3db423dSLydia Wang 	if (err < 0) {
5261f3db423dSLydia Wang 		via_free(codec);
5262f3db423dSLydia Wang 		return err;
5263f3db423dSLydia Wang 	} else if (!err) {
5264f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5265f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5266f3db423dSLydia Wang 	}
5267f3db423dSLydia Wang 
5268f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5269f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5270f3db423dSLydia Wang 
5271f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5272f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5273f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5274f3db423dSLydia Wang 
5275f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5276f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5277f3db423dSLydia Wang 
5278f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5279f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5280f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5281f3db423dSLydia Wang 		get_mux_nids(codec);
5282f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5283f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5284f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5285f3db423dSLydia Wang 		spec->num_mixers++;
5286f3db423dSLydia Wang 	}
5287f3db423dSLydia Wang 
5288f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5289f3db423dSLydia Wang 	spec->num_mixers++;
5290f3db423dSLydia Wang 
5291f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5292f3db423dSLydia Wang 
5293f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5294f3db423dSLydia Wang 
5295f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
5296f3db423dSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
5297f3db423dSLydia Wang 
5298f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5299f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5300f3db423dSLydia Wang #endif
5301f3db423dSLydia Wang 
5302f3db423dSLydia Wang 	return 0;
5303f3db423dSLydia Wang }
5304*25eaba2fSLydia Wang 
5305*25eaba2fSLydia Wang /* for vt2002P */
5306*25eaba2fSLydia Wang 
5307*25eaba2fSLydia Wang /* capture mixer elements */
5308*25eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
5309*25eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5310*25eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5311*25eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5312*25eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5313*25eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5314*25eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
5315*25eaba2fSLydia Wang 			 HDA_INPUT),
5316*25eaba2fSLydia Wang 	{
5317*25eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5318*25eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5319*25eaba2fSLydia Wang 		 * So call somewhat different..
5320*25eaba2fSLydia Wang 		 */
5321*25eaba2fSLydia Wang 		/* .name = "Capture Source", */
5322*25eaba2fSLydia Wang 		.name = "Input Source",
5323*25eaba2fSLydia Wang 		.count = 2,
5324*25eaba2fSLydia Wang 		.info = via_mux_enum_info,
5325*25eaba2fSLydia Wang 		.get = via_mux_enum_get,
5326*25eaba2fSLydia Wang 		.put = via_mux_enum_put,
5327*25eaba2fSLydia Wang 	},
5328*25eaba2fSLydia Wang 	{ } /* end */
5329*25eaba2fSLydia Wang };
5330*25eaba2fSLydia Wang 
5331*25eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
5332*25eaba2fSLydia Wang 	/*
5333*25eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5334*25eaba2fSLydia Wang 	 */
5335*25eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5336*25eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5337*25eaba2fSLydia Wang 
5338*25eaba2fSLydia Wang 
5339*25eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5340*25eaba2fSLydia Wang 	 * mixer widget
5341*25eaba2fSLydia Wang 	 */
5342*25eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5343*25eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5344*25eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5345*25eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5346*25eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5347*25eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5348*25eaba2fSLydia Wang 
5349*25eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
5350*25eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5351*25eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5352*25eaba2fSLydia Wang 
5353*25eaba2fSLydia Wang 	/* PW9 Output enable */
5354*25eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5355*25eaba2fSLydia Wang 
5356*25eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
5357*25eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
5358*25eaba2fSLydia Wang 
5359*25eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5360*25eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5361*25eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5362*25eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5363*25eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5364*25eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5365*25eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5366*25eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5367*25eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5368*25eaba2fSLydia Wang 
5369*25eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
5370*25eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5371*25eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5372*25eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
5373*25eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
5374*25eaba2fSLydia Wang 
5375*25eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
5376*25eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
5377*25eaba2fSLydia Wang 
5378*25eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
5379*25eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
5380*25eaba2fSLydia Wang 	{ }
5381*25eaba2fSLydia Wang };
5382*25eaba2fSLydia Wang 
5383*25eaba2fSLydia Wang 
5384*25eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
5385*25eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
5386*25eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5387*25eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
5388*25eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5389*25eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5390*25eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5391*25eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5392*25eaba2fSLydia Wang 	{ }
5393*25eaba2fSLydia Wang };
5394*25eaba2fSLydia Wang 
5395*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
5396*25eaba2fSLydia Wang 	.substreams = 2,
5397*25eaba2fSLydia Wang 	.channels_min = 2,
5398*25eaba2fSLydia Wang 	.channels_max = 2,
5399*25eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5400*25eaba2fSLydia Wang 	.ops = {
5401*25eaba2fSLydia Wang 		.open = via_playback_pcm_open,
5402*25eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5403*25eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5404*25eaba2fSLydia Wang 		.close = via_pcm_open_close,
5405*25eaba2fSLydia Wang 	},
5406*25eaba2fSLydia Wang };
5407*25eaba2fSLydia Wang 
5408*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
5409*25eaba2fSLydia Wang 	.substreams = 2,
5410*25eaba2fSLydia Wang 	.channels_min = 2,
5411*25eaba2fSLydia Wang 	.channels_max = 2,
5412*25eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5413*25eaba2fSLydia Wang 	.ops = {
5414*25eaba2fSLydia Wang 		.open = via_pcm_open_close,
5415*25eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
5416*25eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5417*25eaba2fSLydia Wang 		.close = via_pcm_open_close,
5418*25eaba2fSLydia Wang 	},
5419*25eaba2fSLydia Wang };
5420*25eaba2fSLydia Wang 
5421*25eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
5422*25eaba2fSLydia Wang 	.substreams = 1,
5423*25eaba2fSLydia Wang 	.channels_min = 2,
5424*25eaba2fSLydia Wang 	.channels_max = 2,
5425*25eaba2fSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
5426*25eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
5427*25eaba2fSLydia Wang 	.ops = {
5428*25eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
5429*25eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
5430*25eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5431*25eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5432*25eaba2fSLydia Wang 	},
5433*25eaba2fSLydia Wang };
5434*25eaba2fSLydia Wang 
5435*25eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5436*25eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
5437*25eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
5438*25eaba2fSLydia Wang {
5439*25eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
5440*25eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5441*25eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
5442*25eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5443*25eaba2fSLydia Wang 	return 0;
5444*25eaba2fSLydia Wang }
5445*25eaba2fSLydia Wang 
5446*25eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
5447*25eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
5448*25eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
5449*25eaba2fSLydia Wang {
5450*25eaba2fSLydia Wang 	int err;
5451*25eaba2fSLydia Wang 
5452*25eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
5453*25eaba2fSLydia Wang 		return -1;
5454*25eaba2fSLydia Wang 
5455*25eaba2fSLydia Wang 
5456*25eaba2fSLydia Wang 	/* Line-Out: PortE */
5457*25eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5458*25eaba2fSLydia Wang 			      "Master Front Playback Volume",
5459*25eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5460*25eaba2fSLydia Wang 	if (err < 0)
5461*25eaba2fSLydia Wang 		return err;
5462*25eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
5463*25eaba2fSLydia Wang 			      "Master Front Playback Switch",
5464*25eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
5465*25eaba2fSLydia Wang 	if (err < 0)
5466*25eaba2fSLydia Wang 		return err;
5467*25eaba2fSLydia Wang 
5468*25eaba2fSLydia Wang 	return 0;
5469*25eaba2fSLydia Wang }
5470*25eaba2fSLydia Wang 
5471*25eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5472*25eaba2fSLydia Wang {
5473*25eaba2fSLydia Wang 	int err;
5474*25eaba2fSLydia Wang 
5475*25eaba2fSLydia Wang 	if (!pin)
5476*25eaba2fSLydia Wang 		return 0;
5477*25eaba2fSLydia Wang 
5478*25eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
5479*25eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
5480*25eaba2fSLydia Wang 
5481*25eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5482*25eaba2fSLydia Wang 			      "Headphone Playback Volume",
5483*25eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5484*25eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5485*25eaba2fSLydia Wang 	if (err < 0)
5486*25eaba2fSLydia Wang 		return err;
5487*25eaba2fSLydia Wang 
5488*25eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5489*25eaba2fSLydia Wang 			      "Headphone Playback Switch",
5490*25eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5491*25eaba2fSLydia Wang 	if (err < 0)
5492*25eaba2fSLydia Wang 		return err;
5493*25eaba2fSLydia Wang 
5494*25eaba2fSLydia Wang 	create_hp_imux(spec);
5495*25eaba2fSLydia Wang 	return 0;
5496*25eaba2fSLydia Wang }
5497*25eaba2fSLydia Wang 
5498*25eaba2fSLydia Wang /* create playback/capture controls for input pins */
5499*25eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
5500*25eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
5501*25eaba2fSLydia Wang {
5502*25eaba2fSLydia Wang 	static char *labels[] = {
5503*25eaba2fSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5504*25eaba2fSLydia Wang 	};
5505*25eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5506*25eaba2fSLydia Wang 	int i, err, idx = 0;
5507*25eaba2fSLydia Wang 
5508*25eaba2fSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
5509*25eaba2fSLydia Wang 		if (!cfg->input_pins[i])
5510*25eaba2fSLydia Wang 			continue;
5511*25eaba2fSLydia Wang 
5512*25eaba2fSLydia Wang 		switch (cfg->input_pins[i]) {
5513*25eaba2fSLydia Wang 		case 0x2b: /* Mic */
5514*25eaba2fSLydia Wang 			idx = 0;
5515*25eaba2fSLydia Wang 			break;
5516*25eaba2fSLydia Wang 
5517*25eaba2fSLydia Wang 		case 0x2a: /* Line In */
5518*25eaba2fSLydia Wang 			idx = 1;
5519*25eaba2fSLydia Wang 			break;
5520*25eaba2fSLydia Wang 
5521*25eaba2fSLydia Wang 		case 0x29: /* Front Mic */
5522*25eaba2fSLydia Wang 			idx = 2;
5523*25eaba2fSLydia Wang 			break;
5524*25eaba2fSLydia Wang 		}
5525*25eaba2fSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
5526*25eaba2fSLydia Wang 		if (err < 0)
5527*25eaba2fSLydia Wang 			return err;
5528*25eaba2fSLydia Wang 		imux->items[imux->num_items].label = labels[i];
5529*25eaba2fSLydia Wang 		imux->items[imux->num_items].index = idx;
5530*25eaba2fSLydia Wang 		imux->num_items++;
5531*25eaba2fSLydia Wang 	}
5532*25eaba2fSLydia Wang 
5533*25eaba2fSLydia Wang 	/* build volume/mute control of loopback */
5534*25eaba2fSLydia Wang 	err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
5535*25eaba2fSLydia Wang 	if (err < 0)
5536*25eaba2fSLydia Wang 		return err;
5537*25eaba2fSLydia Wang 
5538*25eaba2fSLydia Wang 	/* for internal loopback recording select */
5539*25eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
5540*25eaba2fSLydia Wang 	imux->items[imux->num_items].index = 3;
5541*25eaba2fSLydia Wang 	imux->num_items++;
5542*25eaba2fSLydia Wang 
5543*25eaba2fSLydia Wang 	/* for digital mic select */
5544*25eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Digital Mic";
5545*25eaba2fSLydia Wang 	imux->items[imux->num_items].index = 4;
5546*25eaba2fSLydia Wang 	imux->num_items++;
5547*25eaba2fSLydia Wang 
5548*25eaba2fSLydia Wang 	return 0;
5549*25eaba2fSLydia Wang }
5550*25eaba2fSLydia Wang 
5551*25eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
5552*25eaba2fSLydia Wang {
5553*25eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
5554*25eaba2fSLydia Wang 	int err;
5555*25eaba2fSLydia Wang 
5556*25eaba2fSLydia Wang 
5557*25eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5558*25eaba2fSLydia Wang 	if (err < 0)
5559*25eaba2fSLydia Wang 		return err;
5560*25eaba2fSLydia Wang 
5561*25eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
5562*25eaba2fSLydia Wang 	if (err < 0)
5563*25eaba2fSLydia Wang 		return err;
5564*25eaba2fSLydia Wang 
5565*25eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5566*25eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5567*25eaba2fSLydia Wang 
5568*25eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
5569*25eaba2fSLydia Wang 	if (err < 0)
5570*25eaba2fSLydia Wang 		return err;
5571*25eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5572*25eaba2fSLydia Wang 	if (err < 0)
5573*25eaba2fSLydia Wang 		return err;
5574*25eaba2fSLydia Wang 	err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
5575*25eaba2fSLydia Wang 	if (err < 0)
5576*25eaba2fSLydia Wang 		return err;
5577*25eaba2fSLydia Wang 
5578*25eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5579*25eaba2fSLydia Wang 
5580*25eaba2fSLydia Wang 	fill_dig_outs(codec);
5581*25eaba2fSLydia Wang 
5582*25eaba2fSLydia Wang 	if (spec->kctls.list)
5583*25eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5584*25eaba2fSLydia Wang 
5585*25eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5586*25eaba2fSLydia Wang 
5587*25eaba2fSLydia Wang 	if (spec->hp_mux)
5588*25eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
5589*25eaba2fSLydia Wang 
5590*25eaba2fSLydia Wang 	return 1;
5591*25eaba2fSLydia Wang }
5592*25eaba2fSLydia Wang 
5593*25eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5594*25eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
5595*25eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
5596*25eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
5597*25eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
5598*25eaba2fSLydia Wang 	{ } /* end */
5599*25eaba2fSLydia Wang };
5600*25eaba2fSLydia Wang #endif
5601*25eaba2fSLydia Wang 
5602*25eaba2fSLydia Wang 
5603*25eaba2fSLydia Wang /* patch for vt2002P */
5604*25eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
5605*25eaba2fSLydia Wang {
5606*25eaba2fSLydia Wang 	struct via_spec *spec;
5607*25eaba2fSLydia Wang 	int err;
5608*25eaba2fSLydia Wang 
5609*25eaba2fSLydia Wang 	/* create a codec specific record */
5610*25eaba2fSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
5611*25eaba2fSLydia Wang 	if (spec == NULL)
5612*25eaba2fSLydia Wang 		return -ENOMEM;
5613*25eaba2fSLydia Wang 
5614*25eaba2fSLydia Wang 	codec->spec = spec;
5615*25eaba2fSLydia Wang 
5616*25eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
5617*25eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
5618*25eaba2fSLydia Wang 	if (err < 0) {
5619*25eaba2fSLydia Wang 		via_free(codec);
5620*25eaba2fSLydia Wang 		return err;
5621*25eaba2fSLydia Wang 	} else if (!err) {
5622*25eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5623*25eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5624*25eaba2fSLydia Wang 	}
5625*25eaba2fSLydia Wang 
5626*25eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
5627*25eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
5628*25eaba2fSLydia Wang 
5629*25eaba2fSLydia Wang 	spec->stream_name_analog = "VT2002P Analog";
5630*25eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
5631*25eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
5632*25eaba2fSLydia Wang 
5633*25eaba2fSLydia Wang 	spec->stream_name_digital = "VT2002P Digital";
5634*25eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
5635*25eaba2fSLydia Wang 
5636*25eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5637*25eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
5638*25eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
5639*25eaba2fSLydia Wang 		get_mux_nids(codec);
5640*25eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
5641*25eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
5642*25eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
5643*25eaba2fSLydia Wang 		spec->num_mixers++;
5644*25eaba2fSLydia Wang 	}
5645*25eaba2fSLydia Wang 
5646*25eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
5647*25eaba2fSLydia Wang 
5648*25eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
5649*25eaba2fSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
5650*25eaba2fSLydia Wang 
5651*25eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5652*25eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
5653*25eaba2fSLydia Wang #endif
5654*25eaba2fSLydia Wang 
5655*25eaba2fSLydia Wang 	return 0;
5656*25eaba2fSLydia Wang }
5657c577b8a1SJoseph Chan /*
5658c577b8a1SJoseph Chan  * patch entries
5659c577b8a1SJoseph Chan  */
56601289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
56613218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
56623218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
56633218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
56643218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
56653218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
5666f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
56673218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
5668f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
56693218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
5670f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
56713218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
5672f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
56733218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
5674f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
56753218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
5676f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
56773218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
5678f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
56793218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
5680f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
56813218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
5682f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
56833218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
5684f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
56853218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
5686f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
56873218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
5688f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
56893218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
5690f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
56913218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
5692f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
56933218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
5694f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
56953218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
5696f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
56973218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
5698d949cac1SHarald Welte 	  .patch = patch_vt1708S},
56993218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
5700d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57013218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
5702d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57033218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
5704d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57053218c178STakashi Iwai 	{ .id = 0x11064397, .name = "VT1708S",
5706d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57073218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
5708d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57093218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
5710d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57113218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
5712d949cac1SHarald Welte 	  .patch = patch_vt1708S},
57133218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
5714d949cac1SHarald Welte 	  .patch = patch_vt1702},
57153218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
5716d949cac1SHarald Welte 	  .patch = patch_vt1702},
57173218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
5718d949cac1SHarald Welte 	  .patch = patch_vt1702},
57193218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
5720d949cac1SHarald Welte 	  .patch = patch_vt1702},
57213218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
5722d949cac1SHarald Welte 	  .patch = patch_vt1702},
57233218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
5724d949cac1SHarald Welte 	  .patch = patch_vt1702},
57253218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
5726d949cac1SHarald Welte 	  .patch = patch_vt1702},
57273218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
5728d949cac1SHarald Welte 	  .patch = patch_vt1702},
5729eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
5730eb7188caSLydia Wang 	  .patch = patch_vt1718S},
5731eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
5732eb7188caSLydia Wang 	  .patch = patch_vt1718S},
5733bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
5734bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
5735bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
5736bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
5737f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
5738f3db423dSLydia Wang 	  .patch = patch_vt1716S},
5739f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
5740f3db423dSLydia Wang 	  .patch = patch_vt1716S},
5741*25eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
5742*25eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
5743c577b8a1SJoseph Chan 	{} /* terminator */
5744c577b8a1SJoseph Chan };
57451289e9e8STakashi Iwai 
57461289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
57471289e9e8STakashi Iwai 
57481289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
57491289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
57501289e9e8STakashi Iwai 	.owner = THIS_MODULE,
57511289e9e8STakashi Iwai };
57521289e9e8STakashi Iwai 
57531289e9e8STakashi Iwai MODULE_LICENSE("GPL");
57541289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
57551289e9e8STakashi Iwai 
57561289e9e8STakashi Iwai static int __init patch_via_init(void)
57571289e9e8STakashi Iwai {
57581289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
57591289e9e8STakashi Iwai }
57601289e9e8STakashi Iwai 
57611289e9e8STakashi Iwai static void __exit patch_via_exit(void)
57621289e9e8STakashi Iwai {
57631289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
57641289e9e8STakashi Iwai }
57651289e9e8STakashi Iwai 
57661289e9e8STakashi Iwai module_init(patch_via_init)
57671289e9e8STakashi Iwai module_exit(patch_via_exit)
5768