xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 377ff31ae06f0d2644839246cd18c3e17fe62a48)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 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	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
44c577b8a1SJoseph Chan /*									     */
45c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
46c577b8a1SJoseph Chan 
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan #include <linux/init.h>
49c577b8a1SJoseph Chan #include <linux/delay.h>
50c577b8a1SJoseph Chan #include <linux/slab.h>
51c577b8a1SJoseph Chan #include <sound/core.h>
520aa62aefSHarald Welte #include <sound/asoundef.h>
53c577b8a1SJoseph Chan #include "hda_codec.h"
54c577b8a1SJoseph Chan #include "hda_local.h"
55c577b8a1SJoseph Chan 
56c577b8a1SJoseph Chan /* amp values */
57c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
58c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
59c577b8a1SJoseph Chan 
60c577b8a1SJoseph Chan /* Pin Widget NID */
61c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
62c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
63c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
64f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6576d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6676d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
67c577b8a1SJoseph Chan 
68c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
69c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
70c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
71f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
72f7278fd0SJosepch Chan 
73f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
74f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
75f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
76f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
77c577b8a1SJoseph Chan 
78d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
79d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
80d949cac1SHarald Welte 
81d949cac1SHarald Welte #define VT1702_HP_NID		0x17
82d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
83d949cac1SHarald Welte 
84d7426329SHarald Welte enum VIA_HDA_CODEC {
85d7426329SHarald Welte 	UNKNOWN = -1,
86d7426329SHarald Welte 	VT1708,
87d7426329SHarald Welte 	VT1709_10CH,
88d7426329SHarald Welte 	VT1709_6CH,
89d7426329SHarald Welte 	VT1708B_8CH,
90d7426329SHarald Welte 	VT1708B_4CH,
91d7426329SHarald Welte 	VT1708S,
92518bf3baSLydia Wang 	VT1708BCE,
93d7426329SHarald Welte 	VT1702,
94eb7188caSLydia Wang 	VT1718S,
95f3db423dSLydia Wang 	VT1716S,
9625eaba2fSLydia Wang 	VT2002P,
97ab6734e7SLydia Wang 	VT1812,
98d7426329SHarald Welte 	CODEC_TYPES,
99d7426329SHarald Welte };
100d7426329SHarald Welte 
1011f2e99feSLydia Wang struct via_spec {
1021f2e99feSLydia Wang 	/* codec parameterization */
103f3db423dSLydia Wang 	struct snd_kcontrol_new *mixers[6];
1041f2e99feSLydia Wang 	unsigned int num_mixers;
1051f2e99feSLydia Wang 
1061f2e99feSLydia Wang 	struct hda_verb *init_verbs[5];
1071f2e99feSLydia Wang 	unsigned int num_iverbs;
1081f2e99feSLydia Wang 
1091f2e99feSLydia Wang 	char *stream_name_analog;
1101f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_playback;
1111f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_capture;
1121f2e99feSLydia Wang 
1131f2e99feSLydia Wang 	char *stream_name_digital;
1141f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_playback;
1151f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_capture;
1161f2e99feSLydia Wang 
1171f2e99feSLydia Wang 	/* playback */
1181f2e99feSLydia Wang 	struct hda_multi_out multiout;
1191f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1201f2e99feSLydia Wang 
1211f2e99feSLydia Wang 	/* capture */
1221f2e99feSLydia Wang 	unsigned int num_adc_nids;
1231f2e99feSLydia Wang 	hda_nid_t *adc_nids;
1241f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
1251f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1261f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1271f2e99feSLydia Wang 
1281f2e99feSLydia Wang 	/* capture source */
1291f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1301f2e99feSLydia Wang 	unsigned int cur_mux[3];
1311f2e99feSLydia Wang 
1321f2e99feSLydia Wang 	/* PCM information */
1331f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1341f2e99feSLydia Wang 
1351f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1361f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1371f2e99feSLydia Wang 	struct snd_array kctls;
1381f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1391f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1401f2e99feSLydia Wang 
1411f2e99feSLydia Wang 	/* HP mode source */
1421f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1431f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1441f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
1451f2e99feSLydia Wang 	unsigned int smart51_enabled;
146f3db423dSLydia Wang 	unsigned int dmic_enabled;
1471f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1481f2e99feSLydia Wang 
1491f2e99feSLydia Wang 	/* work to check hp jack state */
1501f2e99feSLydia Wang 	struct hda_codec *codec;
1511f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
1521f2e99feSLydia Wang 	int vt1708_jack_detectect;
1531f2e99feSLydia Wang 	int vt1708_hp_present;
1541f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1551f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1561f2e99feSLydia Wang #endif
1571f2e99feSLydia Wang };
1581f2e99feSLydia Wang 
159744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
160d7426329SHarald Welte {
161744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
162d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
163d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
164d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
165d7426329SHarald Welte 
166d7426329SHarald Welte 	/* get codec type */
167d7426329SHarald Welte 	if (ven_id != 0x1106)
168d7426329SHarald Welte 		codec_type = UNKNOWN;
169d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
170d7426329SHarald Welte 		codec_type = VT1708;
171d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
172d7426329SHarald Welte 		codec_type = VT1709_10CH;
173d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
174d7426329SHarald Welte 		codec_type = VT1709_6CH;
175518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
176d7426329SHarald Welte 		codec_type = VT1708B_8CH;
177518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
178518bf3baSLydia Wang 			codec_type = VT1708BCE;
179518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
180d7426329SHarald Welte 		codec_type = VT1708B_4CH;
181d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
182d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
183d7426329SHarald Welte 		codec_type = VT1708S;
184d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
185d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
186d7426329SHarald Welte 		codec_type = VT1702;
187eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
188eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
189eb7188caSLydia Wang 		codec_type = VT1718S;
190f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
191f3db423dSLydia Wang 		codec_type = VT1716S;
192bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
193bb3c6bfcSLydia Wang 		codec_type = VT1718S;
19425eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
19525eaba2fSLydia Wang 		codec_type = VT2002P;
196ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
197ab6734e7SLydia Wang 		codec_type = VT1812;
198d7426329SHarald Welte 	else
199d7426329SHarald Welte 		codec_type = UNKNOWN;
200d7426329SHarald Welte 	return codec_type;
201d7426329SHarald Welte };
202d7426329SHarald Welte 
20369e52a80SHarald Welte #define VIA_HP_EVENT		0x01
20469e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
205a34df19aSLydia Wang #define VIA_JACK_EVENT		0x04
206f3db423dSLydia Wang #define VIA_MONO_EVENT		0x08
20725eaba2fSLydia Wang #define VIA_SPEAKER_EVENT	0x10
20825eaba2fSLydia Wang #define VIA_BIND_HP_EVENT	0x20
20969e52a80SHarald Welte 
210c577b8a1SJoseph Chan enum {
211c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
212c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
213f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
21425eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
215c577b8a1SJoseph Chan };
216c577b8a1SJoseph Chan 
217c577b8a1SJoseph Chan enum {
218eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
219c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
220c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
221c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
222c577b8a1SJoseph Chan };
223c577b8a1SJoseph Chan 
224f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
225f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec);
2261f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2271f2e99feSLydia Wang 
2281f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2291f2e99feSLydia Wang {
2301f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2311f2e99feSLydia Wang 		return;
2321f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2331f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2341f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2351f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2361f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2371f2e99feSLydia Wang }
2381f2e99feSLydia Wang 
2391f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2401f2e99feSLydia Wang {
2411f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2421f2e99feSLydia Wang 		return;
2431f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2441f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2451f2e99feSLydia Wang 		return;
2461f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2471f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2481f2e99feSLydia Wang 	cancel_delayed_work(&spec->vt1708_hp_work);
2491f2e99feSLydia Wang 	flush_scheduled_work();
2501f2e99feSLydia Wang }
251f5271101SLydia Wang 
25225eaba2fSLydia Wang 
253f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
254f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
255f5271101SLydia Wang {
256f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
257f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
258f5271101SLydia Wang 
259f5271101SLydia Wang 	set_jack_power_state(codec);
260f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2611f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2621f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2631f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2641f2e99feSLydia Wang 		else
2651f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2661f2e99feSLydia Wang 	}
267f5271101SLydia Wang 	return change;
268f5271101SLydia Wang }
269f5271101SLydia Wang 
270f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
271f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
272f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
273f5271101SLydia Wang 			.name = NULL,					\
274f5271101SLydia Wang 			.index = 0,					\
275f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
276f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
277f5271101SLydia Wang 			.put = analog_input_switch_put,			\
278f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
279f5271101SLydia Wang 
28025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
28125eaba2fSLydia Wang 
28225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
28325eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
28425eaba2fSLydia Wang {
28525eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
28625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
28725eaba2fSLydia Wang 	int i;
28825eaba2fSLydia Wang 	int change = 0;
28925eaba2fSLydia Wang 
29025eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
29125eaba2fSLydia Wang 	int lmute, rmute;
29225eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
29325eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
29425eaba2fSLydia Wang 		return change;
29525eaba2fSLydia Wang 	}
29625eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
29725eaba2fSLydia Wang 					      ucontrol);
29825eaba2fSLydia Wang 	/* Get mute value */
29925eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
30025eaba2fSLydia Wang 	valp++;
30125eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
30225eaba2fSLydia Wang 
30325eaba2fSLydia Wang 	/* Set hp pins */
30425eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
30525eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
30625eaba2fSLydia Wang 			snd_hda_codec_amp_update(
30725eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
30825eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
30925eaba2fSLydia Wang 				lmute);
31025eaba2fSLydia Wang 			snd_hda_codec_amp_update(
31125eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
31225eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
31325eaba2fSLydia Wang 				rmute);
31425eaba2fSLydia Wang 		}
31525eaba2fSLydia Wang 	}
31625eaba2fSLydia Wang 
31725eaba2fSLydia Wang 	if (!lmute && !rmute) {
31825eaba2fSLydia Wang 		/* Line Outs */
31925eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
32025eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
32125eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
32225eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
32325eaba2fSLydia Wang 		/* Speakers */
32425eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
32525eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
32625eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
32725eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
32825eaba2fSLydia Wang 		/* unmute */
32925eaba2fSLydia Wang 		via_hp_bind_automute(codec);
33025eaba2fSLydia Wang 
33125eaba2fSLydia Wang 	} else {
33225eaba2fSLydia Wang 		if (lmute) {
33325eaba2fSLydia Wang 			/* Mute all left channels */
33425eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
33525eaba2fSLydia Wang 				snd_hda_codec_amp_update(
33625eaba2fSLydia Wang 					codec,
33725eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
33825eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
33925eaba2fSLydia Wang 					lmute);
34025eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
34125eaba2fSLydia Wang 				snd_hda_codec_amp_update(
34225eaba2fSLydia Wang 					codec,
34325eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
34425eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
34525eaba2fSLydia Wang 					lmute);
34625eaba2fSLydia Wang 		}
34725eaba2fSLydia Wang 		if (rmute) {
34825eaba2fSLydia Wang 			/* mute all right channels */
34925eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
35025eaba2fSLydia Wang 				snd_hda_codec_amp_update(
35125eaba2fSLydia Wang 					codec,
35225eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
35325eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35425eaba2fSLydia Wang 					rmute);
35525eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
35625eaba2fSLydia Wang 				snd_hda_codec_amp_update(
35725eaba2fSLydia Wang 					codec,
35825eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
35925eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36025eaba2fSLydia Wang 					rmute);
36125eaba2fSLydia Wang 		}
36225eaba2fSLydia Wang 	}
36325eaba2fSLydia Wang 	return change;
36425eaba2fSLydia Wang }
36525eaba2fSLydia Wang 
36625eaba2fSLydia Wang #define BIND_PIN_MUTE							\
36725eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
36825eaba2fSLydia Wang 			.name = NULL,					\
36925eaba2fSLydia Wang 			.index = 0,					\
37025eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
37125eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
37225eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
37325eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
37425eaba2fSLydia Wang 
37571eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = {
376c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
377c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
378f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
37925eaba2fSLydia Wang 	BIND_PIN_MUTE,
380c577b8a1SJoseph Chan };
381c577b8a1SJoseph Chan 
382c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
383c577b8a1SJoseph Chan 	/* ADC1-2 */
384c577b8a1SJoseph Chan 	0x15, 0x27
385c577b8a1SJoseph Chan };
386c577b8a1SJoseph Chan 
387c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
388c577b8a1SJoseph Chan 	/* ADC1-2 */
389c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
390c577b8a1SJoseph Chan };
391c577b8a1SJoseph Chan 
392f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
393f7278fd0SJosepch Chan 	/* ADC1-2 */
394f7278fd0SJosepch Chan 	0x13, 0x14
395f7278fd0SJosepch Chan };
396f7278fd0SJosepch Chan 
397d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
398d949cac1SHarald Welte 	/* ADC1-2 */
399d949cac1SHarald Welte 	0x13, 0x14
400d949cac1SHarald Welte };
401d949cac1SHarald Welte 
402d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
403d949cac1SHarald Welte 	/* ADC1-2 */
404d949cac1SHarald Welte 	0x12, 0x20, 0x1F
405d949cac1SHarald Welte };
406d949cac1SHarald Welte 
407eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
408eb7188caSLydia Wang 	/* ADC1-2 */
409eb7188caSLydia Wang 	0x10, 0x11
410eb7188caSLydia Wang };
411eb7188caSLydia Wang 
412f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
413f3db423dSLydia Wang 	/* ADC1-2 */
414f3db423dSLydia Wang 	0x13, 0x14
415f3db423dSLydia Wang };
416f3db423dSLydia Wang 
41725eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
41825eaba2fSLydia Wang 	/* ADC1-2 */
41925eaba2fSLydia Wang 	0x10, 0x11
42025eaba2fSLydia Wang };
42125eaba2fSLydia Wang 
422ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = {
423ab6734e7SLydia Wang 	/* ADC1-2 */
424ab6734e7SLydia Wang 	0x10, 0x11
425ab6734e7SLydia Wang };
426ab6734e7SLydia Wang 
427ab6734e7SLydia Wang 
428c577b8a1SJoseph Chan /* add dynamic controls */
429c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name,
430c577b8a1SJoseph Chan 			   unsigned long val)
431c577b8a1SJoseph Chan {
432c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
433c577b8a1SJoseph Chan 
434603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
435603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
436c577b8a1SJoseph Chan 	if (!knew)
437c577b8a1SJoseph Chan 		return -ENOMEM;
43871eb7dccSLydia Wang 	*knew = via_control_templates[type];
439c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
440c577b8a1SJoseph Chan 	if (!knew->name)
441c577b8a1SJoseph Chan 		return -ENOMEM;
442c577b8a1SJoseph Chan 	knew->private_value = val;
443c577b8a1SJoseph Chan 	return 0;
444c577b8a1SJoseph Chan }
445c577b8a1SJoseph Chan 
446603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
447603c4019STakashi Iwai {
448603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
449603c4019STakashi Iwai 
450603c4019STakashi Iwai 	if (spec->kctls.list) {
451603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
452603c4019STakashi Iwai 		int i;
453603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
454603c4019STakashi Iwai 			kfree(kctl[i].name);
455603c4019STakashi Iwai 	}
456603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
457603c4019STakashi Iwai }
458603c4019STakashi Iwai 
459c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4609510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4619510e8ddSLydia Wang 				int idx, int mix_nid)
462c577b8a1SJoseph Chan {
463c577b8a1SJoseph Chan 	char name[32];
464c577b8a1SJoseph Chan 	int err;
465c577b8a1SJoseph Chan 
466c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
467c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
468c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
469c577b8a1SJoseph Chan 	if (err < 0)
470c577b8a1SJoseph Chan 		return err;
471c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
472f5271101SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
473c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
474c577b8a1SJoseph Chan 	if (err < 0)
475c577b8a1SJoseph Chan 		return err;
476c577b8a1SJoseph Chan 	return 0;
477c577b8a1SJoseph Chan }
478c577b8a1SJoseph Chan 
479c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
480c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
481c577b8a1SJoseph Chan 					   int dac_idx)
482c577b8a1SJoseph Chan {
483c577b8a1SJoseph Chan 	/* set as output */
484c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
485c577b8a1SJoseph Chan 			    pin_type);
486c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
487c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
488d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
489d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
490d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
491c577b8a1SJoseph Chan }
492c577b8a1SJoseph Chan 
493c577b8a1SJoseph Chan 
494c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
495c577b8a1SJoseph Chan {
496c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
497c577b8a1SJoseph Chan 	int i;
498c577b8a1SJoseph Chan 
499c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
500c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
501c577b8a1SJoseph Chan 		if (nid)
502c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
503c577b8a1SJoseph Chan 	}
504c577b8a1SJoseph Chan }
505c577b8a1SJoseph Chan 
506c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
507c577b8a1SJoseph Chan {
508c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
509c577b8a1SJoseph Chan 	hda_nid_t pin;
51025eaba2fSLydia Wang 	int i;
511c577b8a1SJoseph Chan 
51225eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
51325eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
514c577b8a1SJoseph Chan 		if (pin) /* connect to front */
515c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
516c577b8a1SJoseph Chan 	}
51725eaba2fSLydia Wang }
518c577b8a1SJoseph Chan 
519c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
520c577b8a1SJoseph Chan {
521c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
522c577b8a1SJoseph Chan 	int i;
523c577b8a1SJoseph Chan 
524c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
525c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.input_pins[i];
526c577b8a1SJoseph Chan 
527c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
528c577b8a1SJoseph Chan 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
529c577b8a1SJoseph Chan 				    (i <= AUTO_PIN_FRONT_MIC ?
530c577b8a1SJoseph Chan 				     PIN_VREF50 : PIN_IN));
531c577b8a1SJoseph Chan 
532c577b8a1SJoseph Chan 	}
533c577b8a1SJoseph Chan }
534f5271101SLydia Wang 
5351564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
5361564b287SLydia Wang 
537f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
538f5271101SLydia Wang 				unsigned int *affected_parm)
539f5271101SLydia Wang {
540f5271101SLydia Wang 	unsigned parm;
541f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
542f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
543f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
544f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
545f5271101SLydia Wang 	unsigned present = snd_hda_codec_read(codec, nid, 0,
546f5271101SLydia Wang 					      AC_VERB_GET_PIN_SENSE, 0) >> 31;
5471564b287SLydia Wang 	struct via_spec *spec = codec->spec;
5481564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5491564b287SLydia Wang 	    || ((no_presence || present)
5501564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
551f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
552f5271101SLydia Wang 		parm = AC_PWRST_D0;
553f5271101SLydia Wang 	} else
554f5271101SLydia Wang 		parm = AC_PWRST_D3;
555f5271101SLydia Wang 
556f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
557f5271101SLydia Wang }
558f5271101SLydia Wang 
559f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec)
560f5271101SLydia Wang {
561f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
562f5271101SLydia Wang 	int imux_is_smixer;
563f5271101SLydia Wang 	unsigned int parm;
564f5271101SLydia Wang 
565f5271101SLydia Wang 	if (spec->codec_type == VT1702) {
566f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
567f5271101SLydia Wang 			codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
568f5271101SLydia Wang 		/* inputs */
569f5271101SLydia Wang 		/* PW 1/2/5 (14h/15h/18h) */
570f5271101SLydia Wang 		parm = AC_PWRST_D3;
571f5271101SLydia Wang 		set_pin_power_state(codec, 0x14, &parm);
572f5271101SLydia Wang 		set_pin_power_state(codec, 0x15, &parm);
573f5271101SLydia Wang 		set_pin_power_state(codec, 0x18, &parm);
574f5271101SLydia Wang 		if (imux_is_smixer)
575f5271101SLydia Wang 			parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
576f5271101SLydia Wang 		/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
577f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
578f5271101SLydia Wang 				    parm);
579f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
580f5271101SLydia Wang 				    parm);
581f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
582f5271101SLydia Wang 				    parm);
583f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
584f5271101SLydia Wang 				    parm);
585f5271101SLydia Wang 
586f5271101SLydia Wang 		/* outputs */
587f5271101SLydia Wang 		/* PW 3/4 (16h/17h) */
588f5271101SLydia Wang 		parm = AC_PWRST_D3;
589f5271101SLydia Wang 		set_pin_power_state(codec, 0x16, &parm);
590f5271101SLydia Wang 		set_pin_power_state(codec, 0x17, &parm);
591f5271101SLydia Wang 		/* MW0 (1ah), AOW 0/1 (10h/1dh) */
592f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
593f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
594f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
595f5271101SLydia Wang 				    parm);
596f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
597f5271101SLydia Wang 				    parm);
598f5271101SLydia Wang 	} else if (spec->codec_type == VT1708B_8CH
599f5271101SLydia Wang 		   || spec->codec_type == VT1708B_4CH
600f5271101SLydia Wang 		   || spec->codec_type == VT1708S) {
601f5271101SLydia Wang 		/* SW0 (17h) = stereo mixer */
602f5271101SLydia Wang 		int is_8ch = spec->codec_type != VT1708B_4CH;
603f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
604f5271101SLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
605f5271101SLydia Wang 			== ((spec->codec_type == VT1708S)  ? 5 : 0);
606f5271101SLydia Wang 		/* inputs */
607f5271101SLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
608f5271101SLydia Wang 		parm = AC_PWRST_D3;
609f5271101SLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
610f5271101SLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
611f5271101SLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
612f5271101SLydia Wang 		if (imux_is_smixer)
613f5271101SLydia Wang 			parm = AC_PWRST_D0;
614f5271101SLydia Wang 		/* SW0 (17h), AIW 0/1 (13h/14h) */
615f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
616f5271101SLydia Wang 				    parm);
617f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
618f5271101SLydia Wang 				    parm);
619f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
620f5271101SLydia Wang 				    parm);
621f5271101SLydia Wang 
622f5271101SLydia Wang 		/* outputs */
623f5271101SLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
624f5271101SLydia Wang 		parm = AC_PWRST_D3;
625f5271101SLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
626f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
627f5271101SLydia Wang 				    parm);
628f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
629f5271101SLydia Wang 				    parm);
630f5271101SLydia Wang 
631f5271101SLydia Wang 		/* PW6 (22h), SW2 (26h), AOW2 (24h) */
632f5271101SLydia Wang 		if (is_8ch) {
633f5271101SLydia Wang 			parm = AC_PWRST_D3;
634f5271101SLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
635f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x26, 0,
636f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
637f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x24, 0,
638f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
639f5271101SLydia Wang 		}
640f5271101SLydia Wang 
641f5271101SLydia Wang 		/* PW 3/4/7 (1ch/1dh/23h) */
642f5271101SLydia Wang 		parm = AC_PWRST_D3;
643f5271101SLydia Wang 		/* force to D0 for internal Speaker */
644f5271101SLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
645f5271101SLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
646f5271101SLydia Wang 		if (is_8ch)
647f5271101SLydia Wang 			set_pin_power_state(codec, 0x23, &parm);
648f5271101SLydia Wang 		/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
649f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
650f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
651f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
652f5271101SLydia Wang 				    parm);
653f5271101SLydia Wang 		if (is_8ch) {
654f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
655f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
656f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x27, 0,
657f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
658f5271101SLydia Wang 		}
659eb7188caSLydia Wang 	}  else if (spec->codec_type == VT1718S) {
660eb7188caSLydia Wang 		/* MUX6 (1eh) = stereo mixer */
661eb7188caSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
662eb7188caSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
663eb7188caSLydia Wang 		/* inputs */
664eb7188caSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
665eb7188caSLydia Wang 		parm = AC_PWRST_D3;
666eb7188caSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
667eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
668eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
669eb7188caSLydia Wang 		if (imux_is_smixer)
670eb7188caSLydia Wang 			parm = AC_PWRST_D0;
671eb7188caSLydia Wang 		/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
672eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
673eb7188caSLydia Wang 				    parm);
674eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
675eb7188caSLydia Wang 				    parm);
676eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
677eb7188caSLydia Wang 				    parm);
678eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
679eb7188caSLydia Wang 				    parm);
680eb7188caSLydia Wang 
681eb7188caSLydia Wang 		/* outputs */
682eb7188caSLydia Wang 		/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
683eb7188caSLydia Wang 		parm = AC_PWRST_D3;
684eb7188caSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
685eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
686eb7188caSLydia Wang 				    parm);
687eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
688eb7188caSLydia Wang 				    parm);
689eb7188caSLydia Wang 
690eb7188caSLydia Wang 		/* PW2 (26h), AOW2 (ah) */
691eb7188caSLydia Wang 		parm = AC_PWRST_D3;
692eb7188caSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
693eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
694eb7188caSLydia Wang 				    parm);
695eb7188caSLydia Wang 
696eb7188caSLydia Wang 		/* PW0/1 (24h/25h) */
697eb7188caSLydia Wang 		parm = AC_PWRST_D3;
698eb7188caSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
699eb7188caSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
700eb7188caSLydia Wang 		if (!spec->hp_independent_mode) /* check for redirected HP */
701eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
702eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
703eb7188caSLydia Wang 				    parm);
704eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
705eb7188caSLydia Wang 				    parm);
706eb7188caSLydia Wang 		/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
707eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
708eb7188caSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
709eb7188caSLydia Wang 		if (spec->hp_independent_mode) {
710eb7188caSLydia Wang 			/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
711eb7188caSLydia Wang 			parm = AC_PWRST_D3;
712eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
713eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x1b, 0,
714eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
715eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
716eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
717eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0xc, 0,
718eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
719eb7188caSLydia Wang 		}
720f3db423dSLydia Wang 	} else if (spec->codec_type == VT1716S) {
721f3db423dSLydia Wang 		unsigned int mono_out, present;
722f3db423dSLydia Wang 		/* SW0 (17h) = stereo mixer */
723f3db423dSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
724f3db423dSLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) ==  5;
725f3db423dSLydia Wang 		/* inputs */
726f3db423dSLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
727f3db423dSLydia Wang 		parm = AC_PWRST_D3;
728f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
729f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
730f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
731f3db423dSLydia Wang 		if (imux_is_smixer)
732f3db423dSLydia Wang 			parm = AC_PWRST_D0;
733f3db423dSLydia Wang 		/* SW0 (17h), AIW0(13h) */
734f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
735f3db423dSLydia Wang 				    parm);
736f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
737f3db423dSLydia Wang 				    parm);
738f3db423dSLydia Wang 
739f3db423dSLydia Wang 		parm = AC_PWRST_D3;
740f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
741f3db423dSLydia Wang 		/* PW11 (22h) */
742f3db423dSLydia Wang 		if (spec->dmic_enabled)
743f3db423dSLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
744f3db423dSLydia Wang 		else
745f3db423dSLydia Wang 			snd_hda_codec_write(
746f3db423dSLydia Wang 				codec, 0x22, 0,
747f3db423dSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
748f3db423dSLydia Wang 
749f3db423dSLydia Wang 		/* SW2(26h), AIW1(14h) */
750f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
751f3db423dSLydia Wang 				    parm);
752f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
753f3db423dSLydia Wang 				    parm);
754f3db423dSLydia Wang 
755f3db423dSLydia Wang 		/* outputs */
756f3db423dSLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
757f3db423dSLydia Wang 		parm = AC_PWRST_D3;
758f3db423dSLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
759f3db423dSLydia Wang 		/* Smart 5.1 PW2(1bh) */
760f3db423dSLydia Wang 		if (spec->smart51_enabled)
761f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1b, &parm);
762f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
763f3db423dSLydia Wang 				    parm);
764f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
765f3db423dSLydia Wang 				    parm);
766f3db423dSLydia Wang 
767f3db423dSLydia Wang 		/* PW7 (23h), SW3 (27h), AOW3 (25h) */
768f3db423dSLydia Wang 		parm = AC_PWRST_D3;
769f3db423dSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
770f3db423dSLydia Wang 		/* Smart 5.1 PW1(1ah) */
771f3db423dSLydia Wang 		if (spec->smart51_enabled)
772f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
773f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
774f3db423dSLydia Wang 				    parm);
775f3db423dSLydia Wang 
776f3db423dSLydia Wang 		/* Smart 5.1 PW5(1eh) */
777f3db423dSLydia Wang 		if (spec->smart51_enabled)
778f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1e, &parm);
779f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
780f3db423dSLydia Wang 				    parm);
781f3db423dSLydia Wang 
782f3db423dSLydia Wang 		/* Mono out */
783f3db423dSLydia Wang 		/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
784f3db423dSLydia Wang 		present = snd_hda_codec_read(
785f3db423dSLydia Wang 			codec, 0x1c, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
786f3db423dSLydia Wang 		if (present)
787f3db423dSLydia Wang 			mono_out = 0;
788f3db423dSLydia Wang 		else {
789f3db423dSLydia Wang 			present = snd_hda_codec_read(
790f3db423dSLydia Wang 				codec, 0x1d, 0, AC_VERB_GET_PIN_SENSE, 0)
791f3db423dSLydia Wang 				& 0x80000000;
792f3db423dSLydia Wang 			if (!spec->hp_independent_mode && present)
793f3db423dSLydia Wang 				mono_out = 0;
794f3db423dSLydia Wang 			else
795f3db423dSLydia Wang 				mono_out = 1;
796f3db423dSLydia Wang 		}
797f3db423dSLydia Wang 		parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
798f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
799f3db423dSLydia Wang 				    parm);
800f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
801f3db423dSLydia Wang 				    parm);
802f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
803f3db423dSLydia Wang 				    parm);
804f3db423dSLydia Wang 
805f3db423dSLydia Wang 		/* PW 3/4 (1ch/1dh) */
806f3db423dSLydia Wang 		parm = AC_PWRST_D3;
807f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
808f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
809f3db423dSLydia Wang 		/* HP Independent Mode, power on AOW3 */
810f3db423dSLydia Wang 		if (spec->hp_independent_mode)
811f3db423dSLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
812f3db423dSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
813f3db423dSLydia Wang 
814f3db423dSLydia Wang 		/* force to D0 for internal Speaker */
815f3db423dSLydia Wang 		/* MW0 (16h), AOW0 (10h) */
816f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
817f3db423dSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
818f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
819f3db423dSLydia Wang 				    mono_out ? AC_PWRST_D0 : parm);
82025eaba2fSLydia Wang 	} else if (spec->codec_type == VT2002P) {
82125eaba2fSLydia Wang 		unsigned int present;
82225eaba2fSLydia Wang 		/* MUX9 (1eh) = stereo mixer */
82325eaba2fSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
82425eaba2fSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
82525eaba2fSLydia Wang 		/* inputs */
82625eaba2fSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
82725eaba2fSLydia Wang 		parm = AC_PWRST_D3;
82825eaba2fSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
82925eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
83025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
83125eaba2fSLydia Wang 		if (imux_is_smixer)
83225eaba2fSLydia Wang 			parm = AC_PWRST_D0;
83325eaba2fSLydia Wang 		/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
83425eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
83525eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
83625eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
83725eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
83825eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
83925eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
84025eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
84125eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
84225eaba2fSLydia Wang 
84325eaba2fSLydia Wang 		/* outputs */
84425eaba2fSLydia Wang 		/* AOW0 (8h)*/
84525eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
84625eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
84725eaba2fSLydia Wang 
84825eaba2fSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
84925eaba2fSLydia Wang 		parm = AC_PWRST_D3;
85025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
85125eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
85225eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
85325eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x37,
85425eaba2fSLydia Wang 				    0, AC_VERB_SET_POWER_STATE, parm);
85525eaba2fSLydia Wang 
85625eaba2fSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
85725eaba2fSLydia Wang 		parm = AC_PWRST_D3;
85825eaba2fSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
85925eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
86025eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
86125eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
86225eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
86325eaba2fSLydia Wang 		if (spec->hp_independent_mode)	{
86425eaba2fSLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
86525eaba2fSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
86625eaba2fSLydia Wang 		}
86725eaba2fSLydia Wang 
86825eaba2fSLydia Wang 		/* Class-D */
86925eaba2fSLydia Wang 		/* PW0 (24h), MW0(18h), MUX0(34h) */
87025eaba2fSLydia Wang 		present = snd_hda_codec_read(
87125eaba2fSLydia Wang 			codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
87225eaba2fSLydia Wang 		parm = AC_PWRST_D3;
87325eaba2fSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
87425eaba2fSLydia Wang 		if (present) {
87525eaba2fSLydia Wang 			snd_hda_codec_write(
87625eaba2fSLydia Wang 				codec, 0x18, 0,
87725eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
87825eaba2fSLydia Wang 			snd_hda_codec_write(
87925eaba2fSLydia Wang 				codec, 0x34, 0,
88025eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
88125eaba2fSLydia Wang 		} else {
88225eaba2fSLydia Wang 			snd_hda_codec_write(
88325eaba2fSLydia Wang 				codec, 0x18, 0,
88425eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
88525eaba2fSLydia Wang 			snd_hda_codec_write(
88625eaba2fSLydia Wang 				codec, 0x34, 0,
88725eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
88825eaba2fSLydia Wang 		}
88925eaba2fSLydia Wang 
89025eaba2fSLydia Wang 		/* Mono Out */
89125eaba2fSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
89225eaba2fSLydia Wang 		present = snd_hda_codec_read(
89325eaba2fSLydia Wang 			codec, 0x26, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
89425eaba2fSLydia Wang 		parm = AC_PWRST_D3;
89525eaba2fSLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
89625eaba2fSLydia Wang 		if (present) {
89725eaba2fSLydia Wang 			snd_hda_codec_write(
89825eaba2fSLydia Wang 				codec, 0x17, 0,
89925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
90025eaba2fSLydia Wang 			snd_hda_codec_write(
90125eaba2fSLydia Wang 				codec, 0x3b, 0,
90225eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
90325eaba2fSLydia Wang 		} else {
90425eaba2fSLydia Wang 			snd_hda_codec_write(
90525eaba2fSLydia Wang 				codec, 0x17, 0,
90625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
90725eaba2fSLydia Wang 			snd_hda_codec_write(
90825eaba2fSLydia Wang 				codec, 0x3b, 0,
90925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
91025eaba2fSLydia Wang 		}
91125eaba2fSLydia Wang 
91225eaba2fSLydia Wang 		/* MW9 (21h) */
91325eaba2fSLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
91425eaba2fSLydia Wang 			snd_hda_codec_write(
91525eaba2fSLydia Wang 				codec, 0x21, 0,
91625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
91725eaba2fSLydia Wang 		else
91825eaba2fSLydia Wang 			snd_hda_codec_write(
91925eaba2fSLydia Wang 				codec, 0x21, 0,
92025eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
921ab6734e7SLydia Wang 	} else if (spec->codec_type == VT1812) {
922ab6734e7SLydia Wang 		unsigned int present;
923ab6734e7SLydia Wang 		/* MUX10 (1eh) = stereo mixer */
924ab6734e7SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
925ab6734e7SLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
926ab6734e7SLydia Wang 		/* inputs */
927ab6734e7SLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
928ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
929ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
930ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
931ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
932ab6734e7SLydia Wang 		if (imux_is_smixer)
933ab6734e7SLydia Wang 			parm = AC_PWRST_D0;
934ab6734e7SLydia Wang 		/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
935ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
936ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
937ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
938ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
939ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
940ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
941ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
942ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
943ab6734e7SLydia Wang 
944ab6734e7SLydia Wang 		/* outputs */
945ab6734e7SLydia Wang 		/* AOW0 (8h)*/
946ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
947ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
948ab6734e7SLydia Wang 
949ab6734e7SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
950ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
951ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
952ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
953ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
954ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
955ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
956ab6734e7SLydia Wang 
957ab6734e7SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
958ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
959ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
960ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
961ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
962ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
963ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
964ab6734e7SLydia Wang 		if (spec->hp_independent_mode)	{
965ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
966ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
967ab6734e7SLydia Wang 		}
968ab6734e7SLydia Wang 
969ab6734e7SLydia Wang 		/* Internal Speaker */
970ab6734e7SLydia Wang 		/* PW0 (24h), MW0(14h), MUX0(34h) */
971ab6734e7SLydia Wang 		present = snd_hda_codec_read(
972ab6734e7SLydia Wang 			codec, 0x25, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
973ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
974ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
975ab6734e7SLydia Wang 		if (present) {
976ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
977ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
978ab6734e7SLydia Wang 					    AC_PWRST_D3);
979ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
980ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
981ab6734e7SLydia Wang 					    AC_PWRST_D3);
982ab6734e7SLydia Wang 		} else {
983ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
984ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
985ab6734e7SLydia Wang 					    AC_PWRST_D0);
986ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
987ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
988ab6734e7SLydia Wang 					    AC_PWRST_D0);
989ab6734e7SLydia Wang 		}
990ab6734e7SLydia Wang 		/* Mono Out */
991ab6734e7SLydia Wang 		/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
992ab6734e7SLydia Wang 		present = snd_hda_codec_read(
993ab6734e7SLydia Wang 			codec, 0x28, 0, AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
994ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
995ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
996ab6734e7SLydia Wang 		if (present) {
997ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
998ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
999ab6734e7SLydia Wang 					    AC_PWRST_D3);
1000ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1001ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1002ab6734e7SLydia Wang 					    AC_PWRST_D3);
1003ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1004ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1005ab6734e7SLydia Wang 					    AC_PWRST_D3);
1006ab6734e7SLydia Wang 		} else {
1007ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
1008ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1009ab6734e7SLydia Wang 					    AC_PWRST_D0);
1010ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1011ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1012ab6734e7SLydia Wang 					    AC_PWRST_D0);
1013ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1014ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1015ab6734e7SLydia Wang 					    AC_PWRST_D0);
1016ab6734e7SLydia Wang 		}
1017ab6734e7SLydia Wang 
1018ab6734e7SLydia Wang 		/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
1019ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1020ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x33, &parm);
1021ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0,
1022ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1023ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x3d, 0,
1024ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1025ab6734e7SLydia Wang 
1026ab6734e7SLydia Wang 		/* MW9 (21h) */
1027ab6734e7SLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
1028ab6734e7SLydia Wang 			snd_hda_codec_write(
1029ab6734e7SLydia Wang 				codec, 0x21, 0,
1030ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1031ab6734e7SLydia Wang 		else
1032ab6734e7SLydia Wang 			snd_hda_codec_write(
1033ab6734e7SLydia Wang 				codec, 0x21, 0,
1034ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
1035f5271101SLydia Wang 	}
1036f5271101SLydia Wang }
1037f5271101SLydia Wang 
1038c577b8a1SJoseph Chan /*
1039c577b8a1SJoseph Chan  * input MUX handling
1040c577b8a1SJoseph Chan  */
1041c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1042c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
1043c577b8a1SJoseph Chan {
1044c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1045c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1046c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
1047c577b8a1SJoseph Chan }
1048c577b8a1SJoseph Chan 
1049c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1050c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1051c577b8a1SJoseph Chan {
1052c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1053c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1054c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1055c577b8a1SJoseph Chan 
1056c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
1057c577b8a1SJoseph Chan 	return 0;
1058c577b8a1SJoseph Chan }
1059c577b8a1SJoseph Chan 
1060c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1061c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1062c577b8a1SJoseph Chan {
1063c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1064c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1065c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1066c577b8a1SJoseph Chan 
1067337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
1068337b9d02STakashi Iwai 		return -EINVAL;
1069a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
1070a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
1071a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1072a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
1073a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1074a80e6e3cSLydia Wang 	/* update jack power state */
1075a80e6e3cSLydia Wang 	set_jack_power_state(codec);
1076a80e6e3cSLydia Wang 
1077c577b8a1SJoseph Chan 	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
1078337b9d02STakashi Iwai 				     spec->mux_nids[adc_idx],
1079c577b8a1SJoseph Chan 				     &spec->cur_mux[adc_idx]);
1080c577b8a1SJoseph Chan }
1081c577b8a1SJoseph Chan 
10820aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
10830aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
10840aa62aefSHarald Welte {
10850aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
10860aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10870aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
10880aa62aefSHarald Welte }
10890aa62aefSHarald Welte 
10900aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
10910aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
10920aa62aefSHarald Welte {
10930aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
10940aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
1095eb7188caSLydia Wang 	hda_nid_t nid;
1096eb7188caSLydia Wang 	unsigned int pinsel;
1097eb7188caSLydia Wang 
1098eb7188caSLydia Wang 	switch (spec->codec_type) {
1099eb7188caSLydia Wang 	case VT1718S:
1100eb7188caSLydia Wang 		nid = 0x34;
1101eb7188caSLydia Wang 		break;
110225eaba2fSLydia Wang 	case VT2002P:
110325eaba2fSLydia Wang 		nid = 0x35;
110425eaba2fSLydia Wang 		break;
1105ab6734e7SLydia Wang 	case VT1812:
1106ab6734e7SLydia Wang 		nid = 0x3d;
1107ab6734e7SLydia Wang 		break;
1108eb7188caSLydia Wang 	default:
1109eb7188caSLydia Wang 		nid = spec->autocfg.hp_pins[0];
1110eb7188caSLydia Wang 		break;
1111eb7188caSLydia Wang 	}
1112eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
1113eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
11140aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
11150aa62aefSHarald Welte 				      0x00);
11160aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
11170aa62aefSHarald Welte 
11180aa62aefSHarald Welte 	return 0;
11190aa62aefSHarald Welte }
11200aa62aefSHarald Welte 
11210713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
11220713efebSLydia Wang {
11230713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
11240713efebSLydia Wang 	if (ctl) {
11250713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11260713efebSLydia Wang 		ctl->vd[0].access |= active
11270713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11280713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
11290713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
11300713efebSLydia Wang 	}
11310713efebSLydia Wang }
11320713efebSLydia Wang 
1133cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
1134cdc1784dSLydia Wang {
1135cdc1784dSLydia Wang 	/* mute side channel */
1136cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
1137cdc1784dSLydia Wang 	unsigned int parm = spec->hp_independent_mode
1138cdc1784dSLydia Wang 		? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
1139cdc1784dSLydia Wang 	hda_nid_t sw3;
1140cdc1784dSLydia Wang 
1141cdc1784dSLydia Wang 	switch (spec->codec_type) {
1142cdc1784dSLydia Wang 	case VT1708:
1143cdc1784dSLydia Wang 		sw3 = 0x1b;
1144cdc1784dSLydia Wang 		break;
1145cdc1784dSLydia Wang 	case VT1709_10CH:
1146cdc1784dSLydia Wang 		sw3 = 0x29;
1147cdc1784dSLydia Wang 		break;
1148cdc1784dSLydia Wang 	case VT1708B_8CH:
1149cdc1784dSLydia Wang 	case VT1708S:
1150cdc1784dSLydia Wang 		sw3 = 0x27;
1151cdc1784dSLydia Wang 		break;
1152cdc1784dSLydia Wang 	default:
1153cdc1784dSLydia Wang 		sw3 = 0;
1154cdc1784dSLydia Wang 		break;
1155cdc1784dSLydia Wang 	}
1156cdc1784dSLydia Wang 
1157cdc1784dSLydia Wang 	if (sw3)
1158cdc1784dSLydia Wang 		snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1159cdc1784dSLydia Wang 				    parm);
1160cdc1784dSLydia Wang 	return 0;
1161cdc1784dSLydia Wang }
1162cdc1784dSLydia Wang 
11630aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
11640aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
11650aa62aefSHarald Welte {
11660aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11670aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11680aa62aefSHarald Welte 	hda_nid_t nid = spec->autocfg.hp_pins[0];
11690aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
1170cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
1171cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
1172cdc1784dSLydia Wang 		? 1 : 0;
11730aa62aefSHarald Welte 
1174eb7188caSLydia Wang 	switch (spec->codec_type) {
1175eb7188caSLydia Wang 	case VT1718S:
1176eb7188caSLydia Wang 		nid = 0x34;
1177eb7188caSLydia Wang 		pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
1178eb7188caSLydia Wang 		spec->multiout.num_dacs = 4;
1179eb7188caSLydia Wang 		break;
118025eaba2fSLydia Wang 	case VT2002P:
118125eaba2fSLydia Wang 		nid = 0x35;
118225eaba2fSLydia Wang 		break;
1183ab6734e7SLydia Wang 	case VT1812:
1184ab6734e7SLydia Wang 		nid = 0x3d;
1185ab6734e7SLydia Wang 		break;
1186eb7188caSLydia Wang 	default:
1187eb7188caSLydia Wang 		nid = spec->autocfg.hp_pins[0];
1188eb7188caSLydia Wang 		break;
1189eb7188caSLydia Wang 	}
1190cdc1784dSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
11910aa62aefSHarald Welte 
1192cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
1193cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
1194cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
11950aa62aefSHarald Welte 					   0, 0, 0);
11960aa62aefSHarald Welte 
1197cdc1784dSLydia Wang 	update_side_mute_status(codec);
11980713efebSLydia Wang 	/* update HP volume/swtich active state */
11990713efebSLydia Wang 	if (spec->codec_type == VT1708S
1200eb7188caSLydia Wang 	    || spec->codec_type == VT1702
1201f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
120225eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
1203ab6734e7SLydia Wang 	    || spec->codec_type == VT2002P
1204ab6734e7SLydia Wang 	    || spec->codec_type == VT1812) {
12050713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
12060713efebSLydia Wang 			     spec->hp_independent_mode);
12070713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
12080713efebSLydia Wang 			     spec->hp_independent_mode);
12090713efebSLydia Wang 	}
12100aa62aefSHarald Welte 	return 0;
12110aa62aefSHarald Welte }
12120aa62aefSHarald Welte 
12130aa62aefSHarald Welte static struct snd_kcontrol_new via_hp_mixer[] = {
12140aa62aefSHarald Welte 	{
12150aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
12160aa62aefSHarald Welte 		.name = "Independent HP",
12170aa62aefSHarald Welte 		.count = 1,
12180aa62aefSHarald Welte 		.info = via_independent_hp_info,
12190aa62aefSHarald Welte 		.get = via_independent_hp_get,
12200aa62aefSHarald Welte 		.put = via_independent_hp_put,
12210aa62aefSHarald Welte 	},
12220aa62aefSHarald Welte 	{ } /* end */
12230aa62aefSHarald Welte };
12240aa62aefSHarald Welte 
12251564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
12261564b287SLydia Wang {
12271564b287SLydia Wang 	int i;
12281564b287SLydia Wang 	struct snd_ctl_elem_id id;
12291564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
12301564b287SLydia Wang 
12311564b287SLydia Wang 	memset(&id, 0, sizeof(id));
12321564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
12331564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
12341564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
12351564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
12361564b287SLydia Wang 			       &id);
12371564b287SLydia Wang 	}
12381564b287SLydia Wang }
12391564b287SLydia Wang 
12401564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
12411564b287SLydia Wang {
12421564b287SLydia Wang 	struct via_spec *spec = codec->spec;
12431564b287SLydia Wang 	hda_nid_t  nid_mixer;
12441564b287SLydia Wang 	int start_idx;
12451564b287SLydia Wang 	int end_idx;
12461564b287SLydia Wang 	int i;
12471564b287SLydia Wang 	/* get nid of MW0 and start & end index */
12481564b287SLydia Wang 	switch (spec->codec_type) {
12491564b287SLydia Wang 	case VT1708:
12501564b287SLydia Wang 		nid_mixer = 0x17;
12511564b287SLydia Wang 		start_idx = 2;
12521564b287SLydia Wang 		end_idx = 4;
12531564b287SLydia Wang 		break;
12541564b287SLydia Wang 	case VT1709_10CH:
12551564b287SLydia Wang 	case VT1709_6CH:
12561564b287SLydia Wang 		nid_mixer = 0x18;
12571564b287SLydia Wang 		start_idx = 2;
12581564b287SLydia Wang 		end_idx = 4;
12591564b287SLydia Wang 		break;
12601564b287SLydia Wang 	case VT1708B_8CH:
12611564b287SLydia Wang 	case VT1708B_4CH:
12621564b287SLydia Wang 	case VT1708S:
1263f3db423dSLydia Wang 	case VT1716S:
12641564b287SLydia Wang 		nid_mixer = 0x16;
12651564b287SLydia Wang 		start_idx = 2;
12661564b287SLydia Wang 		end_idx = 4;
12671564b287SLydia Wang 		break;
12681564b287SLydia Wang 	default:
12691564b287SLydia Wang 		return;
12701564b287SLydia Wang 	}
12711564b287SLydia Wang 	/* check AA path's mute status */
12721564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
12731564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
12741564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
12751564b287SLydia Wang 					 HDA_AMP_MUTE, val);
12761564b287SLydia Wang 	}
12771564b287SLydia Wang }
12781564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
12791564b287SLydia Wang {
12801564b287SLydia Wang 	int res = 0;
12811564b287SLydia Wang 	int index;
12821564b287SLydia Wang 	for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
12831564b287SLydia Wang 		if (pin == spec->autocfg.input_pins[index]) {
12841564b287SLydia Wang 			res = 1;
12851564b287SLydia Wang 			break;
12861564b287SLydia Wang 		}
12871564b287SLydia Wang 	}
12881564b287SLydia Wang 	return res;
12891564b287SLydia Wang }
12901564b287SLydia Wang 
12911564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
12921564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
12931564b287SLydia Wang {
12941564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
12951564b287SLydia Wang 	uinfo->count = 1;
12961564b287SLydia Wang 	uinfo->value.integer.min = 0;
12971564b287SLydia Wang 	uinfo->value.integer.max = 1;
12981564b287SLydia Wang 	return 0;
12991564b287SLydia Wang }
13001564b287SLydia Wang 
13011564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
13021564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13031564b287SLydia Wang {
13041564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13051564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13061564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
13071564b287SLydia Wang 	int on = 1;
13081564b287SLydia Wang 	int i;
13091564b287SLydia Wang 
13101564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
13111564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
13121564b287SLydia Wang 		if (nid) {
13131564b287SLydia Wang 			int ctl =
13141564b287SLydia Wang 			    snd_hda_codec_read(codec, nid, 0,
13151564b287SLydia Wang 					       AC_VERB_GET_PIN_WIDGET_CONTROL,
13161564b287SLydia Wang 					       0);
13171564b287SLydia Wang 			if (i == AUTO_PIN_FRONT_MIC
1318eb7188caSLydia Wang 			    && spec->hp_independent_mode
1319eb7188caSLydia Wang 			    && spec->codec_type != VT1718S)
13201564b287SLydia Wang 				continue; /* ignore FMic for independent HP */
13211564b287SLydia Wang 			if (ctl & AC_PINCTL_IN_EN
13221564b287SLydia Wang 			    && !(ctl & AC_PINCTL_OUT_EN))
13231564b287SLydia Wang 				on = 0;
13241564b287SLydia Wang 		}
13251564b287SLydia Wang 	}
13261564b287SLydia Wang 	*ucontrol->value.integer.value = on;
13271564b287SLydia Wang 	return 0;
13281564b287SLydia Wang }
13291564b287SLydia Wang 
13301564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
13311564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13321564b287SLydia Wang {
13331564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13341564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13351564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
13361564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
13371564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
13381564b287SLydia Wang 	int i;
13391564b287SLydia Wang 
13401564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
13411564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
13421564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC
1343eb7188caSLydia Wang 		    && spec->hp_independent_mode
1344eb7188caSLydia Wang 		    && spec->codec_type != VT1718S)
13451564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
13461564b287SLydia Wang 		if (nid) {
13471564b287SLydia Wang 			unsigned int parm = snd_hda_codec_read(
13481564b287SLydia Wang 				codec, nid, 0,
13491564b287SLydia Wang 				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
13501564b287SLydia Wang 			parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
13511564b287SLydia Wang 			parm |= out_in;
13521564b287SLydia Wang 			snd_hda_codec_write(codec, nid, 0,
13531564b287SLydia Wang 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
13541564b287SLydia Wang 					    parm);
13551564b287SLydia Wang 			if (out_in == AC_PINCTL_OUT_EN) {
13561564b287SLydia Wang 				mute_aa_path(codec, 1);
13571564b287SLydia Wang 				notify_aa_path_ctls(codec);
13581564b287SLydia Wang 			}
1359eb7188caSLydia Wang 			if (spec->codec_type == VT1718S)
1360eb7188caSLydia Wang 				snd_hda_codec_amp_stereo(
1361eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1362eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
13631564b287SLydia Wang 		}
13641564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC) {
1365f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1366f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
13671564b287SLydia Wang 				/* input = index 1 (AOW3) */
13681564b287SLydia Wang 				snd_hda_codec_write(
13691564b287SLydia Wang 					codec, nid, 0,
13701564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
13711564b287SLydia Wang 				snd_hda_codec_amp_stereo(
13721564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
13731564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
13741564b287SLydia Wang 			}
13751564b287SLydia Wang 		}
13761564b287SLydia Wang 	}
13771564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
13781564b287SLydia Wang 	set_jack_power_state(codec);
13791564b287SLydia Wang 	return 1;
13801564b287SLydia Wang }
13811564b287SLydia Wang 
13821564b287SLydia Wang static struct snd_kcontrol_new via_smart51_mixer[] = {
13831564b287SLydia Wang 	{
13841564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
13851564b287SLydia Wang 	 .name = "Smart 5.1",
13861564b287SLydia Wang 	 .count = 1,
13871564b287SLydia Wang 	 .info = via_smart51_info,
13881564b287SLydia Wang 	 .get = via_smart51_get,
13891564b287SLydia Wang 	 .put = via_smart51_put,
13901564b287SLydia Wang 	 },
13911564b287SLydia Wang 	{}			/* end */
13921564b287SLydia Wang };
13931564b287SLydia Wang 
1394c577b8a1SJoseph Chan /* capture mixer elements */
1395c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1396c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1397c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1398c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1399c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1400c577b8a1SJoseph Chan 	{
1401c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1402c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1403c577b8a1SJoseph Chan 		 * So call somewhat different..
1404c577b8a1SJoseph Chan 		 */
1405c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1406c577b8a1SJoseph Chan 		.name = "Input Source",
1407c577b8a1SJoseph Chan 		.count = 1,
1408c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1409c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1410c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1411c577b8a1SJoseph Chan 	},
1412c577b8a1SJoseph Chan 	{ } /* end */
1413c577b8a1SJoseph Chan };
1414f5271101SLydia Wang 
1415f5271101SLydia Wang /* check AA path's mute statue */
1416f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1417f5271101SLydia Wang {
1418f5271101SLydia Wang 	int mute = 1;
1419f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1420f5271101SLydia Wang 	int start_idx;
1421f5271101SLydia Wang 	int end_idx;
1422f5271101SLydia Wang 	int i;
1423f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1424f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1425f5271101SLydia Wang 	switch (spec->codec_type) {
1426f5271101SLydia Wang 	case VT1708B_8CH:
1427f5271101SLydia Wang 	case VT1708B_4CH:
1428f5271101SLydia Wang 	case VT1708S:
1429f3db423dSLydia Wang 	case VT1716S:
1430f5271101SLydia Wang 		nid_mixer = 0x16;
1431f5271101SLydia Wang 		start_idx = 2;
1432f5271101SLydia Wang 		end_idx = 4;
1433f5271101SLydia Wang 		break;
1434f5271101SLydia Wang 	case VT1702:
1435f5271101SLydia Wang 		nid_mixer = 0x1a;
1436f5271101SLydia Wang 		start_idx = 1;
1437f5271101SLydia Wang 		end_idx = 3;
1438f5271101SLydia Wang 		break;
1439eb7188caSLydia Wang 	case VT1718S:
1440eb7188caSLydia Wang 		nid_mixer = 0x21;
1441eb7188caSLydia Wang 		start_idx = 1;
1442eb7188caSLydia Wang 		end_idx = 3;
1443eb7188caSLydia Wang 		break;
144425eaba2fSLydia Wang 	case VT2002P:
1445ab6734e7SLydia Wang 	case VT1812:
144625eaba2fSLydia Wang 		nid_mixer = 0x21;
144725eaba2fSLydia Wang 		start_idx = 0;
144825eaba2fSLydia Wang 		end_idx = 2;
144925eaba2fSLydia Wang 		break;
1450f5271101SLydia Wang 	default:
1451f5271101SLydia Wang 		return 0;
1452f5271101SLydia Wang 	}
1453f5271101SLydia Wang 	/* check AA path's mute status */
1454f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1455f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1456f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1457f5271101SLydia Wang 		int shift = 8 * (i % 4);
1458f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1459f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1460f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1461f5271101SLydia Wang 			/* check mute status while the pin is connected */
1462f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1463f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1464f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1465f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1466f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1467f5271101SLydia Wang 				mute = 0;
1468f5271101SLydia Wang 				break;
1469f5271101SLydia Wang 			}
1470f5271101SLydia Wang 		}
1471f5271101SLydia Wang 	}
1472f5271101SLydia Wang 	return mute;
1473f5271101SLydia Wang }
1474f5271101SLydia Wang 
1475f5271101SLydia Wang /* enter/exit analog low-current mode */
1476f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1477f5271101SLydia Wang {
1478f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1479f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1480f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1481f5271101SLydia Wang 	unsigned int verb = 0;
1482f5271101SLydia Wang 	unsigned int parm = 0;
1483f5271101SLydia Wang 
1484f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1485f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1486f5271101SLydia Wang 	else {
1487f5271101SLydia Wang 		enable = enable && stream_idle;
1488f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1489f5271101SLydia Wang 	}
1490f5271101SLydia Wang 
1491f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1492f5271101SLydia Wang 	switch (spec->codec_type) {
1493f5271101SLydia Wang 	case VT1708B_8CH:
1494f5271101SLydia Wang 	case VT1708B_4CH:
1495f5271101SLydia Wang 		verb = 0xf70;
1496f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1497f5271101SLydia Wang 		break;
1498f5271101SLydia Wang 	case VT1708S:
1499eb7188caSLydia Wang 	case VT1718S:
1500f3db423dSLydia Wang 	case VT1716S:
1501f5271101SLydia Wang 		verb = 0xf73;
1502f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1503f5271101SLydia Wang 		break;
1504f5271101SLydia Wang 	case VT1702:
1505f5271101SLydia Wang 		verb = 0xf73;
1506f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1507f5271101SLydia Wang 		break;
150825eaba2fSLydia Wang 	case VT2002P:
1509ab6734e7SLydia Wang 	case VT1812:
151025eaba2fSLydia Wang 		verb = 0xf93;
151125eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
151225eaba2fSLydia Wang 		break;
1513f5271101SLydia Wang 	default:
1514f5271101SLydia Wang 		return;		/* other codecs are not supported */
1515f5271101SLydia Wang 	}
1516f5271101SLydia Wang 	/* send verb */
1517f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1518f5271101SLydia Wang }
1519f5271101SLydia Wang 
1520c577b8a1SJoseph Chan /*
1521c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1522c577b8a1SJoseph Chan  */
1523c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1524c577b8a1SJoseph Chan 	/*
1525c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1526c577b8a1SJoseph Chan 	 */
1527c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1528c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1529c577b8a1SJoseph Chan 
1530c577b8a1SJoseph Chan 
1531f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1532c577b8a1SJoseph Chan 	 * mixer widget
1533c577b8a1SJoseph Chan 	 */
1534c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1535f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1536f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1537f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1538f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1539f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1540c577b8a1SJoseph Chan 
1541c577b8a1SJoseph Chan 	/*
1542c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1543c577b8a1SJoseph Chan 	 */
1544c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1545c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1546c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1547c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1548c577b8a1SJoseph Chan 
1549bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1550bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1551c577b8a1SJoseph Chan 	/* PW9 Output enable */
1552c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1553f7278fd0SJosepch Chan 	{ }
1554c577b8a1SJoseph Chan };
1555c577b8a1SJoseph Chan 
1556c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1557c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1558c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1559c577b8a1SJoseph Chan {
1560c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
156117314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
156217314379SLydia Wang 		&& substream->ref_count == 0;
156317314379SLydia Wang 	analog_low_current_mode(codec, idle);
15649a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
15659a08160bSTakashi Iwai 					     hinfo);
1566c577b8a1SJoseph Chan }
1567c577b8a1SJoseph Chan 
15680aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
15690aa62aefSHarald Welte 				      unsigned int stream_tag,
15700aa62aefSHarald Welte 				      unsigned int format,
15710aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
15720aa62aefSHarald Welte {
15730aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
15740aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
15750aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
15760aa62aefSHarald Welte 	int chs = substream->runtime->channels;
15770aa62aefSHarald Welte 	int i;
15780aa62aefSHarald Welte 
15790aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
15800aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
15810aa62aefSHarald Welte 		if (chs == 2 &&
15820aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
15830aa62aefSHarald Welte 						format) &&
15840aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
15850aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
15860aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
15870aa62aefSHarald Welte 			 * be updated */
15880aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
15890aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
15900aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
15910aa62aefSHarald Welte 						    codec->spdif_ctls &
15920aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
15930aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
15940aa62aefSHarald Welte 						   stream_tag, 0, format);
15950aa62aefSHarald Welte 			/* turn on again (if needed) */
15960aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
15970aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
15980aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
15990aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
16000aa62aefSHarald Welte 		} else {
16010aa62aefSHarald Welte 			mout->dig_out_used = 0;
16020aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16030aa62aefSHarald Welte 						   0, 0, 0);
16040aa62aefSHarald Welte 		}
16050aa62aefSHarald Welte 	}
16060aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
16070aa62aefSHarald Welte 
16080aa62aefSHarald Welte 	/* front */
16090aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
16100aa62aefSHarald Welte 				   0, format);
16110aa62aefSHarald Welte 
1612eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1613eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
16140aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
16150aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
16160aa62aefSHarald Welte 					   0, format);
16170aa62aefSHarald Welte 
16180aa62aefSHarald Welte 	/* extra outputs copied from front */
16190aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
16200aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
16210aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
16220aa62aefSHarald Welte 						   mout->extra_out_nid[i],
16230aa62aefSHarald Welte 						   stream_tag, 0, format);
16240aa62aefSHarald Welte 
16250aa62aefSHarald Welte 	/* surrounds */
16260aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
16270aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
16280aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
16290aa62aefSHarald Welte 						   i * 2, format);
16300aa62aefSHarald Welte 		else /* copy front */
16310aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
16320aa62aefSHarald Welte 						   0, format);
16330aa62aefSHarald Welte 	}
16340aa62aefSHarald Welte }
16350aa62aefSHarald Welte 
16360aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
16370aa62aefSHarald Welte 					  struct hda_codec *codec,
16380aa62aefSHarald Welte 					  unsigned int stream_tag,
16390aa62aefSHarald Welte 					  unsigned int format,
16400aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
16410aa62aefSHarald Welte {
16420aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
16430aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
16440aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
16450aa62aefSHarald Welte 
16460aa62aefSHarald Welte 	if (substream->number == 0)
16470aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
16480aa62aefSHarald Welte 					  substream);
16490aa62aefSHarald Welte 	else {
16500aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
16510aa62aefSHarald Welte 		    spec->hp_independent_mode)
16520aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
16530aa62aefSHarald Welte 						   stream_tag, 0, format);
16540aa62aefSHarald Welte 	}
16551f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
16560aa62aefSHarald Welte 	return 0;
16570aa62aefSHarald Welte }
16580aa62aefSHarald Welte 
16590aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
16600aa62aefSHarald Welte 				    struct hda_codec *codec,
16610aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
16620aa62aefSHarald Welte {
16630aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
16640aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
16650aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
16660aa62aefSHarald Welte 	int i;
16670aa62aefSHarald Welte 
16680aa62aefSHarald Welte 	if (substream->number == 0) {
16690aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
16700aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
16710aa62aefSHarald Welte 
16720aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
16730aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
16740aa62aefSHarald Welte 						   0, 0, 0);
16750aa62aefSHarald Welte 
16760aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
16770aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
16780aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
16790aa62aefSHarald Welte 							mout->extra_out_nid[i],
16800aa62aefSHarald Welte 							0, 0, 0);
16810aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
16820aa62aefSHarald Welte 		if (mout->dig_out_nid &&
16830aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
16840aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16850aa62aefSHarald Welte 						   0, 0, 0);
16860aa62aefSHarald Welte 			mout->dig_out_used = 0;
16870aa62aefSHarald Welte 		}
16880aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
16890aa62aefSHarald Welte 	} else {
16900aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
16910aa62aefSHarald Welte 		    spec->hp_independent_mode)
16920aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
16930aa62aefSHarald Welte 						   0, 0, 0);
16940aa62aefSHarald Welte 	}
16951f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
16960aa62aefSHarald Welte 	return 0;
16970aa62aefSHarald Welte }
16980aa62aefSHarald Welte 
1699c577b8a1SJoseph Chan /*
1700c577b8a1SJoseph Chan  * Digital out
1701c577b8a1SJoseph Chan  */
1702c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1703c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1704c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1705c577b8a1SJoseph Chan {
1706c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1707c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1708c577b8a1SJoseph Chan }
1709c577b8a1SJoseph Chan 
1710c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1711c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1712c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1713c577b8a1SJoseph Chan {
1714c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1715c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1716c577b8a1SJoseph Chan }
1717c577b8a1SJoseph Chan 
17185691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
171998aa34c0SHarald Welte 					struct hda_codec *codec,
172098aa34c0SHarald Welte 					unsigned int stream_tag,
172198aa34c0SHarald Welte 					unsigned int format,
172298aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
172398aa34c0SHarald Welte {
172498aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
17259da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
17269da29271STakashi Iwai 					     stream_tag, format, substream);
17279da29271STakashi Iwai }
17285691ec7fSHarald Welte 
17299da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
17309da29271STakashi Iwai 					struct hda_codec *codec,
17319da29271STakashi Iwai 					struct snd_pcm_substream *substream)
17329da29271STakashi Iwai {
17339da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
17349da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
173598aa34c0SHarald Welte 	return 0;
173698aa34c0SHarald Welte }
173798aa34c0SHarald Welte 
1738c577b8a1SJoseph Chan /*
1739c577b8a1SJoseph Chan  * Analog capture
1740c577b8a1SJoseph Chan  */
1741c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1742c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1743c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1744c577b8a1SJoseph Chan 				   unsigned int format,
1745c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1746c577b8a1SJoseph Chan {
1747c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1748c577b8a1SJoseph Chan 
1749c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1750c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1751c577b8a1SJoseph Chan 	return 0;
1752c577b8a1SJoseph Chan }
1753c577b8a1SJoseph Chan 
1754c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1755c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1756c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1757c577b8a1SJoseph Chan {
1758c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1759888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1760c577b8a1SJoseph Chan 	return 0;
1761c577b8a1SJoseph Chan }
1762c577b8a1SJoseph Chan 
1763c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
17640aa62aefSHarald Welte 	.substreams = 2,
1765c577b8a1SJoseph Chan 	.channels_min = 2,
1766c577b8a1SJoseph Chan 	.channels_max = 8,
1767c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1768c577b8a1SJoseph Chan 	.ops = {
1769c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
17700aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
17710aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1772c577b8a1SJoseph Chan 	},
1773c577b8a1SJoseph Chan };
1774c577b8a1SJoseph Chan 
1775bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1776c873cc25SLydia Wang 	.substreams = 2,
1777bc9b5623STakashi Iwai 	.channels_min = 2,
1778bc9b5623STakashi Iwai 	.channels_max = 8,
1779bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1780bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1781bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1782bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1783bc9b5623STakashi Iwai 	 */
1784bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1785bc9b5623STakashi Iwai 	.ops = {
1786bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1787c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1788c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1789bc9b5623STakashi Iwai 	},
1790bc9b5623STakashi Iwai };
1791bc9b5623STakashi Iwai 
1792c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1793c577b8a1SJoseph Chan 	.substreams = 2,
1794c577b8a1SJoseph Chan 	.channels_min = 2,
1795c577b8a1SJoseph Chan 	.channels_max = 2,
1796c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1797c577b8a1SJoseph Chan 	.ops = {
1798c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1799c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1800c577b8a1SJoseph Chan 	},
1801c577b8a1SJoseph Chan };
1802c577b8a1SJoseph Chan 
1803c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1804c577b8a1SJoseph Chan 	.substreams = 1,
1805c577b8a1SJoseph Chan 	.channels_min = 2,
1806c577b8a1SJoseph Chan 	.channels_max = 2,
1807c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1808c577b8a1SJoseph Chan 	.ops = {
1809c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
18106b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
18119da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
18129da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1813c577b8a1SJoseph Chan 	},
1814c577b8a1SJoseph Chan };
1815c577b8a1SJoseph Chan 
1816c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1817c577b8a1SJoseph Chan 	.substreams = 1,
1818c577b8a1SJoseph Chan 	.channels_min = 2,
1819c577b8a1SJoseph Chan 	.channels_max = 2,
1820c577b8a1SJoseph Chan };
1821c577b8a1SJoseph Chan 
1822c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1823c577b8a1SJoseph Chan {
1824c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1825c577b8a1SJoseph Chan 	int err;
1826c577b8a1SJoseph Chan 	int i;
1827c577b8a1SJoseph Chan 
1828c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1829c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1830c577b8a1SJoseph Chan 		if (err < 0)
1831c577b8a1SJoseph Chan 			return err;
1832c577b8a1SJoseph Chan 	}
1833c577b8a1SJoseph Chan 
1834c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1835c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1836c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1837c577b8a1SJoseph Chan 		if (err < 0)
1838c577b8a1SJoseph Chan 			return err;
18399a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
18409a08160bSTakashi Iwai 						    &spec->multiout);
18419a08160bSTakashi Iwai 		if (err < 0)
18429a08160bSTakashi Iwai 			return err;
18439a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1844c577b8a1SJoseph Chan 	}
1845c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1846c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1847c577b8a1SJoseph Chan 		if (err < 0)
1848c577b8a1SJoseph Chan 			return err;
1849c577b8a1SJoseph Chan 	}
185017314379SLydia Wang 
185117314379SLydia Wang 	/* init power states */
185217314379SLydia Wang 	set_jack_power_state(codec);
185317314379SLydia Wang 	analog_low_current_mode(codec, 1);
185417314379SLydia Wang 
1855603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1856c577b8a1SJoseph Chan 	return 0;
1857c577b8a1SJoseph Chan }
1858c577b8a1SJoseph Chan 
1859c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1860c577b8a1SJoseph Chan {
1861c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1862c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1863c577b8a1SJoseph Chan 
1864c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1865c577b8a1SJoseph Chan 	codec->pcm_info = info;
1866c577b8a1SJoseph Chan 
1867c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1868*377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1869*377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1870*377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1871*377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1872c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1873c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1874c577b8a1SJoseph Chan 
1875c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1876c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1877c577b8a1SJoseph Chan 
1878c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1879c577b8a1SJoseph Chan 		codec->num_pcms++;
1880c577b8a1SJoseph Chan 		info++;
1881c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
18827ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1883c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1884c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1885c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1886c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1887c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1888c577b8a1SJoseph Chan 		}
1889c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1890c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1891c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1892c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1893c577b8a1SJoseph Chan 				spec->dig_in_nid;
1894c577b8a1SJoseph Chan 		}
1895c577b8a1SJoseph Chan 	}
1896c577b8a1SJoseph Chan 
1897c577b8a1SJoseph Chan 	return 0;
1898c577b8a1SJoseph Chan }
1899c577b8a1SJoseph Chan 
1900c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1901c577b8a1SJoseph Chan {
1902c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1903c577b8a1SJoseph Chan 
1904c577b8a1SJoseph Chan 	if (!spec)
1905c577b8a1SJoseph Chan 		return;
1906c577b8a1SJoseph Chan 
1907603c4019STakashi Iwai 	via_free_kctls(codec);
19081f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1909c577b8a1SJoseph Chan 	kfree(codec->spec);
1910c577b8a1SJoseph Chan }
1911c577b8a1SJoseph Chan 
191269e52a80SHarald Welte /* mute internal speaker if HP is plugged */
191369e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
191469e52a80SHarald Welte {
1915dcf34c8cSLydia Wang 	unsigned int present = 0;
191669e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
191769e52a80SHarald Welte 
191869e52a80SHarald Welte 	present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
191969e52a80SHarald Welte 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1920dcf34c8cSLydia Wang 
1921dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1922dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
1923dcf34c8cSLydia Wang 		/* auto mute */
1924dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
1925dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1926dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1927dcf34c8cSLydia Wang 		/* notify change */
1928dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
1929dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1930dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
1931dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1932dcf34c8cSLydia Wang 			       &id);
1933dcf34c8cSLydia Wang 	}
193469e52a80SHarald Welte }
193569e52a80SHarald Welte 
1936f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1937f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1938f3db423dSLydia Wang {
1939f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1940f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1941f3db423dSLydia Wang 
1942f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1943f3db423dSLydia Wang 		return;
1944f3db423dSLydia Wang 
1945f3db423dSLydia Wang 	lineout_present = snd_hda_codec_read(
1946f3db423dSLydia Wang 		codec, spec->autocfg.line_out_pins[0], 0,
1947f3db423dSLydia Wang 		AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1948f3db423dSLydia Wang 
1949f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1950f3db423dSLydia Wang 	if (lineout_present) {
1951f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1952f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1953f3db423dSLydia Wang 		return;
1954f3db423dSLydia Wang 	}
1955f3db423dSLydia Wang 
1956f3db423dSLydia Wang 	hp_present = snd_hda_codec_read(
1957f3db423dSLydia Wang 		codec, spec->autocfg.hp_pins[0], 0,
1958f3db423dSLydia Wang 		AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
1959f3db423dSLydia Wang 
1960f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
1961f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1962f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1963f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
1964f3db423dSLydia Wang }
1965f3db423dSLydia Wang 
196669e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
196769e52a80SHarald Welte {
196869e52a80SHarald Welte 	unsigned int gpio_data;
196969e52a80SHarald Welte 	unsigned int vol_counter;
197069e52a80SHarald Welte 	unsigned int vol;
197169e52a80SHarald Welte 	unsigned int master_vol;
197269e52a80SHarald Welte 
197369e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
197469e52a80SHarald Welte 
197569e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
197669e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
197769e52a80SHarald Welte 
197869e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
197969e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
198069e52a80SHarald Welte 
198169e52a80SHarald Welte 	vol = vol_counter & 0x1F;
198269e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
198369e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
198469e52a80SHarald Welte 					AC_AMP_GET_INPUT);
198569e52a80SHarald Welte 
198669e52a80SHarald Welte 	if (gpio_data == 0x02) {
198769e52a80SHarald Welte 		/* unmute line out */
198869e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
198969e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
199069e52a80SHarald Welte 
199169e52a80SHarald Welte 		if (vol_counter & 0x20) {
199269e52a80SHarald Welte 			/* decrease volume */
199369e52a80SHarald Welte 			if (vol > master_vol)
199469e52a80SHarald Welte 				vol = master_vol;
199569e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
199669e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
199769e52a80SHarald Welte 						 master_vol-vol);
199869e52a80SHarald Welte 		} else {
199969e52a80SHarald Welte 			/* increase volume */
200069e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
200169e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
200269e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
200369e52a80SHarald Welte 					  (master_vol+vol));
200469e52a80SHarald Welte 		}
200569e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
200669e52a80SHarald Welte 		/* mute line out */
200769e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
200869e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
200969e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
201069e52a80SHarald Welte 					 HDA_AMP_MUTE);
201169e52a80SHarald Welte 	}
201269e52a80SHarald Welte }
201369e52a80SHarald Welte 
201425eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
201525eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
201625eaba2fSLydia Wang {
201725eaba2fSLydia Wang 	unsigned int hp_present;
201825eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
201925eaba2fSLydia Wang 
2020ab6734e7SLydia Wang 	if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
202125eaba2fSLydia Wang 		return;
202225eaba2fSLydia Wang 
202325eaba2fSLydia Wang 	hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
202425eaba2fSLydia Wang 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
202525eaba2fSLydia Wang 
202625eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
202725eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
202825eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
202925eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
203025eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
203125eaba2fSLydia Wang 		/* notify change */
203225eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
203325eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
203425eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
203525eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
203625eaba2fSLydia Wang 			       &id);
203725eaba2fSLydia Wang 	}
203825eaba2fSLydia Wang }
203925eaba2fSLydia Wang 
204025eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
204125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
204225eaba2fSLydia Wang {
204325eaba2fSLydia Wang 	unsigned int hp_present, present = 0;
204425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
204525eaba2fSLydia Wang 	int i;
204625eaba2fSLydia Wang 
204725eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
204825eaba2fSLydia Wang 		return;
204925eaba2fSLydia Wang 
205025eaba2fSLydia Wang 	hp_present = snd_hda_codec_read(codec, spec->autocfg.hp_pins[0], 0,
205125eaba2fSLydia Wang 					AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
205225eaba2fSLydia Wang 
205325eaba2fSLydia Wang 	present = snd_hda_codec_read(codec, spec->autocfg.line_out_pins[0], 0,
205425eaba2fSLydia Wang 				     AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
205525eaba2fSLydia Wang 
205625eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
205725eaba2fSLydia Wang 		/* Mute Line-Outs */
205825eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
205925eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
206025eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
206125eaba2fSLydia Wang 				HDA_OUTPUT, 0,
206225eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
206325eaba2fSLydia Wang 		if (hp_present)
206425eaba2fSLydia Wang 			present = hp_present;
206525eaba2fSLydia Wang 	}
206625eaba2fSLydia Wang 	/* Speakers */
206725eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
206825eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
206925eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
207025eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
207125eaba2fSLydia Wang }
207225eaba2fSLydia Wang 
207325eaba2fSLydia Wang 
207469e52a80SHarald Welte /* unsolicited event for jack sensing */
207569e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
207669e52a80SHarald Welte 				  unsigned int res)
207769e52a80SHarald Welte {
207869e52a80SHarald Welte 	res >>= 26;
2079a34df19aSLydia Wang 	if (res & VIA_HP_EVENT)
208069e52a80SHarald Welte 		via_hp_automute(codec);
2081a34df19aSLydia Wang 	if (res & VIA_GPIO_EVENT)
208269e52a80SHarald Welte 		via_gpio_control(codec);
2083a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
2084a34df19aSLydia Wang 		set_jack_power_state(codec);
2085f3db423dSLydia Wang 	if (res & VIA_MONO_EVENT)
2086f3db423dSLydia Wang 		via_mono_automute(codec);
208725eaba2fSLydia Wang 	if (res & VIA_SPEAKER_EVENT)
208825eaba2fSLydia Wang 		via_speaker_automute(codec);
208925eaba2fSLydia Wang 	if (res & VIA_BIND_HP_EVENT)
209025eaba2fSLydia Wang 		via_hp_bind_automute(codec);
209169e52a80SHarald Welte }
209269e52a80SHarald Welte 
2093c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
2094c577b8a1SJoseph Chan {
2095c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
209669e52a80SHarald Welte 	int i;
209769e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
209869e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
209969e52a80SHarald Welte 
2100518bf3baSLydia Wang 	spec->codec_type = get_codec_type(codec);
2101518bf3baSLydia Wang 	if (spec->codec_type == VT1708BCE)
2102518bf3baSLydia Wang 		spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
2103518bf3baSLydia Wang 					       same */
2104f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
2105f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
210655d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
210755d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2108f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
210912b74c80STakashi Iwai 					    PIN_OUT);
211055d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2111f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
2112f7278fd0SJosepch Chan 		}
211312b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
211412b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
211512b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2116f7278fd0SJosepch Chan 
21179da29271STakashi Iwai 	/* assign slave outs */
21189da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
21199da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
21205691ec7fSHarald Welte 
2121c577b8a1SJoseph Chan 	return 0;
2122c577b8a1SJoseph Chan }
2123c577b8a1SJoseph Chan 
21241f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
21251f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
21261f2e99feSLydia Wang {
21271f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
21281f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
21291f2e99feSLydia Wang 	return 0;
21301f2e99feSLydia Wang }
21311f2e99feSLydia Wang #endif
21321f2e99feSLydia Wang 
2133cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2134cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
2135cb53c626STakashi Iwai {
2136cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
2137cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
2138cb53c626STakashi Iwai }
2139cb53c626STakashi Iwai #endif
2140cb53c626STakashi Iwai 
2141c577b8a1SJoseph Chan /*
2142c577b8a1SJoseph Chan  */
2143c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
2144c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
2145c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
2146c577b8a1SJoseph Chan 	.init = via_init,
2147c577b8a1SJoseph Chan 	.free = via_free,
21481f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
21491f2e99feSLydia Wang 	.suspend = via_suspend,
21501f2e99feSLydia Wang #endif
2151cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2152cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
2153cb53c626STakashi Iwai #endif
2154c577b8a1SJoseph Chan };
2155c577b8a1SJoseph Chan 
2156c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
2157c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
2158c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2159c577b8a1SJoseph Chan {
2160c577b8a1SJoseph Chan 	int i;
2161c577b8a1SJoseph Chan 	hda_nid_t nid;
2162c577b8a1SJoseph Chan 
2163c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
2164c577b8a1SJoseph Chan 
2165c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2166c577b8a1SJoseph Chan 
2167c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
2168c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2169c577b8a1SJoseph Chan 		if (nid) {
2170c577b8a1SJoseph Chan 			/* config dac list */
2171c577b8a1SJoseph Chan 			switch (i) {
2172c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
2173c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
2174c577b8a1SJoseph Chan 				break;
2175c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
2176c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
2177c577b8a1SJoseph Chan 				break;
2178c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
2179fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
2180c577b8a1SJoseph Chan 				break;
2181c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
2182fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
2183c577b8a1SJoseph Chan 				break;
2184c577b8a1SJoseph Chan 			}
2185c577b8a1SJoseph Chan 		}
2186c577b8a1SJoseph Chan 	}
2187c577b8a1SJoseph Chan 
2188c577b8a1SJoseph Chan 	return 0;
2189c577b8a1SJoseph Chan }
2190c577b8a1SJoseph Chan 
2191c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2192c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
2193c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2194c577b8a1SJoseph Chan {
2195c577b8a1SJoseph Chan 	char name[32];
2196c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
21979645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
2198c577b8a1SJoseph Chan 	int i, err;
2199c577b8a1SJoseph Chan 
2200c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2201c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2202c577b8a1SJoseph Chan 
2203c577b8a1SJoseph Chan 		if (!nid)
2204c577b8a1SJoseph Chan 			continue;
2205c577b8a1SJoseph Chan 
22069645c203SLydia Wang 		nid_vol = nid_vols[i];
2207c577b8a1SJoseph Chan 
2208c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2209c577b8a1SJoseph Chan 			/* Center/LFE */
2210c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2211c577b8a1SJoseph Chan 					"Center Playback Volume",
2212f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2213f7278fd0SJosepch Chan 							    HDA_OUTPUT));
2214c577b8a1SJoseph Chan 			if (err < 0)
2215c577b8a1SJoseph Chan 				return err;
2216c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2217c577b8a1SJoseph Chan 					      "LFE Playback Volume",
2218f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2219f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2220c577b8a1SJoseph Chan 			if (err < 0)
2221c577b8a1SJoseph Chan 				return err;
2222c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2223c577b8a1SJoseph Chan 					      "Center Playback Switch",
2224f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2225f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2226c577b8a1SJoseph Chan 			if (err < 0)
2227c577b8a1SJoseph Chan 				return err;
2228c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2229c577b8a1SJoseph Chan 					      "LFE Playback Switch",
2230f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2231f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2232c577b8a1SJoseph Chan 			if (err < 0)
2233c577b8a1SJoseph Chan 				return err;
2234c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
2235c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
2236c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2237c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
22389645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2239f7278fd0SJosepch Chan 								  HDA_INPUT));
2240c577b8a1SJoseph Chan 			if (err < 0)
2241c577b8a1SJoseph Chan 				return err;
2242c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2243c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
22449645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2245f7278fd0SJosepch Chan 								  HDA_INPUT));
2246c577b8a1SJoseph Chan 			if (err < 0)
2247c577b8a1SJoseph Chan 				return err;
2248c577b8a1SJoseph Chan 
2249c577b8a1SJoseph Chan 			/* add control to PW3 */
2250c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2251c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2252f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2253f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2254c577b8a1SJoseph Chan 			if (err < 0)
2255c577b8a1SJoseph Chan 				return err;
2256c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2257c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2258f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2259f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2260c577b8a1SJoseph Chan 			if (err < 0)
2261c577b8a1SJoseph Chan 				return err;
2262c577b8a1SJoseph Chan 		} else {
2263c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2264c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2265f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2266f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2267c577b8a1SJoseph Chan 			if (err < 0)
2268c577b8a1SJoseph Chan 				return err;
2269c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2270c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2271f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2272f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2273c577b8a1SJoseph Chan 			if (err < 0)
2274c577b8a1SJoseph Chan 				return err;
2275c577b8a1SJoseph Chan 		}
2276c577b8a1SJoseph Chan 	}
2277c577b8a1SJoseph Chan 
2278c577b8a1SJoseph Chan 	return 0;
2279c577b8a1SJoseph Chan }
2280c577b8a1SJoseph Chan 
22810aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
22820aa62aefSHarald Welte {
22830aa62aefSHarald Welte 	int i;
22840aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
22850aa62aefSHarald Welte 	static const char *texts[] = { "OFF", "ON", NULL};
22860aa62aefSHarald Welte 
22870aa62aefSHarald Welte 	/* for hp mode select */
22880aa62aefSHarald Welte 	i = 0;
22890aa62aefSHarald Welte 	while (texts[i] != NULL) {
22900aa62aefSHarald Welte 		imux->items[imux->num_items].label =  texts[i];
22910aa62aefSHarald Welte 		imux->items[imux->num_items].index = i;
22920aa62aefSHarald Welte 		imux->num_items++;
22930aa62aefSHarald Welte 		i++;
22940aa62aefSHarald Welte 	}
22950aa62aefSHarald Welte 
22960aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
22970aa62aefSHarald Welte }
22980aa62aefSHarald Welte 
2299c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2300c577b8a1SJoseph Chan {
2301c577b8a1SJoseph Chan 	int err;
2302c577b8a1SJoseph Chan 
2303c577b8a1SJoseph Chan 	if (!pin)
2304c577b8a1SJoseph Chan 		return 0;
2305c577b8a1SJoseph Chan 
2306c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
2307cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2308c577b8a1SJoseph Chan 
2309c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2310c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2311c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2312c577b8a1SJoseph Chan 	if (err < 0)
2313c577b8a1SJoseph Chan 		return err;
2314c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2315c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2316c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2317c577b8a1SJoseph Chan 	if (err < 0)
2318c577b8a1SJoseph Chan 		return err;
2319c577b8a1SJoseph Chan 
23200aa62aefSHarald Welte 	create_hp_imux(spec);
23210aa62aefSHarald Welte 
2322c577b8a1SJoseph Chan 	return 0;
2323c577b8a1SJoseph Chan }
2324c577b8a1SJoseph Chan 
2325c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2326c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
2327c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2328c577b8a1SJoseph Chan {
2329c577b8a1SJoseph Chan 	static char *labels[] = {
2330c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2331c577b8a1SJoseph Chan 	};
23320aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2333c577b8a1SJoseph Chan 	int i, err, idx = 0;
2334c577b8a1SJoseph Chan 
2335c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2336c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
2337c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
2338c577b8a1SJoseph Chan 	imux->num_items++;
2339c577b8a1SJoseph Chan 
2340c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
2341c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
2342c577b8a1SJoseph Chan 			continue;
2343c577b8a1SJoseph Chan 
2344c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
2345c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
2346c577b8a1SJoseph Chan 			idx = 2;
2347c577b8a1SJoseph Chan 			break;
2348c577b8a1SJoseph Chan 
2349c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
2350c577b8a1SJoseph Chan 			idx = 3;
2351c577b8a1SJoseph Chan 			break;
2352c577b8a1SJoseph Chan 
2353c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
2354c577b8a1SJoseph Chan 			idx = 4;
2355c577b8a1SJoseph Chan 			break;
2356c577b8a1SJoseph Chan 
2357c577b8a1SJoseph Chan 		case 0x24: /* CD */
2358c577b8a1SJoseph Chan 			idx = 1;
2359c577b8a1SJoseph Chan 			break;
2360c577b8a1SJoseph Chan 		}
23619510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x17);
2362c577b8a1SJoseph Chan 		if (err < 0)
2363c577b8a1SJoseph Chan 			return err;
2364c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
2365c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
2366c577b8a1SJoseph Chan 		imux->num_items++;
2367c577b8a1SJoseph Chan 	}
2368c577b8a1SJoseph Chan 	return 0;
2369c577b8a1SJoseph Chan }
2370c577b8a1SJoseph Chan 
2371cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2372cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2373cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2374cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2375cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2376cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2377cb53c626STakashi Iwai 	{ } /* end */
2378cb53c626STakashi Iwai };
2379cb53c626STakashi Iwai #endif
2380cb53c626STakashi Iwai 
238176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
238276d9b0ddSHarald Welte {
238376d9b0ddSHarald Welte 	unsigned int def_conf;
238476d9b0ddSHarald Welte 	unsigned char seqassoc;
238576d9b0ddSHarald Welte 
23862f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
238776d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
238876d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
238982ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
239082ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
239176d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
23922f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
239376d9b0ddSHarald Welte 	}
239476d9b0ddSHarald Welte 
239576d9b0ddSHarald Welte 	return;
239676d9b0ddSHarald Welte }
239776d9b0ddSHarald Welte 
23981f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
23991f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
24001f2e99feSLydia Wang {
24011f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24021f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
24031f2e99feSLydia Wang 
24041f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24051f2e99feSLydia Wang 		return 0;
24061f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
24071f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
24081f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
24091f2e99feSLydia Wang 	return 0;
24101f2e99feSLydia Wang }
24111f2e99feSLydia Wang 
24121f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
24131f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
24141f2e99feSLydia Wang {
24151f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24161f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
24171f2e99feSLydia Wang 	int change;
24181f2e99feSLydia Wang 
24191f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24201f2e99feSLydia Wang 		return 0;
24211f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
24221f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
24231f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
24241f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
24251f2e99feSLydia Wang 		mute_aa_path(codec, 1);
24261f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
24271f2e99feSLydia Wang 	}
24281f2e99feSLydia Wang 	return change;
24291f2e99feSLydia Wang }
24301f2e99feSLydia Wang 
24311f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
24321f2e99feSLydia Wang 	{
24331f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24341f2e99feSLydia Wang 		.name = "Jack Detect",
24351f2e99feSLydia Wang 		.count = 1,
24361f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
24371f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
24381f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
24391f2e99feSLydia Wang 	},
24401f2e99feSLydia Wang 	{} /* end */
24411f2e99feSLydia Wang };
24421f2e99feSLydia Wang 
2443c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2444c577b8a1SJoseph Chan {
2445c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2446c577b8a1SJoseph Chan 	int err;
2447c577b8a1SJoseph Chan 
244876d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
244976d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
245076d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
245176d9b0ddSHarald Welte 
2452c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2453c577b8a1SJoseph Chan 	if (err < 0)
2454c577b8a1SJoseph Chan 		return err;
2455c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2456c577b8a1SJoseph Chan 	if (err < 0)
2457c577b8a1SJoseph Chan 		return err;
2458c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2459c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2460c577b8a1SJoseph Chan 
2461c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2462c577b8a1SJoseph Chan 	if (err < 0)
2463c577b8a1SJoseph Chan 		return err;
2464c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2465c577b8a1SJoseph Chan 	if (err < 0)
2466c577b8a1SJoseph Chan 		return err;
2467c577b8a1SJoseph Chan 	err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
2468c577b8a1SJoseph Chan 	if (err < 0)
2469c577b8a1SJoseph Chan 		return err;
24701f2e99feSLydia Wang 	/* add jack detect on/off control */
24711f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
24721f2e99feSLydia Wang 	if (err < 0)
24731f2e99feSLydia Wang 		return err;
2474c577b8a1SJoseph Chan 
2475c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2476c577b8a1SJoseph Chan 
24770852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2478c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
247955d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2480c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2481c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2482c577b8a1SJoseph Chan 
2483603c4019STakashi Iwai 	if (spec->kctls.list)
2484603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2485c577b8a1SJoseph Chan 
248669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2487c577b8a1SJoseph Chan 
24880aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
24890aa62aefSHarald Welte 
2490f8fdd495SHarald Welte 	if (spec->hp_mux)
24910aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
2492c577b8a1SJoseph Chan 
24931564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
2494c577b8a1SJoseph Chan 	return 1;
2495c577b8a1SJoseph Chan }
2496c577b8a1SJoseph Chan 
2497c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2498c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2499c577b8a1SJoseph Chan {
250025eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
250125eaba2fSLydia Wang 
2502c577b8a1SJoseph Chan 	via_init(codec);
2503c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2504c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2505c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
2506ab6734e7SLydia Wang 	if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
250725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
250825eaba2fSLydia Wang 	} else {
250925eaba2fSLydia Wang 		via_hp_automute(codec);
251025eaba2fSLydia Wang 		via_speaker_automute(codec);
251125eaba2fSLydia Wang 	}
251225eaba2fSLydia Wang 
2513c577b8a1SJoseph Chan 	return 0;
2514c577b8a1SJoseph Chan }
2515c577b8a1SJoseph Chan 
25161f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
25171f2e99feSLydia Wang {
25181f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
25191f2e99feSLydia Wang 					     vt1708_hp_work.work);
25201f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25211f2e99feSLydia Wang 		return;
25221f2e99feSLydia Wang 	/* if jack state toggled */
25231f2e99feSLydia Wang 	if (spec->vt1708_hp_present
25241f2e99feSLydia Wang 	    != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0,
25251f2e99feSLydia Wang 				   AC_VERB_GET_PIN_SENSE, 0) >> 31)) {
25261f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
25271f2e99feSLydia Wang 		via_hp_automute(spec->codec);
25281f2e99feSLydia Wang 	}
25291f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
25301f2e99feSLydia Wang }
25311f2e99feSLydia Wang 
2532337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2533337b9d02STakashi Iwai {
2534337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2535337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2536337b9d02STakashi Iwai 	unsigned int type;
2537337b9d02STakashi Iwai 	int i, n;
2538337b9d02STakashi Iwai 
2539337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2540337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2541337b9d02STakashi Iwai 		while (nid) {
2542a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
25431c55d521STakashi Iwai 			if (type == AC_WID_PIN)
25441c55d521STakashi Iwai 				break;
2545337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2546337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2547337b9d02STakashi Iwai 			if (n <= 0)
2548337b9d02STakashi Iwai 				break;
2549337b9d02STakashi Iwai 			if (n > 1) {
2550337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2551337b9d02STakashi Iwai 				break;
2552337b9d02STakashi Iwai 			}
2553337b9d02STakashi Iwai 			nid = conn[0];
2554337b9d02STakashi Iwai 		}
2555337b9d02STakashi Iwai 	}
25561c55d521STakashi Iwai 	return 0;
2557337b9d02STakashi Iwai }
2558337b9d02STakashi Iwai 
2559c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2560c577b8a1SJoseph Chan {
2561c577b8a1SJoseph Chan 	struct via_spec *spec;
2562c577b8a1SJoseph Chan 	int err;
2563c577b8a1SJoseph Chan 
2564c577b8a1SJoseph Chan 	/* create a codec specific record */
2565eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2566c577b8a1SJoseph Chan 	if (spec == NULL)
2567c577b8a1SJoseph Chan 		return -ENOMEM;
2568c577b8a1SJoseph Chan 
2569c577b8a1SJoseph Chan 	codec->spec = spec;
2570c577b8a1SJoseph Chan 
2571c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2572c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2573c577b8a1SJoseph Chan 	if (err < 0) {
2574c577b8a1SJoseph Chan 		via_free(codec);
2575c577b8a1SJoseph Chan 		return err;
2576c577b8a1SJoseph Chan 	} else if (!err) {
2577c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2578c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2579c577b8a1SJoseph Chan 	}
2580c577b8a1SJoseph Chan 
2581c577b8a1SJoseph Chan 
2582c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2583c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2584bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2585bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2586bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2587c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2588c577b8a1SJoseph Chan 
2589c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2590c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2591c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2592c577b8a1SJoseph Chan 
2593c577b8a1SJoseph Chan 
2594c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2595c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2596c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
25970f67a611STakashi Iwai 		get_mux_nids(codec);
2598c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2599c577b8a1SJoseph Chan 		spec->num_mixers++;
2600c577b8a1SJoseph Chan 	}
2601c577b8a1SJoseph Chan 
2602c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2603c577b8a1SJoseph Chan 
2604c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2605cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2606cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2607cb53c626STakashi Iwai #endif
26081f2e99feSLydia Wang 	spec->codec = codec;
26091f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2610c577b8a1SJoseph Chan 	return 0;
2611c577b8a1SJoseph Chan }
2612c577b8a1SJoseph Chan 
2613c577b8a1SJoseph Chan /* capture mixer elements */
2614c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2615c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2616c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2617c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2618c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2619c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2620c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2621c577b8a1SJoseph Chan 	{
2622c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2623c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2624c577b8a1SJoseph Chan 		 * So call somewhat different..
2625c577b8a1SJoseph Chan 		 */
2626c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2627c577b8a1SJoseph Chan 		.name = "Input Source",
2628c577b8a1SJoseph Chan 		.count = 1,
2629c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2630c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2631c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2632c577b8a1SJoseph Chan 	},
2633c577b8a1SJoseph Chan 	{ } /* end */
2634c577b8a1SJoseph Chan };
2635c577b8a1SJoseph Chan 
263669e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2637a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2638a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
263969e52a80SHarald Welte 	{ }
264069e52a80SHarald Welte };
264169e52a80SHarald Welte 
2642c577b8a1SJoseph Chan /*
2643c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2644c577b8a1SJoseph Chan  */
2645c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2646c577b8a1SJoseph Chan 	/*
2647c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2648c577b8a1SJoseph Chan 	 */
2649c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2650c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2651c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2652c577b8a1SJoseph Chan 
2653c577b8a1SJoseph Chan 
2654f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2655c577b8a1SJoseph Chan 	 * mixer widget
2656c577b8a1SJoseph Chan 	 */
2657c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2658f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2659f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2660f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2661f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2662f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2663c577b8a1SJoseph Chan 
2664c577b8a1SJoseph Chan 	/*
2665c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2666c577b8a1SJoseph Chan 	 */
2667c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2668c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2669c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2670c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2671c577b8a1SJoseph Chan 
2672c577b8a1SJoseph Chan 	/*
2673c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2674c577b8a1SJoseph Chan 	 */
2675c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2676c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2677c577b8a1SJoseph Chan 
2678bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2679bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2680c577b8a1SJoseph Chan 	/* PW9 Output enable */
2681c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2682c577b8a1SJoseph Chan 	{ }
2683c577b8a1SJoseph Chan };
2684c577b8a1SJoseph Chan 
2685c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2686c577b8a1SJoseph Chan 	.substreams = 1,
2687c577b8a1SJoseph Chan 	.channels_min = 2,
2688c577b8a1SJoseph Chan 	.channels_max = 10,
2689c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2690c577b8a1SJoseph Chan 	.ops = {
2691c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2692c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2693c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2694c577b8a1SJoseph Chan 	},
2695c577b8a1SJoseph Chan };
2696c577b8a1SJoseph Chan 
2697c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2698c577b8a1SJoseph Chan 	.substreams = 1,
2699c577b8a1SJoseph Chan 	.channels_min = 2,
2700c577b8a1SJoseph Chan 	.channels_max = 6,
2701c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2702c577b8a1SJoseph Chan 	.ops = {
2703c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2704c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2705c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2706c577b8a1SJoseph Chan 	},
2707c577b8a1SJoseph Chan };
2708c577b8a1SJoseph Chan 
2709c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2710c577b8a1SJoseph Chan 	.substreams = 2,
2711c577b8a1SJoseph Chan 	.channels_min = 2,
2712c577b8a1SJoseph Chan 	.channels_max = 2,
2713c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2714c577b8a1SJoseph Chan 	.ops = {
2715c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2716c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2717c577b8a1SJoseph Chan 	},
2718c577b8a1SJoseph Chan };
2719c577b8a1SJoseph Chan 
2720c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2721c577b8a1SJoseph Chan 	.substreams = 1,
2722c577b8a1SJoseph Chan 	.channels_min = 2,
2723c577b8a1SJoseph Chan 	.channels_max = 2,
2724c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2725c577b8a1SJoseph Chan 	.ops = {
2726c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2727c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2728c577b8a1SJoseph Chan 	},
2729c577b8a1SJoseph Chan };
2730c577b8a1SJoseph Chan 
2731c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2732c577b8a1SJoseph Chan 	.substreams = 1,
2733c577b8a1SJoseph Chan 	.channels_min = 2,
2734c577b8a1SJoseph Chan 	.channels_max = 2,
2735c577b8a1SJoseph Chan };
2736c577b8a1SJoseph Chan 
2737c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2738c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2739c577b8a1SJoseph Chan {
2740c577b8a1SJoseph Chan 	int i;
2741c577b8a1SJoseph Chan 	hda_nid_t nid;
2742c577b8a1SJoseph Chan 
2743c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2744c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2745c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2746c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2747c577b8a1SJoseph Chan 
2748c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2749c577b8a1SJoseph Chan 
2750c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2751c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2752c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2753c577b8a1SJoseph Chan 			if (nid) {
2754c577b8a1SJoseph Chan 				/* config dac list */
2755c577b8a1SJoseph Chan 				switch (i) {
2756c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2757c577b8a1SJoseph Chan 					/* AOW0 */
2758c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2759c577b8a1SJoseph Chan 					break;
2760c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2761c577b8a1SJoseph Chan 					/* AOW2 */
2762c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2763c577b8a1SJoseph Chan 					break;
2764c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2765c577b8a1SJoseph Chan 					/* AOW3 */
2766fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2767c577b8a1SJoseph Chan 					break;
2768c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2769c577b8a1SJoseph Chan 					/* AOW1 */
2770fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2771c577b8a1SJoseph Chan 					break;
2772c577b8a1SJoseph Chan 				default:
2773c577b8a1SJoseph Chan 					break;
2774c577b8a1SJoseph Chan 				}
2775c577b8a1SJoseph Chan 			}
2776c577b8a1SJoseph Chan 		}
2777c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2778c577b8a1SJoseph Chan 
2779c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2780c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2781c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2782c577b8a1SJoseph Chan 			if (nid) {
2783c577b8a1SJoseph Chan 				/* config dac list */
2784c577b8a1SJoseph Chan 				switch (i) {
2785c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2786c577b8a1SJoseph Chan 					/* AOW0 */
2787c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2788c577b8a1SJoseph Chan 					break;
2789c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2790c577b8a1SJoseph Chan 					/* AOW2 */
2791c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2792c577b8a1SJoseph Chan 					break;
2793c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2794c577b8a1SJoseph Chan 					/* AOW1 */
2795c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2796c577b8a1SJoseph Chan 					break;
2797c577b8a1SJoseph Chan 				default:
2798c577b8a1SJoseph Chan 					break;
2799c577b8a1SJoseph Chan 				}
2800c577b8a1SJoseph Chan 			}
2801c577b8a1SJoseph Chan 		}
2802c577b8a1SJoseph Chan 	}
2803c577b8a1SJoseph Chan 
2804c577b8a1SJoseph Chan 	return 0;
2805c577b8a1SJoseph Chan }
2806c577b8a1SJoseph Chan 
2807c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2808c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2809c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2810c577b8a1SJoseph Chan {
2811c577b8a1SJoseph Chan 	char name[32];
2812c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
28134483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2814c577b8a1SJoseph Chan 	int i, err;
2815c577b8a1SJoseph Chan 
2816c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2817c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2818c577b8a1SJoseph Chan 
2819c577b8a1SJoseph Chan 		if (!nid)
2820c577b8a1SJoseph Chan 			continue;
2821c577b8a1SJoseph Chan 
28224483a2f5SLydia Wang 		nid_vol = nid_vols[i];
28234483a2f5SLydia Wang 
2824c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2825c577b8a1SJoseph Chan 			/* Center/LFE */
2826c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2827c577b8a1SJoseph Chan 					      "Center Playback Volume",
28284483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2829f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2830c577b8a1SJoseph Chan 			if (err < 0)
2831c577b8a1SJoseph Chan 				return err;
2832c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2833c577b8a1SJoseph Chan 					      "LFE Playback Volume",
28344483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2835f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2836c577b8a1SJoseph Chan 			if (err < 0)
2837c577b8a1SJoseph Chan 				return err;
2838c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2839c577b8a1SJoseph Chan 					      "Center Playback Switch",
28404483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2841f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2842c577b8a1SJoseph Chan 			if (err < 0)
2843c577b8a1SJoseph Chan 				return err;
2844c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2845c577b8a1SJoseph Chan 					      "LFE Playback Switch",
28464483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2847f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2848c577b8a1SJoseph Chan 			if (err < 0)
2849c577b8a1SJoseph Chan 				return err;
2850c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
28514483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2852c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2853c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
28544483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2855f7278fd0SJosepch Chan 								  HDA_INPUT));
2856c577b8a1SJoseph Chan 			if (err < 0)
2857c577b8a1SJoseph Chan 				return err;
2858c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2859c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
28604483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2861f7278fd0SJosepch Chan 								  HDA_INPUT));
2862c577b8a1SJoseph Chan 			if (err < 0)
2863c577b8a1SJoseph Chan 				return err;
2864c577b8a1SJoseph Chan 
2865c577b8a1SJoseph Chan 			/* add control to PW3 */
2866c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2867c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2868f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2869f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2870c577b8a1SJoseph Chan 			if (err < 0)
2871c577b8a1SJoseph Chan 				return err;
2872c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2873c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2874f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2875f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2876c577b8a1SJoseph Chan 			if (err < 0)
2877c577b8a1SJoseph Chan 				return err;
2878c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2879c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2880c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
28814483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2882f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2883c577b8a1SJoseph Chan 			if (err < 0)
2884c577b8a1SJoseph Chan 				return err;
2885c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2886c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
28874483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2888f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2889c577b8a1SJoseph Chan 			if (err < 0)
2890c577b8a1SJoseph Chan 				return err;
2891c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2892c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2893c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
28944483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2895f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2896c577b8a1SJoseph Chan 			if (err < 0)
2897c577b8a1SJoseph Chan 				return err;
2898c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2899c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
29004483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2901f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2902c577b8a1SJoseph Chan 			if (err < 0)
2903c577b8a1SJoseph Chan 				return err;
2904c577b8a1SJoseph Chan 		}
2905c577b8a1SJoseph Chan 	}
2906c577b8a1SJoseph Chan 
2907c577b8a1SJoseph Chan 	return 0;
2908c577b8a1SJoseph Chan }
2909c577b8a1SJoseph Chan 
2910c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2911c577b8a1SJoseph Chan {
2912c577b8a1SJoseph Chan 	int err;
2913c577b8a1SJoseph Chan 
2914c577b8a1SJoseph Chan 	if (!pin)
2915c577b8a1SJoseph Chan 		return 0;
2916c577b8a1SJoseph Chan 
2917c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2918c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2919c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2920c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2921cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2922c577b8a1SJoseph Chan 
2923c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2924c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2925c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2926c577b8a1SJoseph Chan 	if (err < 0)
2927c577b8a1SJoseph Chan 		return err;
2928c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2929c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2930c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2931c577b8a1SJoseph Chan 	if (err < 0)
2932c577b8a1SJoseph Chan 		return err;
2933c577b8a1SJoseph Chan 
2934c577b8a1SJoseph Chan 	return 0;
2935c577b8a1SJoseph Chan }
2936c577b8a1SJoseph Chan 
2937c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2938c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
2939c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2940c577b8a1SJoseph Chan {
2941c577b8a1SJoseph Chan 	static char *labels[] = {
2942c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2943c577b8a1SJoseph Chan 	};
29440aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2945c577b8a1SJoseph Chan 	int i, err, idx = 0;
2946c577b8a1SJoseph Chan 
2947c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2948c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
2949c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
2950c577b8a1SJoseph Chan 	imux->num_items++;
2951c577b8a1SJoseph Chan 
2952c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
2953c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
2954c577b8a1SJoseph Chan 			continue;
2955c577b8a1SJoseph Chan 
2956c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
2957c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
2958c577b8a1SJoseph Chan 			idx = 2;
2959c577b8a1SJoseph Chan 			break;
2960c577b8a1SJoseph Chan 
2961c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
2962c577b8a1SJoseph Chan 			idx = 3;
2963c577b8a1SJoseph Chan 			break;
2964c577b8a1SJoseph Chan 
2965c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
2966c577b8a1SJoseph Chan 			idx = 4;
2967c577b8a1SJoseph Chan 			break;
2968c577b8a1SJoseph Chan 
2969c577b8a1SJoseph Chan 		case 0x23: /* CD */
2970c577b8a1SJoseph Chan 			idx = 1;
2971c577b8a1SJoseph Chan 			break;
2972c577b8a1SJoseph Chan 		}
29739510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x18);
2974c577b8a1SJoseph Chan 		if (err < 0)
2975c577b8a1SJoseph Chan 			return err;
2976c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
2977c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
2978c577b8a1SJoseph Chan 		imux->num_items++;
2979c577b8a1SJoseph Chan 	}
2980c577b8a1SJoseph Chan 	return 0;
2981c577b8a1SJoseph Chan }
2982c577b8a1SJoseph Chan 
2983c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2984c577b8a1SJoseph Chan {
2985c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2986c577b8a1SJoseph Chan 	int err;
2987c577b8a1SJoseph Chan 
2988c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2989c577b8a1SJoseph Chan 	if (err < 0)
2990c577b8a1SJoseph Chan 		return err;
2991c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2992c577b8a1SJoseph Chan 	if (err < 0)
2993c577b8a1SJoseph Chan 		return err;
2994c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2995c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2996c577b8a1SJoseph Chan 
2997c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2998c577b8a1SJoseph Chan 	if (err < 0)
2999c577b8a1SJoseph Chan 		return err;
3000c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3001c577b8a1SJoseph Chan 	if (err < 0)
3002c577b8a1SJoseph Chan 		return err;
3003c577b8a1SJoseph Chan 	err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
3004c577b8a1SJoseph Chan 	if (err < 0)
3005c577b8a1SJoseph Chan 		return err;
3006c577b8a1SJoseph Chan 
3007c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3008c577b8a1SJoseph Chan 
30090852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3010c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
301155d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
3012c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
3013c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
3014c577b8a1SJoseph Chan 
3015603c4019STakashi Iwai 	if (spec->kctls.list)
3016603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3017c577b8a1SJoseph Chan 
30180aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
3019c577b8a1SJoseph Chan 
3020f8fdd495SHarald Welte 	if (spec->hp_mux)
3021f8fdd495SHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
3022f8fdd495SHarald Welte 
30231564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
3024c577b8a1SJoseph Chan 	return 1;
3025c577b8a1SJoseph Chan }
3026c577b8a1SJoseph Chan 
3027cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3028cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
3029cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
3030cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
3031cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
3032cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
3033cb53c626STakashi Iwai 	{ } /* end */
3034cb53c626STakashi Iwai };
3035cb53c626STakashi Iwai #endif
3036cb53c626STakashi Iwai 
3037c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
3038c577b8a1SJoseph Chan {
3039c577b8a1SJoseph Chan 	struct via_spec *spec;
3040c577b8a1SJoseph Chan 	int err;
3041c577b8a1SJoseph Chan 
3042c577b8a1SJoseph Chan 	/* create a codec specific record */
3043eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3044c577b8a1SJoseph Chan 	if (spec == NULL)
3045c577b8a1SJoseph Chan 		return -ENOMEM;
3046c577b8a1SJoseph Chan 
3047c577b8a1SJoseph Chan 	codec->spec = spec;
3048c577b8a1SJoseph Chan 
3049c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3050c577b8a1SJoseph Chan 	if (err < 0) {
3051c577b8a1SJoseph Chan 		via_free(codec);
3052c577b8a1SJoseph Chan 		return err;
3053c577b8a1SJoseph Chan 	} else if (!err) {
3054c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3055c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3056c577b8a1SJoseph Chan 	}
3057c577b8a1SJoseph Chan 
305869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
305969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3060c577b8a1SJoseph Chan 
3061c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3062c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
3063c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3064c577b8a1SJoseph Chan 
3065c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3066c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3067c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3068c577b8a1SJoseph Chan 
3069c577b8a1SJoseph Chan 
3070c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3071c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3072c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3073337b9d02STakashi Iwai 		get_mux_nids(codec);
3074c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3075c577b8a1SJoseph Chan 		spec->num_mixers++;
3076c577b8a1SJoseph Chan 	}
3077c577b8a1SJoseph Chan 
3078c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3079c577b8a1SJoseph Chan 
3080c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
308169e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3082cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3083cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3084cb53c626STakashi Iwai #endif
3085c577b8a1SJoseph Chan 
3086c577b8a1SJoseph Chan 	return 0;
3087c577b8a1SJoseph Chan }
3088c577b8a1SJoseph Chan /*
3089c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
3090c577b8a1SJoseph Chan  */
3091c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
3092c577b8a1SJoseph Chan 	/*
3093c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
3094c577b8a1SJoseph Chan 	 */
3095c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3096c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3097c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3098c577b8a1SJoseph Chan 
3099c577b8a1SJoseph Chan 
3100c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3101c577b8a1SJoseph Chan 	 * mixer widget
3102c577b8a1SJoseph Chan 	 */
3103c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3104c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3105c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3106c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3107c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3108c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3109c577b8a1SJoseph Chan 
3110c577b8a1SJoseph Chan 	/*
3111c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
3112c577b8a1SJoseph Chan 	 */
3113c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
3114c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3115c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3116c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3117c577b8a1SJoseph Chan 
3118c577b8a1SJoseph Chan 	/*
3119c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
3120c577b8a1SJoseph Chan 	 */
3121c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3122c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3123c577b8a1SJoseph Chan 
3124c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
3125c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
3126c577b8a1SJoseph Chan 	/* PW9 Output enable */
3127c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3128c577b8a1SJoseph Chan 	{ }
3129c577b8a1SJoseph Chan };
3130c577b8a1SJoseph Chan 
3131c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
3132c577b8a1SJoseph Chan {
3133c577b8a1SJoseph Chan 	struct via_spec *spec;
3134c577b8a1SJoseph Chan 	int err;
3135c577b8a1SJoseph Chan 
3136c577b8a1SJoseph Chan 	/* create a codec specific record */
3137eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3138c577b8a1SJoseph Chan 	if (spec == NULL)
3139c577b8a1SJoseph Chan 		return -ENOMEM;
3140c577b8a1SJoseph Chan 
3141c577b8a1SJoseph Chan 	codec->spec = spec;
3142c577b8a1SJoseph Chan 
3143c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3144c577b8a1SJoseph Chan 	if (err < 0) {
3145c577b8a1SJoseph Chan 		via_free(codec);
3146c577b8a1SJoseph Chan 		return err;
3147c577b8a1SJoseph Chan 	} else if (!err) {
3148c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3149c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3150c577b8a1SJoseph Chan 	}
3151c577b8a1SJoseph Chan 
315269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
315369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3154c577b8a1SJoseph Chan 
3155c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3156c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
3157c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3158c577b8a1SJoseph Chan 
3159c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3160c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3161c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3162c577b8a1SJoseph Chan 
3163c577b8a1SJoseph Chan 
3164c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3165c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3166c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3167337b9d02STakashi Iwai 		get_mux_nids(codec);
3168c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3169c577b8a1SJoseph Chan 		spec->num_mixers++;
3170c577b8a1SJoseph Chan 	}
3171c577b8a1SJoseph Chan 
3172c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3173c577b8a1SJoseph Chan 
3174c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
317569e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3176cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3177cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3178cb53c626STakashi Iwai #endif
3179f7278fd0SJosepch Chan 	return 0;
3180f7278fd0SJosepch Chan }
3181f7278fd0SJosepch Chan 
3182f7278fd0SJosepch Chan /* capture mixer elements */
3183f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
3184f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3185f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3186f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3187f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3188f7278fd0SJosepch Chan 	{
3189f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3190f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
3191f7278fd0SJosepch Chan 		 * So call somewhat different..
3192f7278fd0SJosepch Chan 		 */
3193f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
3194f7278fd0SJosepch Chan 		.name = "Input Source",
3195f7278fd0SJosepch Chan 		.count = 1,
3196f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
3197f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
3198f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
3199f7278fd0SJosepch Chan 	},
3200f7278fd0SJosepch Chan 	{ } /* end */
3201f7278fd0SJosepch Chan };
3202f7278fd0SJosepch Chan /*
3203f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
3204f7278fd0SJosepch Chan  */
3205f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
3206f7278fd0SJosepch Chan 	/*
3207f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3208f7278fd0SJosepch Chan 	 */
3209f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3210f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3211f7278fd0SJosepch Chan 
3212f7278fd0SJosepch Chan 
3213f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3214f7278fd0SJosepch Chan 	 * mixer widget
3215f7278fd0SJosepch Chan 	 */
3216f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3217f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3218f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3219f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3220f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3221f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3222f7278fd0SJosepch Chan 
3223f7278fd0SJosepch Chan 	/*
3224f7278fd0SJosepch Chan 	 * Set up output mixers
3225f7278fd0SJosepch Chan 	 */
3226f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3227f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3228f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3229f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3230f7278fd0SJosepch Chan 
3231f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
3232bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
3233f7278fd0SJosepch Chan 	/* PW9 Output enable */
3234f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3235f7278fd0SJosepch Chan 	/* PW10 Input enable */
3236f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3237f7278fd0SJosepch Chan 	{ }
3238f7278fd0SJosepch Chan };
3239f7278fd0SJosepch Chan 
3240f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
3241f7278fd0SJosepch Chan 	/*
3242f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3243f7278fd0SJosepch Chan 	 */
3244f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3245f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3246f7278fd0SJosepch Chan 
3247f7278fd0SJosepch Chan 
3248f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3249f7278fd0SJosepch Chan 	 * mixer widget
3250f7278fd0SJosepch Chan 	 */
3251f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3252f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3253f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3254f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3255f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3256f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3257f7278fd0SJosepch Chan 
3258f7278fd0SJosepch Chan 	/*
3259f7278fd0SJosepch Chan 	 * Set up output mixers
3260f7278fd0SJosepch Chan 	 */
3261f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3262f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3263f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3264f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3265f7278fd0SJosepch Chan 
3266f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
3267f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3268f7278fd0SJosepch Chan 	/* PW9 Output enable */
3269f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3270f7278fd0SJosepch Chan 	/* PW10 Input enable */
3271f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3272f7278fd0SJosepch Chan 	{ }
3273f7278fd0SJosepch Chan };
3274f7278fd0SJosepch Chan 
327569e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
3276a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3277a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3278a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3279a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3280a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3281a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3282a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3283a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3284a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
328569e52a80SHarald Welte 	{ }
328669e52a80SHarald Welte };
328769e52a80SHarald Welte 
328817314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
328917314379SLydia Wang 			      struct hda_codec *codec,
329017314379SLydia Wang 			      struct snd_pcm_substream *substream)
329117314379SLydia Wang {
329217314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
329317314379SLydia Wang 		&& substream->ref_count == 0;
329417314379SLydia Wang 
329517314379SLydia Wang 	analog_low_current_mode(codec, idle);
329617314379SLydia Wang 	return 0;
329717314379SLydia Wang }
329817314379SLydia Wang 
3299f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
33000aa62aefSHarald Welte 	.substreams = 2,
3301f7278fd0SJosepch Chan 	.channels_min = 2,
3302f7278fd0SJosepch Chan 	.channels_max = 8,
3303f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3304f7278fd0SJosepch Chan 	.ops = {
3305f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33060aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
330717314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
330817314379SLydia Wang 		.close = via_pcm_open_close
3309f7278fd0SJosepch Chan 	},
3310f7278fd0SJosepch Chan };
3311f7278fd0SJosepch Chan 
3312f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
33130aa62aefSHarald Welte 	.substreams = 2,
3314f7278fd0SJosepch Chan 	.channels_min = 2,
3315f7278fd0SJosepch Chan 	.channels_max = 4,
3316f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3317f7278fd0SJosepch Chan 	.ops = {
3318f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33190aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
33200aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
3321f7278fd0SJosepch Chan 	},
3322f7278fd0SJosepch Chan };
3323f7278fd0SJosepch Chan 
3324f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3325f7278fd0SJosepch Chan 	.substreams = 2,
3326f7278fd0SJosepch Chan 	.channels_min = 2,
3327f7278fd0SJosepch Chan 	.channels_max = 2,
3328f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
3329f7278fd0SJosepch Chan 	.ops = {
333017314379SLydia Wang 		.open = via_pcm_open_close,
3331f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
333217314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
333317314379SLydia Wang 		.close = via_pcm_open_close
3334f7278fd0SJosepch Chan 	},
3335f7278fd0SJosepch Chan };
3336f7278fd0SJosepch Chan 
3337f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3338f7278fd0SJosepch Chan 	.substreams = 1,
3339f7278fd0SJosepch Chan 	.channels_min = 2,
3340f7278fd0SJosepch Chan 	.channels_max = 2,
3341f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
3342f7278fd0SJosepch Chan 	.ops = {
3343f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
3344f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
33459da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
33469da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3347f7278fd0SJosepch Chan 	},
3348f7278fd0SJosepch Chan };
3349f7278fd0SJosepch Chan 
3350f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3351f7278fd0SJosepch Chan 	.substreams = 1,
3352f7278fd0SJosepch Chan 	.channels_min = 2,
3353f7278fd0SJosepch Chan 	.channels_max = 2,
3354f7278fd0SJosepch Chan };
3355f7278fd0SJosepch Chan 
3356f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
3357f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3358f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
3359f7278fd0SJosepch Chan {
3360f7278fd0SJosepch Chan 	int i;
3361f7278fd0SJosepch Chan 	hda_nid_t nid;
3362f7278fd0SJosepch Chan 
3363f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
3364f7278fd0SJosepch Chan 
3365f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3366f7278fd0SJosepch Chan 
3367f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3368f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3369f7278fd0SJosepch Chan 		if (nid) {
3370f7278fd0SJosepch Chan 			/* config dac list */
3371f7278fd0SJosepch Chan 			switch (i) {
3372f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3373f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
3374f7278fd0SJosepch Chan 				break;
3375f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3376f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
3377f7278fd0SJosepch Chan 				break;
3378f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3379fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3380f7278fd0SJosepch Chan 				break;
3381f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3382fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3383f7278fd0SJosepch Chan 				break;
3384f7278fd0SJosepch Chan 			}
3385f7278fd0SJosepch Chan 		}
3386f7278fd0SJosepch Chan 	}
3387f7278fd0SJosepch Chan 
3388f7278fd0SJosepch Chan 	return 0;
3389f7278fd0SJosepch Chan }
3390f7278fd0SJosepch Chan 
3391f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3392f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3393f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3394f7278fd0SJosepch Chan {
3395f7278fd0SJosepch Chan 	char name[32];
3396f7278fd0SJosepch Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3397fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3398f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3399f7278fd0SJosepch Chan 	int i, err;
3400f7278fd0SJosepch Chan 
3401f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3402f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3403f7278fd0SJosepch Chan 
3404f7278fd0SJosepch Chan 		if (!nid)
3405f7278fd0SJosepch Chan 			continue;
3406f7278fd0SJosepch Chan 
3407f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3408f7278fd0SJosepch Chan 
3409f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3410f7278fd0SJosepch Chan 			/* Center/LFE */
3411f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3412f7278fd0SJosepch Chan 					      "Center Playback Volume",
3413f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3414f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3415f7278fd0SJosepch Chan 			if (err < 0)
3416f7278fd0SJosepch Chan 				return err;
3417f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3418f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3419f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3420f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3421f7278fd0SJosepch Chan 			if (err < 0)
3422f7278fd0SJosepch Chan 				return err;
3423f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3424f7278fd0SJosepch Chan 					      "Center Playback Switch",
3425f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3426f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3427f7278fd0SJosepch Chan 			if (err < 0)
3428f7278fd0SJosepch Chan 				return err;
3429f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3430f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3431f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3432f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3433f7278fd0SJosepch Chan 			if (err < 0)
3434f7278fd0SJosepch Chan 				return err;
3435f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3436f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3437f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3438f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3439f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3440f7278fd0SJosepch Chan 								  HDA_INPUT));
3441f7278fd0SJosepch Chan 			if (err < 0)
3442f7278fd0SJosepch Chan 				return err;
3443f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3444f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3445f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3446f7278fd0SJosepch Chan 								  HDA_INPUT));
3447f7278fd0SJosepch Chan 			if (err < 0)
3448f7278fd0SJosepch Chan 				return err;
3449f7278fd0SJosepch Chan 
3450f7278fd0SJosepch Chan 			/* add control to PW3 */
3451f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3452f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3453f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3454f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3455f7278fd0SJosepch Chan 			if (err < 0)
3456f7278fd0SJosepch Chan 				return err;
3457f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3458f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3459f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3460f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3461f7278fd0SJosepch Chan 			if (err < 0)
3462f7278fd0SJosepch Chan 				return err;
3463f7278fd0SJosepch Chan 		} else {
3464f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3465f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3466f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3467f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3468f7278fd0SJosepch Chan 			if (err < 0)
3469f7278fd0SJosepch Chan 				return err;
3470f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3471f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3472f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3473f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3474f7278fd0SJosepch Chan 			if (err < 0)
3475f7278fd0SJosepch Chan 				return err;
3476f7278fd0SJosepch Chan 		}
3477f7278fd0SJosepch Chan 	}
3478f7278fd0SJosepch Chan 
3479f7278fd0SJosepch Chan 	return 0;
3480f7278fd0SJosepch Chan }
3481f7278fd0SJosepch Chan 
3482f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3483f7278fd0SJosepch Chan {
3484f7278fd0SJosepch Chan 	int err;
3485f7278fd0SJosepch Chan 
3486f7278fd0SJosepch Chan 	if (!pin)
3487f7278fd0SJosepch Chan 		return 0;
3488f7278fd0SJosepch Chan 
3489f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3490cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3491f7278fd0SJosepch Chan 
3492f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3493f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3494f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3495f7278fd0SJosepch Chan 	if (err < 0)
3496f7278fd0SJosepch Chan 		return err;
3497f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3498f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3499f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3500f7278fd0SJosepch Chan 	if (err < 0)
3501f7278fd0SJosepch Chan 		return err;
3502f7278fd0SJosepch Chan 
35030aa62aefSHarald Welte 	create_hp_imux(spec);
35040aa62aefSHarald Welte 
3505f7278fd0SJosepch Chan 	return 0;
3506f7278fd0SJosepch Chan }
3507f7278fd0SJosepch Chan 
3508f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
3509f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
3510f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3511f7278fd0SJosepch Chan {
3512f7278fd0SJosepch Chan 	static char *labels[] = {
3513f7278fd0SJosepch Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3514f7278fd0SJosepch Chan 	};
35150aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3516f7278fd0SJosepch Chan 	int i, err, idx = 0;
3517f7278fd0SJosepch Chan 
3518f7278fd0SJosepch Chan 	/* for internal loopback recording select */
3519f7278fd0SJosepch Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
3520f7278fd0SJosepch Chan 	imux->items[imux->num_items].index = idx;
3521f7278fd0SJosepch Chan 	imux->num_items++;
3522f7278fd0SJosepch Chan 
3523f7278fd0SJosepch Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3524f7278fd0SJosepch Chan 		if (!cfg->input_pins[i])
3525f7278fd0SJosepch Chan 			continue;
3526f7278fd0SJosepch Chan 
3527f7278fd0SJosepch Chan 		switch (cfg->input_pins[i]) {
3528f7278fd0SJosepch Chan 		case 0x1a: /* Mic */
3529f7278fd0SJosepch Chan 			idx = 2;
3530f7278fd0SJosepch Chan 			break;
3531f7278fd0SJosepch Chan 
3532f7278fd0SJosepch Chan 		case 0x1b: /* Line In */
3533f7278fd0SJosepch Chan 			idx = 3;
3534f7278fd0SJosepch Chan 			break;
3535f7278fd0SJosepch Chan 
3536f7278fd0SJosepch Chan 		case 0x1e: /* Front Mic */
3537f7278fd0SJosepch Chan 			idx = 4;
3538f7278fd0SJosepch Chan 			break;
3539f7278fd0SJosepch Chan 
3540f7278fd0SJosepch Chan 		case 0x1f: /* CD */
3541f7278fd0SJosepch Chan 			idx = 1;
3542f7278fd0SJosepch Chan 			break;
3543f7278fd0SJosepch Chan 		}
35449510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
3545f7278fd0SJosepch Chan 		if (err < 0)
3546f7278fd0SJosepch Chan 			return err;
3547f7278fd0SJosepch Chan 		imux->items[imux->num_items].label = labels[i];
3548f7278fd0SJosepch Chan 		imux->items[imux->num_items].index = idx;
3549f7278fd0SJosepch Chan 		imux->num_items++;
3550f7278fd0SJosepch Chan 	}
3551f7278fd0SJosepch Chan 	return 0;
3552f7278fd0SJosepch Chan }
3553f7278fd0SJosepch Chan 
3554f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3555f7278fd0SJosepch Chan {
3556f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3557f7278fd0SJosepch Chan 	int err;
3558f7278fd0SJosepch Chan 
3559f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3560f7278fd0SJosepch Chan 	if (err < 0)
3561f7278fd0SJosepch Chan 		return err;
3562f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3563f7278fd0SJosepch Chan 	if (err < 0)
3564f7278fd0SJosepch Chan 		return err;
3565f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3566f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3567f7278fd0SJosepch Chan 
3568f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3569f7278fd0SJosepch Chan 	if (err < 0)
3570f7278fd0SJosepch Chan 		return err;
3571f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3572f7278fd0SJosepch Chan 	if (err < 0)
3573f7278fd0SJosepch Chan 		return err;
3574f7278fd0SJosepch Chan 	err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
3575f7278fd0SJosepch Chan 	if (err < 0)
3576f7278fd0SJosepch Chan 		return err;
3577f7278fd0SJosepch Chan 
3578f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3579f7278fd0SJosepch Chan 
35800852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3581f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
358255d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3583f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3584f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3585f7278fd0SJosepch Chan 
3586603c4019STakashi Iwai 	if (spec->kctls.list)
3587603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3588f7278fd0SJosepch Chan 
35890aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
35900aa62aefSHarald Welte 
3591f8fdd495SHarald Welte 	if (spec->hp_mux)
35920aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
3593f7278fd0SJosepch Chan 
35941564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
3595f7278fd0SJosepch Chan 	return 1;
3596f7278fd0SJosepch Chan }
3597f7278fd0SJosepch Chan 
3598f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3599f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3600f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3601f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3602f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3603f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3604f7278fd0SJosepch Chan 	{ } /* end */
3605f7278fd0SJosepch Chan };
3606f7278fd0SJosepch Chan #endif
3607518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3608f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3609f7278fd0SJosepch Chan {
3610f7278fd0SJosepch Chan 	struct via_spec *spec;
3611f7278fd0SJosepch Chan 	int err;
3612f7278fd0SJosepch Chan 
3613518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3614518bf3baSLydia Wang 		return patch_vt1708S(codec);
3615f7278fd0SJosepch Chan 	/* create a codec specific record */
3616eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3617f7278fd0SJosepch Chan 	if (spec == NULL)
3618f7278fd0SJosepch Chan 		return -ENOMEM;
3619f7278fd0SJosepch Chan 
3620f7278fd0SJosepch Chan 	codec->spec = spec;
3621f7278fd0SJosepch Chan 
3622f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3623f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3624f7278fd0SJosepch Chan 	if (err < 0) {
3625f7278fd0SJosepch Chan 		via_free(codec);
3626f7278fd0SJosepch Chan 		return err;
3627f7278fd0SJosepch Chan 	} else if (!err) {
3628f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3629f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3630f7278fd0SJosepch Chan 	}
3631f7278fd0SJosepch Chan 
363269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
363369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3634f7278fd0SJosepch Chan 
3635f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3636f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3637f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3638f7278fd0SJosepch Chan 
3639f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3640f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3641f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3642f7278fd0SJosepch Chan 
3643f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3644f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3645f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3646337b9d02STakashi Iwai 		get_mux_nids(codec);
3647f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3648f7278fd0SJosepch Chan 		spec->num_mixers++;
3649f7278fd0SJosepch Chan 	}
3650f7278fd0SJosepch Chan 
3651f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3652f7278fd0SJosepch Chan 
3653f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
365469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3655f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3656f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3657f7278fd0SJosepch Chan #endif
3658f7278fd0SJosepch Chan 
3659f7278fd0SJosepch Chan 	return 0;
3660f7278fd0SJosepch Chan }
3661f7278fd0SJosepch Chan 
3662f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3663f7278fd0SJosepch Chan {
3664f7278fd0SJosepch Chan 	struct via_spec *spec;
3665f7278fd0SJosepch Chan 	int err;
3666f7278fd0SJosepch Chan 
3667f7278fd0SJosepch Chan 	/* create a codec specific record */
3668eb14a46cSHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
3669f7278fd0SJosepch Chan 	if (spec == NULL)
3670f7278fd0SJosepch Chan 		return -ENOMEM;
3671f7278fd0SJosepch Chan 
3672f7278fd0SJosepch Chan 	codec->spec = spec;
3673f7278fd0SJosepch Chan 
3674f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3675f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3676f7278fd0SJosepch Chan 	if (err < 0) {
3677f7278fd0SJosepch Chan 		via_free(codec);
3678f7278fd0SJosepch Chan 		return err;
3679f7278fd0SJosepch Chan 	} else if (!err) {
3680f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3681f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3682f7278fd0SJosepch Chan 	}
3683f7278fd0SJosepch Chan 
368469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
368569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3686f7278fd0SJosepch Chan 
3687f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3688f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3689f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3690f7278fd0SJosepch Chan 
3691f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3692f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3693f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3694f7278fd0SJosepch Chan 
3695f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3696f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3697f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3698337b9d02STakashi Iwai 		get_mux_nids(codec);
3699f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3700f7278fd0SJosepch Chan 		spec->num_mixers++;
3701f7278fd0SJosepch Chan 	}
3702f7278fd0SJosepch Chan 
3703f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3704f7278fd0SJosepch Chan 
3705f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
370669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3707f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3708f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3709f7278fd0SJosepch Chan #endif
3710c577b8a1SJoseph Chan 
3711c577b8a1SJoseph Chan 	return 0;
3712c577b8a1SJoseph Chan }
3713c577b8a1SJoseph Chan 
3714d949cac1SHarald Welte /* Patch for VT1708S */
3715d949cac1SHarald Welte 
3716d949cac1SHarald Welte /* capture mixer elements */
3717d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3718d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3719d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3720d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3721d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
37226369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
37236369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
37246369bcfcSLydia Wang 			 HDA_INPUT),
3725d949cac1SHarald Welte 	{
3726d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3727d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3728d949cac1SHarald Welte 		 * So call somewhat different..
3729d949cac1SHarald Welte 		 */
3730d949cac1SHarald Welte 		/* .name = "Capture Source", */
3731d949cac1SHarald Welte 		.name = "Input Source",
3732d949cac1SHarald Welte 		.count = 1,
3733d949cac1SHarald Welte 		.info = via_mux_enum_info,
3734d949cac1SHarald Welte 		.get = via_mux_enum_get,
3735d949cac1SHarald Welte 		.put = via_mux_enum_put,
3736d949cac1SHarald Welte 	},
3737d949cac1SHarald Welte 	{ } /* end */
3738d949cac1SHarald Welte };
3739d949cac1SHarald Welte 
3740d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3741d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3742d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3743d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3744d949cac1SHarald Welte 
3745d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3746d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3747d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3748d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3749d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3750d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3751d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3752d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3753d949cac1SHarald Welte 
3754d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3755d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
37565691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3757d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
37585691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3759d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3760d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3761bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3762bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3763d949cac1SHarald Welte 	{ }
3764d949cac1SHarald Welte };
3765d949cac1SHarald Welte 
376669e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3767a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3768a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3769a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3770a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3771a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3772a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3773a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3774a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3775a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
377669e52a80SHarald Welte 	{ }
377769e52a80SHarald Welte };
377869e52a80SHarald Welte 
3779d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3780d949cac1SHarald Welte 	.substreams = 2,
3781d949cac1SHarald Welte 	.channels_min = 2,
3782d949cac1SHarald Welte 	.channels_max = 8,
3783d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3784d949cac1SHarald Welte 	.ops = {
3785d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3786c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3787c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
378817314379SLydia Wang 		.close = via_pcm_open_close
3789d949cac1SHarald Welte 	},
3790d949cac1SHarald Welte };
3791d949cac1SHarald Welte 
3792d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3793d949cac1SHarald Welte 	.substreams = 2,
3794d949cac1SHarald Welte 	.channels_min = 2,
3795d949cac1SHarald Welte 	.channels_max = 2,
3796d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3797d949cac1SHarald Welte 	.ops = {
379817314379SLydia Wang 		.open = via_pcm_open_close,
3799d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
380017314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
380117314379SLydia Wang 		.close = via_pcm_open_close
3802d949cac1SHarald Welte 	},
3803d949cac1SHarald Welte };
3804d949cac1SHarald Welte 
3805d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
38069da29271STakashi Iwai 	.substreams = 1,
3807d949cac1SHarald Welte 	.channels_min = 2,
3808d949cac1SHarald Welte 	.channels_max = 2,
3809d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3810d949cac1SHarald Welte 	.ops = {
3811d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3812d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
38139da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
38149da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3815d949cac1SHarald Welte 	},
3816d949cac1SHarald Welte };
3817d949cac1SHarald Welte 
3818d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3819d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3820d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3821d949cac1SHarald Welte {
3822d949cac1SHarald Welte 	int i;
3823d949cac1SHarald Welte 	hda_nid_t nid;
3824d949cac1SHarald Welte 
3825d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3826d949cac1SHarald Welte 
3827d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3828d949cac1SHarald Welte 
3829d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3830d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3831d949cac1SHarald Welte 		if (nid) {
3832d949cac1SHarald Welte 			/* config dac list */
3833d949cac1SHarald Welte 			switch (i) {
3834d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3835d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3836d949cac1SHarald Welte 				break;
3837d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3838d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x24;
3839d949cac1SHarald Welte 				break;
3840d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3841d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3842d949cac1SHarald Welte 				break;
3843d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3844d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3845d949cac1SHarald Welte 				break;
3846d949cac1SHarald Welte 			}
3847d949cac1SHarald Welte 		}
3848d949cac1SHarald Welte 	}
3849d949cac1SHarald Welte 
3850d949cac1SHarald Welte 	return 0;
3851d949cac1SHarald Welte }
3852d949cac1SHarald Welte 
3853d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3854d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3855d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3856d949cac1SHarald Welte {
3857d949cac1SHarald Welte 	char name[32];
3858d949cac1SHarald Welte 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3859d949cac1SHarald Welte 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3860d949cac1SHarald Welte 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3861d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3862d949cac1SHarald Welte 	int i, err;
3863d949cac1SHarald Welte 
3864d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3865d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3866d949cac1SHarald Welte 
3867d949cac1SHarald Welte 		if (!nid)
3868d949cac1SHarald Welte 			continue;
3869d949cac1SHarald Welte 
3870d949cac1SHarald Welte 		nid_vol = nid_vols[i];
3871d949cac1SHarald Welte 		nid_mute = nid_mutes[i];
3872d949cac1SHarald Welte 
3873d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3874d949cac1SHarald Welte 			/* Center/LFE */
3875d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3876d949cac1SHarald Welte 					      "Center Playback Volume",
3877d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3878d949cac1SHarald Welte 								  HDA_OUTPUT));
3879d949cac1SHarald Welte 			if (err < 0)
3880d949cac1SHarald Welte 				return err;
3881d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3882d949cac1SHarald Welte 					      "LFE Playback Volume",
3883d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3884d949cac1SHarald Welte 								  HDA_OUTPUT));
3885d949cac1SHarald Welte 			if (err < 0)
3886d949cac1SHarald Welte 				return err;
3887d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3888d949cac1SHarald Welte 					      "Center Playback Switch",
3889d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3890d949cac1SHarald Welte 								  1, 0,
3891d949cac1SHarald Welte 								  HDA_OUTPUT));
3892d949cac1SHarald Welte 			if (err < 0)
3893d949cac1SHarald Welte 				return err;
3894d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3895d949cac1SHarald Welte 					      "LFE Playback Switch",
3896d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3897d949cac1SHarald Welte 								  2, 0,
3898d949cac1SHarald Welte 								  HDA_OUTPUT));
3899d949cac1SHarald Welte 			if (err < 0)
3900d949cac1SHarald Welte 				return err;
3901d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3902d949cac1SHarald Welte 			/* add control to mixer index 0 */
3903d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3904d949cac1SHarald Welte 					      "Master Front Playback Volume",
3905d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3906d949cac1SHarald Welte 								  HDA_INPUT));
3907d949cac1SHarald Welte 			if (err < 0)
3908d949cac1SHarald Welte 				return err;
3909d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3910d949cac1SHarald Welte 					      "Master Front Playback Switch",
3911d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3912d949cac1SHarald Welte 								  HDA_INPUT));
3913d949cac1SHarald Welte 			if (err < 0)
3914d949cac1SHarald Welte 				return err;
3915d949cac1SHarald Welte 
3916d949cac1SHarald Welte 			/* Front */
3917d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3918d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3919d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3920d949cac1SHarald Welte 								  HDA_OUTPUT));
3921d949cac1SHarald Welte 			if (err < 0)
3922d949cac1SHarald Welte 				return err;
3923d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3924d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3925d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3926d949cac1SHarald Welte 								  3, 0,
3927d949cac1SHarald Welte 								  HDA_OUTPUT));
3928d949cac1SHarald Welte 			if (err < 0)
3929d949cac1SHarald Welte 				return err;
3930d949cac1SHarald Welte 		} else {
3931d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3932d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3933d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3934d949cac1SHarald Welte 								  HDA_OUTPUT));
3935d949cac1SHarald Welte 			if (err < 0)
3936d949cac1SHarald Welte 				return err;
3937d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3938d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3939d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3940d949cac1SHarald Welte 								  3, 0,
3941d949cac1SHarald Welte 								  HDA_OUTPUT));
3942d949cac1SHarald Welte 			if (err < 0)
3943d949cac1SHarald Welte 				return err;
3944d949cac1SHarald Welte 		}
3945d949cac1SHarald Welte 	}
3946d949cac1SHarald Welte 
3947d949cac1SHarald Welte 	return 0;
3948d949cac1SHarald Welte }
3949d949cac1SHarald Welte 
3950d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3951d949cac1SHarald Welte {
3952d949cac1SHarald Welte 	int err;
3953d949cac1SHarald Welte 
3954d949cac1SHarald Welte 	if (!pin)
3955d949cac1SHarald Welte 		return 0;
3956d949cac1SHarald Welte 
3957d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3958cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3959d949cac1SHarald Welte 
3960d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3961d949cac1SHarald Welte 			      "Headphone Playback Volume",
3962d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3963d949cac1SHarald Welte 	if (err < 0)
3964d949cac1SHarald Welte 		return err;
3965d949cac1SHarald Welte 
3966d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3967d949cac1SHarald Welte 			      "Headphone Playback Switch",
3968d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3969d949cac1SHarald Welte 	if (err < 0)
3970d949cac1SHarald Welte 		return err;
3971d949cac1SHarald Welte 
39720aa62aefSHarald Welte 	create_hp_imux(spec);
39730aa62aefSHarald Welte 
3974d949cac1SHarald Welte 	return 0;
3975d949cac1SHarald Welte }
3976d949cac1SHarald Welte 
3977d949cac1SHarald Welte /* create playback/capture controls for input pins */
3978d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
3979d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
3980d949cac1SHarald Welte {
3981d949cac1SHarald Welte 	static char *labels[] = {
3982d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3983d949cac1SHarald Welte 	};
39840aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3985d949cac1SHarald Welte 	int i, err, idx = 0;
3986d949cac1SHarald Welte 
3987d949cac1SHarald Welte 	/* for internal loopback recording select */
3988d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
3989d949cac1SHarald Welte 	imux->items[imux->num_items].index = 5;
3990d949cac1SHarald Welte 	imux->num_items++;
3991d949cac1SHarald Welte 
3992d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3993d949cac1SHarald Welte 		if (!cfg->input_pins[i])
3994d949cac1SHarald Welte 			continue;
3995d949cac1SHarald Welte 
3996d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
3997d949cac1SHarald Welte 		case 0x1a: /* Mic */
3998d949cac1SHarald Welte 			idx = 2;
3999d949cac1SHarald Welte 			break;
4000d949cac1SHarald Welte 
4001d949cac1SHarald Welte 		case 0x1b: /* Line In */
4002d949cac1SHarald Welte 			idx = 3;
4003d949cac1SHarald Welte 			break;
4004d949cac1SHarald Welte 
4005d949cac1SHarald Welte 		case 0x1e: /* Front Mic */
4006d949cac1SHarald Welte 			idx = 4;
4007d949cac1SHarald Welte 			break;
4008d949cac1SHarald Welte 
4009d949cac1SHarald Welte 		case 0x1f: /* CD */
4010d949cac1SHarald Welte 			idx = 1;
4011d949cac1SHarald Welte 			break;
4012d949cac1SHarald Welte 		}
40139510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
4014d949cac1SHarald Welte 		if (err < 0)
4015d949cac1SHarald Welte 			return err;
4016d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
4017d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
4018d949cac1SHarald Welte 		imux->num_items++;
4019d949cac1SHarald Welte 	}
4020d949cac1SHarald Welte 	return 0;
4021d949cac1SHarald Welte }
4022d949cac1SHarald Welte 
40239da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
40249da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
40259da29271STakashi Iwai {
40269da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
40279da29271STakashi Iwai 	int i;
40289da29271STakashi Iwai 
40299da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
40309da29271STakashi Iwai 		hda_nid_t nid;
40319da29271STakashi Iwai 		int conn;
40329da29271STakashi Iwai 
40339da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
40349da29271STakashi Iwai 		if (!nid)
40359da29271STakashi Iwai 			continue;
40369da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
40379da29271STakashi Iwai 		if (conn < 1)
40389da29271STakashi Iwai 			continue;
40399da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
40409da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
40419da29271STakashi Iwai 		else {
40429da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
40439da29271STakashi Iwai 			break; /* at most two dig outs */
40449da29271STakashi Iwai 		}
40459da29271STakashi Iwai 	}
40469da29271STakashi Iwai }
40479da29271STakashi Iwai 
4048d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
4049d949cac1SHarald Welte {
4050d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4051d949cac1SHarald Welte 	int err;
4052d949cac1SHarald Welte 
40539da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4054d949cac1SHarald Welte 	if (err < 0)
4055d949cac1SHarald Welte 		return err;
4056d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
4057d949cac1SHarald Welte 	if (err < 0)
4058d949cac1SHarald Welte 		return err;
4059d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4060d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4061d949cac1SHarald Welte 
4062d949cac1SHarald Welte 	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4063d949cac1SHarald Welte 	if (err < 0)
4064d949cac1SHarald Welte 		return err;
4065d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4066d949cac1SHarald Welte 	if (err < 0)
4067d949cac1SHarald Welte 		return err;
4068d949cac1SHarald Welte 	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4069d949cac1SHarald Welte 	if (err < 0)
4070d949cac1SHarald Welte 		return err;
4071d949cac1SHarald Welte 
4072d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4073d949cac1SHarald Welte 
40749da29271STakashi Iwai 	fill_dig_outs(codec);
407598aa34c0SHarald Welte 
4076603c4019STakashi Iwai 	if (spec->kctls.list)
4077603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4078d949cac1SHarald Welte 
40790aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
40800aa62aefSHarald Welte 
4081f8fdd495SHarald Welte 	if (spec->hp_mux)
40820aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
4083d949cac1SHarald Welte 
40841564b287SLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
4085d949cac1SHarald Welte 	return 1;
4086d949cac1SHarald Welte }
4087d949cac1SHarald Welte 
4088d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4089d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
4090d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
4091d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
4092d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
4093d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
4094d949cac1SHarald Welte 	{ } /* end */
4095d949cac1SHarald Welte };
4096d949cac1SHarald Welte #endif
4097d949cac1SHarald Welte 
40986369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
40996369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
41006369bcfcSLydia Wang {
41016369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
41026369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
41036369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
41046369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
41056369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
41066369bcfcSLydia Wang }
41076369bcfcSLydia Wang 
4108d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
4109d949cac1SHarald Welte {
4110d949cac1SHarald Welte 	struct via_spec *spec;
4111d949cac1SHarald Welte 	int err;
4112d949cac1SHarald Welte 
4113d949cac1SHarald Welte 	/* create a codec specific record */
4114d949cac1SHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4115d949cac1SHarald Welte 	if (spec == NULL)
4116d949cac1SHarald Welte 		return -ENOMEM;
4117d949cac1SHarald Welte 
4118d949cac1SHarald Welte 	codec->spec = spec;
4119d949cac1SHarald Welte 
4120d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4121d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
4122d949cac1SHarald Welte 	if (err < 0) {
4123d949cac1SHarald Welte 		via_free(codec);
4124d949cac1SHarald Welte 		return err;
4125d949cac1SHarald Welte 	} else if (!err) {
4126d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4127d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4128d949cac1SHarald Welte 	}
4129d949cac1SHarald Welte 
413069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
413169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
4132d949cac1SHarald Welte 
4133d949cac1SHarald Welte 	spec->stream_name_analog = "VT1708S Analog";
4134d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
4135d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
4136d949cac1SHarald Welte 
4137d949cac1SHarald Welte 	spec->stream_name_digital = "VT1708S Digital";
4138d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
4139d949cac1SHarald Welte 
4140d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4141d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
4142d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
4143337b9d02STakashi Iwai 		get_mux_nids(codec);
41446369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
41456369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
4146d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
4147d949cac1SHarald Welte 		spec->num_mixers++;
4148d949cac1SHarald Welte 	}
4149d949cac1SHarald Welte 
4150d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4151d949cac1SHarald Welte 
4152d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
415369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4154d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4155d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
4156d949cac1SHarald Welte #endif
4157d949cac1SHarald Welte 
4158518bf3baSLydia Wang 	/* correct names for VT1708BCE */
4159518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
4160518bf3baSLydia Wang 		kfree(codec->chip_name);
4161518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
4162518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
4163518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
4164518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
4165518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
4166518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
4167518bf3baSLydia Wang 	}
4168d949cac1SHarald Welte 	return 0;
4169d949cac1SHarald Welte }
4170d949cac1SHarald Welte 
4171d949cac1SHarald Welte /* Patch for VT1702 */
4172d949cac1SHarald Welte 
4173d949cac1SHarald Welte /* capture mixer elements */
4174d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
4175d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
4176d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
4177d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
4178d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
4179d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
4180d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
4181d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
4182d949cac1SHarald Welte 			 HDA_INPUT),
4183d949cac1SHarald Welte 	{
4184d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4185d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
4186d949cac1SHarald Welte 		 * So call somewhat different..
4187d949cac1SHarald Welte 		 */
4188d949cac1SHarald Welte 		/* .name = "Capture Source", */
4189d949cac1SHarald Welte 		.name = "Input Source",
4190d949cac1SHarald Welte 		.count = 1,
4191d949cac1SHarald Welte 		.info = via_mux_enum_info,
4192d949cac1SHarald Welte 		.get = via_mux_enum_get,
4193d949cac1SHarald Welte 		.put = via_mux_enum_put,
4194d949cac1SHarald Welte 	},
4195d949cac1SHarald Welte 	{ } /* end */
4196d949cac1SHarald Welte };
4197d949cac1SHarald Welte 
4198d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
4199d949cac1SHarald Welte 	/*
4200d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
4201d949cac1SHarald Welte 	 */
4202d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4203d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4204d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4205d949cac1SHarald Welte 
4206d949cac1SHarald Welte 
4207d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4208d949cac1SHarald Welte 	 * mixer widget
4209d949cac1SHarald Welte 	 */
4210d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
4211d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4212d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4213d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4214d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4215d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4216d949cac1SHarald Welte 
4217d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
4218d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
4219d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
4220d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4221d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4222bc7e7e5cSLydia Wang 	/* mixer enable */
4223bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
4224bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
4225bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
4226d949cac1SHarald Welte 	{ }
4227d949cac1SHarald Welte };
4228d949cac1SHarald Welte 
422969e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
4230a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
4231a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4232a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4233a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4234a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4235a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
423669e52a80SHarald Welte 	{ }
423769e52a80SHarald Welte };
423869e52a80SHarald Welte 
4239d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
42400aa62aefSHarald Welte 	.substreams = 2,
4241d949cac1SHarald Welte 	.channels_min = 2,
4242d949cac1SHarald Welte 	.channels_max = 2,
4243d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
4244d949cac1SHarald Welte 	.ops = {
4245d949cac1SHarald Welte 		.open = via_playback_pcm_open,
42460aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
424717314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
424817314379SLydia Wang 		.close = via_pcm_open_close
4249d949cac1SHarald Welte 	},
4250d949cac1SHarald Welte };
4251d949cac1SHarald Welte 
4252d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
4253d949cac1SHarald Welte 	.substreams = 3,
4254d949cac1SHarald Welte 	.channels_min = 2,
4255d949cac1SHarald Welte 	.channels_max = 2,
4256d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
4257d949cac1SHarald Welte 	.ops = {
425817314379SLydia Wang 		.open = via_pcm_open_close,
4259d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
426017314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
426117314379SLydia Wang 		.close = via_pcm_open_close
4262d949cac1SHarald Welte 	},
4263d949cac1SHarald Welte };
4264d949cac1SHarald Welte 
4265d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
42665691ec7fSHarald Welte 	.substreams = 2,
4267d949cac1SHarald Welte 	.channels_min = 2,
4268d949cac1SHarald Welte 	.channels_max = 2,
4269d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
4270d949cac1SHarald Welte 	.ops = {
4271d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
4272d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
42739da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
42749da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
4275d949cac1SHarald Welte 	},
4276d949cac1SHarald Welte };
4277d949cac1SHarald Welte 
4278d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4279d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4280d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4281d949cac1SHarald Welte {
4282d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4283d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4284d949cac1SHarald Welte 
4285d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4286d949cac1SHarald Welte 		/* config dac list */
4287d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
4288d949cac1SHarald Welte 	}
4289d949cac1SHarald Welte 
4290d949cac1SHarald Welte 	return 0;
4291d949cac1SHarald Welte }
4292d949cac1SHarald Welte 
4293d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4294d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4295d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4296d949cac1SHarald Welte {
4297d949cac1SHarald Welte 	int err;
4298d949cac1SHarald Welte 
4299d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4300d949cac1SHarald Welte 		return -1;
4301d949cac1SHarald Welte 
4302d949cac1SHarald Welte 	/* add control to mixer index 0 */
4303d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4304d949cac1SHarald Welte 			      "Master Front Playback Volume",
4305d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4306d949cac1SHarald Welte 	if (err < 0)
4307d949cac1SHarald Welte 		return err;
4308d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4309d949cac1SHarald Welte 			      "Master Front Playback Switch",
4310d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4311d949cac1SHarald Welte 	if (err < 0)
4312d949cac1SHarald Welte 		return err;
4313d949cac1SHarald Welte 
4314d949cac1SHarald Welte 	/* Front */
4315d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4316d949cac1SHarald Welte 			      "Front Playback Volume",
4317d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4318d949cac1SHarald Welte 	if (err < 0)
4319d949cac1SHarald Welte 		return err;
4320d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4321d949cac1SHarald Welte 			      "Front Playback Switch",
4322d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4323d949cac1SHarald Welte 	if (err < 0)
4324d949cac1SHarald Welte 		return err;
4325d949cac1SHarald Welte 
4326d949cac1SHarald Welte 	return 0;
4327d949cac1SHarald Welte }
4328d949cac1SHarald Welte 
4329d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4330d949cac1SHarald Welte {
43310713efebSLydia Wang 	int err, i;
43320713efebSLydia Wang 	struct hda_input_mux *imux;
43330713efebSLydia Wang 	static const char *texts[] = { "ON", "OFF", NULL};
4334d949cac1SHarald Welte 	if (!pin)
4335d949cac1SHarald Welte 		return 0;
4336d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4337cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4338d949cac1SHarald Welte 
4339d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4340d949cac1SHarald Welte 			      "Headphone Playback Volume",
4341d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4342d949cac1SHarald Welte 	if (err < 0)
4343d949cac1SHarald Welte 		return err;
4344d949cac1SHarald Welte 
4345d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4346d949cac1SHarald Welte 			      "Headphone Playback Switch",
4347d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4348d949cac1SHarald Welte 	if (err < 0)
4349d949cac1SHarald Welte 		return err;
4350d949cac1SHarald Welte 
43510713efebSLydia Wang 	imux = &spec->private_imux[1];
43520aa62aefSHarald Welte 
43530713efebSLydia Wang 	/* for hp mode select */
43540713efebSLydia Wang 	i = 0;
43550713efebSLydia Wang 	while (texts[i] != NULL)	{
43560713efebSLydia Wang 		imux->items[imux->num_items].label =  texts[i];
43570713efebSLydia Wang 		imux->items[imux->num_items].index = i;
43580713efebSLydia Wang 		imux->num_items++;
43590713efebSLydia Wang 		i++;
43600713efebSLydia Wang 	}
43610713efebSLydia Wang 
43620713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4363d949cac1SHarald Welte 	return 0;
4364d949cac1SHarald Welte }
4365d949cac1SHarald Welte 
4366d949cac1SHarald Welte /* create playback/capture controls for input pins */
4367d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
4368d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4369d949cac1SHarald Welte {
4370d949cac1SHarald Welte 	static char *labels[] = {
4371d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4372d949cac1SHarald Welte 	};
43730aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
4374d949cac1SHarald Welte 	int i, err, idx = 0;
4375d949cac1SHarald Welte 
4376d949cac1SHarald Welte 	/* for internal loopback recording select */
4377d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
4378d949cac1SHarald Welte 	imux->items[imux->num_items].index = 3;
4379d949cac1SHarald Welte 	imux->num_items++;
4380d949cac1SHarald Welte 
4381d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4382d949cac1SHarald Welte 		if (!cfg->input_pins[i])
4383d949cac1SHarald Welte 			continue;
4384d949cac1SHarald Welte 
4385d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
4386d949cac1SHarald Welte 		case 0x14: /* Mic */
4387d949cac1SHarald Welte 			idx = 1;
4388d949cac1SHarald Welte 			break;
4389d949cac1SHarald Welte 
4390d949cac1SHarald Welte 		case 0x15: /* Line In */
4391d949cac1SHarald Welte 			idx = 2;
4392d949cac1SHarald Welte 			break;
4393d949cac1SHarald Welte 
4394d949cac1SHarald Welte 		case 0x18: /* Front Mic */
4395d949cac1SHarald Welte 			idx = 3;
4396d949cac1SHarald Welte 			break;
4397d949cac1SHarald Welte 		}
43989510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x1A);
4399d949cac1SHarald Welte 		if (err < 0)
4400d949cac1SHarald Welte 			return err;
4401d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
4402d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
4403d949cac1SHarald Welte 		imux->num_items++;
4404d949cac1SHarald Welte 	}
4405d949cac1SHarald Welte 	return 0;
4406d949cac1SHarald Welte }
4407d949cac1SHarald Welte 
4408d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4409d949cac1SHarald Welte {
4410d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4411d949cac1SHarald Welte 	int err;
4412d949cac1SHarald Welte 
44139da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4414d949cac1SHarald Welte 	if (err < 0)
4415d949cac1SHarald Welte 		return err;
4416d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4417d949cac1SHarald Welte 	if (err < 0)
4418d949cac1SHarald Welte 		return err;
4419d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4420d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4421d949cac1SHarald Welte 
4422d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4423d949cac1SHarald Welte 	if (err < 0)
4424d949cac1SHarald Welte 		return err;
4425d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4426d949cac1SHarald Welte 	if (err < 0)
4427d949cac1SHarald Welte 		return err;
4428c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4429c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4430c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4431c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4432c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4433c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
4434d949cac1SHarald Welte 	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
4435d949cac1SHarald Welte 	if (err < 0)
4436d949cac1SHarald Welte 		return err;
4437d949cac1SHarald Welte 
4438d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4439d949cac1SHarald Welte 
44409da29271STakashi Iwai 	fill_dig_outs(codec);
444198aa34c0SHarald Welte 
4442603c4019STakashi Iwai 	if (spec->kctls.list)
4443603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4444d949cac1SHarald Welte 
44450aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
44460aa62aefSHarald Welte 
4447f8fdd495SHarald Welte 	if (spec->hp_mux)
44480aa62aefSHarald Welte 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
4449d949cac1SHarald Welte 
4450d949cac1SHarald Welte 	return 1;
4451d949cac1SHarald Welte }
4452d949cac1SHarald Welte 
4453d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4454d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4455d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4456d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4457d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4458d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4459d949cac1SHarald Welte 	{ } /* end */
4460d949cac1SHarald Welte };
4461d949cac1SHarald Welte #endif
4462d949cac1SHarald Welte 
4463d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4464d949cac1SHarald Welte {
4465d949cac1SHarald Welte 	struct via_spec *spec;
4466d949cac1SHarald Welte 	int err;
4467d949cac1SHarald Welte 
4468d949cac1SHarald Welte 	/* create a codec specific record */
4469d949cac1SHarald Welte 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4470d949cac1SHarald Welte 	if (spec == NULL)
4471d949cac1SHarald Welte 		return -ENOMEM;
4472d949cac1SHarald Welte 
4473d949cac1SHarald Welte 	codec->spec = spec;
4474d949cac1SHarald Welte 
4475d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4476d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4477d949cac1SHarald Welte 	if (err < 0) {
4478d949cac1SHarald Welte 		via_free(codec);
4479d949cac1SHarald Welte 		return err;
4480d949cac1SHarald Welte 	} else if (!err) {
4481d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4482d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4483d949cac1SHarald Welte 	}
4484d949cac1SHarald Welte 
448569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
448669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4487d949cac1SHarald Welte 
4488d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4489d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4490d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4491d949cac1SHarald Welte 
4492d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4493d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4494d949cac1SHarald Welte 
4495d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4496d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4497d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4498337b9d02STakashi Iwai 		get_mux_nids(codec);
4499d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4500d949cac1SHarald Welte 		spec->num_mixers++;
4501d949cac1SHarald Welte 	}
4502d949cac1SHarald Welte 
4503d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4504d949cac1SHarald Welte 
4505d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
450669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4507d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4508d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4509d949cac1SHarald Welte #endif
4510d949cac1SHarald Welte 
4511d949cac1SHarald Welte 	return 0;
4512d949cac1SHarald Welte }
4513d949cac1SHarald Welte 
4514eb7188caSLydia Wang /* Patch for VT1718S */
4515eb7188caSLydia Wang 
4516eb7188caSLydia Wang /* capture mixer elements */
4517eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4518eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4519eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4520eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4521eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4522eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4523eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4524eb7188caSLydia Wang 			 HDA_INPUT),
4525eb7188caSLydia Wang 	{
4526eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4527eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4528eb7188caSLydia Wang 		 * So call somewhat different..
4529eb7188caSLydia Wang 		 */
4530eb7188caSLydia Wang 		.name = "Input Source",
4531eb7188caSLydia Wang 		.count = 2,
4532eb7188caSLydia Wang 		.info = via_mux_enum_info,
4533eb7188caSLydia Wang 		.get = via_mux_enum_get,
4534eb7188caSLydia Wang 		.put = via_mux_enum_put,
4535eb7188caSLydia Wang 	},
4536eb7188caSLydia Wang 	{ } /* end */
4537eb7188caSLydia Wang };
4538eb7188caSLydia Wang 
4539eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4540eb7188caSLydia Wang 	/*
4541eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4542eb7188caSLydia Wang 	 */
4543eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4544eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4545eb7188caSLydia Wang 
4546eb7188caSLydia Wang 
4547eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4548eb7188caSLydia Wang 	 * mixer widget
4549eb7188caSLydia Wang 	 */
4550eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4551eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4552eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4553eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4554eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4555eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4556eb7188caSLydia Wang 
4557eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4558eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4559eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4560eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4561eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4562eb7188caSLydia Wang 	/* PW11 Input enable */
4563eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4564eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4565eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4566eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4567eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4568eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4569eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4570eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4571eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4572eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4573eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4574eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4575eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4576eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4577eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4578eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4579eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4580eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4581eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4582eb7188caSLydia Wang 	{ }
4583eb7188caSLydia Wang };
4584eb7188caSLydia Wang 
4585eb7188caSLydia Wang 
4586eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4587eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4588eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4589eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4590eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4591eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4592eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4593eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4594eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4595eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4596eb7188caSLydia Wang 	{ }
4597eb7188caSLydia Wang };
4598eb7188caSLydia Wang 
4599eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4600eb7188caSLydia Wang 	.substreams = 2,
4601eb7188caSLydia Wang 	.channels_min = 2,
4602eb7188caSLydia Wang 	.channels_max = 10,
4603eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4604eb7188caSLydia Wang 	.ops = {
4605eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4606eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4607eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4608eb7188caSLydia Wang 		.close = via_pcm_open_close,
4609eb7188caSLydia Wang 	},
4610eb7188caSLydia Wang };
4611eb7188caSLydia Wang 
4612eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4613eb7188caSLydia Wang 	.substreams = 2,
4614eb7188caSLydia Wang 	.channels_min = 2,
4615eb7188caSLydia Wang 	.channels_max = 2,
4616eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4617eb7188caSLydia Wang 	.ops = {
4618eb7188caSLydia Wang 		.open = via_pcm_open_close,
4619eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4620eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4621eb7188caSLydia Wang 		.close = via_pcm_open_close,
4622eb7188caSLydia Wang 	},
4623eb7188caSLydia Wang };
4624eb7188caSLydia Wang 
4625eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4626eb7188caSLydia Wang 	.substreams = 2,
4627eb7188caSLydia Wang 	.channels_min = 2,
4628eb7188caSLydia Wang 	.channels_max = 2,
4629eb7188caSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
4630eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4631eb7188caSLydia Wang 	.ops = {
4632eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4633eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4634eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4635eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4636eb7188caSLydia Wang 	},
4637eb7188caSLydia Wang };
4638eb7188caSLydia Wang 
4639eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4640eb7188caSLydia Wang 	.substreams = 1,
4641eb7188caSLydia Wang 	.channels_min = 2,
4642eb7188caSLydia Wang 	.channels_max = 2,
4643eb7188caSLydia Wang };
4644eb7188caSLydia Wang 
4645eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4646eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4647eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4648eb7188caSLydia Wang {
4649eb7188caSLydia Wang 	int i;
4650eb7188caSLydia Wang 	hda_nid_t nid;
4651eb7188caSLydia Wang 
4652eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4653eb7188caSLydia Wang 
4654eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4655eb7188caSLydia Wang 
4656eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4657eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4658eb7188caSLydia Wang 		if (nid) {
4659eb7188caSLydia Wang 			/* config dac list */
4660eb7188caSLydia Wang 			switch (i) {
4661eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4662eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4663eb7188caSLydia Wang 				break;
4664eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4665eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4666eb7188caSLydia Wang 				break;
4667eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4668eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4669eb7188caSLydia Wang 				break;
4670eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4671eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4672eb7188caSLydia Wang 				break;
4673eb7188caSLydia Wang 			}
4674eb7188caSLydia Wang 		}
4675eb7188caSLydia Wang 	}
4676eb7188caSLydia Wang 
4677eb7188caSLydia Wang 	return 0;
4678eb7188caSLydia Wang }
4679eb7188caSLydia Wang 
4680eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4681eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4682eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4683eb7188caSLydia Wang {
4684eb7188caSLydia Wang 	char name[32];
4685eb7188caSLydia Wang 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
4686eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4687eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4688eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4689eb7188caSLydia Wang 	int i, err;
4690eb7188caSLydia Wang 
4691eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4692eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4693eb7188caSLydia Wang 
4694eb7188caSLydia Wang 		if (!nid)
4695eb7188caSLydia Wang 			continue;
4696eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4697eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4698eb7188caSLydia Wang 
4699eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4700eb7188caSLydia Wang 			/* Center/LFE */
4701eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4702eb7188caSLydia Wang 					      "Center Playback Volume",
4703eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4704eb7188caSLydia Wang 								  HDA_OUTPUT));
4705eb7188caSLydia Wang 			if (err < 0)
4706eb7188caSLydia Wang 				return err;
4707eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4708eb7188caSLydia Wang 					      "LFE Playback Volume",
4709eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4710eb7188caSLydia Wang 								  HDA_OUTPUT));
4711eb7188caSLydia Wang 			if (err < 0)
4712eb7188caSLydia Wang 				return err;
4713eb7188caSLydia Wang 			err = via_add_control(
4714eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4715eb7188caSLydia Wang 				"Center Playback Switch",
4716eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4717eb7188caSLydia Wang 						    HDA_OUTPUT));
4718eb7188caSLydia Wang 			if (err < 0)
4719eb7188caSLydia Wang 				return err;
4720eb7188caSLydia Wang 			err = via_add_control(
4721eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4722eb7188caSLydia Wang 				"LFE Playback Switch",
4723eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4724eb7188caSLydia Wang 						    HDA_OUTPUT));
4725eb7188caSLydia Wang 			if (err < 0)
4726eb7188caSLydia Wang 				return err;
4727eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4728eb7188caSLydia Wang 			/* Front */
4729eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4730eb7188caSLydia Wang 			err = via_add_control(
4731eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4732eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4733eb7188caSLydia Wang 			if (err < 0)
4734eb7188caSLydia Wang 				return err;
4735eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4736eb7188caSLydia Wang 			err = via_add_control(
4737eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4738eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4739eb7188caSLydia Wang 						    HDA_OUTPUT));
4740eb7188caSLydia Wang 			if (err < 0)
4741eb7188caSLydia Wang 				return err;
4742eb7188caSLydia Wang 		} else {
4743eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4744eb7188caSLydia Wang 			err = via_add_control(
4745eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4746eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4747eb7188caSLydia Wang 			if (err < 0)
4748eb7188caSLydia Wang 				return err;
4749eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4750eb7188caSLydia Wang 			err = via_add_control(
4751eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4752eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4753eb7188caSLydia Wang 						    HDA_OUTPUT));
4754eb7188caSLydia Wang 			if (err < 0)
4755eb7188caSLydia Wang 				return err;
4756eb7188caSLydia Wang 		}
4757eb7188caSLydia Wang 	}
4758eb7188caSLydia Wang 	return 0;
4759eb7188caSLydia Wang }
4760eb7188caSLydia Wang 
4761eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4762eb7188caSLydia Wang {
4763eb7188caSLydia Wang 	int err;
4764eb7188caSLydia Wang 
4765eb7188caSLydia Wang 	if (!pin)
4766eb7188caSLydia Wang 		return 0;
4767eb7188caSLydia Wang 
4768eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4769eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4770eb7188caSLydia Wang 
4771eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4772eb7188caSLydia Wang 			      "Headphone Playback Volume",
4773eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4774eb7188caSLydia Wang 	if (err < 0)
4775eb7188caSLydia Wang 		return err;
4776eb7188caSLydia Wang 
4777eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4778eb7188caSLydia Wang 			      "Headphone Playback Switch",
4779eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4780eb7188caSLydia Wang 	if (err < 0)
4781eb7188caSLydia Wang 		return err;
4782eb7188caSLydia Wang 
4783eb7188caSLydia Wang 	create_hp_imux(spec);
4784eb7188caSLydia Wang 	return 0;
4785eb7188caSLydia Wang }
4786eb7188caSLydia Wang 
4787eb7188caSLydia Wang /* create playback/capture controls for input pins */
4788eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
4789eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4790eb7188caSLydia Wang {
4791eb7188caSLydia Wang 	static char *labels[] = {
4792eb7188caSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4793eb7188caSLydia Wang 	};
4794eb7188caSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
4795eb7188caSLydia Wang 	int i, err, idx = 0;
4796eb7188caSLydia Wang 
4797eb7188caSLydia Wang 	/* for internal loopback recording select */
4798eb7188caSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
4799eb7188caSLydia Wang 	imux->items[imux->num_items].index = 5;
4800eb7188caSLydia Wang 	imux->num_items++;
4801eb7188caSLydia Wang 
4802eb7188caSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4803eb7188caSLydia Wang 		if (!cfg->input_pins[i])
4804eb7188caSLydia Wang 			continue;
4805eb7188caSLydia Wang 
4806eb7188caSLydia Wang 		switch (cfg->input_pins[i]) {
4807eb7188caSLydia Wang 		case 0x2b: /* Mic */
4808eb7188caSLydia Wang 			idx = 1;
4809eb7188caSLydia Wang 			break;
4810eb7188caSLydia Wang 
4811eb7188caSLydia Wang 		case 0x2a: /* Line In */
4812eb7188caSLydia Wang 			idx = 2;
4813eb7188caSLydia Wang 			break;
4814eb7188caSLydia Wang 
4815eb7188caSLydia Wang 		case 0x29: /* Front Mic */
4816eb7188caSLydia Wang 			idx = 3;
4817eb7188caSLydia Wang 			break;
4818eb7188caSLydia Wang 
4819eb7188caSLydia Wang 		case 0x2c: /* CD */
4820eb7188caSLydia Wang 			idx = 0;
4821eb7188caSLydia Wang 			break;
4822eb7188caSLydia Wang 		}
4823eb7188caSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
4824eb7188caSLydia Wang 		if (err < 0)
4825eb7188caSLydia Wang 			return err;
4826eb7188caSLydia Wang 		imux->items[imux->num_items].label = labels[i];
4827eb7188caSLydia Wang 		imux->items[imux->num_items].index = idx;
4828eb7188caSLydia Wang 		imux->num_items++;
4829eb7188caSLydia Wang 	}
4830eb7188caSLydia Wang 	return 0;
4831eb7188caSLydia Wang }
4832eb7188caSLydia Wang 
4833eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4834eb7188caSLydia Wang {
4835eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4836eb7188caSLydia Wang 	int err;
4837eb7188caSLydia Wang 
4838eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4839eb7188caSLydia Wang 
4840eb7188caSLydia Wang 	if (err < 0)
4841eb7188caSLydia Wang 		return err;
4842eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4843eb7188caSLydia Wang 	if (err < 0)
4844eb7188caSLydia Wang 		return err;
4845eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4846eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4847eb7188caSLydia Wang 
4848eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4849eb7188caSLydia Wang 	if (err < 0)
4850eb7188caSLydia Wang 		return err;
4851eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4852eb7188caSLydia Wang 	if (err < 0)
4853eb7188caSLydia Wang 		return err;
4854eb7188caSLydia Wang 	err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4855eb7188caSLydia Wang 	if (err < 0)
4856eb7188caSLydia Wang 		return err;
4857eb7188caSLydia Wang 
4858eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4859eb7188caSLydia Wang 
4860eb7188caSLydia Wang 	fill_dig_outs(codec);
4861eb7188caSLydia Wang 
4862eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4863eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4864eb7188caSLydia Wang 
4865eb7188caSLydia Wang 	if (spec->kctls.list)
4866eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4867eb7188caSLydia Wang 
4868eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4869eb7188caSLydia Wang 
4870eb7188caSLydia Wang 	if (spec->hp_mux)
4871eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
4872eb7188caSLydia Wang 
4873eb7188caSLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
4874eb7188caSLydia Wang 
4875eb7188caSLydia Wang 	return 1;
4876eb7188caSLydia Wang }
4877eb7188caSLydia Wang 
4878eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4879eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = {
4880eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4881eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4882eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4883eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4884eb7188caSLydia Wang 	{ } /* end */
4885eb7188caSLydia Wang };
4886eb7188caSLydia Wang #endif
4887eb7188caSLydia Wang 
4888eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4889eb7188caSLydia Wang {
4890eb7188caSLydia Wang 	struct via_spec *spec;
4891eb7188caSLydia Wang 	int err;
4892eb7188caSLydia Wang 
4893eb7188caSLydia Wang 	/* create a codec specific record */
4894eb7188caSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
4895eb7188caSLydia Wang 	if (spec == NULL)
4896eb7188caSLydia Wang 		return -ENOMEM;
4897eb7188caSLydia Wang 
4898eb7188caSLydia Wang 	codec->spec = spec;
4899eb7188caSLydia Wang 
4900eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4901eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4902eb7188caSLydia Wang 	if (err < 0) {
4903eb7188caSLydia Wang 		via_free(codec);
4904eb7188caSLydia Wang 		return err;
4905eb7188caSLydia Wang 	} else if (!err) {
4906eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4907eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4908eb7188caSLydia Wang 	}
4909eb7188caSLydia Wang 
4910eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4911eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4912eb7188caSLydia Wang 
4913bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4914bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4915bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4916bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4917bb3c6bfcSLydia Wang 	else
4918eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4919eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4920eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4921eb7188caSLydia Wang 
4922bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4923bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4924bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4925bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4926bb3c6bfcSLydia Wang 	else
4927eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4928eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4929bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4930eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4931eb7188caSLydia Wang 
4932eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4933eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4934eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4935eb7188caSLydia Wang 		get_mux_nids(codec);
4936bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4937bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4938eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4939eb7188caSLydia Wang 		spec->num_mixers++;
4940eb7188caSLydia Wang 	}
4941eb7188caSLydia Wang 
4942eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4943eb7188caSLydia Wang 
4944eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
4945eb7188caSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
4946eb7188caSLydia Wang 
4947eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4948eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4949eb7188caSLydia Wang #endif
4950eb7188caSLydia Wang 
4951eb7188caSLydia Wang 	return 0;
4952eb7188caSLydia Wang }
4953f3db423dSLydia Wang 
4954f3db423dSLydia Wang /* Patch for VT1716S */
4955f3db423dSLydia Wang 
4956f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4957f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4958f3db423dSLydia Wang {
4959f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4960f3db423dSLydia Wang 	uinfo->count = 1;
4961f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4962f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4963f3db423dSLydia Wang 	return 0;
4964f3db423dSLydia Wang }
4965f3db423dSLydia Wang 
4966f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4967f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4968f3db423dSLydia Wang {
4969f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4970f3db423dSLydia Wang 	int index = 0;
4971f3db423dSLydia Wang 
4972f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4973f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4974f3db423dSLydia Wang 	if (index != -1)
4975f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4976f3db423dSLydia Wang 
4977f3db423dSLydia Wang 	return 0;
4978f3db423dSLydia Wang }
4979f3db423dSLydia Wang 
4980f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4981f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4982f3db423dSLydia Wang {
4983f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4984f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4985f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4986f3db423dSLydia Wang 
4987f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4988f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4989f3db423dSLydia Wang 	spec->dmic_enabled = index;
4990f3db423dSLydia Wang 	set_jack_power_state(codec);
4991f3db423dSLydia Wang 
4992f3db423dSLydia Wang 	return 1;
4993f3db423dSLydia Wang }
4994f3db423dSLydia Wang 
4995f3db423dSLydia Wang /* capture mixer elements */
4996f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4997f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4998f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4999f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
5000f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
5001f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
5002f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
5003f3db423dSLydia Wang 			 HDA_INPUT),
5004f3db423dSLydia Wang 	{
5005f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5006f3db423dSLydia Wang 		.name = "Input Source",
5007f3db423dSLydia Wang 		.count = 1,
5008f3db423dSLydia Wang 		.info = via_mux_enum_info,
5009f3db423dSLydia Wang 		.get = via_mux_enum_get,
5010f3db423dSLydia Wang 		.put = via_mux_enum_put,
5011f3db423dSLydia Wang 	},
5012f3db423dSLydia Wang 	{ } /* end */
5013f3db423dSLydia Wang };
5014f3db423dSLydia Wang 
5015f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
5016f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
5017f3db423dSLydia Wang 	{
5018f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5019f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
5020f3db423dSLydia Wang 	 .count = 1,
5021f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
5022f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
5023f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
5024f3db423dSLydia Wang 	 },
5025f3db423dSLydia Wang 	{}			/* end */
5026f3db423dSLydia Wang };
5027f3db423dSLydia Wang 
5028f3db423dSLydia Wang 
5029f3db423dSLydia Wang /* mono-out mixer elements */
5030f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
5031f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
5032f3db423dSLydia Wang 	{ } /* end */
5033f3db423dSLydia Wang };
5034f3db423dSLydia Wang 
5035f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
5036f3db423dSLydia Wang 	/*
5037f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5038f3db423dSLydia Wang 	 */
5039f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5040f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5041f3db423dSLydia Wang 
5042f3db423dSLydia Wang 
5043f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5044f3db423dSLydia Wang 	 * mixer widget
5045f3db423dSLydia Wang 	 */
5046f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5047f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5048f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5049f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5050f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5051f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5052f3db423dSLydia Wang 
5053f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
5054f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
5055f3db423dSLydia Wang 
5056f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
5057f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
5058f3db423dSLydia Wang 
5059f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
5060f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
5061f3db423dSLydia Wang 
5062f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
5063f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
5064f3db423dSLydia Wang 
5065f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
5066f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5067f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5068f3db423dSLydia Wang 
5069f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
5070f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5071f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5072f3db423dSLydia Wang 	/* PW12 Output enable */
5073f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5074f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
5075f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
5076f3db423dSLydia Wang 	/* don't bybass mixer */
5077f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
5078f3db423dSLydia Wang 	/* Enable mono output */
5079f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
5080f3db423dSLydia Wang 	{ }
5081f3db423dSLydia Wang };
5082f3db423dSLydia Wang 
5083f3db423dSLydia Wang 
5084f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
5085f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
5086f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
5087f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5088f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5089f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5090f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
5091f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
5092f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5093f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5094f3db423dSLydia Wang 	{ }
5095f3db423dSLydia Wang };
5096f3db423dSLydia Wang 
5097f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
5098f3db423dSLydia Wang 	.substreams = 2,
5099f3db423dSLydia Wang 	.channels_min = 2,
5100f3db423dSLydia Wang 	.channels_max = 6,
5101f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5102f3db423dSLydia Wang 	.ops = {
5103f3db423dSLydia Wang 		.open = via_playback_pcm_open,
5104f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5105f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5106f3db423dSLydia Wang 		.close = via_pcm_open_close,
5107f3db423dSLydia Wang 	},
5108f3db423dSLydia Wang };
5109f3db423dSLydia Wang 
5110f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
5111f3db423dSLydia Wang 	.substreams = 2,
5112f3db423dSLydia Wang 	.channels_min = 2,
5113f3db423dSLydia Wang 	.channels_max = 2,
5114f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
5115f3db423dSLydia Wang 	.ops = {
5116f3db423dSLydia Wang 		.open = via_pcm_open_close,
5117f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
5118f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5119f3db423dSLydia Wang 		.close = via_pcm_open_close,
5120f3db423dSLydia Wang 	},
5121f3db423dSLydia Wang };
5122f3db423dSLydia Wang 
5123f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
5124f3db423dSLydia Wang 	.substreams = 2,
5125f3db423dSLydia Wang 	.channels_min = 2,
5126f3db423dSLydia Wang 	.channels_max = 2,
5127f3db423dSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
5128f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
5129f3db423dSLydia Wang 	.ops = {
5130f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
5131f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
5132f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5133f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5134f3db423dSLydia Wang 	},
5135f3db423dSLydia Wang };
5136f3db423dSLydia Wang 
5137f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5138f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
5139f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
5140f3db423dSLydia Wang {	int i;
5141f3db423dSLydia Wang 	hda_nid_t nid;
5142f3db423dSLydia Wang 
5143f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
5144f3db423dSLydia Wang 
5145f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5146f3db423dSLydia Wang 
5147f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
5148f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5149f3db423dSLydia Wang 		if (nid) {
5150f3db423dSLydia Wang 			/* config dac list */
5151f3db423dSLydia Wang 			switch (i) {
5152f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
5153f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
5154f3db423dSLydia Wang 				break;
5155f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
5156f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
5157f3db423dSLydia Wang 				break;
5158f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
5159f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
5160f3db423dSLydia Wang 				break;
5161f3db423dSLydia Wang 			}
5162f3db423dSLydia Wang 		}
5163f3db423dSLydia Wang 	}
5164f3db423dSLydia Wang 
5165f3db423dSLydia Wang 	return 0;
5166f3db423dSLydia Wang }
5167f3db423dSLydia Wang 
5168f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
5169f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
5170f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
5171f3db423dSLydia Wang {
5172f3db423dSLydia Wang 	char name[32];
5173f3db423dSLydia Wang 	static const char *chname[3] = { "Front", "Surround", "C/LFE" };
5174f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
5175f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
5176f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
5177f3db423dSLydia Wang 	int i, err;
5178f3db423dSLydia Wang 
5179f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
5180f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5181f3db423dSLydia Wang 
5182f3db423dSLydia Wang 		if (!nid)
5183f3db423dSLydia Wang 			continue;
5184f3db423dSLydia Wang 
5185f3db423dSLydia Wang 		nid_vol = nid_vols[i];
5186f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
5187f3db423dSLydia Wang 
5188f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
5189f3db423dSLydia Wang 			err = via_add_control(
5190f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5191f3db423dSLydia Wang 				"Center Playback Volume",
5192f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
5193f3db423dSLydia Wang 			if (err < 0)
5194f3db423dSLydia Wang 				return err;
5195f3db423dSLydia Wang 			err = via_add_control(
5196f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5197f3db423dSLydia Wang 				"LFE Playback Volume",
5198f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
5199f3db423dSLydia Wang 			if (err < 0)
5200f3db423dSLydia Wang 				return err;
5201f3db423dSLydia Wang 			err = via_add_control(
5202f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5203f3db423dSLydia Wang 				"Center Playback Switch",
5204f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
5205f3db423dSLydia Wang 						    HDA_OUTPUT));
5206f3db423dSLydia Wang 			if (err < 0)
5207f3db423dSLydia Wang 				return err;
5208f3db423dSLydia Wang 			err = via_add_control(
5209f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5210f3db423dSLydia Wang 				"LFE Playback Switch",
5211f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
5212f3db423dSLydia Wang 						    HDA_OUTPUT));
5213f3db423dSLydia Wang 			if (err < 0)
5214f3db423dSLydia Wang 				return err;
5215f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
5216f3db423dSLydia Wang 
5217f3db423dSLydia Wang 			err = via_add_control(
5218f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5219f3db423dSLydia Wang 				"Master Front Playback Volume",
5220f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5221f3db423dSLydia Wang 			if (err < 0)
5222f3db423dSLydia Wang 				return err;
5223f3db423dSLydia Wang 			err = via_add_control(
5224f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5225f3db423dSLydia Wang 				"Master Front Playback Switch",
5226f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5227f3db423dSLydia Wang 			if (err < 0)
5228f3db423dSLydia Wang 				return err;
5229f3db423dSLydia Wang 
5230f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5231f3db423dSLydia Wang 			err = via_add_control(
5232f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5233f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5234f3db423dSLydia Wang 			if (err < 0)
5235f3db423dSLydia Wang 				return err;
5236f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5237f3db423dSLydia Wang 			err = via_add_control(
5238f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5239f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5240f3db423dSLydia Wang 						    HDA_OUTPUT));
5241f3db423dSLydia Wang 			if (err < 0)
5242f3db423dSLydia Wang 				return err;
5243f3db423dSLydia Wang 		} else {
5244f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5245f3db423dSLydia Wang 			err = via_add_control(
5246f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5247f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5248f3db423dSLydia Wang 			if (err < 0)
5249f3db423dSLydia Wang 				return err;
5250f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5251f3db423dSLydia Wang 			err = via_add_control(
5252f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5253f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5254f3db423dSLydia Wang 						    HDA_OUTPUT));
5255f3db423dSLydia Wang 			if (err < 0)
5256f3db423dSLydia Wang 				return err;
5257f3db423dSLydia Wang 		}
5258f3db423dSLydia Wang 	}
5259f3db423dSLydia Wang 	return 0;
5260f3db423dSLydia Wang }
5261f3db423dSLydia Wang 
5262f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5263f3db423dSLydia Wang {
5264f3db423dSLydia Wang 	int err;
5265f3db423dSLydia Wang 
5266f3db423dSLydia Wang 	if (!pin)
5267f3db423dSLydia Wang 		return 0;
5268f3db423dSLydia Wang 
5269f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5270f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5271f3db423dSLydia Wang 
5272f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5273f3db423dSLydia Wang 			      "Headphone Playback Volume",
5274f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5275f3db423dSLydia Wang 	if (err < 0)
5276f3db423dSLydia Wang 		return err;
5277f3db423dSLydia Wang 
5278f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5279f3db423dSLydia Wang 			      "Headphone Playback Switch",
5280f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5281f3db423dSLydia Wang 	if (err < 0)
5282f3db423dSLydia Wang 		return err;
5283f3db423dSLydia Wang 
5284f3db423dSLydia Wang 	create_hp_imux(spec);
5285f3db423dSLydia Wang 	return 0;
5286f3db423dSLydia Wang }
5287f3db423dSLydia Wang 
5288f3db423dSLydia Wang /* create playback/capture controls for input pins */
5289f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
5290f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5291f3db423dSLydia Wang {
5292f3db423dSLydia Wang 	static char *labels[] = {
5293f3db423dSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5294f3db423dSLydia Wang 	};
5295f3db423dSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5296f3db423dSLydia Wang 	int i, err, idx = 0;
5297f3db423dSLydia Wang 
5298f3db423dSLydia Wang 	/* for internal loopback recording select */
5299f3db423dSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
5300f3db423dSLydia Wang 	imux->items[imux->num_items].index = 5;
5301f3db423dSLydia Wang 	imux->num_items++;
5302f3db423dSLydia Wang 
5303f3db423dSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
5304f3db423dSLydia Wang 		if (!cfg->input_pins[i])
5305f3db423dSLydia Wang 			continue;
5306f3db423dSLydia Wang 
5307f3db423dSLydia Wang 		switch (cfg->input_pins[i]) {
5308f3db423dSLydia Wang 		case 0x1a: /* Mic */
5309f3db423dSLydia Wang 			idx = 2;
5310f3db423dSLydia Wang 			break;
5311f3db423dSLydia Wang 
5312f3db423dSLydia Wang 		case 0x1b: /* Line In */
5313f3db423dSLydia Wang 			idx = 3;
5314f3db423dSLydia Wang 			break;
5315f3db423dSLydia Wang 
5316f3db423dSLydia Wang 		case 0x1e: /* Front Mic */
5317f3db423dSLydia Wang 			idx = 4;
5318f3db423dSLydia Wang 			break;
5319f3db423dSLydia Wang 
5320f3db423dSLydia Wang 		case 0x1f: /* CD */
5321f3db423dSLydia Wang 			idx = 1;
5322f3db423dSLydia Wang 			break;
5323f3db423dSLydia Wang 		}
5324f3db423dSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
5325f3db423dSLydia Wang 		if (err < 0)
5326f3db423dSLydia Wang 			return err;
5327f3db423dSLydia Wang 		imux->items[imux->num_items].label = labels[i];
5328f3db423dSLydia Wang 		imux->items[imux->num_items].index = idx-1;
5329f3db423dSLydia Wang 		imux->num_items++;
5330f3db423dSLydia Wang 	}
5331f3db423dSLydia Wang 	return 0;
5332f3db423dSLydia Wang }
5333f3db423dSLydia Wang 
5334f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5335f3db423dSLydia Wang {
5336f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5337f3db423dSLydia Wang 	int err;
5338f3db423dSLydia Wang 
5339f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5340f3db423dSLydia Wang 	if (err < 0)
5341f3db423dSLydia Wang 		return err;
5342f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5343f3db423dSLydia Wang 	if (err < 0)
5344f3db423dSLydia Wang 		return err;
5345f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5346f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5347f3db423dSLydia Wang 
5348f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5349f3db423dSLydia Wang 	if (err < 0)
5350f3db423dSLydia Wang 		return err;
5351f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5352f3db423dSLydia Wang 	if (err < 0)
5353f3db423dSLydia Wang 		return err;
5354f3db423dSLydia Wang 	err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
5355f3db423dSLydia Wang 	if (err < 0)
5356f3db423dSLydia Wang 		return err;
5357f3db423dSLydia Wang 
5358f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5359f3db423dSLydia Wang 
5360f3db423dSLydia Wang 	fill_dig_outs(codec);
5361f3db423dSLydia Wang 
5362f3db423dSLydia Wang 	if (spec->kctls.list)
5363f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5364f3db423dSLydia Wang 
5365f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5366f3db423dSLydia Wang 
5367f3db423dSLydia Wang 	if (spec->hp_mux)
5368f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
5369f3db423dSLydia Wang 
5370f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = via_smart51_mixer;
5371f3db423dSLydia Wang 
5372f3db423dSLydia Wang 	return 1;
5373f3db423dSLydia Wang }
5374f3db423dSLydia Wang 
5375f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5376f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5377f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5378f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5379f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5380f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5381f3db423dSLydia Wang 	{ } /* end */
5382f3db423dSLydia Wang };
5383f3db423dSLydia Wang #endif
5384f3db423dSLydia Wang 
5385f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5386f3db423dSLydia Wang {
5387f3db423dSLydia Wang 	struct via_spec *spec;
5388f3db423dSLydia Wang 	int err;
5389f3db423dSLydia Wang 
5390f3db423dSLydia Wang 	/* create a codec specific record */
5391f3db423dSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
5392f3db423dSLydia Wang 	if (spec == NULL)
5393f3db423dSLydia Wang 		return -ENOMEM;
5394f3db423dSLydia Wang 
5395f3db423dSLydia Wang 	codec->spec = spec;
5396f3db423dSLydia Wang 
5397f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5398f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5399f3db423dSLydia Wang 	if (err < 0) {
5400f3db423dSLydia Wang 		via_free(codec);
5401f3db423dSLydia Wang 		return err;
5402f3db423dSLydia Wang 	} else if (!err) {
5403f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5404f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5405f3db423dSLydia Wang 	}
5406f3db423dSLydia Wang 
5407f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5408f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5409f3db423dSLydia Wang 
5410f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5411f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5412f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5413f3db423dSLydia Wang 
5414f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5415f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5416f3db423dSLydia Wang 
5417f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5418f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5419f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5420f3db423dSLydia Wang 		get_mux_nids(codec);
5421f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5422f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5423f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5424f3db423dSLydia Wang 		spec->num_mixers++;
5425f3db423dSLydia Wang 	}
5426f3db423dSLydia Wang 
5427f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5428f3db423dSLydia Wang 	spec->num_mixers++;
5429f3db423dSLydia Wang 
5430f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5431f3db423dSLydia Wang 
5432f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5433f3db423dSLydia Wang 
5434f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
5435f3db423dSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
5436f3db423dSLydia Wang 
5437f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5438f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5439f3db423dSLydia Wang #endif
5440f3db423dSLydia Wang 
5441f3db423dSLydia Wang 	return 0;
5442f3db423dSLydia Wang }
544325eaba2fSLydia Wang 
544425eaba2fSLydia Wang /* for vt2002P */
544525eaba2fSLydia Wang 
544625eaba2fSLydia Wang /* capture mixer elements */
544725eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
544825eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
544925eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
545025eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
545125eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
545225eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
545325eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
545425eaba2fSLydia Wang 			 HDA_INPUT),
545525eaba2fSLydia Wang 	{
545625eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
545725eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
545825eaba2fSLydia Wang 		 * So call somewhat different..
545925eaba2fSLydia Wang 		 */
546025eaba2fSLydia Wang 		/* .name = "Capture Source", */
546125eaba2fSLydia Wang 		.name = "Input Source",
546225eaba2fSLydia Wang 		.count = 2,
546325eaba2fSLydia Wang 		.info = via_mux_enum_info,
546425eaba2fSLydia Wang 		.get = via_mux_enum_get,
546525eaba2fSLydia Wang 		.put = via_mux_enum_put,
546625eaba2fSLydia Wang 	},
546725eaba2fSLydia Wang 	{ } /* end */
546825eaba2fSLydia Wang };
546925eaba2fSLydia Wang 
547025eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
547125eaba2fSLydia Wang 	/*
547225eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
547325eaba2fSLydia Wang 	 */
547425eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
547525eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
547625eaba2fSLydia Wang 
547725eaba2fSLydia Wang 
547825eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
547925eaba2fSLydia Wang 	 * mixer widget
548025eaba2fSLydia Wang 	 */
548125eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
548225eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
548325eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
548425eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
548525eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
548625eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
548725eaba2fSLydia Wang 
548825eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
548925eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
549025eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
549125eaba2fSLydia Wang 
549225eaba2fSLydia Wang 	/* PW9 Output enable */
549325eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
549425eaba2fSLydia Wang 
549525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
549625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
549725eaba2fSLydia Wang 
549825eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
549925eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
550025eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
550125eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
550225eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
550325eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
550425eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
550525eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
550625eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
550725eaba2fSLydia Wang 
550825eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
550925eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
551025eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
551125eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
551225eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
551325eaba2fSLydia Wang 
551425eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
551525eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
551625eaba2fSLydia Wang 
551725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
551825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
551925eaba2fSLydia Wang 	{ }
552025eaba2fSLydia Wang };
552125eaba2fSLydia Wang 
552225eaba2fSLydia Wang 
552325eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
552425eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
552525eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
552625eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
552725eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
552825eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
552925eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
553025eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
553125eaba2fSLydia Wang 	{ }
553225eaba2fSLydia Wang };
553325eaba2fSLydia Wang 
553425eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
553525eaba2fSLydia Wang 	.substreams = 2,
553625eaba2fSLydia Wang 	.channels_min = 2,
553725eaba2fSLydia Wang 	.channels_max = 2,
553825eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
553925eaba2fSLydia Wang 	.ops = {
554025eaba2fSLydia Wang 		.open = via_playback_pcm_open,
554125eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
554225eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
554325eaba2fSLydia Wang 		.close = via_pcm_open_close,
554425eaba2fSLydia Wang 	},
554525eaba2fSLydia Wang };
554625eaba2fSLydia Wang 
554725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
554825eaba2fSLydia Wang 	.substreams = 2,
554925eaba2fSLydia Wang 	.channels_min = 2,
555025eaba2fSLydia Wang 	.channels_max = 2,
555125eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
555225eaba2fSLydia Wang 	.ops = {
555325eaba2fSLydia Wang 		.open = via_pcm_open_close,
555425eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
555525eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
555625eaba2fSLydia Wang 		.close = via_pcm_open_close,
555725eaba2fSLydia Wang 	},
555825eaba2fSLydia Wang };
555925eaba2fSLydia Wang 
556025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
556125eaba2fSLydia Wang 	.substreams = 1,
556225eaba2fSLydia Wang 	.channels_min = 2,
556325eaba2fSLydia Wang 	.channels_max = 2,
556425eaba2fSLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
556525eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
556625eaba2fSLydia Wang 	.ops = {
556725eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
556825eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
556925eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
557025eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
557125eaba2fSLydia Wang 	},
557225eaba2fSLydia Wang };
557325eaba2fSLydia Wang 
557425eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
557525eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
557625eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
557725eaba2fSLydia Wang {
557825eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
557925eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
558025eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
558125eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
558225eaba2fSLydia Wang 	return 0;
558325eaba2fSLydia Wang }
558425eaba2fSLydia Wang 
558525eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
558625eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
558725eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
558825eaba2fSLydia Wang {
558925eaba2fSLydia Wang 	int err;
559025eaba2fSLydia Wang 
559125eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
559225eaba2fSLydia Wang 		return -1;
559325eaba2fSLydia Wang 
559425eaba2fSLydia Wang 
559525eaba2fSLydia Wang 	/* Line-Out: PortE */
559625eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
559725eaba2fSLydia Wang 			      "Master Front Playback Volume",
559825eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
559925eaba2fSLydia Wang 	if (err < 0)
560025eaba2fSLydia Wang 		return err;
560125eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
560225eaba2fSLydia Wang 			      "Master Front Playback Switch",
560325eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
560425eaba2fSLydia Wang 	if (err < 0)
560525eaba2fSLydia Wang 		return err;
560625eaba2fSLydia Wang 
560725eaba2fSLydia Wang 	return 0;
560825eaba2fSLydia Wang }
560925eaba2fSLydia Wang 
561025eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
561125eaba2fSLydia Wang {
561225eaba2fSLydia Wang 	int err;
561325eaba2fSLydia Wang 
561425eaba2fSLydia Wang 	if (!pin)
561525eaba2fSLydia Wang 		return 0;
561625eaba2fSLydia Wang 
561725eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
561825eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
561925eaba2fSLydia Wang 
562025eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
562125eaba2fSLydia Wang 			      "Headphone Playback Volume",
562225eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
562325eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
562425eaba2fSLydia Wang 	if (err < 0)
562525eaba2fSLydia Wang 		return err;
562625eaba2fSLydia Wang 
562725eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
562825eaba2fSLydia Wang 			      "Headphone Playback Switch",
562925eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
563025eaba2fSLydia Wang 	if (err < 0)
563125eaba2fSLydia Wang 		return err;
563225eaba2fSLydia Wang 
563325eaba2fSLydia Wang 	create_hp_imux(spec);
563425eaba2fSLydia Wang 	return 0;
563525eaba2fSLydia Wang }
563625eaba2fSLydia Wang 
563725eaba2fSLydia Wang /* create playback/capture controls for input pins */
563825eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
563925eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
564025eaba2fSLydia Wang {
564125eaba2fSLydia Wang 	static char *labels[] = {
564225eaba2fSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
564325eaba2fSLydia Wang 	};
564425eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
564525eaba2fSLydia Wang 	int i, err, idx = 0;
564625eaba2fSLydia Wang 
564725eaba2fSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
564825eaba2fSLydia Wang 		if (!cfg->input_pins[i])
564925eaba2fSLydia Wang 			continue;
565025eaba2fSLydia Wang 
565125eaba2fSLydia Wang 		switch (cfg->input_pins[i]) {
565225eaba2fSLydia Wang 		case 0x2b: /* Mic */
565325eaba2fSLydia Wang 			idx = 0;
565425eaba2fSLydia Wang 			break;
565525eaba2fSLydia Wang 
565625eaba2fSLydia Wang 		case 0x2a: /* Line In */
565725eaba2fSLydia Wang 			idx = 1;
565825eaba2fSLydia Wang 			break;
565925eaba2fSLydia Wang 
566025eaba2fSLydia Wang 		case 0x29: /* Front Mic */
566125eaba2fSLydia Wang 			idx = 2;
566225eaba2fSLydia Wang 			break;
566325eaba2fSLydia Wang 		}
566425eaba2fSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
566525eaba2fSLydia Wang 		if (err < 0)
566625eaba2fSLydia Wang 			return err;
566725eaba2fSLydia Wang 		imux->items[imux->num_items].label = labels[i];
566825eaba2fSLydia Wang 		imux->items[imux->num_items].index = idx;
566925eaba2fSLydia Wang 		imux->num_items++;
567025eaba2fSLydia Wang 	}
567125eaba2fSLydia Wang 
567225eaba2fSLydia Wang 	/* build volume/mute control of loopback */
567325eaba2fSLydia Wang 	err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
567425eaba2fSLydia Wang 	if (err < 0)
567525eaba2fSLydia Wang 		return err;
567625eaba2fSLydia Wang 
567725eaba2fSLydia Wang 	/* for internal loopback recording select */
567825eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
567925eaba2fSLydia Wang 	imux->items[imux->num_items].index = 3;
568025eaba2fSLydia Wang 	imux->num_items++;
568125eaba2fSLydia Wang 
568225eaba2fSLydia Wang 	/* for digital mic select */
568325eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Digital Mic";
568425eaba2fSLydia Wang 	imux->items[imux->num_items].index = 4;
568525eaba2fSLydia Wang 	imux->num_items++;
568625eaba2fSLydia Wang 
568725eaba2fSLydia Wang 	return 0;
568825eaba2fSLydia Wang }
568925eaba2fSLydia Wang 
569025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
569125eaba2fSLydia Wang {
569225eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
569325eaba2fSLydia Wang 	int err;
569425eaba2fSLydia Wang 
569525eaba2fSLydia Wang 
569625eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
569725eaba2fSLydia Wang 	if (err < 0)
569825eaba2fSLydia Wang 		return err;
569925eaba2fSLydia Wang 
570025eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
570125eaba2fSLydia Wang 	if (err < 0)
570225eaba2fSLydia Wang 		return err;
570325eaba2fSLydia Wang 
570425eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
570525eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
570625eaba2fSLydia Wang 
570725eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
570825eaba2fSLydia Wang 	if (err < 0)
570925eaba2fSLydia Wang 		return err;
571025eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
571125eaba2fSLydia Wang 	if (err < 0)
571225eaba2fSLydia Wang 		return err;
571325eaba2fSLydia Wang 	err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
571425eaba2fSLydia Wang 	if (err < 0)
571525eaba2fSLydia Wang 		return err;
571625eaba2fSLydia Wang 
571725eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
571825eaba2fSLydia Wang 
571925eaba2fSLydia Wang 	fill_dig_outs(codec);
572025eaba2fSLydia Wang 
572125eaba2fSLydia Wang 	if (spec->kctls.list)
572225eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
572325eaba2fSLydia Wang 
572425eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
572525eaba2fSLydia Wang 
572625eaba2fSLydia Wang 	if (spec->hp_mux)
572725eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
572825eaba2fSLydia Wang 
572925eaba2fSLydia Wang 	return 1;
573025eaba2fSLydia Wang }
573125eaba2fSLydia Wang 
573225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
573325eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
573425eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
573525eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
573625eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
573725eaba2fSLydia Wang 	{ } /* end */
573825eaba2fSLydia Wang };
573925eaba2fSLydia Wang #endif
574025eaba2fSLydia Wang 
574125eaba2fSLydia Wang 
574225eaba2fSLydia Wang /* patch for vt2002P */
574325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
574425eaba2fSLydia Wang {
574525eaba2fSLydia Wang 	struct via_spec *spec;
574625eaba2fSLydia Wang 	int err;
574725eaba2fSLydia Wang 
574825eaba2fSLydia Wang 	/* create a codec specific record */
574925eaba2fSLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
575025eaba2fSLydia Wang 	if (spec == NULL)
575125eaba2fSLydia Wang 		return -ENOMEM;
575225eaba2fSLydia Wang 
575325eaba2fSLydia Wang 	codec->spec = spec;
575425eaba2fSLydia Wang 
575525eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
575625eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
575725eaba2fSLydia Wang 	if (err < 0) {
575825eaba2fSLydia Wang 		via_free(codec);
575925eaba2fSLydia Wang 		return err;
576025eaba2fSLydia Wang 	} else if (!err) {
576125eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
576225eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
576325eaba2fSLydia Wang 	}
576425eaba2fSLydia Wang 
576525eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
576625eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
576725eaba2fSLydia Wang 
576825eaba2fSLydia Wang 	spec->stream_name_analog = "VT2002P Analog";
576925eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
577025eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
577125eaba2fSLydia Wang 
577225eaba2fSLydia Wang 	spec->stream_name_digital = "VT2002P Digital";
577325eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
577425eaba2fSLydia Wang 
577525eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
577625eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
577725eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
577825eaba2fSLydia Wang 		get_mux_nids(codec);
577925eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
578025eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
578125eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
578225eaba2fSLydia Wang 		spec->num_mixers++;
578325eaba2fSLydia Wang 	}
578425eaba2fSLydia Wang 
578525eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
578625eaba2fSLydia Wang 
578725eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
578825eaba2fSLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
578925eaba2fSLydia Wang 
579025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
579125eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
579225eaba2fSLydia Wang #endif
579325eaba2fSLydia Wang 
579425eaba2fSLydia Wang 	return 0;
579525eaba2fSLydia Wang }
5796ab6734e7SLydia Wang 
5797ab6734e7SLydia Wang /* for vt1812 */
5798ab6734e7SLydia Wang 
5799ab6734e7SLydia Wang /* capture mixer elements */
5800ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5801ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5802ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5803ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5804ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5805ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5806ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5807ab6734e7SLydia Wang 		       HDA_INPUT),
5808ab6734e7SLydia Wang 	{
5809ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5810ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5811ab6734e7SLydia Wang 		 * So call somewhat different..
5812ab6734e7SLydia Wang 		 */
5813ab6734e7SLydia Wang 		.name = "Input Source",
5814ab6734e7SLydia Wang 		.count = 2,
5815ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5816ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5817ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5818ab6734e7SLydia Wang 	},
5819ab6734e7SLydia Wang 	{ } /* end */
5820ab6734e7SLydia Wang };
5821ab6734e7SLydia Wang 
5822ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = {
5823ab6734e7SLydia Wang 	/*
5824ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5825ab6734e7SLydia Wang 	 */
5826ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5827ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5828ab6734e7SLydia Wang 
5829ab6734e7SLydia Wang 
5830ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5831ab6734e7SLydia Wang 	 * mixer widget
5832ab6734e7SLydia Wang 	 */
5833ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5834ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5835ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5836ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5837ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5838ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5839ab6734e7SLydia Wang 
5840ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5841ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5842ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5843ab6734e7SLydia Wang 
5844ab6734e7SLydia Wang 	/* PW9 Output enable */
5845ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5846ab6734e7SLydia Wang 
5847ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5848ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5849ab6734e7SLydia Wang 
5850ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5851ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5852ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5853ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5854ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5855ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5856ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5857ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5858ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5859ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5860ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5861ab6734e7SLydia Wang 
5862ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5863ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5864ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5865ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5866ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5867ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5868ab6734e7SLydia Wang 
5869ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5870ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5871ab6734e7SLydia Wang 	{ }
5872ab6734e7SLydia Wang };
5873ab6734e7SLydia Wang 
5874ab6734e7SLydia Wang 
5875ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = {
5876ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5877ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5878ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5879ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5880ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5881ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5882ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5883ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5884ab6734e7SLydia Wang 	{ }
5885ab6734e7SLydia Wang };
5886ab6734e7SLydia Wang 
5887ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5888ab6734e7SLydia Wang 	.substreams = 2,
5889ab6734e7SLydia Wang 	.channels_min = 2,
5890ab6734e7SLydia Wang 	.channels_max = 2,
5891ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5892ab6734e7SLydia Wang 	.ops = {
5893ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5894ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5895ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5896ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5897ab6734e7SLydia Wang 	},
5898ab6734e7SLydia Wang };
5899ab6734e7SLydia Wang 
5900ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5901ab6734e7SLydia Wang 	.substreams = 2,
5902ab6734e7SLydia Wang 	.channels_min = 2,
5903ab6734e7SLydia Wang 	.channels_max = 2,
5904ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5905ab6734e7SLydia Wang 	.ops = {
5906ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5907ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5908ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5909ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5910ab6734e7SLydia Wang 	},
5911ab6734e7SLydia Wang };
5912ab6734e7SLydia Wang 
5913ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5914ab6734e7SLydia Wang 	.substreams = 1,
5915ab6734e7SLydia Wang 	.channels_min = 2,
5916ab6734e7SLydia Wang 	.channels_max = 2,
5917ab6734e7SLydia Wang 	.rates = SNDRV_PCM_RATE_48000,
5918ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5919ab6734e7SLydia Wang 	.ops = {
5920ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5921ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5922ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5923ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5924ab6734e7SLydia Wang 	},
5925ab6734e7SLydia Wang };
5926ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5927ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5928ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5929ab6734e7SLydia Wang {
5930ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5931ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5932ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5933ab6734e7SLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5934ab6734e7SLydia Wang 	return 0;
5935ab6734e7SLydia Wang }
5936ab6734e7SLydia Wang 
5937ab6734e7SLydia Wang 
5938ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5939ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5940ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5941ab6734e7SLydia Wang {
5942ab6734e7SLydia Wang 	int err;
5943ab6734e7SLydia Wang 
5944ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
5945ab6734e7SLydia Wang 		return -1;
5946ab6734e7SLydia Wang 
5947ab6734e7SLydia Wang 	/* Line-Out: PortE */
5948ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5949ab6734e7SLydia Wang 			      "Master Front Playback Volume",
5950ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5951ab6734e7SLydia Wang 	if (err < 0)
5952ab6734e7SLydia Wang 		return err;
5953ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
5954ab6734e7SLydia Wang 			      "Master Front Playback Switch",
5955ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5956ab6734e7SLydia Wang 	if (err < 0)
5957ab6734e7SLydia Wang 		return err;
5958ab6734e7SLydia Wang 
5959ab6734e7SLydia Wang 	return 0;
5960ab6734e7SLydia Wang }
5961ab6734e7SLydia Wang 
5962ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5963ab6734e7SLydia Wang {
5964ab6734e7SLydia Wang 	int err;
5965ab6734e7SLydia Wang 
5966ab6734e7SLydia Wang 	if (!pin)
5967ab6734e7SLydia Wang 		return 0;
5968ab6734e7SLydia Wang 
5969ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
5970ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
5971ab6734e7SLydia Wang 
5972ab6734e7SLydia Wang 
5973ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5974ab6734e7SLydia Wang 			      "Headphone Playback Volume",
5975ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5976ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5977ab6734e7SLydia Wang 	if (err < 0)
5978ab6734e7SLydia Wang 		return err;
5979ab6734e7SLydia Wang 
5980ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5981ab6734e7SLydia Wang 			      "Headphone Playback Switch",
5982ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5983ab6734e7SLydia Wang 	if (err < 0)
5984ab6734e7SLydia Wang 		return err;
5985ab6734e7SLydia Wang 
5986ab6734e7SLydia Wang 	create_hp_imux(spec);
5987ab6734e7SLydia Wang 	return 0;
5988ab6734e7SLydia Wang }
5989ab6734e7SLydia Wang 
5990ab6734e7SLydia Wang /* create playback/capture controls for input pins */
5991ab6734e7SLydia Wang static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
5992ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
5993ab6734e7SLydia Wang {
5994ab6734e7SLydia Wang 	static char *labels[] = {
5995ab6734e7SLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5996ab6734e7SLydia Wang 	};
5997ab6734e7SLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5998ab6734e7SLydia Wang 	int i, err, idx = 0;
5999ab6734e7SLydia Wang 
6000ab6734e7SLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
6001ab6734e7SLydia Wang 		if (!cfg->input_pins[i])
6002ab6734e7SLydia Wang 			continue;
6003ab6734e7SLydia Wang 
6004ab6734e7SLydia Wang 		switch (cfg->input_pins[i]) {
6005ab6734e7SLydia Wang 		case 0x2b: /* Mic */
6006ab6734e7SLydia Wang 			idx = 0;
6007ab6734e7SLydia Wang 			break;
6008ab6734e7SLydia Wang 
6009ab6734e7SLydia Wang 		case 0x2a: /* Line In */
6010ab6734e7SLydia Wang 			idx = 1;
6011ab6734e7SLydia Wang 			break;
6012ab6734e7SLydia Wang 
6013ab6734e7SLydia Wang 		case 0x29: /* Front Mic */
6014ab6734e7SLydia Wang 			idx = 2;
6015ab6734e7SLydia Wang 			break;
6016ab6734e7SLydia Wang 		}
6017ab6734e7SLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
6018ab6734e7SLydia Wang 		if (err < 0)
6019ab6734e7SLydia Wang 			return err;
6020ab6734e7SLydia Wang 		imux->items[imux->num_items].label = labels[i];
6021ab6734e7SLydia Wang 		imux->items[imux->num_items].index = idx;
6022ab6734e7SLydia Wang 		imux->num_items++;
6023ab6734e7SLydia Wang 	}
6024ab6734e7SLydia Wang 	/* build volume/mute control of loopback */
6025ab6734e7SLydia Wang 	err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
6026ab6734e7SLydia Wang 	if (err < 0)
6027ab6734e7SLydia Wang 		return err;
6028ab6734e7SLydia Wang 
6029ab6734e7SLydia Wang 	/* for internal loopback recording select */
6030ab6734e7SLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
6031ab6734e7SLydia Wang 	imux->items[imux->num_items].index = 5;
6032ab6734e7SLydia Wang 	imux->num_items++;
6033ab6734e7SLydia Wang 
6034ab6734e7SLydia Wang 	/* for digital mic select */
6035ab6734e7SLydia Wang 	imux->items[imux->num_items].label = "Digital Mic";
6036ab6734e7SLydia Wang 	imux->items[imux->num_items].index = 6;
6037ab6734e7SLydia Wang 	imux->num_items++;
6038ab6734e7SLydia Wang 
6039ab6734e7SLydia Wang 	return 0;
6040ab6734e7SLydia Wang }
6041ab6734e7SLydia Wang 
6042ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
6043ab6734e7SLydia Wang {
6044ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
6045ab6734e7SLydia Wang 	int err;
6046ab6734e7SLydia Wang 
6047ab6734e7SLydia Wang 
6048ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
6049ab6734e7SLydia Wang 	if (err < 0)
6050ab6734e7SLydia Wang 		return err;
6051ab6734e7SLydia Wang 	fill_dig_outs(codec);
6052ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
6053ab6734e7SLydia Wang 	if (err < 0)
6054ab6734e7SLydia Wang 		return err;
6055ab6734e7SLydia Wang 
6056ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
6057ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
6058ab6734e7SLydia Wang 
6059ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
6060ab6734e7SLydia Wang 	if (err < 0)
6061ab6734e7SLydia Wang 		return err;
6062ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
6063ab6734e7SLydia Wang 	if (err < 0)
6064ab6734e7SLydia Wang 		return err;
6065ab6734e7SLydia Wang 	err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
6066ab6734e7SLydia Wang 	if (err < 0)
6067ab6734e7SLydia Wang 		return err;
6068ab6734e7SLydia Wang 
6069ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
6070ab6734e7SLydia Wang 
6071ab6734e7SLydia Wang 	fill_dig_outs(codec);
6072ab6734e7SLydia Wang 
6073ab6734e7SLydia Wang 	if (spec->kctls.list)
6074ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
6075ab6734e7SLydia Wang 
6076ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
6077ab6734e7SLydia Wang 
6078ab6734e7SLydia Wang 	if (spec->hp_mux)
6079ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = via_hp_mixer;
6080ab6734e7SLydia Wang 
6081ab6734e7SLydia Wang 	return 1;
6082ab6734e7SLydia Wang }
6083ab6734e7SLydia Wang 
6084ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6085ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = {
6086ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
6087ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
6088ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
6089ab6734e7SLydia Wang 	{ } /* end */
6090ab6734e7SLydia Wang };
6091ab6734e7SLydia Wang #endif
6092ab6734e7SLydia Wang 
6093ab6734e7SLydia Wang 
6094ab6734e7SLydia Wang /* patch for vt1812 */
6095ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
6096ab6734e7SLydia Wang {
6097ab6734e7SLydia Wang 	struct via_spec *spec;
6098ab6734e7SLydia Wang 	int err;
6099ab6734e7SLydia Wang 
6100ab6734e7SLydia Wang 	/* create a codec specific record */
6101ab6734e7SLydia Wang 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
6102ab6734e7SLydia Wang 	if (spec == NULL)
6103ab6734e7SLydia Wang 		return -ENOMEM;
6104ab6734e7SLydia Wang 
6105ab6734e7SLydia Wang 	codec->spec = spec;
6106ab6734e7SLydia Wang 
6107ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
6108ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
6109ab6734e7SLydia Wang 	if (err < 0) {
6110ab6734e7SLydia Wang 		via_free(codec);
6111ab6734e7SLydia Wang 		return err;
6112ab6734e7SLydia Wang 	} else if (!err) {
6113ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
6114ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
6115ab6734e7SLydia Wang 	}
6116ab6734e7SLydia Wang 
6117ab6734e7SLydia Wang 
6118ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
6119ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6120ab6734e7SLydia Wang 
6121ab6734e7SLydia Wang 	spec->stream_name_analog = "VT1812 Analog";
6122ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6123ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6124ab6734e7SLydia Wang 
6125ab6734e7SLydia Wang 	spec->stream_name_digital = "VT1812 Digital";
6126ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6127ab6734e7SLydia Wang 
6128ab6734e7SLydia Wang 
6129ab6734e7SLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
6130ab6734e7SLydia Wang 		spec->adc_nids = vt1812_adc_nids;
6131ab6734e7SLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6132ab6734e7SLydia Wang 		get_mux_nids(codec);
6133ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
6134ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
6135ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6136ab6734e7SLydia Wang 		spec->num_mixers++;
6137ab6734e7SLydia Wang 	}
6138ab6734e7SLydia Wang 
6139ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
6140ab6734e7SLydia Wang 
6141ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
6142ab6734e7SLydia Wang 	codec->patch_ops.unsol_event = via_unsol_event,
6143ab6734e7SLydia Wang 
6144ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6145ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
6146ab6734e7SLydia Wang #endif
6147ab6734e7SLydia Wang 
6148ab6734e7SLydia Wang 	return 0;
6149ab6734e7SLydia Wang }
6150ab6734e7SLydia Wang 
6151c577b8a1SJoseph Chan /*
6152c577b8a1SJoseph Chan  * patch entries
6153c577b8a1SJoseph Chan  */
61541289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
61553218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
61563218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
61573218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
61583218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
61593218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
6160f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61613218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
6162f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61633218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
6164f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61653218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
6166f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61673218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
6168f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61693218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
6170f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61713218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
6172f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61733218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
6174f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61753218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
6176f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61773218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
6178f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61793218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
6180f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61813218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6182f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61833218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6184f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61853218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6186f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61873218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6188f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61893218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6190f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61913218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6192d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61933218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6194d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61953218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6196d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61973218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6198d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61993218c178STakashi Iwai 	{ .id = 0x11064397, .name = "VT1708S",
6200d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62013218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6202d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62033218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6204d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62053218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6206d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62073218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6208d949cac1SHarald Welte 	  .patch = patch_vt1702},
62093218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6210d949cac1SHarald Welte 	  .patch = patch_vt1702},
62113218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6212d949cac1SHarald Welte 	  .patch = patch_vt1702},
62133218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6214d949cac1SHarald Welte 	  .patch = patch_vt1702},
62153218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6216d949cac1SHarald Welte 	  .patch = patch_vt1702},
62173218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6218d949cac1SHarald Welte 	  .patch = patch_vt1702},
62193218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6220d949cac1SHarald Welte 	  .patch = patch_vt1702},
62213218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6222d949cac1SHarald Welte 	  .patch = patch_vt1702},
6223eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6224eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6225eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6226eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6227bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6228bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6229bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6230bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6231f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6232f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6233f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6234f3db423dSLydia Wang 	  .patch = patch_vt1716S},
623525eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
623625eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6237ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
6238c577b8a1SJoseph Chan 	{} /* terminator */
6239c577b8a1SJoseph Chan };
62401289e9e8STakashi Iwai 
62411289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
62421289e9e8STakashi Iwai 
62431289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
62441289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
62451289e9e8STakashi Iwai 	.owner = THIS_MODULE,
62461289e9e8STakashi Iwai };
62471289e9e8STakashi Iwai 
62481289e9e8STakashi Iwai MODULE_LICENSE("GPL");
62491289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
62501289e9e8STakashi Iwai 
62511289e9e8STakashi Iwai static int __init patch_via_init(void)
62521289e9e8STakashi Iwai {
62531289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
62541289e9e8STakashi Iwai }
62551289e9e8STakashi Iwai 
62561289e9e8STakashi Iwai static void __exit patch_via_exit(void)
62571289e9e8STakashi Iwai {
62581289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
62591289e9e8STakashi Iwai }
62601289e9e8STakashi Iwai 
62611289e9e8STakashi Iwai module_init(patch_via_init)
62621289e9e8STakashi Iwai module_exit(patch_via_exit)
6263