xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 5e26dfd0615868872cb44842f1e1428c7b414ab0)
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			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
575b0cb1d8SJaroslav Kysela #define NID_MAPPING		(-1)
585b0cb1d8SJaroslav Kysela 
59c577b8a1SJoseph Chan /* amp values */
60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan /* Pin Widget NID */
64c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
70c577b8a1SJoseph Chan 
71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
75f7278fd0SJosepch Chan 
76f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
80c577b8a1SJoseph Chan 
81d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
83d949cac1SHarald Welte 
84d949cac1SHarald Welte #define VT1702_HP_NID		0x17
85d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
86d949cac1SHarald Welte 
87d7426329SHarald Welte enum VIA_HDA_CODEC {
88d7426329SHarald Welte 	UNKNOWN = -1,
89d7426329SHarald Welte 	VT1708,
90d7426329SHarald Welte 	VT1709_10CH,
91d7426329SHarald Welte 	VT1709_6CH,
92d7426329SHarald Welte 	VT1708B_8CH,
93d7426329SHarald Welte 	VT1708B_4CH,
94d7426329SHarald Welte 	VT1708S,
95518bf3baSLydia Wang 	VT1708BCE,
96d7426329SHarald Welte 	VT1702,
97eb7188caSLydia Wang 	VT1718S,
98f3db423dSLydia Wang 	VT1716S,
9925eaba2fSLydia Wang 	VT2002P,
100ab6734e7SLydia Wang 	VT1812,
101d7426329SHarald Welte 	CODEC_TYPES,
102d7426329SHarald Welte };
103d7426329SHarald Welte 
1041f2e99feSLydia Wang struct via_spec {
1051f2e99feSLydia Wang 	/* codec parameterization */
106f3db423dSLydia Wang 	struct snd_kcontrol_new *mixers[6];
1071f2e99feSLydia Wang 	unsigned int num_mixers;
1081f2e99feSLydia Wang 
1091f2e99feSLydia Wang 	struct hda_verb *init_verbs[5];
1101f2e99feSLydia Wang 	unsigned int num_iverbs;
1111f2e99feSLydia Wang 
1121f2e99feSLydia Wang 	char *stream_name_analog;
1131f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_playback;
1141f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_capture;
1151f2e99feSLydia Wang 
1161f2e99feSLydia Wang 	char *stream_name_digital;
1171f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_playback;
1181f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_capture;
1191f2e99feSLydia Wang 
1201f2e99feSLydia Wang 	/* playback */
1211f2e99feSLydia Wang 	struct hda_multi_out multiout;
1221f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1231f2e99feSLydia Wang 
1241f2e99feSLydia Wang 	/* capture */
1251f2e99feSLydia Wang 	unsigned int num_adc_nids;
1261f2e99feSLydia Wang 	hda_nid_t *adc_nids;
1271f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
1281f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1291f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1301f2e99feSLydia Wang 
1311f2e99feSLydia Wang 	/* capture source */
1321f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1331f2e99feSLydia Wang 	unsigned int cur_mux[3];
1341f2e99feSLydia Wang 
1351f2e99feSLydia Wang 	/* PCM information */
1361f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1371f2e99feSLydia Wang 
1381f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1391f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1401f2e99feSLydia Wang 	struct snd_array kctls;
1411f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1421f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1431f2e99feSLydia Wang 
1441f2e99feSLydia Wang 	/* HP mode source */
1451f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1461f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1471f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
1481f2e99feSLydia Wang 	unsigned int smart51_enabled;
149f3db423dSLydia Wang 	unsigned int dmic_enabled;
1501f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1511f2e99feSLydia Wang 
1521f2e99feSLydia Wang 	/* work to check hp jack state */
1531f2e99feSLydia Wang 	struct hda_codec *codec;
1541f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
1551f2e99feSLydia Wang 	int vt1708_jack_detectect;
1561f2e99feSLydia Wang 	int vt1708_hp_present;
1571f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1581f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1591f2e99feSLydia Wang #endif
1601f2e99feSLydia Wang };
1611f2e99feSLydia Wang 
1625b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1635b0cb1d8SJaroslav Kysela {
1645b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1655b0cb1d8SJaroslav Kysela 
1665b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1675b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1685b0cb1d8SJaroslav Kysela 		return NULL;
1695b0cb1d8SJaroslav Kysela 
1705b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1715b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1725b0cb1d8SJaroslav Kysela 	return spec;
1735b0cb1d8SJaroslav Kysela }
1745b0cb1d8SJaroslav Kysela 
175744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
176d7426329SHarald Welte {
177744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
178d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
179d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
180d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
181d7426329SHarald Welte 
182d7426329SHarald Welte 	/* get codec type */
183d7426329SHarald Welte 	if (ven_id != 0x1106)
184d7426329SHarald Welte 		codec_type = UNKNOWN;
185d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
186d7426329SHarald Welte 		codec_type = VT1708;
187d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
188d7426329SHarald Welte 		codec_type = VT1709_10CH;
189d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
190d7426329SHarald Welte 		codec_type = VT1709_6CH;
191518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
192d7426329SHarald Welte 		codec_type = VT1708B_8CH;
193518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
194518bf3baSLydia Wang 			codec_type = VT1708BCE;
195518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
196d7426329SHarald Welte 		codec_type = VT1708B_4CH;
197d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
198d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
199d7426329SHarald Welte 		codec_type = VT1708S;
200d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
201d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
202d7426329SHarald Welte 		codec_type = VT1702;
203eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
204eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
205eb7188caSLydia Wang 		codec_type = VT1718S;
206f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
207f3db423dSLydia Wang 		codec_type = VT1716S;
208bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
209bb3c6bfcSLydia Wang 		codec_type = VT1718S;
21025eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
21125eaba2fSLydia Wang 		codec_type = VT2002P;
212ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
213ab6734e7SLydia Wang 		codec_type = VT1812;
21436dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
21536dd5c4aSLydia Wang 		codec_type = VT1708S;
216d7426329SHarald Welte 	else
217d7426329SHarald Welte 		codec_type = UNKNOWN;
218d7426329SHarald Welte 	return codec_type;
219d7426329SHarald Welte };
220d7426329SHarald Welte 
22169e52a80SHarald Welte #define VIA_HP_EVENT		0x01
22269e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
223a34df19aSLydia Wang #define VIA_JACK_EVENT		0x04
224f3db423dSLydia Wang #define VIA_MONO_EVENT		0x08
22525eaba2fSLydia Wang #define VIA_SPEAKER_EVENT	0x10
22625eaba2fSLydia Wang #define VIA_BIND_HP_EVENT	0x20
22769e52a80SHarald Welte 
228c577b8a1SJoseph Chan enum {
229c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
230c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
231f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
23225eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
233c577b8a1SJoseph Chan };
234c577b8a1SJoseph Chan 
235c577b8a1SJoseph Chan enum {
236eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
237c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
238c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
239c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
240c577b8a1SJoseph Chan };
241c577b8a1SJoseph Chan 
242f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
243f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec);
2441f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2451f2e99feSLydia Wang 
2461f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2471f2e99feSLydia Wang {
2481f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2491f2e99feSLydia Wang 		return;
2501f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2511f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2521f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2531f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2541f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2551f2e99feSLydia Wang }
2561f2e99feSLydia Wang 
2571f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2581f2e99feSLydia Wang {
2591f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2601f2e99feSLydia Wang 		return;
2611f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2621f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2631f2e99feSLydia Wang 		return;
2641f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2651f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2661f2e99feSLydia Wang 	cancel_delayed_work(&spec->vt1708_hp_work);
2671f2e99feSLydia Wang 	flush_scheduled_work();
2681f2e99feSLydia Wang }
269f5271101SLydia Wang 
27025eaba2fSLydia Wang 
271f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
272f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
273f5271101SLydia Wang {
274f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
275f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
276f5271101SLydia Wang 
277f5271101SLydia Wang 	set_jack_power_state(codec);
278f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2791f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2801f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2811f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2821f2e99feSLydia Wang 		else
2831f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2841f2e99feSLydia Wang 	}
285f5271101SLydia Wang 	return change;
286f5271101SLydia Wang }
287f5271101SLydia Wang 
288f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
289f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
290f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
291f5271101SLydia Wang 			.name = NULL,					\
292f5271101SLydia Wang 			.index = 0,					\
293f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
294f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
295f5271101SLydia Wang 			.put = analog_input_switch_put,			\
296f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
297f5271101SLydia Wang 
29825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
29925eaba2fSLydia Wang 
30025eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
30125eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
30225eaba2fSLydia Wang {
30325eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
30425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
30525eaba2fSLydia Wang 	int i;
30625eaba2fSLydia Wang 	int change = 0;
30725eaba2fSLydia Wang 
30825eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
30925eaba2fSLydia Wang 	int lmute, rmute;
31025eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
31125eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
31225eaba2fSLydia Wang 		return change;
31325eaba2fSLydia Wang 	}
31425eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
31525eaba2fSLydia Wang 					      ucontrol);
31625eaba2fSLydia Wang 	/* Get mute value */
31725eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
31825eaba2fSLydia Wang 	valp++;
31925eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
32025eaba2fSLydia Wang 
32125eaba2fSLydia Wang 	/* Set hp pins */
32225eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
32325eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
32425eaba2fSLydia Wang 			snd_hda_codec_amp_update(
32525eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
32625eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
32725eaba2fSLydia Wang 				lmute);
32825eaba2fSLydia Wang 			snd_hda_codec_amp_update(
32925eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
33025eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
33125eaba2fSLydia Wang 				rmute);
33225eaba2fSLydia Wang 		}
33325eaba2fSLydia Wang 	}
33425eaba2fSLydia Wang 
33525eaba2fSLydia Wang 	if (!lmute && !rmute) {
33625eaba2fSLydia Wang 		/* Line Outs */
33725eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
33825eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
33925eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
34025eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
34125eaba2fSLydia Wang 		/* Speakers */
34225eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
34325eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
34425eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
34525eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
34625eaba2fSLydia Wang 		/* unmute */
34725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
34825eaba2fSLydia Wang 
34925eaba2fSLydia Wang 	} else {
35025eaba2fSLydia Wang 		if (lmute) {
35125eaba2fSLydia Wang 			/* Mute all left channels */
35225eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
35325eaba2fSLydia Wang 				snd_hda_codec_amp_update(
35425eaba2fSLydia Wang 					codec,
35525eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
35625eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35725eaba2fSLydia Wang 					lmute);
35825eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
35925eaba2fSLydia Wang 				snd_hda_codec_amp_update(
36025eaba2fSLydia Wang 					codec,
36125eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
36225eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36325eaba2fSLydia Wang 					lmute);
36425eaba2fSLydia Wang 		}
36525eaba2fSLydia Wang 		if (rmute) {
36625eaba2fSLydia Wang 			/* mute all right channels */
36725eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
36825eaba2fSLydia Wang 				snd_hda_codec_amp_update(
36925eaba2fSLydia Wang 					codec,
37025eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
37125eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37225eaba2fSLydia Wang 					rmute);
37325eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
37425eaba2fSLydia Wang 				snd_hda_codec_amp_update(
37525eaba2fSLydia Wang 					codec,
37625eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
37725eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37825eaba2fSLydia Wang 					rmute);
37925eaba2fSLydia Wang 		}
38025eaba2fSLydia Wang 	}
38125eaba2fSLydia Wang 	return change;
38225eaba2fSLydia Wang }
38325eaba2fSLydia Wang 
38425eaba2fSLydia Wang #define BIND_PIN_MUTE							\
38525eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
38625eaba2fSLydia Wang 			.name = NULL,					\
38725eaba2fSLydia Wang 			.index = 0,					\
38825eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
38925eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
39025eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
39125eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
39225eaba2fSLydia Wang 
39371eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = {
394c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
395c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
396f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
39725eaba2fSLydia Wang 	BIND_PIN_MUTE,
398c577b8a1SJoseph Chan };
399c577b8a1SJoseph Chan 
400c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
401c577b8a1SJoseph Chan 	/* ADC1-2 */
402c577b8a1SJoseph Chan 	0x15, 0x27
403c577b8a1SJoseph Chan };
404c577b8a1SJoseph Chan 
405c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
406c577b8a1SJoseph Chan 	/* ADC1-2 */
407c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
408c577b8a1SJoseph Chan };
409c577b8a1SJoseph Chan 
410f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
411f7278fd0SJosepch Chan 	/* ADC1-2 */
412f7278fd0SJosepch Chan 	0x13, 0x14
413f7278fd0SJosepch Chan };
414f7278fd0SJosepch Chan 
415d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
416d949cac1SHarald Welte 	/* ADC1-2 */
417d949cac1SHarald Welte 	0x13, 0x14
418d949cac1SHarald Welte };
419d949cac1SHarald Welte 
420d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
421d949cac1SHarald Welte 	/* ADC1-2 */
422d949cac1SHarald Welte 	0x12, 0x20, 0x1F
423d949cac1SHarald Welte };
424d949cac1SHarald Welte 
425eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
426eb7188caSLydia Wang 	/* ADC1-2 */
427eb7188caSLydia Wang 	0x10, 0x11
428eb7188caSLydia Wang };
429eb7188caSLydia Wang 
430f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
431f3db423dSLydia Wang 	/* ADC1-2 */
432f3db423dSLydia Wang 	0x13, 0x14
433f3db423dSLydia Wang };
434f3db423dSLydia Wang 
43525eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
43625eaba2fSLydia Wang 	/* ADC1-2 */
43725eaba2fSLydia Wang 	0x10, 0x11
43825eaba2fSLydia Wang };
43925eaba2fSLydia Wang 
440ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = {
441ab6734e7SLydia Wang 	/* ADC1-2 */
442ab6734e7SLydia Wang 	0x10, 0x11
443ab6734e7SLydia Wang };
444ab6734e7SLydia Wang 
445ab6734e7SLydia Wang 
446c577b8a1SJoseph Chan /* add dynamic controls */
447c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name,
448c577b8a1SJoseph Chan 			   unsigned long val)
449c577b8a1SJoseph Chan {
450c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
451c577b8a1SJoseph Chan 
452603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
453603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
454c577b8a1SJoseph Chan 	if (!knew)
455c577b8a1SJoseph Chan 		return -ENOMEM;
45671eb7dccSLydia Wang 	*knew = via_control_templates[type];
457c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
458c577b8a1SJoseph Chan 	if (!knew->name)
459c577b8a1SJoseph Chan 		return -ENOMEM;
4604d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
461*5e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
462c577b8a1SJoseph Chan 	knew->private_value = val;
463c577b8a1SJoseph Chan 	return 0;
464c577b8a1SJoseph Chan }
465c577b8a1SJoseph Chan 
4665b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
4675b0cb1d8SJaroslav Kysela 						struct snd_kcontrol_new *tmpl)
4685b0cb1d8SJaroslav Kysela {
4695b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
4705b0cb1d8SJaroslav Kysela 
4715b0cb1d8SJaroslav Kysela 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
4725b0cb1d8SJaroslav Kysela 	knew = snd_array_new(&spec->kctls);
4735b0cb1d8SJaroslav Kysela 	if (!knew)
4745b0cb1d8SJaroslav Kysela 		return NULL;
4755b0cb1d8SJaroslav Kysela 	*knew = *tmpl;
4765b0cb1d8SJaroslav Kysela 	knew->name = kstrdup(tmpl->name, GFP_KERNEL);
4775b0cb1d8SJaroslav Kysela 	if (!knew->name)
4785b0cb1d8SJaroslav Kysela 		return NULL;
4795b0cb1d8SJaroslav Kysela 	return 0;
4805b0cb1d8SJaroslav Kysela }
4815b0cb1d8SJaroslav Kysela 
482603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
483603c4019STakashi Iwai {
484603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
485603c4019STakashi Iwai 
486603c4019STakashi Iwai 	if (spec->kctls.list) {
487603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
488603c4019STakashi Iwai 		int i;
489603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
490603c4019STakashi Iwai 			kfree(kctl[i].name);
491603c4019STakashi Iwai 	}
492603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
493603c4019STakashi Iwai }
494603c4019STakashi Iwai 
495c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4969510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4979510e8ddSLydia Wang 				int idx, int mix_nid)
498c577b8a1SJoseph Chan {
499c577b8a1SJoseph Chan 	char name[32];
500c577b8a1SJoseph Chan 	int err;
501c577b8a1SJoseph Chan 
502c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
503c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
504c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
505c577b8a1SJoseph Chan 	if (err < 0)
506c577b8a1SJoseph Chan 		return err;
507c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
508f5271101SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name,
509c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
510c577b8a1SJoseph Chan 	if (err < 0)
511c577b8a1SJoseph Chan 		return err;
512c577b8a1SJoseph Chan 	return 0;
513c577b8a1SJoseph Chan }
514c577b8a1SJoseph Chan 
515c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
516c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
517c577b8a1SJoseph Chan 					   int dac_idx)
518c577b8a1SJoseph Chan {
519c577b8a1SJoseph Chan 	/* set as output */
520c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
521c577b8a1SJoseph Chan 			    pin_type);
522c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
523c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
524d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
525d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
526d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
527c577b8a1SJoseph Chan }
528c577b8a1SJoseph Chan 
529c577b8a1SJoseph Chan 
530c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
531c577b8a1SJoseph Chan {
532c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
533c577b8a1SJoseph Chan 	int i;
534c577b8a1SJoseph Chan 
535c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
536c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
537c577b8a1SJoseph Chan 		if (nid)
538c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
539c577b8a1SJoseph Chan 	}
540c577b8a1SJoseph Chan }
541c577b8a1SJoseph Chan 
542c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
543c577b8a1SJoseph Chan {
544c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
545c577b8a1SJoseph Chan 	hda_nid_t pin;
54625eaba2fSLydia Wang 	int i;
547c577b8a1SJoseph Chan 
54825eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
54925eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
550c577b8a1SJoseph Chan 		if (pin) /* connect to front */
551c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
552c577b8a1SJoseph Chan 	}
55325eaba2fSLydia Wang }
554c577b8a1SJoseph Chan 
555c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
556c577b8a1SJoseph Chan {
557c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
558c577b8a1SJoseph Chan 	int i;
559c577b8a1SJoseph Chan 
560c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
561c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.input_pins[i];
562c577b8a1SJoseph Chan 
563c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
564c577b8a1SJoseph Chan 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
565c577b8a1SJoseph Chan 				    (i <= AUTO_PIN_FRONT_MIC ?
566c577b8a1SJoseph Chan 				     PIN_VREF50 : PIN_IN));
567c577b8a1SJoseph Chan 
568c577b8a1SJoseph Chan 	}
569c577b8a1SJoseph Chan }
570f5271101SLydia Wang 
5711564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
5721564b287SLydia Wang 
573f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
574f5271101SLydia Wang 				unsigned int *affected_parm)
575f5271101SLydia Wang {
576f5271101SLydia Wang 	unsigned parm;
577f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
578f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
579f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
580f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
581d56757abSTakashi Iwai 	unsigned present = snd_hda_jack_detect(codec, nid);
5821564b287SLydia Wang 	struct via_spec *spec = codec->spec;
5831564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5841564b287SLydia Wang 	    || ((no_presence || present)
5851564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
586f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
587f5271101SLydia Wang 		parm = AC_PWRST_D0;
588f5271101SLydia Wang 	} else
589f5271101SLydia Wang 		parm = AC_PWRST_D3;
590f5271101SLydia Wang 
591f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
592f5271101SLydia Wang }
593f5271101SLydia Wang 
594f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec)
595f5271101SLydia Wang {
596f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
597f5271101SLydia Wang 	int imux_is_smixer;
598f5271101SLydia Wang 	unsigned int parm;
599f5271101SLydia Wang 
600f5271101SLydia Wang 	if (spec->codec_type == VT1702) {
601f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
602f5271101SLydia Wang 			codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
603f5271101SLydia Wang 		/* inputs */
604f5271101SLydia Wang 		/* PW 1/2/5 (14h/15h/18h) */
605f5271101SLydia Wang 		parm = AC_PWRST_D3;
606f5271101SLydia Wang 		set_pin_power_state(codec, 0x14, &parm);
607f5271101SLydia Wang 		set_pin_power_state(codec, 0x15, &parm);
608f5271101SLydia Wang 		set_pin_power_state(codec, 0x18, &parm);
609f5271101SLydia Wang 		if (imux_is_smixer)
610f5271101SLydia Wang 			parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
611f5271101SLydia Wang 		/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
612f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
613f5271101SLydia Wang 				    parm);
614f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
615f5271101SLydia Wang 				    parm);
616f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
617f5271101SLydia Wang 				    parm);
618f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
619f5271101SLydia Wang 				    parm);
620f5271101SLydia Wang 
621f5271101SLydia Wang 		/* outputs */
622f5271101SLydia Wang 		/* PW 3/4 (16h/17h) */
623f5271101SLydia Wang 		parm = AC_PWRST_D3;
624f5271101SLydia Wang 		set_pin_power_state(codec, 0x16, &parm);
625f5271101SLydia Wang 		set_pin_power_state(codec, 0x17, &parm);
626f5271101SLydia Wang 		/* MW0 (1ah), AOW 0/1 (10h/1dh) */
627f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
628f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
629f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
630f5271101SLydia Wang 				    parm);
631f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
632f5271101SLydia Wang 				    parm);
633f5271101SLydia Wang 	} else if (spec->codec_type == VT1708B_8CH
634f5271101SLydia Wang 		   || spec->codec_type == VT1708B_4CH
635f5271101SLydia Wang 		   || spec->codec_type == VT1708S) {
636f5271101SLydia Wang 		/* SW0 (17h) = stereo mixer */
637f5271101SLydia Wang 		int is_8ch = spec->codec_type != VT1708B_4CH;
638f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
639f5271101SLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
640f5271101SLydia Wang 			== ((spec->codec_type == VT1708S)  ? 5 : 0);
641f5271101SLydia Wang 		/* inputs */
642f5271101SLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
643f5271101SLydia Wang 		parm = AC_PWRST_D3;
644f5271101SLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
645f5271101SLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
646f5271101SLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
647f5271101SLydia Wang 		if (imux_is_smixer)
648f5271101SLydia Wang 			parm = AC_PWRST_D0;
649f5271101SLydia Wang 		/* SW0 (17h), AIW 0/1 (13h/14h) */
650f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
651f5271101SLydia Wang 				    parm);
652f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
653f5271101SLydia Wang 				    parm);
654f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
655f5271101SLydia Wang 				    parm);
656f5271101SLydia Wang 
657f5271101SLydia Wang 		/* outputs */
658f5271101SLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
659f5271101SLydia Wang 		parm = AC_PWRST_D3;
660f5271101SLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
661f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
662f5271101SLydia Wang 				    parm);
663f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
664f5271101SLydia Wang 				    parm);
665f5271101SLydia Wang 
666f5271101SLydia Wang 		/* PW6 (22h), SW2 (26h), AOW2 (24h) */
667f5271101SLydia Wang 		if (is_8ch) {
668f5271101SLydia Wang 			parm = AC_PWRST_D3;
669f5271101SLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
670f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x26, 0,
671f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
672f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x24, 0,
673f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
674f5271101SLydia Wang 		}
675f5271101SLydia Wang 
676f5271101SLydia Wang 		/* PW 3/4/7 (1ch/1dh/23h) */
677f5271101SLydia Wang 		parm = AC_PWRST_D3;
678f5271101SLydia Wang 		/* force to D0 for internal Speaker */
679f5271101SLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
680f5271101SLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
681f5271101SLydia Wang 		if (is_8ch)
682f5271101SLydia Wang 			set_pin_power_state(codec, 0x23, &parm);
683f5271101SLydia Wang 		/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
684f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
685f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
686f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
687f5271101SLydia Wang 				    parm);
688f5271101SLydia Wang 		if (is_8ch) {
689f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
690f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
691f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x27, 0,
692f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
693f5271101SLydia Wang 		}
694eb7188caSLydia Wang 	}  else if (spec->codec_type == VT1718S) {
695eb7188caSLydia Wang 		/* MUX6 (1eh) = stereo mixer */
696eb7188caSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
697eb7188caSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
698eb7188caSLydia Wang 		/* inputs */
699eb7188caSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
700eb7188caSLydia Wang 		parm = AC_PWRST_D3;
701eb7188caSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
702eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
703eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
704eb7188caSLydia Wang 		if (imux_is_smixer)
705eb7188caSLydia Wang 			parm = AC_PWRST_D0;
706eb7188caSLydia Wang 		/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
707eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
708eb7188caSLydia Wang 				    parm);
709eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
710eb7188caSLydia Wang 				    parm);
711eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
712eb7188caSLydia Wang 				    parm);
713eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
714eb7188caSLydia Wang 				    parm);
715eb7188caSLydia Wang 
716eb7188caSLydia Wang 		/* outputs */
717eb7188caSLydia Wang 		/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
718eb7188caSLydia Wang 		parm = AC_PWRST_D3;
719eb7188caSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
720eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
721eb7188caSLydia Wang 				    parm);
722eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
723eb7188caSLydia Wang 				    parm);
724eb7188caSLydia Wang 
725eb7188caSLydia Wang 		/* PW2 (26h), AOW2 (ah) */
726eb7188caSLydia Wang 		parm = AC_PWRST_D3;
727eb7188caSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
728eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
729eb7188caSLydia Wang 				    parm);
730eb7188caSLydia Wang 
731eb7188caSLydia Wang 		/* PW0/1 (24h/25h) */
732eb7188caSLydia Wang 		parm = AC_PWRST_D3;
733eb7188caSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
734eb7188caSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
735eb7188caSLydia Wang 		if (!spec->hp_independent_mode) /* check for redirected HP */
736eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
737eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
738eb7188caSLydia Wang 				    parm);
739eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
740eb7188caSLydia Wang 				    parm);
741eb7188caSLydia Wang 		/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
742eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
743eb7188caSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
744eb7188caSLydia Wang 		if (spec->hp_independent_mode) {
745eb7188caSLydia Wang 			/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
746eb7188caSLydia Wang 			parm = AC_PWRST_D3;
747eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
748eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x1b, 0,
749eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
750eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
751eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
752eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0xc, 0,
753eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
754eb7188caSLydia Wang 		}
755f3db423dSLydia Wang 	} else if (spec->codec_type == VT1716S) {
756f3db423dSLydia Wang 		unsigned int mono_out, present;
757f3db423dSLydia Wang 		/* SW0 (17h) = stereo mixer */
758f3db423dSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
759f3db423dSLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) ==  5;
760f3db423dSLydia Wang 		/* inputs */
761f3db423dSLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
762f3db423dSLydia Wang 		parm = AC_PWRST_D3;
763f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
764f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
765f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
766f3db423dSLydia Wang 		if (imux_is_smixer)
767f3db423dSLydia Wang 			parm = AC_PWRST_D0;
768f3db423dSLydia Wang 		/* SW0 (17h), AIW0(13h) */
769f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
770f3db423dSLydia Wang 				    parm);
771f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
772f3db423dSLydia Wang 				    parm);
773f3db423dSLydia Wang 
774f3db423dSLydia Wang 		parm = AC_PWRST_D3;
775f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
776f3db423dSLydia Wang 		/* PW11 (22h) */
777f3db423dSLydia Wang 		if (spec->dmic_enabled)
778f3db423dSLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
779f3db423dSLydia Wang 		else
780f3db423dSLydia Wang 			snd_hda_codec_write(
781f3db423dSLydia Wang 				codec, 0x22, 0,
782f3db423dSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
783f3db423dSLydia Wang 
784f3db423dSLydia Wang 		/* SW2(26h), AIW1(14h) */
785f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
786f3db423dSLydia Wang 				    parm);
787f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
788f3db423dSLydia Wang 				    parm);
789f3db423dSLydia Wang 
790f3db423dSLydia Wang 		/* outputs */
791f3db423dSLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
792f3db423dSLydia Wang 		parm = AC_PWRST_D3;
793f3db423dSLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
794f3db423dSLydia Wang 		/* Smart 5.1 PW2(1bh) */
795f3db423dSLydia Wang 		if (spec->smart51_enabled)
796f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1b, &parm);
797f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
798f3db423dSLydia Wang 				    parm);
799f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
800f3db423dSLydia Wang 				    parm);
801f3db423dSLydia Wang 
802f3db423dSLydia Wang 		/* PW7 (23h), SW3 (27h), AOW3 (25h) */
803f3db423dSLydia Wang 		parm = AC_PWRST_D3;
804f3db423dSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
805f3db423dSLydia Wang 		/* Smart 5.1 PW1(1ah) */
806f3db423dSLydia Wang 		if (spec->smart51_enabled)
807f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
808f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
809f3db423dSLydia Wang 				    parm);
810f3db423dSLydia Wang 
811f3db423dSLydia Wang 		/* Smart 5.1 PW5(1eh) */
812f3db423dSLydia Wang 		if (spec->smart51_enabled)
813f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1e, &parm);
814f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
815f3db423dSLydia Wang 				    parm);
816f3db423dSLydia Wang 
817f3db423dSLydia Wang 		/* Mono out */
818f3db423dSLydia Wang 		/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
819d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x1c);
820f3db423dSLydia Wang 		if (present)
821f3db423dSLydia Wang 			mono_out = 0;
822f3db423dSLydia Wang 		else {
823d56757abSTakashi Iwai 			present = snd_hda_jack_detect(codec, 0x1d);
824f3db423dSLydia Wang 			if (!spec->hp_independent_mode && present)
825f3db423dSLydia Wang 				mono_out = 0;
826f3db423dSLydia Wang 			else
827f3db423dSLydia Wang 				mono_out = 1;
828f3db423dSLydia Wang 		}
829f3db423dSLydia Wang 		parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
830f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
831f3db423dSLydia Wang 				    parm);
832f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
833f3db423dSLydia Wang 				    parm);
834f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
835f3db423dSLydia Wang 				    parm);
836f3db423dSLydia Wang 
837f3db423dSLydia Wang 		/* PW 3/4 (1ch/1dh) */
838f3db423dSLydia Wang 		parm = AC_PWRST_D3;
839f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
840f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
841f3db423dSLydia Wang 		/* HP Independent Mode, power on AOW3 */
842f3db423dSLydia Wang 		if (spec->hp_independent_mode)
843f3db423dSLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
844f3db423dSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
845f3db423dSLydia Wang 
846f3db423dSLydia Wang 		/* force to D0 for internal Speaker */
847f3db423dSLydia Wang 		/* MW0 (16h), AOW0 (10h) */
848f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
849f3db423dSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
850f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
851f3db423dSLydia Wang 				    mono_out ? AC_PWRST_D0 : parm);
85225eaba2fSLydia Wang 	} else if (spec->codec_type == VT2002P) {
85325eaba2fSLydia Wang 		unsigned int present;
85425eaba2fSLydia Wang 		/* MUX9 (1eh) = stereo mixer */
85525eaba2fSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
85625eaba2fSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
85725eaba2fSLydia Wang 		/* inputs */
85825eaba2fSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
85925eaba2fSLydia Wang 		parm = AC_PWRST_D3;
86025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
86125eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
86225eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
86325eaba2fSLydia Wang 		if (imux_is_smixer)
86425eaba2fSLydia Wang 			parm = AC_PWRST_D0;
86525eaba2fSLydia Wang 		/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
86625eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
86725eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
86825eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
86925eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
87025eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
87125eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
87225eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
87325eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
87425eaba2fSLydia Wang 
87525eaba2fSLydia Wang 		/* outputs */
87625eaba2fSLydia Wang 		/* AOW0 (8h)*/
87725eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
87825eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
87925eaba2fSLydia Wang 
88025eaba2fSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
88125eaba2fSLydia Wang 		parm = AC_PWRST_D3;
88225eaba2fSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
88325eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
88425eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
88525eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x37,
88625eaba2fSLydia Wang 				    0, AC_VERB_SET_POWER_STATE, parm);
88725eaba2fSLydia Wang 
88825eaba2fSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
88925eaba2fSLydia Wang 		parm = AC_PWRST_D3;
89025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
89125eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
89225eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
89325eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
89425eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
89525eaba2fSLydia Wang 		if (spec->hp_independent_mode)	{
89625eaba2fSLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
89725eaba2fSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
89825eaba2fSLydia Wang 		}
89925eaba2fSLydia Wang 
90025eaba2fSLydia Wang 		/* Class-D */
90125eaba2fSLydia Wang 		/* PW0 (24h), MW0(18h), MUX0(34h) */
902d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x25);
90325eaba2fSLydia Wang 		parm = AC_PWRST_D3;
90425eaba2fSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
90525eaba2fSLydia Wang 		if (present) {
90625eaba2fSLydia Wang 			snd_hda_codec_write(
90725eaba2fSLydia Wang 				codec, 0x18, 0,
90825eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
90925eaba2fSLydia Wang 			snd_hda_codec_write(
91025eaba2fSLydia Wang 				codec, 0x34, 0,
91125eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
91225eaba2fSLydia Wang 		} else {
91325eaba2fSLydia Wang 			snd_hda_codec_write(
91425eaba2fSLydia Wang 				codec, 0x18, 0,
91525eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
91625eaba2fSLydia Wang 			snd_hda_codec_write(
91725eaba2fSLydia Wang 				codec, 0x34, 0,
91825eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
91925eaba2fSLydia Wang 		}
92025eaba2fSLydia Wang 
92125eaba2fSLydia Wang 		/* Mono Out */
92225eaba2fSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
923d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x26);
92425eaba2fSLydia Wang 		parm = AC_PWRST_D3;
92525eaba2fSLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
92625eaba2fSLydia Wang 		if (present) {
92725eaba2fSLydia Wang 			snd_hda_codec_write(
92825eaba2fSLydia Wang 				codec, 0x17, 0,
92925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
93025eaba2fSLydia Wang 			snd_hda_codec_write(
93125eaba2fSLydia Wang 				codec, 0x3b, 0,
93225eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
93325eaba2fSLydia Wang 		} else {
93425eaba2fSLydia Wang 			snd_hda_codec_write(
93525eaba2fSLydia Wang 				codec, 0x17, 0,
93625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
93725eaba2fSLydia Wang 			snd_hda_codec_write(
93825eaba2fSLydia Wang 				codec, 0x3b, 0,
93925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
94025eaba2fSLydia Wang 		}
94125eaba2fSLydia Wang 
94225eaba2fSLydia Wang 		/* MW9 (21h) */
94325eaba2fSLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
94425eaba2fSLydia Wang 			snd_hda_codec_write(
94525eaba2fSLydia Wang 				codec, 0x21, 0,
94625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
94725eaba2fSLydia Wang 		else
94825eaba2fSLydia Wang 			snd_hda_codec_write(
94925eaba2fSLydia Wang 				codec, 0x21, 0,
95025eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
951ab6734e7SLydia Wang 	} else if (spec->codec_type == VT1812) {
952ab6734e7SLydia Wang 		unsigned int present;
953ab6734e7SLydia Wang 		/* MUX10 (1eh) = stereo mixer */
954ab6734e7SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
955ab6734e7SLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
956ab6734e7SLydia Wang 		/* inputs */
957ab6734e7SLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
958ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
959ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
960ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
961ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
962ab6734e7SLydia Wang 		if (imux_is_smixer)
963ab6734e7SLydia Wang 			parm = AC_PWRST_D0;
964ab6734e7SLydia Wang 		/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
965ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
966ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
967ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
968ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
969ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
970ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
971ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
972ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
973ab6734e7SLydia Wang 
974ab6734e7SLydia Wang 		/* outputs */
975ab6734e7SLydia Wang 		/* AOW0 (8h)*/
976ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
977ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
978ab6734e7SLydia Wang 
979ab6734e7SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
980ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
981ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
982ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
983ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
984ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
985ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
986ab6734e7SLydia Wang 
987ab6734e7SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
988ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
989ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
990ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
991ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
992ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
993ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
994ab6734e7SLydia Wang 		if (spec->hp_independent_mode)	{
995ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
996ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
997ab6734e7SLydia Wang 		}
998ab6734e7SLydia Wang 
999ab6734e7SLydia Wang 		/* Internal Speaker */
1000ab6734e7SLydia Wang 		/* PW0 (24h), MW0(14h), MUX0(34h) */
1001d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x25);
1002ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1003ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
1004ab6734e7SLydia Wang 		if (present) {
1005ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
1006ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1007ab6734e7SLydia Wang 					    AC_PWRST_D3);
1008ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
1009ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1010ab6734e7SLydia Wang 					    AC_PWRST_D3);
1011ab6734e7SLydia Wang 		} else {
1012ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
1013ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1014ab6734e7SLydia Wang 					    AC_PWRST_D0);
1015ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
1016ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1017ab6734e7SLydia Wang 					    AC_PWRST_D0);
1018ab6734e7SLydia Wang 		}
1019ab6734e7SLydia Wang 		/* Mono Out */
1020ab6734e7SLydia Wang 		/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
1021d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x28);
1022ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1023ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
1024ab6734e7SLydia Wang 		if (present) {
1025ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
1026ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1027ab6734e7SLydia Wang 					    AC_PWRST_D3);
1028ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1029ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1030ab6734e7SLydia Wang 					    AC_PWRST_D3);
1031ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1032ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1033ab6734e7SLydia Wang 					    AC_PWRST_D3);
1034ab6734e7SLydia Wang 		} else {
1035ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
1036ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1037ab6734e7SLydia Wang 					    AC_PWRST_D0);
1038ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1039ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1040ab6734e7SLydia Wang 					    AC_PWRST_D0);
1041ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1042ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1043ab6734e7SLydia Wang 					    AC_PWRST_D0);
1044ab6734e7SLydia Wang 		}
1045ab6734e7SLydia Wang 
1046ab6734e7SLydia Wang 		/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
1047ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1048ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x33, &parm);
1049ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0,
1050ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1051ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x3d, 0,
1052ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1053ab6734e7SLydia Wang 
1054ab6734e7SLydia Wang 		/* MW9 (21h) */
1055ab6734e7SLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
1056ab6734e7SLydia Wang 			snd_hda_codec_write(
1057ab6734e7SLydia Wang 				codec, 0x21, 0,
1058ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1059ab6734e7SLydia Wang 		else
1060ab6734e7SLydia Wang 			snd_hda_codec_write(
1061ab6734e7SLydia Wang 				codec, 0x21, 0,
1062ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
1063f5271101SLydia Wang 	}
1064f5271101SLydia Wang }
1065f5271101SLydia Wang 
1066c577b8a1SJoseph Chan /*
1067c577b8a1SJoseph Chan  * input MUX handling
1068c577b8a1SJoseph Chan  */
1069c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1070c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
1071c577b8a1SJoseph Chan {
1072c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1073c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1074c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
1075c577b8a1SJoseph Chan }
1076c577b8a1SJoseph Chan 
1077c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1078c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1079c577b8a1SJoseph Chan {
1080c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1081c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1082c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1083c577b8a1SJoseph Chan 
1084c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
1085c577b8a1SJoseph Chan 	return 0;
1086c577b8a1SJoseph Chan }
1087c577b8a1SJoseph Chan 
1088c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1089c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1090c577b8a1SJoseph Chan {
1091c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1092c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1093c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1094c577b8a1SJoseph Chan 
1095337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
1096337b9d02STakashi Iwai 		return -EINVAL;
1097a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
1098a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
1099a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1100a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
1101a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1102a80e6e3cSLydia Wang 	/* update jack power state */
1103a80e6e3cSLydia Wang 	set_jack_power_state(codec);
1104a80e6e3cSLydia Wang 
1105c577b8a1SJoseph Chan 	return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
1106337b9d02STakashi Iwai 				     spec->mux_nids[adc_idx],
1107c577b8a1SJoseph Chan 				     &spec->cur_mux[adc_idx]);
1108c577b8a1SJoseph Chan }
1109c577b8a1SJoseph Chan 
11100aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
11110aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
11120aa62aefSHarald Welte {
11130aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11140aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11150aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
11160aa62aefSHarald Welte }
11170aa62aefSHarald Welte 
11180aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
11190aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
11200aa62aefSHarald Welte {
11210aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11225b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
1123eb7188caSLydia Wang 	unsigned int pinsel;
1124eb7188caSLydia Wang 
1125eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
1126eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
11270aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
11280aa62aefSHarald Welte 				      0x00);
11290aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
11300aa62aefSHarald Welte 
11310aa62aefSHarald Welte 	return 0;
11320aa62aefSHarald Welte }
11330aa62aefSHarald Welte 
11340713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
11350713efebSLydia Wang {
11360713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
11370713efebSLydia Wang 	if (ctl) {
11380713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11390713efebSLydia Wang 		ctl->vd[0].access |= active
11400713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11410713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
11420713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
11430713efebSLydia Wang 	}
11440713efebSLydia Wang }
11450713efebSLydia Wang 
11465b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
11475b0cb1d8SJaroslav Kysela {
11485b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
11495b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
11505b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
11515b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
11525b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
11535b0cb1d8SJaroslav Kysela 	default:		return 0;
11545b0cb1d8SJaroslav Kysela 	}
11555b0cb1d8SJaroslav Kysela }
11565b0cb1d8SJaroslav Kysela 
1157cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
1158cdc1784dSLydia Wang {
1159cdc1784dSLydia Wang 	/* mute side channel */
1160cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
1161cdc1784dSLydia Wang 	unsigned int parm = spec->hp_independent_mode
1162cdc1784dSLydia Wang 		? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
11635b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
1164cdc1784dSLydia Wang 
1165cdc1784dSLydia Wang 	if (sw3)
1166cdc1784dSLydia Wang 		snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1167cdc1784dSLydia Wang 				    parm);
1168cdc1784dSLydia Wang 	return 0;
1169cdc1784dSLydia Wang }
1170cdc1784dSLydia Wang 
11710aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
11720aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
11730aa62aefSHarald Welte {
11740aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11750aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11765b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
11770aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
1178cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
1179cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
1180cdc1784dSLydia Wang 		? 1 : 0;
1181cdc1784dSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
11820aa62aefSHarald Welte 
1183cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
1184cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
1185cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
11860aa62aefSHarald Welte 					   0, 0, 0);
11870aa62aefSHarald Welte 
1188cdc1784dSLydia Wang 	update_side_mute_status(codec);
11890713efebSLydia Wang 	/* update HP volume/swtich active state */
11900713efebSLydia Wang 	if (spec->codec_type == VT1708S
1191eb7188caSLydia Wang 	    || spec->codec_type == VT1702
1192f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
119325eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
1194ab6734e7SLydia Wang 	    || spec->codec_type == VT2002P
1195ab6734e7SLydia Wang 	    || spec->codec_type == VT1812) {
11960713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
11970713efebSLydia Wang 			     spec->hp_independent_mode);
11980713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
11990713efebSLydia Wang 			     spec->hp_independent_mode);
12000713efebSLydia Wang 	}
12010aa62aefSHarald Welte 	return 0;
12020aa62aefSHarald Welte }
12030aa62aefSHarald Welte 
12045b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = {
12050aa62aefSHarald Welte 	{
12060aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
12070aa62aefSHarald Welte 		.name = "Independent HP",
12080aa62aefSHarald Welte 		.info = via_independent_hp_info,
12090aa62aefSHarald Welte 		.get = via_independent_hp_get,
12100aa62aefSHarald Welte 		.put = via_independent_hp_put,
12110aa62aefSHarald Welte 	},
12125b0cb1d8SJaroslav Kysela 	{
12135b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
12145b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
12155b0cb1d8SJaroslav Kysela 	},
12160aa62aefSHarald Welte };
12170aa62aefSHarald Welte 
12185b0cb1d8SJaroslav Kysela static int via_hp_build(struct via_spec *spec)
12195b0cb1d8SJaroslav Kysela {
12205b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
12215b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
12225b0cb1d8SJaroslav Kysela 
12235b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[0]);
12245b0cb1d8SJaroslav Kysela 	if (knew == NULL)
12255b0cb1d8SJaroslav Kysela 		return -ENOMEM;
12265b0cb1d8SJaroslav Kysela 
12275b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
12285b0cb1d8SJaroslav Kysela 	case VT1718S:
12295b0cb1d8SJaroslav Kysela 		nid = 0x34;
12305b0cb1d8SJaroslav Kysela 		break;
12315b0cb1d8SJaroslav Kysela 	case VT2002P:
12325b0cb1d8SJaroslav Kysela 		nid = 0x35;
12335b0cb1d8SJaroslav Kysela 		break;
12345b0cb1d8SJaroslav Kysela 	case VT1812:
12355b0cb1d8SJaroslav Kysela 		nid = 0x3d;
12365b0cb1d8SJaroslav Kysela 		break;
12375b0cb1d8SJaroslav Kysela 	default:
12385b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
12395b0cb1d8SJaroslav Kysela 		break;
12405b0cb1d8SJaroslav Kysela 	}
12415b0cb1d8SJaroslav Kysela 
12425b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
12435b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
12445b0cb1d8SJaroslav Kysela 
12455b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
12465b0cb1d8SJaroslav Kysela 	if (knew == NULL)
12475b0cb1d8SJaroslav Kysela 		return -ENOMEM;
12485b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
12495b0cb1d8SJaroslav Kysela 
12505b0cb1d8SJaroslav Kysela 	return 0;
12515b0cb1d8SJaroslav Kysela }
12525b0cb1d8SJaroslav Kysela 
12531564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
12541564b287SLydia Wang {
12551564b287SLydia Wang 	int i;
12561564b287SLydia Wang 	struct snd_ctl_elem_id id;
12571564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
12581564b287SLydia Wang 
12591564b287SLydia Wang 	memset(&id, 0, sizeof(id));
12601564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
12611564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
12621564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
12631564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
12641564b287SLydia Wang 			       &id);
12651564b287SLydia Wang 	}
12661564b287SLydia Wang }
12671564b287SLydia Wang 
12681564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
12691564b287SLydia Wang {
12701564b287SLydia Wang 	struct via_spec *spec = codec->spec;
12711564b287SLydia Wang 	hda_nid_t  nid_mixer;
12721564b287SLydia Wang 	int start_idx;
12731564b287SLydia Wang 	int end_idx;
12741564b287SLydia Wang 	int i;
12751564b287SLydia Wang 	/* get nid of MW0 and start & end index */
12761564b287SLydia Wang 	switch (spec->codec_type) {
12771564b287SLydia Wang 	case VT1708:
12781564b287SLydia Wang 		nid_mixer = 0x17;
12791564b287SLydia Wang 		start_idx = 2;
12801564b287SLydia Wang 		end_idx = 4;
12811564b287SLydia Wang 		break;
12821564b287SLydia Wang 	case VT1709_10CH:
12831564b287SLydia Wang 	case VT1709_6CH:
12841564b287SLydia Wang 		nid_mixer = 0x18;
12851564b287SLydia Wang 		start_idx = 2;
12861564b287SLydia Wang 		end_idx = 4;
12871564b287SLydia Wang 		break;
12881564b287SLydia Wang 	case VT1708B_8CH:
12891564b287SLydia Wang 	case VT1708B_4CH:
12901564b287SLydia Wang 	case VT1708S:
1291f3db423dSLydia Wang 	case VT1716S:
12921564b287SLydia Wang 		nid_mixer = 0x16;
12931564b287SLydia Wang 		start_idx = 2;
12941564b287SLydia Wang 		end_idx = 4;
12951564b287SLydia Wang 		break;
12961564b287SLydia Wang 	default:
12971564b287SLydia Wang 		return;
12981564b287SLydia Wang 	}
12991564b287SLydia Wang 	/* check AA path's mute status */
13001564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
13011564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
13021564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
13031564b287SLydia Wang 					 HDA_AMP_MUTE, val);
13041564b287SLydia Wang 	}
13051564b287SLydia Wang }
13061564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
13071564b287SLydia Wang {
13081564b287SLydia Wang 	int res = 0;
13091564b287SLydia Wang 	int index;
13101564b287SLydia Wang 	for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) {
13111564b287SLydia Wang 		if (pin == spec->autocfg.input_pins[index]) {
13121564b287SLydia Wang 			res = 1;
13131564b287SLydia Wang 			break;
13141564b287SLydia Wang 		}
13151564b287SLydia Wang 	}
13161564b287SLydia Wang 	return res;
13171564b287SLydia Wang }
13181564b287SLydia Wang 
13191564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
13201564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
13211564b287SLydia Wang {
13221564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
13231564b287SLydia Wang 	uinfo->count = 1;
13241564b287SLydia Wang 	uinfo->value.integer.min = 0;
13251564b287SLydia Wang 	uinfo->value.integer.max = 1;
13261564b287SLydia Wang 	return 0;
13271564b287SLydia Wang }
13281564b287SLydia Wang 
13291564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
13301564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13311564b287SLydia Wang {
13321564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13331564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13341564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
13351564b287SLydia Wang 	int on = 1;
13361564b287SLydia Wang 	int i;
13371564b287SLydia Wang 
13381564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
13391564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
13401564b287SLydia Wang 		if (nid) {
13411564b287SLydia Wang 			int ctl =
13421564b287SLydia Wang 			    snd_hda_codec_read(codec, nid, 0,
13431564b287SLydia Wang 					       AC_VERB_GET_PIN_WIDGET_CONTROL,
13441564b287SLydia Wang 					       0);
13451564b287SLydia Wang 			if (i == AUTO_PIN_FRONT_MIC
1346eb7188caSLydia Wang 			    && spec->hp_independent_mode
1347eb7188caSLydia Wang 			    && spec->codec_type != VT1718S)
13481564b287SLydia Wang 				continue; /* ignore FMic for independent HP */
13491564b287SLydia Wang 			if (ctl & AC_PINCTL_IN_EN
13501564b287SLydia Wang 			    && !(ctl & AC_PINCTL_OUT_EN))
13511564b287SLydia Wang 				on = 0;
13521564b287SLydia Wang 		}
13531564b287SLydia Wang 	}
13541564b287SLydia Wang 	*ucontrol->value.integer.value = on;
13551564b287SLydia Wang 	return 0;
13561564b287SLydia Wang }
13571564b287SLydia Wang 
13581564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
13591564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13601564b287SLydia Wang {
13611564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13621564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13631564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
13641564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
13651564b287SLydia Wang 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
13661564b287SLydia Wang 	int i;
13671564b287SLydia Wang 
13681564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(index); i++) {
13691564b287SLydia Wang 		hda_nid_t nid = spec->autocfg.input_pins[index[i]];
13701564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC
1371eb7188caSLydia Wang 		    && spec->hp_independent_mode
1372eb7188caSLydia Wang 		    && spec->codec_type != VT1718S)
13731564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
13741564b287SLydia Wang 		if (nid) {
13751564b287SLydia Wang 			unsigned int parm = snd_hda_codec_read(
13761564b287SLydia Wang 				codec, nid, 0,
13771564b287SLydia Wang 				AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
13781564b287SLydia Wang 			parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
13791564b287SLydia Wang 			parm |= out_in;
13801564b287SLydia Wang 			snd_hda_codec_write(codec, nid, 0,
13811564b287SLydia Wang 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
13821564b287SLydia Wang 					    parm);
13831564b287SLydia Wang 			if (out_in == AC_PINCTL_OUT_EN) {
13841564b287SLydia Wang 				mute_aa_path(codec, 1);
13851564b287SLydia Wang 				notify_aa_path_ctls(codec);
13861564b287SLydia Wang 			}
1387eb7188caSLydia Wang 			if (spec->codec_type == VT1718S)
1388eb7188caSLydia Wang 				snd_hda_codec_amp_stereo(
1389eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1390eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
13911564b287SLydia Wang 		}
13921564b287SLydia Wang 		if (i == AUTO_PIN_FRONT_MIC) {
1393f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1394f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
13951564b287SLydia Wang 				/* input = index 1 (AOW3) */
13961564b287SLydia Wang 				snd_hda_codec_write(
13971564b287SLydia Wang 					codec, nid, 0,
13981564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
13991564b287SLydia Wang 				snd_hda_codec_amp_stereo(
14001564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
14011564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
14021564b287SLydia Wang 			}
14031564b287SLydia Wang 		}
14041564b287SLydia Wang 	}
14051564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
14061564b287SLydia Wang 	set_jack_power_state(codec);
14071564b287SLydia Wang 	return 1;
14081564b287SLydia Wang }
14091564b287SLydia Wang 
14105b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = {
14111564b287SLydia Wang 	{
14121564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
14131564b287SLydia Wang 	 .name = "Smart 5.1",
14141564b287SLydia Wang 	 .count = 1,
14151564b287SLydia Wang 	 .info = via_smart51_info,
14161564b287SLydia Wang 	 .get = via_smart51_get,
14171564b287SLydia Wang 	 .put = via_smart51_put,
14181564b287SLydia Wang 	 },
14195b0cb1d8SJaroslav Kysela 	{
14205b0cb1d8SJaroslav Kysela 	 .iface = NID_MAPPING,
14215b0cb1d8SJaroslav Kysela 	 .name = "Smart 5.1",
14225b0cb1d8SJaroslav Kysela 	}
14231564b287SLydia Wang };
14241564b287SLydia Wang 
14255b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
14265b0cb1d8SJaroslav Kysela {
14275b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
14285b0cb1d8SJaroslav Kysela 	int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
14295b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
14305b0cb1d8SJaroslav Kysela 	int i;
14315b0cb1d8SJaroslav Kysela 
14325b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_smart51_mixer[0]);
14335b0cb1d8SJaroslav Kysela 	if (knew == NULL)
14345b0cb1d8SJaroslav Kysela 		return -ENOMEM;
14355b0cb1d8SJaroslav Kysela 
14365b0cb1d8SJaroslav Kysela 	for (i = 0; i < ARRAY_SIZE(index); i++) {
14375b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.input_pins[index[i]];
14385b0cb1d8SJaroslav Kysela 		if (nid) {
14395b0cb1d8SJaroslav Kysela 			knew = via_clone_control(spec, &via_smart51_mixer[1]);
14405b0cb1d8SJaroslav Kysela 			if (knew == NULL)
14415b0cb1d8SJaroslav Kysela 				return -ENOMEM;
14425b0cb1d8SJaroslav Kysela 			knew->subdevice = nid;
14435b0cb1d8SJaroslav Kysela 		}
14445b0cb1d8SJaroslav Kysela 	}
14455b0cb1d8SJaroslav Kysela 
14465b0cb1d8SJaroslav Kysela 	return 0;
14475b0cb1d8SJaroslav Kysela }
14485b0cb1d8SJaroslav Kysela 
1449c577b8a1SJoseph Chan /* capture mixer elements */
1450c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1451c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1452c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1453c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1454c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1455c577b8a1SJoseph Chan 	{
1456c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1457c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1458c577b8a1SJoseph Chan 		 * So call somewhat different..
1459c577b8a1SJoseph Chan 		 */
1460c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1461c577b8a1SJoseph Chan 		.name = "Input Source",
1462c577b8a1SJoseph Chan 		.count = 1,
1463c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1464c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1465c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1466c577b8a1SJoseph Chan 	},
1467c577b8a1SJoseph Chan 	{ } /* end */
1468c577b8a1SJoseph Chan };
1469f5271101SLydia Wang 
1470f5271101SLydia Wang /* check AA path's mute statue */
1471f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1472f5271101SLydia Wang {
1473f5271101SLydia Wang 	int mute = 1;
1474f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1475f5271101SLydia Wang 	int start_idx;
1476f5271101SLydia Wang 	int end_idx;
1477f5271101SLydia Wang 	int i;
1478f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1479f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1480f5271101SLydia Wang 	switch (spec->codec_type) {
1481f5271101SLydia Wang 	case VT1708B_8CH:
1482f5271101SLydia Wang 	case VT1708B_4CH:
1483f5271101SLydia Wang 	case VT1708S:
1484f3db423dSLydia Wang 	case VT1716S:
1485f5271101SLydia Wang 		nid_mixer = 0x16;
1486f5271101SLydia Wang 		start_idx = 2;
1487f5271101SLydia Wang 		end_idx = 4;
1488f5271101SLydia Wang 		break;
1489f5271101SLydia Wang 	case VT1702:
1490f5271101SLydia Wang 		nid_mixer = 0x1a;
1491f5271101SLydia Wang 		start_idx = 1;
1492f5271101SLydia Wang 		end_idx = 3;
1493f5271101SLydia Wang 		break;
1494eb7188caSLydia Wang 	case VT1718S:
1495eb7188caSLydia Wang 		nid_mixer = 0x21;
1496eb7188caSLydia Wang 		start_idx = 1;
1497eb7188caSLydia Wang 		end_idx = 3;
1498eb7188caSLydia Wang 		break;
149925eaba2fSLydia Wang 	case VT2002P:
1500ab6734e7SLydia Wang 	case VT1812:
150125eaba2fSLydia Wang 		nid_mixer = 0x21;
150225eaba2fSLydia Wang 		start_idx = 0;
150325eaba2fSLydia Wang 		end_idx = 2;
150425eaba2fSLydia Wang 		break;
1505f5271101SLydia Wang 	default:
1506f5271101SLydia Wang 		return 0;
1507f5271101SLydia Wang 	}
1508f5271101SLydia Wang 	/* check AA path's mute status */
1509f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1510f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1511f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1512f5271101SLydia Wang 		int shift = 8 * (i % 4);
1513f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1514f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1515f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1516f5271101SLydia Wang 			/* check mute status while the pin is connected */
1517f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1518f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1519f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1520f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1521f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1522f5271101SLydia Wang 				mute = 0;
1523f5271101SLydia Wang 				break;
1524f5271101SLydia Wang 			}
1525f5271101SLydia Wang 		}
1526f5271101SLydia Wang 	}
1527f5271101SLydia Wang 	return mute;
1528f5271101SLydia Wang }
1529f5271101SLydia Wang 
1530f5271101SLydia Wang /* enter/exit analog low-current mode */
1531f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1532f5271101SLydia Wang {
1533f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1534f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1535f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1536f5271101SLydia Wang 	unsigned int verb = 0;
1537f5271101SLydia Wang 	unsigned int parm = 0;
1538f5271101SLydia Wang 
1539f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1540f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1541f5271101SLydia Wang 	else {
1542f5271101SLydia Wang 		enable = enable && stream_idle;
1543f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1544f5271101SLydia Wang 	}
1545f5271101SLydia Wang 
1546f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1547f5271101SLydia Wang 	switch (spec->codec_type) {
1548f5271101SLydia Wang 	case VT1708B_8CH:
1549f5271101SLydia Wang 	case VT1708B_4CH:
1550f5271101SLydia Wang 		verb = 0xf70;
1551f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1552f5271101SLydia Wang 		break;
1553f5271101SLydia Wang 	case VT1708S:
1554eb7188caSLydia Wang 	case VT1718S:
1555f3db423dSLydia Wang 	case VT1716S:
1556f5271101SLydia Wang 		verb = 0xf73;
1557f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1558f5271101SLydia Wang 		break;
1559f5271101SLydia Wang 	case VT1702:
1560f5271101SLydia Wang 		verb = 0xf73;
1561f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1562f5271101SLydia Wang 		break;
156325eaba2fSLydia Wang 	case VT2002P:
1564ab6734e7SLydia Wang 	case VT1812:
156525eaba2fSLydia Wang 		verb = 0xf93;
156625eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
156725eaba2fSLydia Wang 		break;
1568f5271101SLydia Wang 	default:
1569f5271101SLydia Wang 		return;		/* other codecs are not supported */
1570f5271101SLydia Wang 	}
1571f5271101SLydia Wang 	/* send verb */
1572f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1573f5271101SLydia Wang }
1574f5271101SLydia Wang 
1575c577b8a1SJoseph Chan /*
1576c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1577c577b8a1SJoseph Chan  */
1578c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1579c577b8a1SJoseph Chan 	/*
1580c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1581c577b8a1SJoseph Chan 	 */
1582c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1583c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1584c577b8a1SJoseph Chan 
1585c577b8a1SJoseph Chan 
1586f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1587c577b8a1SJoseph Chan 	 * mixer widget
1588c577b8a1SJoseph Chan 	 */
1589c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1590f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1591f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1592f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1593f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1594f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1595c577b8a1SJoseph Chan 
1596c577b8a1SJoseph Chan 	/*
1597c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1598c577b8a1SJoseph Chan 	 */
1599c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1600c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1601c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1602c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1603c577b8a1SJoseph Chan 
1604bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1605bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1606c577b8a1SJoseph Chan 	/* PW9 Output enable */
1607c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1608f7278fd0SJosepch Chan 	{ }
1609c577b8a1SJoseph Chan };
1610c577b8a1SJoseph Chan 
1611c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1612c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1613c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1614c577b8a1SJoseph Chan {
1615c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
161617314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
161717314379SLydia Wang 		&& substream->ref_count == 0;
161817314379SLydia Wang 	analog_low_current_mode(codec, idle);
16199a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
16209a08160bSTakashi Iwai 					     hinfo);
1621c577b8a1SJoseph Chan }
1622c577b8a1SJoseph Chan 
16230aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
16240aa62aefSHarald Welte 				      unsigned int stream_tag,
16250aa62aefSHarald Welte 				      unsigned int format,
16260aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
16270aa62aefSHarald Welte {
16280aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
16290aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
16300aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
16310aa62aefSHarald Welte 	int chs = substream->runtime->channels;
16320aa62aefSHarald Welte 	int i;
16330aa62aefSHarald Welte 
16340aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
16350aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
16360aa62aefSHarald Welte 		if (chs == 2 &&
16370aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
16380aa62aefSHarald Welte 						format) &&
16390aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
16400aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
16410aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
16420aa62aefSHarald Welte 			 * be updated */
16430aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
16440aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
16450aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
16460aa62aefSHarald Welte 						    codec->spdif_ctls &
16470aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
16480aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16490aa62aefSHarald Welte 						   stream_tag, 0, format);
16500aa62aefSHarald Welte 			/* turn on again (if needed) */
16510aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
16520aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
16530aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
16540aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
16550aa62aefSHarald Welte 		} else {
16560aa62aefSHarald Welte 			mout->dig_out_used = 0;
16570aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16580aa62aefSHarald Welte 						   0, 0, 0);
16590aa62aefSHarald Welte 		}
16600aa62aefSHarald Welte 	}
16610aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
16620aa62aefSHarald Welte 
16630aa62aefSHarald Welte 	/* front */
16640aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
16650aa62aefSHarald Welte 				   0, format);
16660aa62aefSHarald Welte 
1667eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1668eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
16690aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
16700aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
16710aa62aefSHarald Welte 					   0, format);
16720aa62aefSHarald Welte 
16730aa62aefSHarald Welte 	/* extra outputs copied from front */
16740aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
16750aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
16760aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
16770aa62aefSHarald Welte 						   mout->extra_out_nid[i],
16780aa62aefSHarald Welte 						   stream_tag, 0, format);
16790aa62aefSHarald Welte 
16800aa62aefSHarald Welte 	/* surrounds */
16810aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
16820aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
16830aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
16840aa62aefSHarald Welte 						   i * 2, format);
16850aa62aefSHarald Welte 		else /* copy front */
16860aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
16870aa62aefSHarald Welte 						   0, format);
16880aa62aefSHarald Welte 	}
16890aa62aefSHarald Welte }
16900aa62aefSHarald Welte 
16910aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
16920aa62aefSHarald Welte 					  struct hda_codec *codec,
16930aa62aefSHarald Welte 					  unsigned int stream_tag,
16940aa62aefSHarald Welte 					  unsigned int format,
16950aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
16960aa62aefSHarald Welte {
16970aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
16980aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
16990aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
17000aa62aefSHarald Welte 
17010aa62aefSHarald Welte 	if (substream->number == 0)
17020aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
17030aa62aefSHarald Welte 					  substream);
17040aa62aefSHarald Welte 	else {
17050aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
17060aa62aefSHarald Welte 		    spec->hp_independent_mode)
17070aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17080aa62aefSHarald Welte 						   stream_tag, 0, format);
17090aa62aefSHarald Welte 	}
17101f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
17110aa62aefSHarald Welte 	return 0;
17120aa62aefSHarald Welte }
17130aa62aefSHarald Welte 
17140aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
17150aa62aefSHarald Welte 				    struct hda_codec *codec,
17160aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
17170aa62aefSHarald Welte {
17180aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
17190aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
17200aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
17210aa62aefSHarald Welte 	int i;
17220aa62aefSHarald Welte 
17230aa62aefSHarald Welte 	if (substream->number == 0) {
17240aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
17250aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
17260aa62aefSHarald Welte 
17270aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
17280aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17290aa62aefSHarald Welte 						   0, 0, 0);
17300aa62aefSHarald Welte 
17310aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
17320aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
17330aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
17340aa62aefSHarald Welte 							mout->extra_out_nid[i],
17350aa62aefSHarald Welte 							0, 0, 0);
17360aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
17370aa62aefSHarald Welte 		if (mout->dig_out_nid &&
17380aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
17390aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
17400aa62aefSHarald Welte 						   0, 0, 0);
17410aa62aefSHarald Welte 			mout->dig_out_used = 0;
17420aa62aefSHarald Welte 		}
17430aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
17440aa62aefSHarald Welte 	} else {
17450aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
17460aa62aefSHarald Welte 		    spec->hp_independent_mode)
17470aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17480aa62aefSHarald Welte 						   0, 0, 0);
17490aa62aefSHarald Welte 	}
17501f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
17510aa62aefSHarald Welte 	return 0;
17520aa62aefSHarald Welte }
17530aa62aefSHarald Welte 
1754c577b8a1SJoseph Chan /*
1755c577b8a1SJoseph Chan  * Digital out
1756c577b8a1SJoseph Chan  */
1757c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1758c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1759c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1760c577b8a1SJoseph Chan {
1761c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1762c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1763c577b8a1SJoseph Chan }
1764c577b8a1SJoseph Chan 
1765c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1766c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1767c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1768c577b8a1SJoseph Chan {
1769c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1770c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1771c577b8a1SJoseph Chan }
1772c577b8a1SJoseph Chan 
17735691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
177498aa34c0SHarald Welte 					struct hda_codec *codec,
177598aa34c0SHarald Welte 					unsigned int stream_tag,
177698aa34c0SHarald Welte 					unsigned int format,
177798aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
177898aa34c0SHarald Welte {
177998aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
17809da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
17819da29271STakashi Iwai 					     stream_tag, format, substream);
17829da29271STakashi Iwai }
17835691ec7fSHarald Welte 
17849da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
17859da29271STakashi Iwai 					struct hda_codec *codec,
17869da29271STakashi Iwai 					struct snd_pcm_substream *substream)
17879da29271STakashi Iwai {
17889da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
17899da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
179098aa34c0SHarald Welte 	return 0;
179198aa34c0SHarald Welte }
179298aa34c0SHarald Welte 
1793c577b8a1SJoseph Chan /*
1794c577b8a1SJoseph Chan  * Analog capture
1795c577b8a1SJoseph Chan  */
1796c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1797c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1798c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1799c577b8a1SJoseph Chan 				   unsigned int format,
1800c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1801c577b8a1SJoseph Chan {
1802c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1803c577b8a1SJoseph Chan 
1804c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1805c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1806c577b8a1SJoseph Chan 	return 0;
1807c577b8a1SJoseph Chan }
1808c577b8a1SJoseph Chan 
1809c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1810c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1811c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1812c577b8a1SJoseph Chan {
1813c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1814888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1815c577b8a1SJoseph Chan 	return 0;
1816c577b8a1SJoseph Chan }
1817c577b8a1SJoseph Chan 
1818c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
18190aa62aefSHarald Welte 	.substreams = 2,
1820c577b8a1SJoseph Chan 	.channels_min = 2,
1821c577b8a1SJoseph Chan 	.channels_max = 8,
1822c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1823c577b8a1SJoseph Chan 	.ops = {
1824c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
18250aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
18260aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1827c577b8a1SJoseph Chan 	},
1828c577b8a1SJoseph Chan };
1829c577b8a1SJoseph Chan 
1830bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1831c873cc25SLydia Wang 	.substreams = 2,
1832bc9b5623STakashi Iwai 	.channels_min = 2,
1833bc9b5623STakashi Iwai 	.channels_max = 8,
1834bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1835bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1836bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1837bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1838bc9b5623STakashi Iwai 	 */
1839bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1840bc9b5623STakashi Iwai 	.ops = {
1841bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1842c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1843c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1844bc9b5623STakashi Iwai 	},
1845bc9b5623STakashi Iwai };
1846bc9b5623STakashi Iwai 
1847c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1848c577b8a1SJoseph Chan 	.substreams = 2,
1849c577b8a1SJoseph Chan 	.channels_min = 2,
1850c577b8a1SJoseph Chan 	.channels_max = 2,
1851c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1852c577b8a1SJoseph Chan 	.ops = {
1853c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1854c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1855c577b8a1SJoseph Chan 	},
1856c577b8a1SJoseph Chan };
1857c577b8a1SJoseph Chan 
1858c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1859c577b8a1SJoseph Chan 	.substreams = 1,
1860c577b8a1SJoseph Chan 	.channels_min = 2,
1861c577b8a1SJoseph Chan 	.channels_max = 2,
1862c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1863c577b8a1SJoseph Chan 	.ops = {
1864c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
18656b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
18669da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
18679da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1868c577b8a1SJoseph Chan 	},
1869c577b8a1SJoseph Chan };
1870c577b8a1SJoseph Chan 
1871c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1872c577b8a1SJoseph Chan 	.substreams = 1,
1873c577b8a1SJoseph Chan 	.channels_min = 2,
1874c577b8a1SJoseph Chan 	.channels_max = 2,
1875c577b8a1SJoseph Chan };
1876c577b8a1SJoseph Chan 
1877c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1878c577b8a1SJoseph Chan {
1879c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
18805b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
18815b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
18825b0cb1d8SJaroslav Kysela 	int err, i;
1883c577b8a1SJoseph Chan 
1884c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1885c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1886c577b8a1SJoseph Chan 		if (err < 0)
1887c577b8a1SJoseph Chan 			return err;
1888c577b8a1SJoseph Chan 	}
1889c577b8a1SJoseph Chan 
1890c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1891c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1892c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1893c577b8a1SJoseph Chan 		if (err < 0)
1894c577b8a1SJoseph Chan 			return err;
18959a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
18969a08160bSTakashi Iwai 						    &spec->multiout);
18979a08160bSTakashi Iwai 		if (err < 0)
18989a08160bSTakashi Iwai 			return err;
18999a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1900c577b8a1SJoseph Chan 	}
1901c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1902c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1903c577b8a1SJoseph Chan 		if (err < 0)
1904c577b8a1SJoseph Chan 			return err;
1905c577b8a1SJoseph Chan 	}
190617314379SLydia Wang 
19075b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
19085b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
19095b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
19105b0cb1d8SJaroslav Kysela 		err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids,
19115b0cb1d8SJaroslav Kysela 				       spec->input_mux->num_items);
19125b0cb1d8SJaroslav Kysela 		if (err < 0)
19135b0cb1d8SJaroslav Kysela 			return err;
19145b0cb1d8SJaroslav Kysela 	}
19155b0cb1d8SJaroslav Kysela 
19165b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
19175b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
19185b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
19195b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
19205b0cb1d8SJaroslav Kysela 				continue;
19215b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
19225b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
19235b0cb1d8SJaroslav Kysela 				continue;
19245b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
19255b0cb1d8SJaroslav Kysela 					      knew->subdevice);
19265b0cb1d8SJaroslav Kysela 		}
19275b0cb1d8SJaroslav Kysela 	}
19285b0cb1d8SJaroslav Kysela 
192917314379SLydia Wang 	/* init power states */
193017314379SLydia Wang 	set_jack_power_state(codec);
193117314379SLydia Wang 	analog_low_current_mode(codec, 1);
193217314379SLydia Wang 
1933603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1934c577b8a1SJoseph Chan 	return 0;
1935c577b8a1SJoseph Chan }
1936c577b8a1SJoseph Chan 
1937c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1938c577b8a1SJoseph Chan {
1939c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1940c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1941c577b8a1SJoseph Chan 
1942c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1943c577b8a1SJoseph Chan 	codec->pcm_info = info;
1944c577b8a1SJoseph Chan 
1945c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1946377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1947377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1948377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1949377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1950c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1951c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1952c577b8a1SJoseph Chan 
1953c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1954c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1955c577b8a1SJoseph Chan 
1956c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1957c577b8a1SJoseph Chan 		codec->num_pcms++;
1958c577b8a1SJoseph Chan 		info++;
1959c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
19607ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1961c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1962c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1963c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1964c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1965c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1966c577b8a1SJoseph Chan 		}
1967c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1968c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1969c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1970c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1971c577b8a1SJoseph Chan 				spec->dig_in_nid;
1972c577b8a1SJoseph Chan 		}
1973c577b8a1SJoseph Chan 	}
1974c577b8a1SJoseph Chan 
1975c577b8a1SJoseph Chan 	return 0;
1976c577b8a1SJoseph Chan }
1977c577b8a1SJoseph Chan 
1978c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1979c577b8a1SJoseph Chan {
1980c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1981c577b8a1SJoseph Chan 
1982c577b8a1SJoseph Chan 	if (!spec)
1983c577b8a1SJoseph Chan 		return;
1984c577b8a1SJoseph Chan 
1985603c4019STakashi Iwai 	via_free_kctls(codec);
19861f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1987c577b8a1SJoseph Chan 	kfree(codec->spec);
1988c577b8a1SJoseph Chan }
1989c577b8a1SJoseph Chan 
199069e52a80SHarald Welte /* mute internal speaker if HP is plugged */
199169e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
199269e52a80SHarald Welte {
1993dcf34c8cSLydia Wang 	unsigned int present = 0;
199469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
199569e52a80SHarald Welte 
1996d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1997dcf34c8cSLydia Wang 
1998dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1999dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
2000dcf34c8cSLydia Wang 		/* auto mute */
2001dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
2002dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
2003dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
2004dcf34c8cSLydia Wang 		/* notify change */
2005dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
2006dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
2007dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
2008dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
2009dcf34c8cSLydia Wang 			       &id);
2010dcf34c8cSLydia Wang 	}
201169e52a80SHarald Welte }
201269e52a80SHarald Welte 
2013f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
2014f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
2015f3db423dSLydia Wang {
2016f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
2017f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
2018f3db423dSLydia Wang 
2019f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
2020f3db423dSLydia Wang 		return;
2021f3db423dSLydia Wang 
2022d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
2023d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
2024f3db423dSLydia Wang 
2025f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
2026f3db423dSLydia Wang 	if (lineout_present) {
2027f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
2028f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
2029f3db423dSLydia Wang 		return;
2030f3db423dSLydia Wang 	}
2031f3db423dSLydia Wang 
2032d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
2033f3db423dSLydia Wang 
2034f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
2035f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
2036f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
2037f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
2038f3db423dSLydia Wang }
2039f3db423dSLydia Wang 
204069e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
204169e52a80SHarald Welte {
204269e52a80SHarald Welte 	unsigned int gpio_data;
204369e52a80SHarald Welte 	unsigned int vol_counter;
204469e52a80SHarald Welte 	unsigned int vol;
204569e52a80SHarald Welte 	unsigned int master_vol;
204669e52a80SHarald Welte 
204769e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
204869e52a80SHarald Welte 
204969e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
205069e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
205169e52a80SHarald Welte 
205269e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
205369e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
205469e52a80SHarald Welte 
205569e52a80SHarald Welte 	vol = vol_counter & 0x1F;
205669e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
205769e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
205869e52a80SHarald Welte 					AC_AMP_GET_INPUT);
205969e52a80SHarald Welte 
206069e52a80SHarald Welte 	if (gpio_data == 0x02) {
206169e52a80SHarald Welte 		/* unmute line out */
206269e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
206369e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
206469e52a80SHarald Welte 
206569e52a80SHarald Welte 		if (vol_counter & 0x20) {
206669e52a80SHarald Welte 			/* decrease volume */
206769e52a80SHarald Welte 			if (vol > master_vol)
206869e52a80SHarald Welte 				vol = master_vol;
206969e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
207069e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
207169e52a80SHarald Welte 						 master_vol-vol);
207269e52a80SHarald Welte 		} else {
207369e52a80SHarald Welte 			/* increase volume */
207469e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
207569e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
207669e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
207769e52a80SHarald Welte 					  (master_vol+vol));
207869e52a80SHarald Welte 		}
207969e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
208069e52a80SHarald Welte 		/* mute line out */
208169e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
208269e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
208369e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
208469e52a80SHarald Welte 					 HDA_AMP_MUTE);
208569e52a80SHarald Welte 	}
208669e52a80SHarald Welte }
208769e52a80SHarald Welte 
208825eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
208925eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
209025eaba2fSLydia Wang {
209125eaba2fSLydia Wang 	unsigned int hp_present;
209225eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
209325eaba2fSLydia Wang 
2094ab6734e7SLydia Wang 	if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
209525eaba2fSLydia Wang 		return;
209625eaba2fSLydia Wang 
2097d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
209825eaba2fSLydia Wang 
209925eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
210025eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
210125eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
210225eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
210325eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
210425eaba2fSLydia Wang 		/* notify change */
210525eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
210625eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
210725eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
210825eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
210925eaba2fSLydia Wang 			       &id);
211025eaba2fSLydia Wang 	}
211125eaba2fSLydia Wang }
211225eaba2fSLydia Wang 
211325eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
211425eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
211525eaba2fSLydia Wang {
211601a1796bSakpm@linux-foundation.org 	/* use long instead of int below just to avoid an internal compiler
211701a1796bSakpm@linux-foundation.org 	 * error with gcc 4.0.x
211801a1796bSakpm@linux-foundation.org 	 */
211901a1796bSakpm@linux-foundation.org 	unsigned long hp_present, present = 0;
212025eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
212125eaba2fSLydia Wang 	int i;
212225eaba2fSLydia Wang 
212325eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
212425eaba2fSLydia Wang 		return;
212525eaba2fSLydia Wang 
2126d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
212725eaba2fSLydia Wang 
2128d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
212925eaba2fSLydia Wang 
213025eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
213125eaba2fSLydia Wang 		/* Mute Line-Outs */
213225eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
213325eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
213425eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
213525eaba2fSLydia Wang 				HDA_OUTPUT, 0,
213625eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
213725eaba2fSLydia Wang 		if (hp_present)
213825eaba2fSLydia Wang 			present = hp_present;
213925eaba2fSLydia Wang 	}
214025eaba2fSLydia Wang 	/* Speakers */
214125eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
214225eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
214325eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
214425eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
214525eaba2fSLydia Wang }
214625eaba2fSLydia Wang 
214725eaba2fSLydia Wang 
214869e52a80SHarald Welte /* unsolicited event for jack sensing */
214969e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
215069e52a80SHarald Welte 				  unsigned int res)
215169e52a80SHarald Welte {
215269e52a80SHarald Welte 	res >>= 26;
2153a34df19aSLydia Wang 	if (res & VIA_HP_EVENT)
215469e52a80SHarald Welte 		via_hp_automute(codec);
2155a34df19aSLydia Wang 	if (res & VIA_GPIO_EVENT)
215669e52a80SHarald Welte 		via_gpio_control(codec);
2157a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
2158a34df19aSLydia Wang 		set_jack_power_state(codec);
2159f3db423dSLydia Wang 	if (res & VIA_MONO_EVENT)
2160f3db423dSLydia Wang 		via_mono_automute(codec);
216125eaba2fSLydia Wang 	if (res & VIA_SPEAKER_EVENT)
216225eaba2fSLydia Wang 		via_speaker_automute(codec);
216325eaba2fSLydia Wang 	if (res & VIA_BIND_HP_EVENT)
216425eaba2fSLydia Wang 		via_hp_bind_automute(codec);
216569e52a80SHarald Welte }
216669e52a80SHarald Welte 
2167c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
2168c577b8a1SJoseph Chan {
2169c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
217069e52a80SHarald Welte 	int i;
217169e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
217269e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
217369e52a80SHarald Welte 
2174518bf3baSLydia Wang 	spec->codec_type = get_codec_type(codec);
2175518bf3baSLydia Wang 	if (spec->codec_type == VT1708BCE)
2176518bf3baSLydia Wang 		spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
2177518bf3baSLydia Wang 					       same */
2178f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
2179f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
218055d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
218155d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2182f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
218312b74c80STakashi Iwai 					    PIN_OUT);
218455d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2185f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
2186f7278fd0SJosepch Chan 		}
218712b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
218812b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
218912b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2190f7278fd0SJosepch Chan 
21919da29271STakashi Iwai 	/* assign slave outs */
21929da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
21939da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
21945691ec7fSHarald Welte 
2195c577b8a1SJoseph Chan 	return 0;
2196c577b8a1SJoseph Chan }
2197c577b8a1SJoseph Chan 
21981f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
21991f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
22001f2e99feSLydia Wang {
22011f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22021f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
22031f2e99feSLydia Wang 	return 0;
22041f2e99feSLydia Wang }
22051f2e99feSLydia Wang #endif
22061f2e99feSLydia Wang 
2207cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2208cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
2209cb53c626STakashi Iwai {
2210cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
2211cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
2212cb53c626STakashi Iwai }
2213cb53c626STakashi Iwai #endif
2214cb53c626STakashi Iwai 
2215c577b8a1SJoseph Chan /*
2216c577b8a1SJoseph Chan  */
2217c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
2218c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
2219c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
2220c577b8a1SJoseph Chan 	.init = via_init,
2221c577b8a1SJoseph Chan 	.free = via_free,
22221f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
22231f2e99feSLydia Wang 	.suspend = via_suspend,
22241f2e99feSLydia Wang #endif
2225cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2226cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
2227cb53c626STakashi Iwai #endif
2228c577b8a1SJoseph Chan };
2229c577b8a1SJoseph Chan 
2230c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
2231c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
2232c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2233c577b8a1SJoseph Chan {
2234c577b8a1SJoseph Chan 	int i;
2235c577b8a1SJoseph Chan 	hda_nid_t nid;
2236c577b8a1SJoseph Chan 
2237c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
2238c577b8a1SJoseph Chan 
2239c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2240c577b8a1SJoseph Chan 
2241c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
2242c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2243c577b8a1SJoseph Chan 		if (nid) {
2244c577b8a1SJoseph Chan 			/* config dac list */
2245c577b8a1SJoseph Chan 			switch (i) {
2246c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
2247c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
2248c577b8a1SJoseph Chan 				break;
2249c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
2250c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
2251c577b8a1SJoseph Chan 				break;
2252c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
2253fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
2254c577b8a1SJoseph Chan 				break;
2255c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
2256fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
2257c577b8a1SJoseph Chan 				break;
2258c577b8a1SJoseph Chan 			}
2259c577b8a1SJoseph Chan 		}
2260c577b8a1SJoseph Chan 	}
2261c577b8a1SJoseph Chan 
2262c577b8a1SJoseph Chan 	return 0;
2263c577b8a1SJoseph Chan }
2264c577b8a1SJoseph Chan 
2265c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2266c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
2267c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2268c577b8a1SJoseph Chan {
2269c577b8a1SJoseph Chan 	char name[32];
2270c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
22719645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
2272c577b8a1SJoseph Chan 	int i, err;
2273c577b8a1SJoseph Chan 
2274c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2275c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2276c577b8a1SJoseph Chan 
2277c577b8a1SJoseph Chan 		if (!nid)
2278c577b8a1SJoseph Chan 			continue;
2279c577b8a1SJoseph Chan 
22809645c203SLydia Wang 		nid_vol = nid_vols[i];
2281c577b8a1SJoseph Chan 
2282c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2283c577b8a1SJoseph Chan 			/* Center/LFE */
2284c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2285c577b8a1SJoseph Chan 					"Center Playback Volume",
2286f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2287f7278fd0SJosepch Chan 							    HDA_OUTPUT));
2288c577b8a1SJoseph Chan 			if (err < 0)
2289c577b8a1SJoseph Chan 				return err;
2290c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2291c577b8a1SJoseph Chan 					      "LFE Playback Volume",
2292f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2293f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2294c577b8a1SJoseph Chan 			if (err < 0)
2295c577b8a1SJoseph Chan 				return err;
2296c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2297c577b8a1SJoseph Chan 					      "Center Playback Switch",
2298f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2299f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2300c577b8a1SJoseph Chan 			if (err < 0)
2301c577b8a1SJoseph Chan 				return err;
2302c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2303c577b8a1SJoseph Chan 					      "LFE Playback Switch",
2304f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2305f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2306c577b8a1SJoseph Chan 			if (err < 0)
2307c577b8a1SJoseph Chan 				return err;
2308c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
2309c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
2310c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2311c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
23129645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2313f7278fd0SJosepch Chan 								  HDA_INPUT));
2314c577b8a1SJoseph Chan 			if (err < 0)
2315c577b8a1SJoseph Chan 				return err;
2316c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2317c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
23189645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2319f7278fd0SJosepch Chan 								  HDA_INPUT));
2320c577b8a1SJoseph Chan 			if (err < 0)
2321c577b8a1SJoseph Chan 				return err;
2322c577b8a1SJoseph Chan 
2323c577b8a1SJoseph Chan 			/* add control to PW3 */
2324c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2325c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2326f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2327f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2328c577b8a1SJoseph Chan 			if (err < 0)
2329c577b8a1SJoseph Chan 				return err;
2330c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2331c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2332f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2333f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2334c577b8a1SJoseph Chan 			if (err < 0)
2335c577b8a1SJoseph Chan 				return err;
2336c577b8a1SJoseph Chan 		} else {
2337c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2338c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2339f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2340f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2341c577b8a1SJoseph Chan 			if (err < 0)
2342c577b8a1SJoseph Chan 				return err;
2343c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2344c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2345f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2346f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2347c577b8a1SJoseph Chan 			if (err < 0)
2348c577b8a1SJoseph Chan 				return err;
2349c577b8a1SJoseph Chan 		}
2350c577b8a1SJoseph Chan 	}
2351c577b8a1SJoseph Chan 
2352c577b8a1SJoseph Chan 	return 0;
2353c577b8a1SJoseph Chan }
2354c577b8a1SJoseph Chan 
23550aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
23560aa62aefSHarald Welte {
23570aa62aefSHarald Welte 	int i;
23580aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
23590aa62aefSHarald Welte 	static const char *texts[] = { "OFF", "ON", NULL};
23600aa62aefSHarald Welte 
23610aa62aefSHarald Welte 	/* for hp mode select */
23620aa62aefSHarald Welte 	i = 0;
23630aa62aefSHarald Welte 	while (texts[i] != NULL) {
23640aa62aefSHarald Welte 		imux->items[imux->num_items].label =  texts[i];
23650aa62aefSHarald Welte 		imux->items[imux->num_items].index = i;
23660aa62aefSHarald Welte 		imux->num_items++;
23670aa62aefSHarald Welte 		i++;
23680aa62aefSHarald Welte 	}
23690aa62aefSHarald Welte 
23700aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
23710aa62aefSHarald Welte }
23720aa62aefSHarald Welte 
2373c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2374c577b8a1SJoseph Chan {
2375c577b8a1SJoseph Chan 	int err;
2376c577b8a1SJoseph Chan 
2377c577b8a1SJoseph Chan 	if (!pin)
2378c577b8a1SJoseph Chan 		return 0;
2379c577b8a1SJoseph Chan 
2380c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
2381cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2382c577b8a1SJoseph Chan 
2383c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2384c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2385c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2386c577b8a1SJoseph Chan 	if (err < 0)
2387c577b8a1SJoseph Chan 		return err;
2388c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2389c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2390c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2391c577b8a1SJoseph Chan 	if (err < 0)
2392c577b8a1SJoseph Chan 		return err;
2393c577b8a1SJoseph Chan 
23940aa62aefSHarald Welte 	create_hp_imux(spec);
23950aa62aefSHarald Welte 
2396c577b8a1SJoseph Chan 	return 0;
2397c577b8a1SJoseph Chan }
2398c577b8a1SJoseph Chan 
2399c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2400c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec,
2401c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2402c577b8a1SJoseph Chan {
2403c577b8a1SJoseph Chan 	static char *labels[] = {
2404c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
2405c577b8a1SJoseph Chan 	};
24060aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2407c577b8a1SJoseph Chan 	int i, err, idx = 0;
2408c577b8a1SJoseph Chan 
2409c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2410c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
2411c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
2412c577b8a1SJoseph Chan 	imux->num_items++;
2413c577b8a1SJoseph Chan 
2414c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
2415c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
2416c577b8a1SJoseph Chan 			continue;
2417c577b8a1SJoseph Chan 
2418c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
2419c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
2420c577b8a1SJoseph Chan 			idx = 2;
2421c577b8a1SJoseph Chan 			break;
2422c577b8a1SJoseph Chan 
2423c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
2424c577b8a1SJoseph Chan 			idx = 3;
2425c577b8a1SJoseph Chan 			break;
2426c577b8a1SJoseph Chan 
2427c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
2428c577b8a1SJoseph Chan 			idx = 4;
2429c577b8a1SJoseph Chan 			break;
2430c577b8a1SJoseph Chan 
2431c577b8a1SJoseph Chan 		case 0x24: /* CD */
2432c577b8a1SJoseph Chan 			idx = 1;
2433c577b8a1SJoseph Chan 			break;
2434c577b8a1SJoseph Chan 		}
24359510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x17);
2436c577b8a1SJoseph Chan 		if (err < 0)
2437c577b8a1SJoseph Chan 			return err;
2438c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
2439c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
2440c577b8a1SJoseph Chan 		imux->num_items++;
2441c577b8a1SJoseph Chan 	}
2442c577b8a1SJoseph Chan 	return 0;
2443c577b8a1SJoseph Chan }
2444c577b8a1SJoseph Chan 
2445cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2446cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2447cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2448cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2449cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2450cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2451cb53c626STakashi Iwai 	{ } /* end */
2452cb53c626STakashi Iwai };
2453cb53c626STakashi Iwai #endif
2454cb53c626STakashi Iwai 
245576d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
245676d9b0ddSHarald Welte {
245776d9b0ddSHarald Welte 	unsigned int def_conf;
245876d9b0ddSHarald Welte 	unsigned char seqassoc;
245976d9b0ddSHarald Welte 
24602f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
246176d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
246276d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
246382ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
246482ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
246576d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
24662f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
246776d9b0ddSHarald Welte 	}
246876d9b0ddSHarald Welte 
246976d9b0ddSHarald Welte 	return;
247076d9b0ddSHarald Welte }
247176d9b0ddSHarald Welte 
24721f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
24731f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
24741f2e99feSLydia Wang {
24751f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24761f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
24771f2e99feSLydia Wang 
24781f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24791f2e99feSLydia Wang 		return 0;
24801f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
24811f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
24821f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
24831f2e99feSLydia Wang 	return 0;
24841f2e99feSLydia Wang }
24851f2e99feSLydia Wang 
24861f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
24871f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
24881f2e99feSLydia Wang {
24891f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24901f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
24911f2e99feSLydia Wang 	int change;
24921f2e99feSLydia Wang 
24931f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24941f2e99feSLydia Wang 		return 0;
24951f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
24961f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
24971f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
24981f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
24991f2e99feSLydia Wang 		mute_aa_path(codec, 1);
25001f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
25011f2e99feSLydia Wang 	}
25021f2e99feSLydia Wang 	return change;
25031f2e99feSLydia Wang }
25041f2e99feSLydia Wang 
25051f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
25061f2e99feSLydia Wang 	{
25071f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25081f2e99feSLydia Wang 		.name = "Jack Detect",
25091f2e99feSLydia Wang 		.count = 1,
25101f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
25111f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
25121f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
25131f2e99feSLydia Wang 	},
25141f2e99feSLydia Wang 	{} /* end */
25151f2e99feSLydia Wang };
25161f2e99feSLydia Wang 
2517c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2518c577b8a1SJoseph Chan {
2519c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2520c577b8a1SJoseph Chan 	int err;
2521c577b8a1SJoseph Chan 
252276d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
252376d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
252476d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
252576d9b0ddSHarald Welte 
2526c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2527c577b8a1SJoseph Chan 	if (err < 0)
2528c577b8a1SJoseph Chan 		return err;
2529c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2530c577b8a1SJoseph Chan 	if (err < 0)
2531c577b8a1SJoseph Chan 		return err;
2532c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2533c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2534c577b8a1SJoseph Chan 
2535c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2536c577b8a1SJoseph Chan 	if (err < 0)
2537c577b8a1SJoseph Chan 		return err;
2538c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2539c577b8a1SJoseph Chan 	if (err < 0)
2540c577b8a1SJoseph Chan 		return err;
2541c577b8a1SJoseph Chan 	err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg);
2542c577b8a1SJoseph Chan 	if (err < 0)
2543c577b8a1SJoseph Chan 		return err;
25441f2e99feSLydia Wang 	/* add jack detect on/off control */
25451f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
25461f2e99feSLydia Wang 	if (err < 0)
25471f2e99feSLydia Wang 		return err;
2548c577b8a1SJoseph Chan 
2549c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2550c577b8a1SJoseph Chan 
25510852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2552c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
255355d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2554c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2555c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2556c577b8a1SJoseph Chan 
2557603c4019STakashi Iwai 	if (spec->kctls.list)
2558603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2559c577b8a1SJoseph Chan 
256069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2561c577b8a1SJoseph Chan 
25620aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
25630aa62aefSHarald Welte 
2564f8fdd495SHarald Welte 	if (spec->hp_mux)
25655b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
2566c577b8a1SJoseph Chan 
25675b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2568c577b8a1SJoseph Chan 	return 1;
2569c577b8a1SJoseph Chan }
2570c577b8a1SJoseph Chan 
2571c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2572c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2573c577b8a1SJoseph Chan {
257425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
257525eaba2fSLydia Wang 
2576c577b8a1SJoseph Chan 	via_init(codec);
2577c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2578c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2579c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
2580ab6734e7SLydia Wang 	if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
258125eaba2fSLydia Wang 		via_hp_bind_automute(codec);
258225eaba2fSLydia Wang 	} else {
258325eaba2fSLydia Wang 		via_hp_automute(codec);
258425eaba2fSLydia Wang 		via_speaker_automute(codec);
258525eaba2fSLydia Wang 	}
258625eaba2fSLydia Wang 
2587c577b8a1SJoseph Chan 	return 0;
2588c577b8a1SJoseph Chan }
2589c577b8a1SJoseph Chan 
25901f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
25911f2e99feSLydia Wang {
25921f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
25931f2e99feSLydia Wang 					     vt1708_hp_work.work);
25941f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25951f2e99feSLydia Wang 		return;
25961f2e99feSLydia Wang 	/* if jack state toggled */
25971f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2598d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
25991f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
26001f2e99feSLydia Wang 		via_hp_automute(spec->codec);
26011f2e99feSLydia Wang 	}
26021f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
26031f2e99feSLydia Wang }
26041f2e99feSLydia Wang 
2605337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2606337b9d02STakashi Iwai {
2607337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2608337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2609337b9d02STakashi Iwai 	unsigned int type;
2610337b9d02STakashi Iwai 	int i, n;
2611337b9d02STakashi Iwai 
2612337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2613337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2614337b9d02STakashi Iwai 		while (nid) {
2615a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
26161c55d521STakashi Iwai 			if (type == AC_WID_PIN)
26171c55d521STakashi Iwai 				break;
2618337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2619337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2620337b9d02STakashi Iwai 			if (n <= 0)
2621337b9d02STakashi Iwai 				break;
2622337b9d02STakashi Iwai 			if (n > 1) {
2623337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2624337b9d02STakashi Iwai 				break;
2625337b9d02STakashi Iwai 			}
2626337b9d02STakashi Iwai 			nid = conn[0];
2627337b9d02STakashi Iwai 		}
2628337b9d02STakashi Iwai 	}
26291c55d521STakashi Iwai 	return 0;
2630337b9d02STakashi Iwai }
2631337b9d02STakashi Iwai 
2632c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2633c577b8a1SJoseph Chan {
2634c577b8a1SJoseph Chan 	struct via_spec *spec;
2635c577b8a1SJoseph Chan 	int err;
2636c577b8a1SJoseph Chan 
2637c577b8a1SJoseph Chan 	/* create a codec specific record */
26385b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2639c577b8a1SJoseph Chan 	if (spec == NULL)
2640c577b8a1SJoseph Chan 		return -ENOMEM;
2641c577b8a1SJoseph Chan 
2642c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2643c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2644c577b8a1SJoseph Chan 	if (err < 0) {
2645c577b8a1SJoseph Chan 		via_free(codec);
2646c577b8a1SJoseph Chan 		return err;
2647c577b8a1SJoseph Chan 	} else if (!err) {
2648c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2649c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2650c577b8a1SJoseph Chan 	}
2651c577b8a1SJoseph Chan 
2652c577b8a1SJoseph Chan 
2653c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2654c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2655bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2656bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2657bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2658c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2659c577b8a1SJoseph Chan 
2660c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2661c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2662c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2663c577b8a1SJoseph Chan 
2664c577b8a1SJoseph Chan 
2665c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2666c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2667c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
26680f67a611STakashi Iwai 		get_mux_nids(codec);
2669c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2670c577b8a1SJoseph Chan 		spec->num_mixers++;
2671c577b8a1SJoseph Chan 	}
2672c577b8a1SJoseph Chan 
2673c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2674c577b8a1SJoseph Chan 
2675c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2676cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2677cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2678cb53c626STakashi Iwai #endif
26791f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2680c577b8a1SJoseph Chan 	return 0;
2681c577b8a1SJoseph Chan }
2682c577b8a1SJoseph Chan 
2683c577b8a1SJoseph Chan /* capture mixer elements */
2684c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2685c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2686c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2687c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2688c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2689c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2690c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2691c577b8a1SJoseph Chan 	{
2692c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2693c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2694c577b8a1SJoseph Chan 		 * So call somewhat different..
2695c577b8a1SJoseph Chan 		 */
2696c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2697c577b8a1SJoseph Chan 		.name = "Input Source",
2698c577b8a1SJoseph Chan 		.count = 1,
2699c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2700c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2701c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2702c577b8a1SJoseph Chan 	},
2703c577b8a1SJoseph Chan 	{ } /* end */
2704c577b8a1SJoseph Chan };
2705c577b8a1SJoseph Chan 
270669e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2707a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2708a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
270969e52a80SHarald Welte 	{ }
271069e52a80SHarald Welte };
271169e52a80SHarald Welte 
2712c577b8a1SJoseph Chan /*
2713c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2714c577b8a1SJoseph Chan  */
2715c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2716c577b8a1SJoseph Chan 	/*
2717c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2718c577b8a1SJoseph Chan 	 */
2719c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2720c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2721c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2722c577b8a1SJoseph Chan 
2723c577b8a1SJoseph Chan 
2724f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2725c577b8a1SJoseph Chan 	 * mixer widget
2726c577b8a1SJoseph Chan 	 */
2727c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2728f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2729f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2730f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2731f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2732f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2733c577b8a1SJoseph Chan 
2734c577b8a1SJoseph Chan 	/*
2735c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2736c577b8a1SJoseph Chan 	 */
2737c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2738c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2739c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2740c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2741c577b8a1SJoseph Chan 
2742c577b8a1SJoseph Chan 	/*
2743c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2744c577b8a1SJoseph Chan 	 */
2745c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2746c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2747c577b8a1SJoseph Chan 
2748bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2749bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2750c577b8a1SJoseph Chan 	/* PW9 Output enable */
2751c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2752c577b8a1SJoseph Chan 	{ }
2753c577b8a1SJoseph Chan };
2754c577b8a1SJoseph Chan 
2755c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2756c577b8a1SJoseph Chan 	.substreams = 1,
2757c577b8a1SJoseph Chan 	.channels_min = 2,
2758c577b8a1SJoseph Chan 	.channels_max = 10,
2759c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2760c577b8a1SJoseph Chan 	.ops = {
2761c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2762c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2763c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2764c577b8a1SJoseph Chan 	},
2765c577b8a1SJoseph Chan };
2766c577b8a1SJoseph Chan 
2767c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2768c577b8a1SJoseph Chan 	.substreams = 1,
2769c577b8a1SJoseph Chan 	.channels_min = 2,
2770c577b8a1SJoseph Chan 	.channels_max = 6,
2771c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2772c577b8a1SJoseph Chan 	.ops = {
2773c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2774c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2775c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2776c577b8a1SJoseph Chan 	},
2777c577b8a1SJoseph Chan };
2778c577b8a1SJoseph Chan 
2779c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2780c577b8a1SJoseph Chan 	.substreams = 2,
2781c577b8a1SJoseph Chan 	.channels_min = 2,
2782c577b8a1SJoseph Chan 	.channels_max = 2,
2783c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2784c577b8a1SJoseph Chan 	.ops = {
2785c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2786c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2787c577b8a1SJoseph Chan 	},
2788c577b8a1SJoseph Chan };
2789c577b8a1SJoseph Chan 
2790c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2791c577b8a1SJoseph Chan 	.substreams = 1,
2792c577b8a1SJoseph Chan 	.channels_min = 2,
2793c577b8a1SJoseph Chan 	.channels_max = 2,
2794c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2795c577b8a1SJoseph Chan 	.ops = {
2796c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2797c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2798c577b8a1SJoseph Chan 	},
2799c577b8a1SJoseph Chan };
2800c577b8a1SJoseph Chan 
2801c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2802c577b8a1SJoseph Chan 	.substreams = 1,
2803c577b8a1SJoseph Chan 	.channels_min = 2,
2804c577b8a1SJoseph Chan 	.channels_max = 2,
2805c577b8a1SJoseph Chan };
2806c577b8a1SJoseph Chan 
2807c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2808c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2809c577b8a1SJoseph Chan {
2810c577b8a1SJoseph Chan 	int i;
2811c577b8a1SJoseph Chan 	hda_nid_t nid;
2812c577b8a1SJoseph Chan 
2813c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2814c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2815c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2816c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2817c577b8a1SJoseph Chan 
2818c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2819c577b8a1SJoseph Chan 
2820c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2821c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2822c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2823c577b8a1SJoseph Chan 			if (nid) {
2824c577b8a1SJoseph Chan 				/* config dac list */
2825c577b8a1SJoseph Chan 				switch (i) {
2826c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2827c577b8a1SJoseph Chan 					/* AOW0 */
2828c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2829c577b8a1SJoseph Chan 					break;
2830c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2831c577b8a1SJoseph Chan 					/* AOW2 */
2832c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2833c577b8a1SJoseph Chan 					break;
2834c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2835c577b8a1SJoseph Chan 					/* AOW3 */
2836fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2837c577b8a1SJoseph Chan 					break;
2838c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2839c577b8a1SJoseph Chan 					/* AOW1 */
2840fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2841c577b8a1SJoseph Chan 					break;
2842c577b8a1SJoseph Chan 				default:
2843c577b8a1SJoseph Chan 					break;
2844c577b8a1SJoseph Chan 				}
2845c577b8a1SJoseph Chan 			}
2846c577b8a1SJoseph Chan 		}
2847c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2848c577b8a1SJoseph Chan 
2849c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2850c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2851c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2852c577b8a1SJoseph Chan 			if (nid) {
2853c577b8a1SJoseph Chan 				/* config dac list */
2854c577b8a1SJoseph Chan 				switch (i) {
2855c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2856c577b8a1SJoseph Chan 					/* AOW0 */
2857c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2858c577b8a1SJoseph Chan 					break;
2859c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2860c577b8a1SJoseph Chan 					/* AOW2 */
2861c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2862c577b8a1SJoseph Chan 					break;
2863c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2864c577b8a1SJoseph Chan 					/* AOW1 */
2865c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2866c577b8a1SJoseph Chan 					break;
2867c577b8a1SJoseph Chan 				default:
2868c577b8a1SJoseph Chan 					break;
2869c577b8a1SJoseph Chan 				}
2870c577b8a1SJoseph Chan 			}
2871c577b8a1SJoseph Chan 		}
2872c577b8a1SJoseph Chan 	}
2873c577b8a1SJoseph Chan 
2874c577b8a1SJoseph Chan 	return 0;
2875c577b8a1SJoseph Chan }
2876c577b8a1SJoseph Chan 
2877c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2878c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2879c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2880c577b8a1SJoseph Chan {
2881c577b8a1SJoseph Chan 	char name[32];
2882c577b8a1SJoseph Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
28834483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2884c577b8a1SJoseph Chan 	int i, err;
2885c577b8a1SJoseph Chan 
2886c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2887c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2888c577b8a1SJoseph Chan 
2889c577b8a1SJoseph Chan 		if (!nid)
2890c577b8a1SJoseph Chan 			continue;
2891c577b8a1SJoseph Chan 
28924483a2f5SLydia Wang 		nid_vol = nid_vols[i];
28934483a2f5SLydia Wang 
2894c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2895c577b8a1SJoseph Chan 			/* Center/LFE */
2896c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2897c577b8a1SJoseph Chan 					      "Center Playback Volume",
28984483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2899f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2900c577b8a1SJoseph Chan 			if (err < 0)
2901c577b8a1SJoseph Chan 				return err;
2902c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2903c577b8a1SJoseph Chan 					      "LFE Playback Volume",
29044483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2905f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2906c577b8a1SJoseph Chan 			if (err < 0)
2907c577b8a1SJoseph Chan 				return err;
2908c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2909c577b8a1SJoseph Chan 					      "Center Playback Switch",
29104483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2911f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2912c577b8a1SJoseph Chan 			if (err < 0)
2913c577b8a1SJoseph Chan 				return err;
2914c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2915c577b8a1SJoseph Chan 					      "LFE Playback Switch",
29164483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2917f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2918c577b8a1SJoseph Chan 			if (err < 0)
2919c577b8a1SJoseph Chan 				return err;
2920c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
29214483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2922c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2923c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
29244483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2925f7278fd0SJosepch Chan 								  HDA_INPUT));
2926c577b8a1SJoseph Chan 			if (err < 0)
2927c577b8a1SJoseph Chan 				return err;
2928c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2929c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
29304483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2931f7278fd0SJosepch Chan 								  HDA_INPUT));
2932c577b8a1SJoseph Chan 			if (err < 0)
2933c577b8a1SJoseph Chan 				return err;
2934c577b8a1SJoseph Chan 
2935c577b8a1SJoseph Chan 			/* add control to PW3 */
2936c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2937c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2938f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2939f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2940c577b8a1SJoseph Chan 			if (err < 0)
2941c577b8a1SJoseph Chan 				return err;
2942c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2943c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2944f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2945f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2946c577b8a1SJoseph Chan 			if (err < 0)
2947c577b8a1SJoseph Chan 				return err;
2948c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2949c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2950c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
29514483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2952f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2953c577b8a1SJoseph Chan 			if (err < 0)
2954c577b8a1SJoseph Chan 				return err;
2955c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2956c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
29574483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2958f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2959c577b8a1SJoseph Chan 			if (err < 0)
2960c577b8a1SJoseph Chan 				return err;
2961c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2962c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2963c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
29644483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2965f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2966c577b8a1SJoseph Chan 			if (err < 0)
2967c577b8a1SJoseph Chan 				return err;
2968c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2969c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
29704483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2971f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2972c577b8a1SJoseph Chan 			if (err < 0)
2973c577b8a1SJoseph Chan 				return err;
2974c577b8a1SJoseph Chan 		}
2975c577b8a1SJoseph Chan 	}
2976c577b8a1SJoseph Chan 
2977c577b8a1SJoseph Chan 	return 0;
2978c577b8a1SJoseph Chan }
2979c577b8a1SJoseph Chan 
2980c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2981c577b8a1SJoseph Chan {
2982c577b8a1SJoseph Chan 	int err;
2983c577b8a1SJoseph Chan 
2984c577b8a1SJoseph Chan 	if (!pin)
2985c577b8a1SJoseph Chan 		return 0;
2986c577b8a1SJoseph Chan 
2987c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2988c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2989c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2990c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2991cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2992c577b8a1SJoseph Chan 
2993c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2994c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2995c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2996c577b8a1SJoseph Chan 	if (err < 0)
2997c577b8a1SJoseph Chan 		return err;
2998c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2999c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
3000c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3001c577b8a1SJoseph Chan 	if (err < 0)
3002c577b8a1SJoseph Chan 		return err;
3003c577b8a1SJoseph Chan 
3004c577b8a1SJoseph Chan 	return 0;
3005c577b8a1SJoseph Chan }
3006c577b8a1SJoseph Chan 
3007c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
3008c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec,
3009c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
3010c577b8a1SJoseph Chan {
3011c577b8a1SJoseph Chan 	static char *labels[] = {
3012c577b8a1SJoseph Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3013c577b8a1SJoseph Chan 	};
30140aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3015c577b8a1SJoseph Chan 	int i, err, idx = 0;
3016c577b8a1SJoseph Chan 
3017c577b8a1SJoseph Chan 	/* for internal loopback recording select */
3018c577b8a1SJoseph Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
3019c577b8a1SJoseph Chan 	imux->items[imux->num_items].index = idx;
3020c577b8a1SJoseph Chan 	imux->num_items++;
3021c577b8a1SJoseph Chan 
3022c577b8a1SJoseph Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3023c577b8a1SJoseph Chan 		if (!cfg->input_pins[i])
3024c577b8a1SJoseph Chan 			continue;
3025c577b8a1SJoseph Chan 
3026c577b8a1SJoseph Chan 		switch (cfg->input_pins[i]) {
3027c577b8a1SJoseph Chan 		case 0x1d: /* Mic */
3028c577b8a1SJoseph Chan 			idx = 2;
3029c577b8a1SJoseph Chan 			break;
3030c577b8a1SJoseph Chan 
3031c577b8a1SJoseph Chan 		case 0x1e: /* Line In */
3032c577b8a1SJoseph Chan 			idx = 3;
3033c577b8a1SJoseph Chan 			break;
3034c577b8a1SJoseph Chan 
3035c577b8a1SJoseph Chan 		case 0x21: /* Front Mic */
3036c577b8a1SJoseph Chan 			idx = 4;
3037c577b8a1SJoseph Chan 			break;
3038c577b8a1SJoseph Chan 
3039c577b8a1SJoseph Chan 		case 0x23: /* CD */
3040c577b8a1SJoseph Chan 			idx = 1;
3041c577b8a1SJoseph Chan 			break;
3042c577b8a1SJoseph Chan 		}
30439510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x18);
3044c577b8a1SJoseph Chan 		if (err < 0)
3045c577b8a1SJoseph Chan 			return err;
3046c577b8a1SJoseph Chan 		imux->items[imux->num_items].label = labels[i];
3047c577b8a1SJoseph Chan 		imux->items[imux->num_items].index = idx;
3048c577b8a1SJoseph Chan 		imux->num_items++;
3049c577b8a1SJoseph Chan 	}
3050c577b8a1SJoseph Chan 	return 0;
3051c577b8a1SJoseph Chan }
3052c577b8a1SJoseph Chan 
3053c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
3054c577b8a1SJoseph Chan {
3055c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
3056c577b8a1SJoseph Chan 	int err;
3057c577b8a1SJoseph Chan 
3058c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3059c577b8a1SJoseph Chan 	if (err < 0)
3060c577b8a1SJoseph Chan 		return err;
3061c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
3062c577b8a1SJoseph Chan 	if (err < 0)
3063c577b8a1SJoseph Chan 		return err;
3064c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3065c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
3066c577b8a1SJoseph Chan 
3067c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
3068c577b8a1SJoseph Chan 	if (err < 0)
3069c577b8a1SJoseph Chan 		return err;
3070c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3071c577b8a1SJoseph Chan 	if (err < 0)
3072c577b8a1SJoseph Chan 		return err;
3073c577b8a1SJoseph Chan 	err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg);
3074c577b8a1SJoseph Chan 	if (err < 0)
3075c577b8a1SJoseph Chan 		return err;
3076c577b8a1SJoseph Chan 
3077c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3078c577b8a1SJoseph Chan 
30790852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3080c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
308155d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
3082c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
3083c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
3084c577b8a1SJoseph Chan 
3085603c4019STakashi Iwai 	if (spec->kctls.list)
3086603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3087c577b8a1SJoseph Chan 
30880aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
3089c577b8a1SJoseph Chan 
3090f8fdd495SHarald Welte 	if (spec->hp_mux)
30915b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
3092f8fdd495SHarald Welte 
30935b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3094c577b8a1SJoseph Chan 	return 1;
3095c577b8a1SJoseph Chan }
3096c577b8a1SJoseph Chan 
3097cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3098cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
3099cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
3100cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
3101cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
3102cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
3103cb53c626STakashi Iwai 	{ } /* end */
3104cb53c626STakashi Iwai };
3105cb53c626STakashi Iwai #endif
3106cb53c626STakashi Iwai 
3107c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
3108c577b8a1SJoseph Chan {
3109c577b8a1SJoseph Chan 	struct via_spec *spec;
3110c577b8a1SJoseph Chan 	int err;
3111c577b8a1SJoseph Chan 
3112c577b8a1SJoseph Chan 	/* create a codec specific record */
31135b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3114c577b8a1SJoseph Chan 	if (spec == NULL)
3115c577b8a1SJoseph Chan 		return -ENOMEM;
3116c577b8a1SJoseph Chan 
3117c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3118c577b8a1SJoseph Chan 	if (err < 0) {
3119c577b8a1SJoseph Chan 		via_free(codec);
3120c577b8a1SJoseph Chan 		return err;
3121c577b8a1SJoseph Chan 	} else if (!err) {
3122c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3123c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3124c577b8a1SJoseph Chan 	}
3125c577b8a1SJoseph Chan 
312669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
312769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3128c577b8a1SJoseph Chan 
3129c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3130c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
3131c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3132c577b8a1SJoseph Chan 
3133c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3134c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3135c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3136c577b8a1SJoseph Chan 
3137c577b8a1SJoseph Chan 
3138c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3139c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3140c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3141337b9d02STakashi Iwai 		get_mux_nids(codec);
3142c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3143c577b8a1SJoseph Chan 		spec->num_mixers++;
3144c577b8a1SJoseph Chan 	}
3145c577b8a1SJoseph Chan 
3146c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3147c577b8a1SJoseph Chan 
3148c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
314969e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3150cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3151cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3152cb53c626STakashi Iwai #endif
3153c577b8a1SJoseph Chan 
3154c577b8a1SJoseph Chan 	return 0;
3155c577b8a1SJoseph Chan }
3156c577b8a1SJoseph Chan /*
3157c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
3158c577b8a1SJoseph Chan  */
3159c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
3160c577b8a1SJoseph Chan 	/*
3161c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
3162c577b8a1SJoseph Chan 	 */
3163c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3164c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3165c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3166c577b8a1SJoseph Chan 
3167c577b8a1SJoseph Chan 
3168c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3169c577b8a1SJoseph Chan 	 * mixer widget
3170c577b8a1SJoseph Chan 	 */
3171c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3172c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3173c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3174c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3175c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3176c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3177c577b8a1SJoseph Chan 
3178c577b8a1SJoseph Chan 	/*
3179c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
3180c577b8a1SJoseph Chan 	 */
3181c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
3182c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3183c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3184c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3185c577b8a1SJoseph Chan 
3186c577b8a1SJoseph Chan 	/*
3187c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
3188c577b8a1SJoseph Chan 	 */
3189c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3190c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3191c577b8a1SJoseph Chan 
3192c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
3193c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
3194c577b8a1SJoseph Chan 	/* PW9 Output enable */
3195c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3196c577b8a1SJoseph Chan 	{ }
3197c577b8a1SJoseph Chan };
3198c577b8a1SJoseph Chan 
3199c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
3200c577b8a1SJoseph Chan {
3201c577b8a1SJoseph Chan 	struct via_spec *spec;
3202c577b8a1SJoseph Chan 	int err;
3203c577b8a1SJoseph Chan 
3204c577b8a1SJoseph Chan 	/* create a codec specific record */
32055b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3206c577b8a1SJoseph Chan 	if (spec == NULL)
3207c577b8a1SJoseph Chan 		return -ENOMEM;
3208c577b8a1SJoseph Chan 
3209c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3210c577b8a1SJoseph Chan 	if (err < 0) {
3211c577b8a1SJoseph Chan 		via_free(codec);
3212c577b8a1SJoseph Chan 		return err;
3213c577b8a1SJoseph Chan 	} else if (!err) {
3214c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3215c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3216c577b8a1SJoseph Chan 	}
3217c577b8a1SJoseph Chan 
321869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
321969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3220c577b8a1SJoseph Chan 
3221c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3222c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
3223c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3224c577b8a1SJoseph Chan 
3225c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3226c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3227c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3228c577b8a1SJoseph Chan 
3229c577b8a1SJoseph Chan 
3230c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3231c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3232c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3233337b9d02STakashi Iwai 		get_mux_nids(codec);
3234c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3235c577b8a1SJoseph Chan 		spec->num_mixers++;
3236c577b8a1SJoseph Chan 	}
3237c577b8a1SJoseph Chan 
3238c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3239c577b8a1SJoseph Chan 
3240c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
324169e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3242cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3243cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3244cb53c626STakashi Iwai #endif
3245f7278fd0SJosepch Chan 	return 0;
3246f7278fd0SJosepch Chan }
3247f7278fd0SJosepch Chan 
3248f7278fd0SJosepch Chan /* capture mixer elements */
3249f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
3250f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3251f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3252f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3253f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3254f7278fd0SJosepch Chan 	{
3255f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3256f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
3257f7278fd0SJosepch Chan 		 * So call somewhat different..
3258f7278fd0SJosepch Chan 		 */
3259f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
3260f7278fd0SJosepch Chan 		.name = "Input Source",
3261f7278fd0SJosepch Chan 		.count = 1,
3262f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
3263f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
3264f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
3265f7278fd0SJosepch Chan 	},
3266f7278fd0SJosepch Chan 	{ } /* end */
3267f7278fd0SJosepch Chan };
3268f7278fd0SJosepch Chan /*
3269f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
3270f7278fd0SJosepch Chan  */
3271f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
3272f7278fd0SJosepch Chan 	/*
3273f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3274f7278fd0SJosepch Chan 	 */
3275f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3276f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3277f7278fd0SJosepch Chan 
3278f7278fd0SJosepch Chan 
3279f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3280f7278fd0SJosepch Chan 	 * mixer widget
3281f7278fd0SJosepch Chan 	 */
3282f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3283f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3284f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3285f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3286f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3287f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3288f7278fd0SJosepch Chan 
3289f7278fd0SJosepch Chan 	/*
3290f7278fd0SJosepch Chan 	 * Set up output mixers
3291f7278fd0SJosepch Chan 	 */
3292f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3293f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3294f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3295f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3296f7278fd0SJosepch Chan 
3297f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
3298bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
3299f7278fd0SJosepch Chan 	/* PW9 Output enable */
3300f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3301f7278fd0SJosepch Chan 	/* PW10 Input enable */
3302f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3303f7278fd0SJosepch Chan 	{ }
3304f7278fd0SJosepch Chan };
3305f7278fd0SJosepch Chan 
3306f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
3307f7278fd0SJosepch Chan 	/*
3308f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3309f7278fd0SJosepch Chan 	 */
3310f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3311f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3312f7278fd0SJosepch Chan 
3313f7278fd0SJosepch Chan 
3314f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3315f7278fd0SJosepch Chan 	 * mixer widget
3316f7278fd0SJosepch Chan 	 */
3317f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3318f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3319f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3320f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3321f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3322f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3323f7278fd0SJosepch Chan 
3324f7278fd0SJosepch Chan 	/*
3325f7278fd0SJosepch Chan 	 * Set up output mixers
3326f7278fd0SJosepch Chan 	 */
3327f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3328f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3329f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3330f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3331f7278fd0SJosepch Chan 
3332f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
3333f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3334f7278fd0SJosepch Chan 	/* PW9 Output enable */
3335f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3336f7278fd0SJosepch Chan 	/* PW10 Input enable */
3337f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3338f7278fd0SJosepch Chan 	{ }
3339f7278fd0SJosepch Chan };
3340f7278fd0SJosepch Chan 
334169e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
3342a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3343a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3344a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3345a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3346a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3347a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3348a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3349a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3350a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
335169e52a80SHarald Welte 	{ }
335269e52a80SHarald Welte };
335369e52a80SHarald Welte 
335417314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
335517314379SLydia Wang 			      struct hda_codec *codec,
335617314379SLydia Wang 			      struct snd_pcm_substream *substream)
335717314379SLydia Wang {
335817314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
335917314379SLydia Wang 		&& substream->ref_count == 0;
336017314379SLydia Wang 
336117314379SLydia Wang 	analog_low_current_mode(codec, idle);
336217314379SLydia Wang 	return 0;
336317314379SLydia Wang }
336417314379SLydia Wang 
3365f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
33660aa62aefSHarald Welte 	.substreams = 2,
3367f7278fd0SJosepch Chan 	.channels_min = 2,
3368f7278fd0SJosepch Chan 	.channels_max = 8,
3369f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3370f7278fd0SJosepch Chan 	.ops = {
3371f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33720aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
337317314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
337417314379SLydia Wang 		.close = via_pcm_open_close
3375f7278fd0SJosepch Chan 	},
3376f7278fd0SJosepch Chan };
3377f7278fd0SJosepch Chan 
3378f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
33790aa62aefSHarald Welte 	.substreams = 2,
3380f7278fd0SJosepch Chan 	.channels_min = 2,
3381f7278fd0SJosepch Chan 	.channels_max = 4,
3382f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3383f7278fd0SJosepch Chan 	.ops = {
3384f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33850aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
33860aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
3387f7278fd0SJosepch Chan 	},
3388f7278fd0SJosepch Chan };
3389f7278fd0SJosepch Chan 
3390f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3391f7278fd0SJosepch Chan 	.substreams = 2,
3392f7278fd0SJosepch Chan 	.channels_min = 2,
3393f7278fd0SJosepch Chan 	.channels_max = 2,
3394f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
3395f7278fd0SJosepch Chan 	.ops = {
339617314379SLydia Wang 		.open = via_pcm_open_close,
3397f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
339817314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
339917314379SLydia Wang 		.close = via_pcm_open_close
3400f7278fd0SJosepch Chan 	},
3401f7278fd0SJosepch Chan };
3402f7278fd0SJosepch Chan 
3403f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3404f7278fd0SJosepch Chan 	.substreams = 1,
3405f7278fd0SJosepch Chan 	.channels_min = 2,
3406f7278fd0SJosepch Chan 	.channels_max = 2,
3407f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
3408f7278fd0SJosepch Chan 	.ops = {
3409f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
3410f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
34119da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
34129da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3413f7278fd0SJosepch Chan 	},
3414f7278fd0SJosepch Chan };
3415f7278fd0SJosepch Chan 
3416f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3417f7278fd0SJosepch Chan 	.substreams = 1,
3418f7278fd0SJosepch Chan 	.channels_min = 2,
3419f7278fd0SJosepch Chan 	.channels_max = 2,
3420f7278fd0SJosepch Chan };
3421f7278fd0SJosepch Chan 
3422f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
3423f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3424f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
3425f7278fd0SJosepch Chan {
3426f7278fd0SJosepch Chan 	int i;
3427f7278fd0SJosepch Chan 	hda_nid_t nid;
3428f7278fd0SJosepch Chan 
3429f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
3430f7278fd0SJosepch Chan 
3431f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3432f7278fd0SJosepch Chan 
3433f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3434f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3435f7278fd0SJosepch Chan 		if (nid) {
3436f7278fd0SJosepch Chan 			/* config dac list */
3437f7278fd0SJosepch Chan 			switch (i) {
3438f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3439f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
3440f7278fd0SJosepch Chan 				break;
3441f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3442f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
3443f7278fd0SJosepch Chan 				break;
3444f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3445fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3446f7278fd0SJosepch Chan 				break;
3447f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3448fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3449f7278fd0SJosepch Chan 				break;
3450f7278fd0SJosepch Chan 			}
3451f7278fd0SJosepch Chan 		}
3452f7278fd0SJosepch Chan 	}
3453f7278fd0SJosepch Chan 
3454f7278fd0SJosepch Chan 	return 0;
3455f7278fd0SJosepch Chan }
3456f7278fd0SJosepch Chan 
3457f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3458f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3459f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3460f7278fd0SJosepch Chan {
3461f7278fd0SJosepch Chan 	char name[32];
3462f7278fd0SJosepch Chan 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3463fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3464f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3465f7278fd0SJosepch Chan 	int i, err;
3466f7278fd0SJosepch Chan 
3467f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3468f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3469f7278fd0SJosepch Chan 
3470f7278fd0SJosepch Chan 		if (!nid)
3471f7278fd0SJosepch Chan 			continue;
3472f7278fd0SJosepch Chan 
3473f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3474f7278fd0SJosepch Chan 
3475f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3476f7278fd0SJosepch Chan 			/* Center/LFE */
3477f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3478f7278fd0SJosepch Chan 					      "Center Playback Volume",
3479f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3480f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3481f7278fd0SJosepch Chan 			if (err < 0)
3482f7278fd0SJosepch Chan 				return err;
3483f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3484f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3485f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3486f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3487f7278fd0SJosepch Chan 			if (err < 0)
3488f7278fd0SJosepch Chan 				return err;
3489f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3490f7278fd0SJosepch Chan 					      "Center Playback Switch",
3491f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3492f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3493f7278fd0SJosepch Chan 			if (err < 0)
3494f7278fd0SJosepch Chan 				return err;
3495f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3496f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3497f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3498f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3499f7278fd0SJosepch Chan 			if (err < 0)
3500f7278fd0SJosepch Chan 				return err;
3501f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3502f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3503f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3504f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3505f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3506f7278fd0SJosepch Chan 								  HDA_INPUT));
3507f7278fd0SJosepch Chan 			if (err < 0)
3508f7278fd0SJosepch Chan 				return err;
3509f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3510f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3511f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3512f7278fd0SJosepch Chan 								  HDA_INPUT));
3513f7278fd0SJosepch Chan 			if (err < 0)
3514f7278fd0SJosepch Chan 				return err;
3515f7278fd0SJosepch Chan 
3516f7278fd0SJosepch Chan 			/* add control to PW3 */
3517f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3518f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3519f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3520f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3521f7278fd0SJosepch Chan 			if (err < 0)
3522f7278fd0SJosepch Chan 				return err;
3523f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3524f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3525f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3526f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3527f7278fd0SJosepch Chan 			if (err < 0)
3528f7278fd0SJosepch Chan 				return err;
3529f7278fd0SJosepch Chan 		} else {
3530f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3531f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3532f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3533f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3534f7278fd0SJosepch Chan 			if (err < 0)
3535f7278fd0SJosepch Chan 				return err;
3536f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3537f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3538f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3539f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3540f7278fd0SJosepch Chan 			if (err < 0)
3541f7278fd0SJosepch Chan 				return err;
3542f7278fd0SJosepch Chan 		}
3543f7278fd0SJosepch Chan 	}
3544f7278fd0SJosepch Chan 
3545f7278fd0SJosepch Chan 	return 0;
3546f7278fd0SJosepch Chan }
3547f7278fd0SJosepch Chan 
3548f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3549f7278fd0SJosepch Chan {
3550f7278fd0SJosepch Chan 	int err;
3551f7278fd0SJosepch Chan 
3552f7278fd0SJosepch Chan 	if (!pin)
3553f7278fd0SJosepch Chan 		return 0;
3554f7278fd0SJosepch Chan 
3555f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3556cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3557f7278fd0SJosepch Chan 
3558f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3559f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3560f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3561f7278fd0SJosepch Chan 	if (err < 0)
3562f7278fd0SJosepch Chan 		return err;
3563f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3564f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3565f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3566f7278fd0SJosepch Chan 	if (err < 0)
3567f7278fd0SJosepch Chan 		return err;
3568f7278fd0SJosepch Chan 
35690aa62aefSHarald Welte 	create_hp_imux(spec);
35700aa62aefSHarald Welte 
3571f7278fd0SJosepch Chan 	return 0;
3572f7278fd0SJosepch Chan }
3573f7278fd0SJosepch Chan 
3574f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
3575f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec,
3576f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3577f7278fd0SJosepch Chan {
3578f7278fd0SJosepch Chan 	static char *labels[] = {
3579f7278fd0SJosepch Chan 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
3580f7278fd0SJosepch Chan 	};
35810aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
3582f7278fd0SJosepch Chan 	int i, err, idx = 0;
3583f7278fd0SJosepch Chan 
3584f7278fd0SJosepch Chan 	/* for internal loopback recording select */
3585f7278fd0SJosepch Chan 	imux->items[imux->num_items].label = "Stereo Mixer";
3586f7278fd0SJosepch Chan 	imux->items[imux->num_items].index = idx;
3587f7278fd0SJosepch Chan 	imux->num_items++;
3588f7278fd0SJosepch Chan 
3589f7278fd0SJosepch Chan 	for (i = 0; i < AUTO_PIN_LAST; i++) {
3590f7278fd0SJosepch Chan 		if (!cfg->input_pins[i])
3591f7278fd0SJosepch Chan 			continue;
3592f7278fd0SJosepch Chan 
3593f7278fd0SJosepch Chan 		switch (cfg->input_pins[i]) {
3594f7278fd0SJosepch Chan 		case 0x1a: /* Mic */
3595f7278fd0SJosepch Chan 			idx = 2;
3596f7278fd0SJosepch Chan 			break;
3597f7278fd0SJosepch Chan 
3598f7278fd0SJosepch Chan 		case 0x1b: /* Line In */
3599f7278fd0SJosepch Chan 			idx = 3;
3600f7278fd0SJosepch Chan 			break;
3601f7278fd0SJosepch Chan 
3602f7278fd0SJosepch Chan 		case 0x1e: /* Front Mic */
3603f7278fd0SJosepch Chan 			idx = 4;
3604f7278fd0SJosepch Chan 			break;
3605f7278fd0SJosepch Chan 
3606f7278fd0SJosepch Chan 		case 0x1f: /* CD */
3607f7278fd0SJosepch Chan 			idx = 1;
3608f7278fd0SJosepch Chan 			break;
3609f7278fd0SJosepch Chan 		}
36109510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
3611f7278fd0SJosepch Chan 		if (err < 0)
3612f7278fd0SJosepch Chan 			return err;
3613f7278fd0SJosepch Chan 		imux->items[imux->num_items].label = labels[i];
3614f7278fd0SJosepch Chan 		imux->items[imux->num_items].index = idx;
3615f7278fd0SJosepch Chan 		imux->num_items++;
3616f7278fd0SJosepch Chan 	}
3617f7278fd0SJosepch Chan 	return 0;
3618f7278fd0SJosepch Chan }
3619f7278fd0SJosepch Chan 
3620f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3621f7278fd0SJosepch Chan {
3622f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3623f7278fd0SJosepch Chan 	int err;
3624f7278fd0SJosepch Chan 
3625f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3626f7278fd0SJosepch Chan 	if (err < 0)
3627f7278fd0SJosepch Chan 		return err;
3628f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3629f7278fd0SJosepch Chan 	if (err < 0)
3630f7278fd0SJosepch Chan 		return err;
3631f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3632f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3633f7278fd0SJosepch Chan 
3634f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3635f7278fd0SJosepch Chan 	if (err < 0)
3636f7278fd0SJosepch Chan 		return err;
3637f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3638f7278fd0SJosepch Chan 	if (err < 0)
3639f7278fd0SJosepch Chan 		return err;
3640f7278fd0SJosepch Chan 	err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg);
3641f7278fd0SJosepch Chan 	if (err < 0)
3642f7278fd0SJosepch Chan 		return err;
3643f7278fd0SJosepch Chan 
3644f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3645f7278fd0SJosepch Chan 
36460852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3647f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
364855d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3649f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3650f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3651f7278fd0SJosepch Chan 
3652603c4019STakashi Iwai 	if (spec->kctls.list)
3653603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3654f7278fd0SJosepch Chan 
36550aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
36560aa62aefSHarald Welte 
3657f8fdd495SHarald Welte 	if (spec->hp_mux)
36585b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
3659f7278fd0SJosepch Chan 
36605b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3661f7278fd0SJosepch Chan 	return 1;
3662f7278fd0SJosepch Chan }
3663f7278fd0SJosepch Chan 
3664f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3665f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3666f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3667f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3668f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3669f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3670f7278fd0SJosepch Chan 	{ } /* end */
3671f7278fd0SJosepch Chan };
3672f7278fd0SJosepch Chan #endif
3673518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3674f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3675f7278fd0SJosepch Chan {
3676f7278fd0SJosepch Chan 	struct via_spec *spec;
3677f7278fd0SJosepch Chan 	int err;
3678f7278fd0SJosepch Chan 
3679518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3680518bf3baSLydia Wang 		return patch_vt1708S(codec);
3681f7278fd0SJosepch Chan 	/* create a codec specific record */
36825b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3683f7278fd0SJosepch Chan 	if (spec == NULL)
3684f7278fd0SJosepch Chan 		return -ENOMEM;
3685f7278fd0SJosepch Chan 
3686f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3687f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3688f7278fd0SJosepch Chan 	if (err < 0) {
3689f7278fd0SJosepch Chan 		via_free(codec);
3690f7278fd0SJosepch Chan 		return err;
3691f7278fd0SJosepch Chan 	} else if (!err) {
3692f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3693f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3694f7278fd0SJosepch Chan 	}
3695f7278fd0SJosepch Chan 
369669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
369769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3698f7278fd0SJosepch Chan 
3699f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3700f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3701f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3702f7278fd0SJosepch Chan 
3703f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3704f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3705f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3706f7278fd0SJosepch Chan 
3707f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3708f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3709f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3710337b9d02STakashi Iwai 		get_mux_nids(codec);
3711f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3712f7278fd0SJosepch Chan 		spec->num_mixers++;
3713f7278fd0SJosepch Chan 	}
3714f7278fd0SJosepch Chan 
3715f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3716f7278fd0SJosepch Chan 
3717f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
371869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3719f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3720f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3721f7278fd0SJosepch Chan #endif
3722f7278fd0SJosepch Chan 
3723f7278fd0SJosepch Chan 	return 0;
3724f7278fd0SJosepch Chan }
3725f7278fd0SJosepch Chan 
3726f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3727f7278fd0SJosepch Chan {
3728f7278fd0SJosepch Chan 	struct via_spec *spec;
3729f7278fd0SJosepch Chan 	int err;
3730f7278fd0SJosepch Chan 
3731f7278fd0SJosepch Chan 	/* create a codec specific record */
37325b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3733f7278fd0SJosepch Chan 	if (spec == NULL)
3734f7278fd0SJosepch Chan 		return -ENOMEM;
3735f7278fd0SJosepch Chan 
3736f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3737f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3738f7278fd0SJosepch Chan 	if (err < 0) {
3739f7278fd0SJosepch Chan 		via_free(codec);
3740f7278fd0SJosepch Chan 		return err;
3741f7278fd0SJosepch Chan 	} else if (!err) {
3742f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3743f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3744f7278fd0SJosepch Chan 	}
3745f7278fd0SJosepch Chan 
374669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
374769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3748f7278fd0SJosepch Chan 
3749f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3750f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3751f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3752f7278fd0SJosepch Chan 
3753f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3754f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3755f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3756f7278fd0SJosepch Chan 
3757f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3758f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3759f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3760337b9d02STakashi Iwai 		get_mux_nids(codec);
3761f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3762f7278fd0SJosepch Chan 		spec->num_mixers++;
3763f7278fd0SJosepch Chan 	}
3764f7278fd0SJosepch Chan 
3765f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3766f7278fd0SJosepch Chan 
3767f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
376869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3769f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3770f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3771f7278fd0SJosepch Chan #endif
3772c577b8a1SJoseph Chan 
3773c577b8a1SJoseph Chan 	return 0;
3774c577b8a1SJoseph Chan }
3775c577b8a1SJoseph Chan 
3776d949cac1SHarald Welte /* Patch for VT1708S */
3777d949cac1SHarald Welte 
3778d949cac1SHarald Welte /* capture mixer elements */
3779d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3780d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3781d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3782d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3783d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
37846369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
37856369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
37866369bcfcSLydia Wang 			 HDA_INPUT),
3787d949cac1SHarald Welte 	{
3788d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3789d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3790d949cac1SHarald Welte 		 * So call somewhat different..
3791d949cac1SHarald Welte 		 */
3792d949cac1SHarald Welte 		/* .name = "Capture Source", */
3793d949cac1SHarald Welte 		.name = "Input Source",
3794d949cac1SHarald Welte 		.count = 1,
3795d949cac1SHarald Welte 		.info = via_mux_enum_info,
3796d949cac1SHarald Welte 		.get = via_mux_enum_get,
3797d949cac1SHarald Welte 		.put = via_mux_enum_put,
3798d949cac1SHarald Welte 	},
3799d949cac1SHarald Welte 	{ } /* end */
3800d949cac1SHarald Welte };
3801d949cac1SHarald Welte 
3802d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3803d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3804d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3805d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3806d949cac1SHarald Welte 
3807d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3808d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3809d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3810d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3811d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3812d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3813d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3814d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3815d949cac1SHarald Welte 
3816d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3817d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
38185691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3819d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
38205691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3821d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3822d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3823bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3824bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3825d949cac1SHarald Welte 	{ }
3826d949cac1SHarald Welte };
3827d949cac1SHarald Welte 
382869e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3829a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3830a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3831a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3832a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3833a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3834a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3835a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3836a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3837a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
383869e52a80SHarald Welte 	{ }
383969e52a80SHarald Welte };
384069e52a80SHarald Welte 
3841d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3842d949cac1SHarald Welte 	.substreams = 2,
3843d949cac1SHarald Welte 	.channels_min = 2,
3844d949cac1SHarald Welte 	.channels_max = 8,
3845d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3846d949cac1SHarald Welte 	.ops = {
3847d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3848c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3849c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
385017314379SLydia Wang 		.close = via_pcm_open_close
3851d949cac1SHarald Welte 	},
3852d949cac1SHarald Welte };
3853d949cac1SHarald Welte 
3854d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3855d949cac1SHarald Welte 	.substreams = 2,
3856d949cac1SHarald Welte 	.channels_min = 2,
3857d949cac1SHarald Welte 	.channels_max = 2,
3858d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3859d949cac1SHarald Welte 	.ops = {
386017314379SLydia Wang 		.open = via_pcm_open_close,
3861d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
386217314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
386317314379SLydia Wang 		.close = via_pcm_open_close
3864d949cac1SHarald Welte 	},
3865d949cac1SHarald Welte };
3866d949cac1SHarald Welte 
3867d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
38689da29271STakashi Iwai 	.substreams = 1,
3869d949cac1SHarald Welte 	.channels_min = 2,
3870d949cac1SHarald Welte 	.channels_max = 2,
3871d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3872d949cac1SHarald Welte 	.ops = {
3873d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3874d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
38759da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
38769da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3877d949cac1SHarald Welte 	},
3878d949cac1SHarald Welte };
3879d949cac1SHarald Welte 
3880d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3881d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3882d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3883d949cac1SHarald Welte {
3884d949cac1SHarald Welte 	int i;
3885d949cac1SHarald Welte 	hda_nid_t nid;
3886d949cac1SHarald Welte 
3887d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3888d949cac1SHarald Welte 
3889d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3890d949cac1SHarald Welte 
3891d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3892d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3893d949cac1SHarald Welte 		if (nid) {
3894d949cac1SHarald Welte 			/* config dac list */
3895d949cac1SHarald Welte 			switch (i) {
3896d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3897d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3898d949cac1SHarald Welte 				break;
3899d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3900d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x24;
3901d949cac1SHarald Welte 				break;
3902d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3903d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3904d949cac1SHarald Welte 				break;
3905d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3906d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3907d949cac1SHarald Welte 				break;
3908d949cac1SHarald Welte 			}
3909d949cac1SHarald Welte 		}
3910d949cac1SHarald Welte 	}
3911d949cac1SHarald Welte 
3912d949cac1SHarald Welte 	return 0;
3913d949cac1SHarald Welte }
3914d949cac1SHarald Welte 
3915d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3916d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3917d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3918d949cac1SHarald Welte {
3919d949cac1SHarald Welte 	char name[32];
3920d949cac1SHarald Welte 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
3921d949cac1SHarald Welte 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3922d949cac1SHarald Welte 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3923d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3924d949cac1SHarald Welte 	int i, err;
3925d949cac1SHarald Welte 
3926d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3927d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3928d949cac1SHarald Welte 
3929d949cac1SHarald Welte 		if (!nid)
3930d949cac1SHarald Welte 			continue;
3931d949cac1SHarald Welte 
3932d949cac1SHarald Welte 		nid_vol = nid_vols[i];
3933d949cac1SHarald Welte 		nid_mute = nid_mutes[i];
3934d949cac1SHarald Welte 
3935d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3936d949cac1SHarald Welte 			/* Center/LFE */
3937d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3938d949cac1SHarald Welte 					      "Center Playback Volume",
3939d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3940d949cac1SHarald Welte 								  HDA_OUTPUT));
3941d949cac1SHarald Welte 			if (err < 0)
3942d949cac1SHarald Welte 				return err;
3943d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3944d949cac1SHarald Welte 					      "LFE Playback Volume",
3945d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3946d949cac1SHarald Welte 								  HDA_OUTPUT));
3947d949cac1SHarald Welte 			if (err < 0)
3948d949cac1SHarald Welte 				return err;
3949d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3950d949cac1SHarald Welte 					      "Center Playback Switch",
3951d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3952d949cac1SHarald Welte 								  1, 0,
3953d949cac1SHarald Welte 								  HDA_OUTPUT));
3954d949cac1SHarald Welte 			if (err < 0)
3955d949cac1SHarald Welte 				return err;
3956d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3957d949cac1SHarald Welte 					      "LFE Playback Switch",
3958d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3959d949cac1SHarald Welte 								  2, 0,
3960d949cac1SHarald Welte 								  HDA_OUTPUT));
3961d949cac1SHarald Welte 			if (err < 0)
3962d949cac1SHarald Welte 				return err;
3963d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3964d949cac1SHarald Welte 			/* add control to mixer index 0 */
3965d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3966d949cac1SHarald Welte 					      "Master Front Playback Volume",
3967d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3968d949cac1SHarald Welte 								  HDA_INPUT));
3969d949cac1SHarald Welte 			if (err < 0)
3970d949cac1SHarald Welte 				return err;
3971d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3972d949cac1SHarald Welte 					      "Master Front Playback Switch",
3973d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3974d949cac1SHarald Welte 								  HDA_INPUT));
3975d949cac1SHarald Welte 			if (err < 0)
3976d949cac1SHarald Welte 				return err;
3977d949cac1SHarald Welte 
3978d949cac1SHarald Welte 			/* Front */
3979d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3980d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3981d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3982d949cac1SHarald Welte 								  HDA_OUTPUT));
3983d949cac1SHarald Welte 			if (err < 0)
3984d949cac1SHarald Welte 				return err;
3985d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3986d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3987d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3988d949cac1SHarald Welte 								  3, 0,
3989d949cac1SHarald Welte 								  HDA_OUTPUT));
3990d949cac1SHarald Welte 			if (err < 0)
3991d949cac1SHarald Welte 				return err;
3992d949cac1SHarald Welte 		} else {
3993d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3994d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3995d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3996d949cac1SHarald Welte 								  HDA_OUTPUT));
3997d949cac1SHarald Welte 			if (err < 0)
3998d949cac1SHarald Welte 				return err;
3999d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
4000d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
4001d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
4002d949cac1SHarald Welte 								  3, 0,
4003d949cac1SHarald Welte 								  HDA_OUTPUT));
4004d949cac1SHarald Welte 			if (err < 0)
4005d949cac1SHarald Welte 				return err;
4006d949cac1SHarald Welte 		}
4007d949cac1SHarald Welte 	}
4008d949cac1SHarald Welte 
4009d949cac1SHarald Welte 	return 0;
4010d949cac1SHarald Welte }
4011d949cac1SHarald Welte 
4012d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4013d949cac1SHarald Welte {
4014d949cac1SHarald Welte 	int err;
4015d949cac1SHarald Welte 
4016d949cac1SHarald Welte 	if (!pin)
4017d949cac1SHarald Welte 		return 0;
4018d949cac1SHarald Welte 
4019d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
4020cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
4021d949cac1SHarald Welte 
4022d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4023d949cac1SHarald Welte 			      "Headphone Playback Volume",
4024d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
4025d949cac1SHarald Welte 	if (err < 0)
4026d949cac1SHarald Welte 		return err;
4027d949cac1SHarald Welte 
4028d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4029d949cac1SHarald Welte 			      "Headphone Playback Switch",
4030d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4031d949cac1SHarald Welte 	if (err < 0)
4032d949cac1SHarald Welte 		return err;
4033d949cac1SHarald Welte 
40340aa62aefSHarald Welte 	create_hp_imux(spec);
40350aa62aefSHarald Welte 
4036d949cac1SHarald Welte 	return 0;
4037d949cac1SHarald Welte }
4038d949cac1SHarald Welte 
4039d949cac1SHarald Welte /* create playback/capture controls for input pins */
4040d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec,
4041d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4042d949cac1SHarald Welte {
4043d949cac1SHarald Welte 	static char *labels[] = {
4044d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4045d949cac1SHarald Welte 	};
40460aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
4047d949cac1SHarald Welte 	int i, err, idx = 0;
4048d949cac1SHarald Welte 
4049d949cac1SHarald Welte 	/* for internal loopback recording select */
4050d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
4051d949cac1SHarald Welte 	imux->items[imux->num_items].index = 5;
4052d949cac1SHarald Welte 	imux->num_items++;
4053d949cac1SHarald Welte 
4054d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4055d949cac1SHarald Welte 		if (!cfg->input_pins[i])
4056d949cac1SHarald Welte 			continue;
4057d949cac1SHarald Welte 
4058d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
4059d949cac1SHarald Welte 		case 0x1a: /* Mic */
4060d949cac1SHarald Welte 			idx = 2;
4061d949cac1SHarald Welte 			break;
4062d949cac1SHarald Welte 
4063d949cac1SHarald Welte 		case 0x1b: /* Line In */
4064d949cac1SHarald Welte 			idx = 3;
4065d949cac1SHarald Welte 			break;
4066d949cac1SHarald Welte 
4067d949cac1SHarald Welte 		case 0x1e: /* Front Mic */
4068d949cac1SHarald Welte 			idx = 4;
4069d949cac1SHarald Welte 			break;
4070d949cac1SHarald Welte 
4071d949cac1SHarald Welte 		case 0x1f: /* CD */
4072d949cac1SHarald Welte 			idx = 1;
4073d949cac1SHarald Welte 			break;
4074d949cac1SHarald Welte 		}
40759510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
4076d949cac1SHarald Welte 		if (err < 0)
4077d949cac1SHarald Welte 			return err;
4078d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
4079d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
4080d949cac1SHarald Welte 		imux->num_items++;
4081d949cac1SHarald Welte 	}
4082d949cac1SHarald Welte 	return 0;
4083d949cac1SHarald Welte }
4084d949cac1SHarald Welte 
40859da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
40869da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
40879da29271STakashi Iwai {
40889da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
40899da29271STakashi Iwai 	int i;
40909da29271STakashi Iwai 
40919da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
40929da29271STakashi Iwai 		hda_nid_t nid;
40939da29271STakashi Iwai 		int conn;
40949da29271STakashi Iwai 
40959da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
40969da29271STakashi Iwai 		if (!nid)
40979da29271STakashi Iwai 			continue;
40989da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
40999da29271STakashi Iwai 		if (conn < 1)
41009da29271STakashi Iwai 			continue;
41019da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
41029da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
41039da29271STakashi Iwai 		else {
41049da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
41059da29271STakashi Iwai 			break; /* at most two dig outs */
41069da29271STakashi Iwai 		}
41079da29271STakashi Iwai 	}
41089da29271STakashi Iwai }
41099da29271STakashi Iwai 
4110d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
4111d949cac1SHarald Welte {
4112d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4113d949cac1SHarald Welte 	int err;
4114d949cac1SHarald Welte 
41159da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4116d949cac1SHarald Welte 	if (err < 0)
4117d949cac1SHarald Welte 		return err;
4118d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
4119d949cac1SHarald Welte 	if (err < 0)
4120d949cac1SHarald Welte 		return err;
4121d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4122d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4123d949cac1SHarald Welte 
4124d949cac1SHarald Welte 	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4125d949cac1SHarald Welte 	if (err < 0)
4126d949cac1SHarald Welte 		return err;
4127d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4128d949cac1SHarald Welte 	if (err < 0)
4129d949cac1SHarald Welte 		return err;
4130d949cac1SHarald Welte 	err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4131d949cac1SHarald Welte 	if (err < 0)
4132d949cac1SHarald Welte 		return err;
4133d949cac1SHarald Welte 
4134d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4135d949cac1SHarald Welte 
41369da29271STakashi Iwai 	fill_dig_outs(codec);
413798aa34c0SHarald Welte 
4138603c4019STakashi Iwai 	if (spec->kctls.list)
4139603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4140d949cac1SHarald Welte 
41410aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
41420aa62aefSHarald Welte 
4143f8fdd495SHarald Welte 	if (spec->hp_mux)
41445b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
4145d949cac1SHarald Welte 
41465b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4147d949cac1SHarald Welte 	return 1;
4148d949cac1SHarald Welte }
4149d949cac1SHarald Welte 
4150d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4151d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
4152d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
4153d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
4154d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
4155d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
4156d949cac1SHarald Welte 	{ } /* end */
4157d949cac1SHarald Welte };
4158d949cac1SHarald Welte #endif
4159d949cac1SHarald Welte 
41606369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
41616369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
41626369bcfcSLydia Wang {
41636369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
41646369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
41656369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
41666369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
41676369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
41686369bcfcSLydia Wang }
41696369bcfcSLydia Wang 
4170d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
4171d949cac1SHarald Welte {
4172d949cac1SHarald Welte 	struct via_spec *spec;
4173d949cac1SHarald Welte 	int err;
4174d949cac1SHarald Welte 
4175d949cac1SHarald Welte 	/* create a codec specific record */
41765b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4177d949cac1SHarald Welte 	if (spec == NULL)
4178d949cac1SHarald Welte 		return -ENOMEM;
4179d949cac1SHarald Welte 
4180d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4181d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
4182d949cac1SHarald Welte 	if (err < 0) {
4183d949cac1SHarald Welte 		via_free(codec);
4184d949cac1SHarald Welte 		return err;
4185d949cac1SHarald Welte 	} else if (!err) {
4186d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4187d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4188d949cac1SHarald Welte 	}
4189d949cac1SHarald Welte 
419069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
419169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
4192d949cac1SHarald Welte 
419336dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
419436dd5c4aSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
419536dd5c4aSLydia Wang 	else
4196d949cac1SHarald Welte 		spec->stream_name_analog = "VT1708S Analog";
4197d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
4198d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
4199d949cac1SHarald Welte 
420036dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
420136dd5c4aSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
420236dd5c4aSLydia Wang 	else
4203d949cac1SHarald Welte 		spec->stream_name_digital = "VT1708S Digital";
4204d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
4205d949cac1SHarald Welte 
4206d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4207d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
4208d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
4209337b9d02STakashi Iwai 		get_mux_nids(codec);
42106369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
42116369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
4212d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
4213d949cac1SHarald Welte 		spec->num_mixers++;
4214d949cac1SHarald Welte 	}
4215d949cac1SHarald Welte 
4216d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4217d949cac1SHarald Welte 
4218d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
421969e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4220d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4221d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
4222d949cac1SHarald Welte #endif
4223d949cac1SHarald Welte 
4224518bf3baSLydia Wang 	/* correct names for VT1708BCE */
4225518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
4226518bf3baSLydia Wang 		kfree(codec->chip_name);
4227518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
4228518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
4229518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
4230518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
4231518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
4232518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
4233518bf3baSLydia Wang 	}
4234d949cac1SHarald Welte 	return 0;
4235d949cac1SHarald Welte }
4236d949cac1SHarald Welte 
4237d949cac1SHarald Welte /* Patch for VT1702 */
4238d949cac1SHarald Welte 
4239d949cac1SHarald Welte /* capture mixer elements */
4240d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
4241d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
4242d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
4243d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
4244d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
4245d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
4246d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
4247d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
4248d949cac1SHarald Welte 			 HDA_INPUT),
4249d949cac1SHarald Welte 	{
4250d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4251d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
4252d949cac1SHarald Welte 		 * So call somewhat different..
4253d949cac1SHarald Welte 		 */
4254d949cac1SHarald Welte 		/* .name = "Capture Source", */
4255d949cac1SHarald Welte 		.name = "Input Source",
4256d949cac1SHarald Welte 		.count = 1,
4257d949cac1SHarald Welte 		.info = via_mux_enum_info,
4258d949cac1SHarald Welte 		.get = via_mux_enum_get,
4259d949cac1SHarald Welte 		.put = via_mux_enum_put,
4260d949cac1SHarald Welte 	},
4261d949cac1SHarald Welte 	{ } /* end */
4262d949cac1SHarald Welte };
4263d949cac1SHarald Welte 
4264d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
4265d949cac1SHarald Welte 	/*
4266d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
4267d949cac1SHarald Welte 	 */
4268d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4269d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4270d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4271d949cac1SHarald Welte 
4272d949cac1SHarald Welte 
4273d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4274d949cac1SHarald Welte 	 * mixer widget
4275d949cac1SHarald Welte 	 */
4276d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
4277d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4278d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4279d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4280d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4281d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4282d949cac1SHarald Welte 
4283d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
4284d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
4285d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
4286d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4287d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4288bc7e7e5cSLydia Wang 	/* mixer enable */
4289bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
4290bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
4291bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
4292d949cac1SHarald Welte 	{ }
4293d949cac1SHarald Welte };
4294d949cac1SHarald Welte 
429569e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
4296a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
4297a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4298a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4299a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4300a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4301a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
430269e52a80SHarald Welte 	{ }
430369e52a80SHarald Welte };
430469e52a80SHarald Welte 
4305d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
43060aa62aefSHarald Welte 	.substreams = 2,
4307d949cac1SHarald Welte 	.channels_min = 2,
4308d949cac1SHarald Welte 	.channels_max = 2,
4309d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
4310d949cac1SHarald Welte 	.ops = {
4311d949cac1SHarald Welte 		.open = via_playback_pcm_open,
43120aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
431317314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
431417314379SLydia Wang 		.close = via_pcm_open_close
4315d949cac1SHarald Welte 	},
4316d949cac1SHarald Welte };
4317d949cac1SHarald Welte 
4318d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
4319d949cac1SHarald Welte 	.substreams = 3,
4320d949cac1SHarald Welte 	.channels_min = 2,
4321d949cac1SHarald Welte 	.channels_max = 2,
4322d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
4323d949cac1SHarald Welte 	.ops = {
432417314379SLydia Wang 		.open = via_pcm_open_close,
4325d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
432617314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
432717314379SLydia Wang 		.close = via_pcm_open_close
4328d949cac1SHarald Welte 	},
4329d949cac1SHarald Welte };
4330d949cac1SHarald Welte 
4331d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
43325691ec7fSHarald Welte 	.substreams = 2,
4333d949cac1SHarald Welte 	.channels_min = 2,
4334d949cac1SHarald Welte 	.channels_max = 2,
4335d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
4336d949cac1SHarald Welte 	.ops = {
4337d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
4338d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
43399da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
43409da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
4341d949cac1SHarald Welte 	},
4342d949cac1SHarald Welte };
4343d949cac1SHarald Welte 
4344d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4345d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4346d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4347d949cac1SHarald Welte {
4348d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4349d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4350d949cac1SHarald Welte 
4351d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4352d949cac1SHarald Welte 		/* config dac list */
4353d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
4354d949cac1SHarald Welte 	}
4355d949cac1SHarald Welte 
4356d949cac1SHarald Welte 	return 0;
4357d949cac1SHarald Welte }
4358d949cac1SHarald Welte 
4359d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4360d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4361d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4362d949cac1SHarald Welte {
4363d949cac1SHarald Welte 	int err;
4364d949cac1SHarald Welte 
4365d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4366d949cac1SHarald Welte 		return -1;
4367d949cac1SHarald Welte 
4368d949cac1SHarald Welte 	/* add control to mixer index 0 */
4369d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4370d949cac1SHarald Welte 			      "Master Front Playback Volume",
4371d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4372d949cac1SHarald Welte 	if (err < 0)
4373d949cac1SHarald Welte 		return err;
4374d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4375d949cac1SHarald Welte 			      "Master Front Playback Switch",
4376d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4377d949cac1SHarald Welte 	if (err < 0)
4378d949cac1SHarald Welte 		return err;
4379d949cac1SHarald Welte 
4380d949cac1SHarald Welte 	/* Front */
4381d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4382d949cac1SHarald Welte 			      "Front Playback Volume",
4383d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4384d949cac1SHarald Welte 	if (err < 0)
4385d949cac1SHarald Welte 		return err;
4386d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4387d949cac1SHarald Welte 			      "Front Playback Switch",
4388d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4389d949cac1SHarald Welte 	if (err < 0)
4390d949cac1SHarald Welte 		return err;
4391d949cac1SHarald Welte 
4392d949cac1SHarald Welte 	return 0;
4393d949cac1SHarald Welte }
4394d949cac1SHarald Welte 
4395d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4396d949cac1SHarald Welte {
43970713efebSLydia Wang 	int err, i;
43980713efebSLydia Wang 	struct hda_input_mux *imux;
43990713efebSLydia Wang 	static const char *texts[] = { "ON", "OFF", NULL};
4400d949cac1SHarald Welte 	if (!pin)
4401d949cac1SHarald Welte 		return 0;
4402d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4403cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4404d949cac1SHarald Welte 
4405d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4406d949cac1SHarald Welte 			      "Headphone Playback Volume",
4407d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4408d949cac1SHarald Welte 	if (err < 0)
4409d949cac1SHarald Welte 		return err;
4410d949cac1SHarald Welte 
4411d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4412d949cac1SHarald Welte 			      "Headphone Playback Switch",
4413d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4414d949cac1SHarald Welte 	if (err < 0)
4415d949cac1SHarald Welte 		return err;
4416d949cac1SHarald Welte 
44170713efebSLydia Wang 	imux = &spec->private_imux[1];
44180aa62aefSHarald Welte 
44190713efebSLydia Wang 	/* for hp mode select */
44200713efebSLydia Wang 	i = 0;
44210713efebSLydia Wang 	while (texts[i] != NULL)	{
44220713efebSLydia Wang 		imux->items[imux->num_items].label =  texts[i];
44230713efebSLydia Wang 		imux->items[imux->num_items].index = i;
44240713efebSLydia Wang 		imux->num_items++;
44250713efebSLydia Wang 		i++;
44260713efebSLydia Wang 	}
44270713efebSLydia Wang 
44280713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4429d949cac1SHarald Welte 	return 0;
4430d949cac1SHarald Welte }
4431d949cac1SHarald Welte 
4432d949cac1SHarald Welte /* create playback/capture controls for input pins */
4433d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec,
4434d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4435d949cac1SHarald Welte {
4436d949cac1SHarald Welte 	static char *labels[] = {
4437d949cac1SHarald Welte 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4438d949cac1SHarald Welte 	};
44390aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
4440d949cac1SHarald Welte 	int i, err, idx = 0;
4441d949cac1SHarald Welte 
4442d949cac1SHarald Welte 	/* for internal loopback recording select */
4443d949cac1SHarald Welte 	imux->items[imux->num_items].label = "Stereo Mixer";
4444d949cac1SHarald Welte 	imux->items[imux->num_items].index = 3;
4445d949cac1SHarald Welte 	imux->num_items++;
4446d949cac1SHarald Welte 
4447d949cac1SHarald Welte 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4448d949cac1SHarald Welte 		if (!cfg->input_pins[i])
4449d949cac1SHarald Welte 			continue;
4450d949cac1SHarald Welte 
4451d949cac1SHarald Welte 		switch (cfg->input_pins[i]) {
4452d949cac1SHarald Welte 		case 0x14: /* Mic */
4453d949cac1SHarald Welte 			idx = 1;
4454d949cac1SHarald Welte 			break;
4455d949cac1SHarald Welte 
4456d949cac1SHarald Welte 		case 0x15: /* Line In */
4457d949cac1SHarald Welte 			idx = 2;
4458d949cac1SHarald Welte 			break;
4459d949cac1SHarald Welte 
4460d949cac1SHarald Welte 		case 0x18: /* Front Mic */
4461d949cac1SHarald Welte 			idx = 3;
4462d949cac1SHarald Welte 			break;
4463d949cac1SHarald Welte 		}
44649510e8ddSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x1A);
4465d949cac1SHarald Welte 		if (err < 0)
4466d949cac1SHarald Welte 			return err;
4467d949cac1SHarald Welte 		imux->items[imux->num_items].label = labels[i];
4468d949cac1SHarald Welte 		imux->items[imux->num_items].index = idx-1;
4469d949cac1SHarald Welte 		imux->num_items++;
4470d949cac1SHarald Welte 	}
4471d949cac1SHarald Welte 	return 0;
4472d949cac1SHarald Welte }
4473d949cac1SHarald Welte 
4474d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4475d949cac1SHarald Welte {
4476d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4477d949cac1SHarald Welte 	int err;
4478d949cac1SHarald Welte 
44799da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4480d949cac1SHarald Welte 	if (err < 0)
4481d949cac1SHarald Welte 		return err;
4482d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4483d949cac1SHarald Welte 	if (err < 0)
4484d949cac1SHarald Welte 		return err;
4485d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4486d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4487d949cac1SHarald Welte 
4488d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4489d949cac1SHarald Welte 	if (err < 0)
4490d949cac1SHarald Welte 		return err;
4491d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4492d949cac1SHarald Welte 	if (err < 0)
4493d949cac1SHarald Welte 		return err;
4494c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4495c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4496c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4497c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4498c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4499c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
4500d949cac1SHarald Welte 	err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg);
4501d949cac1SHarald Welte 	if (err < 0)
4502d949cac1SHarald Welte 		return err;
4503d949cac1SHarald Welte 
4504d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4505d949cac1SHarald Welte 
45069da29271STakashi Iwai 	fill_dig_outs(codec);
450798aa34c0SHarald Welte 
4508603c4019STakashi Iwai 	if (spec->kctls.list)
4509603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4510d949cac1SHarald Welte 
45110aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
45120aa62aefSHarald Welte 
4513f8fdd495SHarald Welte 	if (spec->hp_mux)
45145b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
4515d949cac1SHarald Welte 
4516d949cac1SHarald Welte 	return 1;
4517d949cac1SHarald Welte }
4518d949cac1SHarald Welte 
4519d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4520d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4521d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4522d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4523d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4524d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4525d949cac1SHarald Welte 	{ } /* end */
4526d949cac1SHarald Welte };
4527d949cac1SHarald Welte #endif
4528d949cac1SHarald Welte 
4529d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4530d949cac1SHarald Welte {
4531d949cac1SHarald Welte 	struct via_spec *spec;
4532d949cac1SHarald Welte 	int err;
4533d949cac1SHarald Welte 
4534d949cac1SHarald Welte 	/* create a codec specific record */
45355b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4536d949cac1SHarald Welte 	if (spec == NULL)
4537d949cac1SHarald Welte 		return -ENOMEM;
4538d949cac1SHarald Welte 
4539d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4540d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4541d949cac1SHarald Welte 	if (err < 0) {
4542d949cac1SHarald Welte 		via_free(codec);
4543d949cac1SHarald Welte 		return err;
4544d949cac1SHarald Welte 	} else if (!err) {
4545d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4546d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4547d949cac1SHarald Welte 	}
4548d949cac1SHarald Welte 
454969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
455069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4551d949cac1SHarald Welte 
4552d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4553d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4554d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4555d949cac1SHarald Welte 
4556d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4557d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4558d949cac1SHarald Welte 
4559d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4560d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4561d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4562337b9d02STakashi Iwai 		get_mux_nids(codec);
4563d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4564d949cac1SHarald Welte 		spec->num_mixers++;
4565d949cac1SHarald Welte 	}
4566d949cac1SHarald Welte 
4567d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4568d949cac1SHarald Welte 
4569d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
457069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4571d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4572d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4573d949cac1SHarald Welte #endif
4574d949cac1SHarald Welte 
4575d949cac1SHarald Welte 	return 0;
4576d949cac1SHarald Welte }
4577d949cac1SHarald Welte 
4578eb7188caSLydia Wang /* Patch for VT1718S */
4579eb7188caSLydia Wang 
4580eb7188caSLydia Wang /* capture mixer elements */
4581eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4582eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4583eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4584eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4585eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4586eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4587eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4588eb7188caSLydia Wang 			 HDA_INPUT),
4589eb7188caSLydia Wang 	{
4590eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4591eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4592eb7188caSLydia Wang 		 * So call somewhat different..
4593eb7188caSLydia Wang 		 */
4594eb7188caSLydia Wang 		.name = "Input Source",
4595eb7188caSLydia Wang 		.count = 2,
4596eb7188caSLydia Wang 		.info = via_mux_enum_info,
4597eb7188caSLydia Wang 		.get = via_mux_enum_get,
4598eb7188caSLydia Wang 		.put = via_mux_enum_put,
4599eb7188caSLydia Wang 	},
4600eb7188caSLydia Wang 	{ } /* end */
4601eb7188caSLydia Wang };
4602eb7188caSLydia Wang 
4603eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4604eb7188caSLydia Wang 	/*
4605eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4606eb7188caSLydia Wang 	 */
4607eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4608eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4609eb7188caSLydia Wang 
4610eb7188caSLydia Wang 
4611eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4612eb7188caSLydia Wang 	 * mixer widget
4613eb7188caSLydia Wang 	 */
4614eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4615eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4616eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4617eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4618eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4619eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4620eb7188caSLydia Wang 
4621eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4622eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4623eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4624eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4625eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4626eb7188caSLydia Wang 	/* PW11 Input enable */
4627eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4628eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4629eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4630eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4631eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4632eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4633eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4634eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4635eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4636eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4637eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4638eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4639eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4640eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4641eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4642eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4643eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4644eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4645eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4646eb7188caSLydia Wang 	{ }
4647eb7188caSLydia Wang };
4648eb7188caSLydia Wang 
4649eb7188caSLydia Wang 
4650eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4651eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4652eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4653eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4654eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4655eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4656eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4657eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4658eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4659eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4660eb7188caSLydia Wang 	{ }
4661eb7188caSLydia Wang };
4662eb7188caSLydia Wang 
4663eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4664eb7188caSLydia Wang 	.substreams = 2,
4665eb7188caSLydia Wang 	.channels_min = 2,
4666eb7188caSLydia Wang 	.channels_max = 10,
4667eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4668eb7188caSLydia Wang 	.ops = {
4669eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4670eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4671eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4672eb7188caSLydia Wang 		.close = via_pcm_open_close,
4673eb7188caSLydia Wang 	},
4674eb7188caSLydia Wang };
4675eb7188caSLydia Wang 
4676eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4677eb7188caSLydia Wang 	.substreams = 2,
4678eb7188caSLydia Wang 	.channels_min = 2,
4679eb7188caSLydia Wang 	.channels_max = 2,
4680eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4681eb7188caSLydia Wang 	.ops = {
4682eb7188caSLydia Wang 		.open = via_pcm_open_close,
4683eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4684eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4685eb7188caSLydia Wang 		.close = via_pcm_open_close,
4686eb7188caSLydia Wang 	},
4687eb7188caSLydia Wang };
4688eb7188caSLydia Wang 
4689eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4690eb7188caSLydia Wang 	.substreams = 2,
4691eb7188caSLydia Wang 	.channels_min = 2,
4692eb7188caSLydia Wang 	.channels_max = 2,
4693eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4694eb7188caSLydia Wang 	.ops = {
4695eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4696eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4697eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4698eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4699eb7188caSLydia Wang 	},
4700eb7188caSLydia Wang };
4701eb7188caSLydia Wang 
4702eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4703eb7188caSLydia Wang 	.substreams = 1,
4704eb7188caSLydia Wang 	.channels_min = 2,
4705eb7188caSLydia Wang 	.channels_max = 2,
4706eb7188caSLydia Wang };
4707eb7188caSLydia Wang 
4708eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4709eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4710eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4711eb7188caSLydia Wang {
4712eb7188caSLydia Wang 	int i;
4713eb7188caSLydia Wang 	hda_nid_t nid;
4714eb7188caSLydia Wang 
4715eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4716eb7188caSLydia Wang 
4717eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4718eb7188caSLydia Wang 
4719eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4720eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4721eb7188caSLydia Wang 		if (nid) {
4722eb7188caSLydia Wang 			/* config dac list */
4723eb7188caSLydia Wang 			switch (i) {
4724eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4725eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4726eb7188caSLydia Wang 				break;
4727eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4728eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4729eb7188caSLydia Wang 				break;
4730eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4731eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4732eb7188caSLydia Wang 				break;
4733eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4734eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4735eb7188caSLydia Wang 				break;
4736eb7188caSLydia Wang 			}
4737eb7188caSLydia Wang 		}
4738eb7188caSLydia Wang 	}
4739eb7188caSLydia Wang 
4740eb7188caSLydia Wang 	return 0;
4741eb7188caSLydia Wang }
4742eb7188caSLydia Wang 
4743eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4744eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4745eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4746eb7188caSLydia Wang {
4747eb7188caSLydia Wang 	char name[32];
4748eb7188caSLydia Wang 	static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" };
4749eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4750eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4751eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4752eb7188caSLydia Wang 	int i, err;
4753eb7188caSLydia Wang 
4754eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4755eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4756eb7188caSLydia Wang 
4757eb7188caSLydia Wang 		if (!nid)
4758eb7188caSLydia Wang 			continue;
4759eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4760eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4761eb7188caSLydia Wang 
4762eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4763eb7188caSLydia Wang 			/* Center/LFE */
4764eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4765eb7188caSLydia Wang 					      "Center Playback Volume",
4766eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4767eb7188caSLydia Wang 								  HDA_OUTPUT));
4768eb7188caSLydia Wang 			if (err < 0)
4769eb7188caSLydia Wang 				return err;
4770eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4771eb7188caSLydia Wang 					      "LFE Playback Volume",
4772eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4773eb7188caSLydia Wang 								  HDA_OUTPUT));
4774eb7188caSLydia Wang 			if (err < 0)
4775eb7188caSLydia Wang 				return err;
4776eb7188caSLydia Wang 			err = via_add_control(
4777eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4778eb7188caSLydia Wang 				"Center Playback Switch",
4779eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4780eb7188caSLydia Wang 						    HDA_OUTPUT));
4781eb7188caSLydia Wang 			if (err < 0)
4782eb7188caSLydia Wang 				return err;
4783eb7188caSLydia Wang 			err = via_add_control(
4784eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4785eb7188caSLydia Wang 				"LFE Playback Switch",
4786eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4787eb7188caSLydia Wang 						    HDA_OUTPUT));
4788eb7188caSLydia Wang 			if (err < 0)
4789eb7188caSLydia Wang 				return err;
4790eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4791eb7188caSLydia Wang 			/* Front */
4792eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4793eb7188caSLydia Wang 			err = via_add_control(
4794eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4795eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4796eb7188caSLydia Wang 			if (err < 0)
4797eb7188caSLydia Wang 				return err;
4798eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4799eb7188caSLydia Wang 			err = via_add_control(
4800eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4801eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4802eb7188caSLydia Wang 						    HDA_OUTPUT));
4803eb7188caSLydia Wang 			if (err < 0)
4804eb7188caSLydia Wang 				return err;
4805eb7188caSLydia Wang 		} else {
4806eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4807eb7188caSLydia Wang 			err = via_add_control(
4808eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4809eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4810eb7188caSLydia Wang 			if (err < 0)
4811eb7188caSLydia Wang 				return err;
4812eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4813eb7188caSLydia Wang 			err = via_add_control(
4814eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4815eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4816eb7188caSLydia Wang 						    HDA_OUTPUT));
4817eb7188caSLydia Wang 			if (err < 0)
4818eb7188caSLydia Wang 				return err;
4819eb7188caSLydia Wang 		}
4820eb7188caSLydia Wang 	}
4821eb7188caSLydia Wang 	return 0;
4822eb7188caSLydia Wang }
4823eb7188caSLydia Wang 
4824eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4825eb7188caSLydia Wang {
4826eb7188caSLydia Wang 	int err;
4827eb7188caSLydia Wang 
4828eb7188caSLydia Wang 	if (!pin)
4829eb7188caSLydia Wang 		return 0;
4830eb7188caSLydia Wang 
4831eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4832eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4833eb7188caSLydia Wang 
4834eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4835eb7188caSLydia Wang 			      "Headphone Playback Volume",
4836eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4837eb7188caSLydia Wang 	if (err < 0)
4838eb7188caSLydia Wang 		return err;
4839eb7188caSLydia Wang 
4840eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4841eb7188caSLydia Wang 			      "Headphone Playback Switch",
4842eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4843eb7188caSLydia Wang 	if (err < 0)
4844eb7188caSLydia Wang 		return err;
4845eb7188caSLydia Wang 
4846eb7188caSLydia Wang 	create_hp_imux(spec);
4847eb7188caSLydia Wang 	return 0;
4848eb7188caSLydia Wang }
4849eb7188caSLydia Wang 
4850eb7188caSLydia Wang /* create playback/capture controls for input pins */
4851eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec,
4852eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4853eb7188caSLydia Wang {
4854eb7188caSLydia Wang 	static char *labels[] = {
4855eb7188caSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
4856eb7188caSLydia Wang 	};
4857eb7188caSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
4858eb7188caSLydia Wang 	int i, err, idx = 0;
4859eb7188caSLydia Wang 
4860eb7188caSLydia Wang 	/* for internal loopback recording select */
4861eb7188caSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
4862eb7188caSLydia Wang 	imux->items[imux->num_items].index = 5;
4863eb7188caSLydia Wang 	imux->num_items++;
4864eb7188caSLydia Wang 
4865eb7188caSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
4866eb7188caSLydia Wang 		if (!cfg->input_pins[i])
4867eb7188caSLydia Wang 			continue;
4868eb7188caSLydia Wang 
4869eb7188caSLydia Wang 		switch (cfg->input_pins[i]) {
4870eb7188caSLydia Wang 		case 0x2b: /* Mic */
4871eb7188caSLydia Wang 			idx = 1;
4872eb7188caSLydia Wang 			break;
4873eb7188caSLydia Wang 
4874eb7188caSLydia Wang 		case 0x2a: /* Line In */
4875eb7188caSLydia Wang 			idx = 2;
4876eb7188caSLydia Wang 			break;
4877eb7188caSLydia Wang 
4878eb7188caSLydia Wang 		case 0x29: /* Front Mic */
4879eb7188caSLydia Wang 			idx = 3;
4880eb7188caSLydia Wang 			break;
4881eb7188caSLydia Wang 
4882eb7188caSLydia Wang 		case 0x2c: /* CD */
4883eb7188caSLydia Wang 			idx = 0;
4884eb7188caSLydia Wang 			break;
4885eb7188caSLydia Wang 		}
4886eb7188caSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
4887eb7188caSLydia Wang 		if (err < 0)
4888eb7188caSLydia Wang 			return err;
4889eb7188caSLydia Wang 		imux->items[imux->num_items].label = labels[i];
4890eb7188caSLydia Wang 		imux->items[imux->num_items].index = idx;
4891eb7188caSLydia Wang 		imux->num_items++;
4892eb7188caSLydia Wang 	}
4893eb7188caSLydia Wang 	return 0;
4894eb7188caSLydia Wang }
4895eb7188caSLydia Wang 
4896eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4897eb7188caSLydia Wang {
4898eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4899eb7188caSLydia Wang 	int err;
4900eb7188caSLydia Wang 
4901eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4902eb7188caSLydia Wang 
4903eb7188caSLydia Wang 	if (err < 0)
4904eb7188caSLydia Wang 		return err;
4905eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4906eb7188caSLydia Wang 	if (err < 0)
4907eb7188caSLydia Wang 		return err;
4908eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4909eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4910eb7188caSLydia Wang 
4911eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4912eb7188caSLydia Wang 	if (err < 0)
4913eb7188caSLydia Wang 		return err;
4914eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4915eb7188caSLydia Wang 	if (err < 0)
4916eb7188caSLydia Wang 		return err;
4917eb7188caSLydia Wang 	err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg);
4918eb7188caSLydia Wang 	if (err < 0)
4919eb7188caSLydia Wang 		return err;
4920eb7188caSLydia Wang 
4921eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4922eb7188caSLydia Wang 
4923eb7188caSLydia Wang 	fill_dig_outs(codec);
4924eb7188caSLydia Wang 
4925eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4926eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4927eb7188caSLydia Wang 
4928eb7188caSLydia Wang 	if (spec->kctls.list)
4929eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4930eb7188caSLydia Wang 
4931eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4932eb7188caSLydia Wang 
4933eb7188caSLydia Wang 	if (spec->hp_mux)
49345b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
4935eb7188caSLydia Wang 
49365b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4937eb7188caSLydia Wang 
4938eb7188caSLydia Wang 	return 1;
4939eb7188caSLydia Wang }
4940eb7188caSLydia Wang 
4941eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4942eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = {
4943eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4944eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4945eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4946eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4947eb7188caSLydia Wang 	{ } /* end */
4948eb7188caSLydia Wang };
4949eb7188caSLydia Wang #endif
4950eb7188caSLydia Wang 
4951eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4952eb7188caSLydia Wang {
4953eb7188caSLydia Wang 	struct via_spec *spec;
4954eb7188caSLydia Wang 	int err;
4955eb7188caSLydia Wang 
4956eb7188caSLydia Wang 	/* create a codec specific record */
49575b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4958eb7188caSLydia Wang 	if (spec == NULL)
4959eb7188caSLydia Wang 		return -ENOMEM;
4960eb7188caSLydia Wang 
4961eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4962eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4963eb7188caSLydia Wang 	if (err < 0) {
4964eb7188caSLydia Wang 		via_free(codec);
4965eb7188caSLydia Wang 		return err;
4966eb7188caSLydia Wang 	} else if (!err) {
4967eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4968eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4969eb7188caSLydia Wang 	}
4970eb7188caSLydia Wang 
4971eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4972eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4973eb7188caSLydia Wang 
4974bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4975bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4976bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4977bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4978bb3c6bfcSLydia Wang 	else
4979eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4980eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4981eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4982eb7188caSLydia Wang 
4983bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4984bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4985bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4986bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4987bb3c6bfcSLydia Wang 	else
4988eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4989eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4990bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4991eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4992eb7188caSLydia Wang 
4993eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4994eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4995eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4996eb7188caSLydia Wang 		get_mux_nids(codec);
4997bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4998bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4999eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
5000eb7188caSLydia Wang 		spec->num_mixers++;
5001eb7188caSLydia Wang 	}
5002eb7188caSLydia Wang 
5003eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
5004eb7188caSLydia Wang 
5005eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
50060f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5007eb7188caSLydia Wang 
5008eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5009eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
5010eb7188caSLydia Wang #endif
5011eb7188caSLydia Wang 
5012eb7188caSLydia Wang 	return 0;
5013eb7188caSLydia Wang }
5014f3db423dSLydia Wang 
5015f3db423dSLydia Wang /* Patch for VT1716S */
5016f3db423dSLydia Wang 
5017f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
5018f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
5019f3db423dSLydia Wang {
5020f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
5021f3db423dSLydia Wang 	uinfo->count = 1;
5022f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
5023f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
5024f3db423dSLydia Wang 	return 0;
5025f3db423dSLydia Wang }
5026f3db423dSLydia Wang 
5027f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
5028f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
5029f3db423dSLydia Wang {
5030f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5031f3db423dSLydia Wang 	int index = 0;
5032f3db423dSLydia Wang 
5033f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
5034f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
5035f3db423dSLydia Wang 	if (index != -1)
5036f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
5037f3db423dSLydia Wang 
5038f3db423dSLydia Wang 	return 0;
5039f3db423dSLydia Wang }
5040f3db423dSLydia Wang 
5041f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
5042f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
5043f3db423dSLydia Wang {
5044f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5045f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5046f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
5047f3db423dSLydia Wang 
5048f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
5049f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
5050f3db423dSLydia Wang 	spec->dmic_enabled = index;
5051f3db423dSLydia Wang 	set_jack_power_state(codec);
5052f3db423dSLydia Wang 
5053f3db423dSLydia Wang 	return 1;
5054f3db423dSLydia Wang }
5055f3db423dSLydia Wang 
5056f3db423dSLydia Wang /* capture mixer elements */
5057f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
5058f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
5059f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
5060f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
5061f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
5062f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
5063f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
5064f3db423dSLydia Wang 			 HDA_INPUT),
5065f3db423dSLydia Wang 	{
5066f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5067f3db423dSLydia Wang 		.name = "Input Source",
5068f3db423dSLydia Wang 		.count = 1,
5069f3db423dSLydia Wang 		.info = via_mux_enum_info,
5070f3db423dSLydia Wang 		.get = via_mux_enum_get,
5071f3db423dSLydia Wang 		.put = via_mux_enum_put,
5072f3db423dSLydia Wang 	},
5073f3db423dSLydia Wang 	{ } /* end */
5074f3db423dSLydia Wang };
5075f3db423dSLydia Wang 
5076f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
5077f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
5078f3db423dSLydia Wang 	{
5079f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5080f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
50815b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
5082f3db423dSLydia Wang 	 .count = 1,
5083f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
5084f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
5085f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
5086f3db423dSLydia Wang 	 },
5087f3db423dSLydia Wang 	{}			/* end */
5088f3db423dSLydia Wang };
5089f3db423dSLydia Wang 
5090f3db423dSLydia Wang 
5091f3db423dSLydia Wang /* mono-out mixer elements */
5092f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
5093f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
5094f3db423dSLydia Wang 	{ } /* end */
5095f3db423dSLydia Wang };
5096f3db423dSLydia Wang 
5097f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
5098f3db423dSLydia Wang 	/*
5099f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5100f3db423dSLydia Wang 	 */
5101f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5102f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5103f3db423dSLydia Wang 
5104f3db423dSLydia Wang 
5105f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5106f3db423dSLydia Wang 	 * mixer widget
5107f3db423dSLydia Wang 	 */
5108f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5109f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5110f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5111f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5112f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5113f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5114f3db423dSLydia Wang 
5115f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
5116f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
5117f3db423dSLydia Wang 
5118f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
5119f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
5120f3db423dSLydia Wang 
5121f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
5122f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
5123f3db423dSLydia Wang 
5124f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
5125f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
5126f3db423dSLydia Wang 
5127f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
5128f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5129f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5130f3db423dSLydia Wang 
5131f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
5132f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5133f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
5134f3db423dSLydia Wang 	/* PW12 Output enable */
5135f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
5136f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
5137f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
5138f3db423dSLydia Wang 	/* don't bybass mixer */
5139f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
5140f3db423dSLydia Wang 	/* Enable mono output */
5141f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
5142f3db423dSLydia Wang 	{ }
5143f3db423dSLydia Wang };
5144f3db423dSLydia Wang 
5145f3db423dSLydia Wang 
5146f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
5147f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
5148f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
5149f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5150f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5151f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5152f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
5153f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
5154f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5155f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5156f3db423dSLydia Wang 	{ }
5157f3db423dSLydia Wang };
5158f3db423dSLydia Wang 
5159f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
5160f3db423dSLydia Wang 	.substreams = 2,
5161f3db423dSLydia Wang 	.channels_min = 2,
5162f3db423dSLydia Wang 	.channels_max = 6,
5163f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5164f3db423dSLydia Wang 	.ops = {
5165f3db423dSLydia Wang 		.open = via_playback_pcm_open,
5166f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5167f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5168f3db423dSLydia Wang 		.close = via_pcm_open_close,
5169f3db423dSLydia Wang 	},
5170f3db423dSLydia Wang };
5171f3db423dSLydia Wang 
5172f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
5173f3db423dSLydia Wang 	.substreams = 2,
5174f3db423dSLydia Wang 	.channels_min = 2,
5175f3db423dSLydia Wang 	.channels_max = 2,
5176f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
5177f3db423dSLydia Wang 	.ops = {
5178f3db423dSLydia Wang 		.open = via_pcm_open_close,
5179f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
5180f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5181f3db423dSLydia Wang 		.close = via_pcm_open_close,
5182f3db423dSLydia Wang 	},
5183f3db423dSLydia Wang };
5184f3db423dSLydia Wang 
5185f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
5186f3db423dSLydia Wang 	.substreams = 2,
5187f3db423dSLydia Wang 	.channels_min = 2,
5188f3db423dSLydia Wang 	.channels_max = 2,
5189f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
5190f3db423dSLydia Wang 	.ops = {
5191f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
5192f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
5193f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5194f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5195f3db423dSLydia Wang 	},
5196f3db423dSLydia Wang };
5197f3db423dSLydia Wang 
5198f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5199f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
5200f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
5201f3db423dSLydia Wang {	int i;
5202f3db423dSLydia Wang 	hda_nid_t nid;
5203f3db423dSLydia Wang 
5204f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
5205f3db423dSLydia Wang 
5206f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5207f3db423dSLydia Wang 
5208f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
5209f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5210f3db423dSLydia Wang 		if (nid) {
5211f3db423dSLydia Wang 			/* config dac list */
5212f3db423dSLydia Wang 			switch (i) {
5213f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
5214f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
5215f3db423dSLydia Wang 				break;
5216f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
5217f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
5218f3db423dSLydia Wang 				break;
5219f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
5220f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
5221f3db423dSLydia Wang 				break;
5222f3db423dSLydia Wang 			}
5223f3db423dSLydia Wang 		}
5224f3db423dSLydia Wang 	}
5225f3db423dSLydia Wang 
5226f3db423dSLydia Wang 	return 0;
5227f3db423dSLydia Wang }
5228f3db423dSLydia Wang 
5229f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
5230f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
5231f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
5232f3db423dSLydia Wang {
5233f3db423dSLydia Wang 	char name[32];
5234f3db423dSLydia Wang 	static const char *chname[3] = { "Front", "Surround", "C/LFE" };
5235f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
5236f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
5237f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
5238f3db423dSLydia Wang 	int i, err;
5239f3db423dSLydia Wang 
5240f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
5241f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5242f3db423dSLydia Wang 
5243f3db423dSLydia Wang 		if (!nid)
5244f3db423dSLydia Wang 			continue;
5245f3db423dSLydia Wang 
5246f3db423dSLydia Wang 		nid_vol = nid_vols[i];
5247f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
5248f3db423dSLydia Wang 
5249f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
5250f3db423dSLydia Wang 			err = via_add_control(
5251f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5252f3db423dSLydia Wang 				"Center Playback Volume",
5253f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
5254f3db423dSLydia Wang 			if (err < 0)
5255f3db423dSLydia Wang 				return err;
5256f3db423dSLydia Wang 			err = via_add_control(
5257f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5258f3db423dSLydia Wang 				"LFE Playback Volume",
5259f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
5260f3db423dSLydia Wang 			if (err < 0)
5261f3db423dSLydia Wang 				return err;
5262f3db423dSLydia Wang 			err = via_add_control(
5263f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5264f3db423dSLydia Wang 				"Center Playback Switch",
5265f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
5266f3db423dSLydia Wang 						    HDA_OUTPUT));
5267f3db423dSLydia Wang 			if (err < 0)
5268f3db423dSLydia Wang 				return err;
5269f3db423dSLydia Wang 			err = via_add_control(
5270f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5271f3db423dSLydia Wang 				"LFE Playback Switch",
5272f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
5273f3db423dSLydia Wang 						    HDA_OUTPUT));
5274f3db423dSLydia Wang 			if (err < 0)
5275f3db423dSLydia Wang 				return err;
5276f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
5277f3db423dSLydia Wang 
5278f3db423dSLydia Wang 			err = via_add_control(
5279f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5280f3db423dSLydia Wang 				"Master Front Playback Volume",
5281f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5282f3db423dSLydia Wang 			if (err < 0)
5283f3db423dSLydia Wang 				return err;
5284f3db423dSLydia Wang 			err = via_add_control(
5285f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5286f3db423dSLydia Wang 				"Master Front Playback Switch",
5287f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5288f3db423dSLydia Wang 			if (err < 0)
5289f3db423dSLydia Wang 				return err;
5290f3db423dSLydia Wang 
5291f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5292f3db423dSLydia Wang 			err = via_add_control(
5293f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5294f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5295f3db423dSLydia Wang 			if (err < 0)
5296f3db423dSLydia Wang 				return err;
5297f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5298f3db423dSLydia Wang 			err = via_add_control(
5299f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5300f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5301f3db423dSLydia Wang 						    HDA_OUTPUT));
5302f3db423dSLydia Wang 			if (err < 0)
5303f3db423dSLydia Wang 				return err;
5304f3db423dSLydia Wang 		} else {
5305f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5306f3db423dSLydia Wang 			err = via_add_control(
5307f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5308f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5309f3db423dSLydia Wang 			if (err < 0)
5310f3db423dSLydia Wang 				return err;
5311f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5312f3db423dSLydia Wang 			err = via_add_control(
5313f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5314f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5315f3db423dSLydia Wang 						    HDA_OUTPUT));
5316f3db423dSLydia Wang 			if (err < 0)
5317f3db423dSLydia Wang 				return err;
5318f3db423dSLydia Wang 		}
5319f3db423dSLydia Wang 	}
5320f3db423dSLydia Wang 	return 0;
5321f3db423dSLydia Wang }
5322f3db423dSLydia Wang 
5323f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5324f3db423dSLydia Wang {
5325f3db423dSLydia Wang 	int err;
5326f3db423dSLydia Wang 
5327f3db423dSLydia Wang 	if (!pin)
5328f3db423dSLydia Wang 		return 0;
5329f3db423dSLydia Wang 
5330f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5331f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5332f3db423dSLydia Wang 
5333f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5334f3db423dSLydia Wang 			      "Headphone Playback Volume",
5335f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5336f3db423dSLydia Wang 	if (err < 0)
5337f3db423dSLydia Wang 		return err;
5338f3db423dSLydia Wang 
5339f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5340f3db423dSLydia Wang 			      "Headphone Playback Switch",
5341f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5342f3db423dSLydia Wang 	if (err < 0)
5343f3db423dSLydia Wang 		return err;
5344f3db423dSLydia Wang 
5345f3db423dSLydia Wang 	create_hp_imux(spec);
5346f3db423dSLydia Wang 	return 0;
5347f3db423dSLydia Wang }
5348f3db423dSLydia Wang 
5349f3db423dSLydia Wang /* create playback/capture controls for input pins */
5350f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec,
5351f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5352f3db423dSLydia Wang {
5353f3db423dSLydia Wang 	static char *labels[] = {
5354f3db423dSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
5355f3db423dSLydia Wang 	};
5356f3db423dSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5357f3db423dSLydia Wang 	int i, err, idx = 0;
5358f3db423dSLydia Wang 
5359f3db423dSLydia Wang 	/* for internal loopback recording select */
5360f3db423dSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
5361f3db423dSLydia Wang 	imux->items[imux->num_items].index = 5;
5362f3db423dSLydia Wang 	imux->num_items++;
5363f3db423dSLydia Wang 
5364f3db423dSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
5365f3db423dSLydia Wang 		if (!cfg->input_pins[i])
5366f3db423dSLydia Wang 			continue;
5367f3db423dSLydia Wang 
5368f3db423dSLydia Wang 		switch (cfg->input_pins[i]) {
5369f3db423dSLydia Wang 		case 0x1a: /* Mic */
5370f3db423dSLydia Wang 			idx = 2;
5371f3db423dSLydia Wang 			break;
5372f3db423dSLydia Wang 
5373f3db423dSLydia Wang 		case 0x1b: /* Line In */
5374f3db423dSLydia Wang 			idx = 3;
5375f3db423dSLydia Wang 			break;
5376f3db423dSLydia Wang 
5377f3db423dSLydia Wang 		case 0x1e: /* Front Mic */
5378f3db423dSLydia Wang 			idx = 4;
5379f3db423dSLydia Wang 			break;
5380f3db423dSLydia Wang 
5381f3db423dSLydia Wang 		case 0x1f: /* CD */
5382f3db423dSLydia Wang 			idx = 1;
5383f3db423dSLydia Wang 			break;
5384f3db423dSLydia Wang 		}
5385f3db423dSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x16);
5386f3db423dSLydia Wang 		if (err < 0)
5387f3db423dSLydia Wang 			return err;
5388f3db423dSLydia Wang 		imux->items[imux->num_items].label = labels[i];
5389f3db423dSLydia Wang 		imux->items[imux->num_items].index = idx-1;
5390f3db423dSLydia Wang 		imux->num_items++;
5391f3db423dSLydia Wang 	}
5392f3db423dSLydia Wang 	return 0;
5393f3db423dSLydia Wang }
5394f3db423dSLydia Wang 
5395f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5396f3db423dSLydia Wang {
5397f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5398f3db423dSLydia Wang 	int err;
5399f3db423dSLydia Wang 
5400f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5401f3db423dSLydia Wang 	if (err < 0)
5402f3db423dSLydia Wang 		return err;
5403f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5404f3db423dSLydia Wang 	if (err < 0)
5405f3db423dSLydia Wang 		return err;
5406f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5407f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5408f3db423dSLydia Wang 
5409f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5410f3db423dSLydia Wang 	if (err < 0)
5411f3db423dSLydia Wang 		return err;
5412f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5413f3db423dSLydia Wang 	if (err < 0)
5414f3db423dSLydia Wang 		return err;
5415f3db423dSLydia Wang 	err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg);
5416f3db423dSLydia Wang 	if (err < 0)
5417f3db423dSLydia Wang 		return err;
5418f3db423dSLydia Wang 
5419f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5420f3db423dSLydia Wang 
5421f3db423dSLydia Wang 	fill_dig_outs(codec);
5422f3db423dSLydia Wang 
5423f3db423dSLydia Wang 	if (spec->kctls.list)
5424f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5425f3db423dSLydia Wang 
5426f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5427f3db423dSLydia Wang 
5428f3db423dSLydia Wang 	if (spec->hp_mux)
54295b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
5430f3db423dSLydia Wang 
54315b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
5432f3db423dSLydia Wang 
5433f3db423dSLydia Wang 	return 1;
5434f3db423dSLydia Wang }
5435f3db423dSLydia Wang 
5436f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5437f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5438f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5439f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5440f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5441f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5442f3db423dSLydia Wang 	{ } /* end */
5443f3db423dSLydia Wang };
5444f3db423dSLydia Wang #endif
5445f3db423dSLydia Wang 
5446f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5447f3db423dSLydia Wang {
5448f3db423dSLydia Wang 	struct via_spec *spec;
5449f3db423dSLydia Wang 	int err;
5450f3db423dSLydia Wang 
5451f3db423dSLydia Wang 	/* create a codec specific record */
54525b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5453f3db423dSLydia Wang 	if (spec == NULL)
5454f3db423dSLydia Wang 		return -ENOMEM;
5455f3db423dSLydia Wang 
5456f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5457f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5458f3db423dSLydia Wang 	if (err < 0) {
5459f3db423dSLydia Wang 		via_free(codec);
5460f3db423dSLydia Wang 		return err;
5461f3db423dSLydia Wang 	} else if (!err) {
5462f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5463f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5464f3db423dSLydia Wang 	}
5465f3db423dSLydia Wang 
5466f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5467f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5468f3db423dSLydia Wang 
5469f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5470f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5471f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5472f3db423dSLydia Wang 
5473f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5474f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5475f3db423dSLydia Wang 
5476f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5477f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5478f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5479f3db423dSLydia Wang 		get_mux_nids(codec);
5480f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5481f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5482f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5483f3db423dSLydia Wang 		spec->num_mixers++;
5484f3db423dSLydia Wang 	}
5485f3db423dSLydia Wang 
5486f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5487f3db423dSLydia Wang 	spec->num_mixers++;
5488f3db423dSLydia Wang 
5489f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5490f3db423dSLydia Wang 
5491f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5492f3db423dSLydia Wang 
5493f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
54940f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5495f3db423dSLydia Wang 
5496f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5497f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5498f3db423dSLydia Wang #endif
5499f3db423dSLydia Wang 
5500f3db423dSLydia Wang 	return 0;
5501f3db423dSLydia Wang }
550225eaba2fSLydia Wang 
550325eaba2fSLydia Wang /* for vt2002P */
550425eaba2fSLydia Wang 
550525eaba2fSLydia Wang /* capture mixer elements */
550625eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
550725eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
550825eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
550925eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
551025eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
551125eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
551225eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
551325eaba2fSLydia Wang 			 HDA_INPUT),
551425eaba2fSLydia Wang 	{
551525eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
551625eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
551725eaba2fSLydia Wang 		 * So call somewhat different..
551825eaba2fSLydia Wang 		 */
551925eaba2fSLydia Wang 		/* .name = "Capture Source", */
552025eaba2fSLydia Wang 		.name = "Input Source",
552125eaba2fSLydia Wang 		.count = 2,
552225eaba2fSLydia Wang 		.info = via_mux_enum_info,
552325eaba2fSLydia Wang 		.get = via_mux_enum_get,
552425eaba2fSLydia Wang 		.put = via_mux_enum_put,
552525eaba2fSLydia Wang 	},
552625eaba2fSLydia Wang 	{ } /* end */
552725eaba2fSLydia Wang };
552825eaba2fSLydia Wang 
552925eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
553025eaba2fSLydia Wang 	/*
553125eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
553225eaba2fSLydia Wang 	 */
553325eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
553425eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
553525eaba2fSLydia Wang 
553625eaba2fSLydia Wang 
553725eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
553825eaba2fSLydia Wang 	 * mixer widget
553925eaba2fSLydia Wang 	 */
554025eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
554125eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
554225eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
554325eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
554425eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
554525eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
554625eaba2fSLydia Wang 
554725eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
554825eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
554925eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
555025eaba2fSLydia Wang 
555125eaba2fSLydia Wang 	/* PW9 Output enable */
555225eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
555325eaba2fSLydia Wang 
555425eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
555525eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
555625eaba2fSLydia Wang 
555725eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
555825eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
555925eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
556025eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
556125eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
556225eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
556325eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
556425eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
556525eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
556625eaba2fSLydia Wang 
556725eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
556825eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
556925eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
557025eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
557125eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
557225eaba2fSLydia Wang 
557325eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
557425eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
557525eaba2fSLydia Wang 
557625eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
557725eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
557825eaba2fSLydia Wang 	{ }
557925eaba2fSLydia Wang };
558025eaba2fSLydia Wang 
558125eaba2fSLydia Wang 
558225eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
558325eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
558425eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
558525eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
558625eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
558725eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
558825eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
558925eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
559025eaba2fSLydia Wang 	{ }
559125eaba2fSLydia Wang };
559225eaba2fSLydia Wang 
559325eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
559425eaba2fSLydia Wang 	.substreams = 2,
559525eaba2fSLydia Wang 	.channels_min = 2,
559625eaba2fSLydia Wang 	.channels_max = 2,
559725eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
559825eaba2fSLydia Wang 	.ops = {
559925eaba2fSLydia Wang 		.open = via_playback_pcm_open,
560025eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
560125eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
560225eaba2fSLydia Wang 		.close = via_pcm_open_close,
560325eaba2fSLydia Wang 	},
560425eaba2fSLydia Wang };
560525eaba2fSLydia Wang 
560625eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
560725eaba2fSLydia Wang 	.substreams = 2,
560825eaba2fSLydia Wang 	.channels_min = 2,
560925eaba2fSLydia Wang 	.channels_max = 2,
561025eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
561125eaba2fSLydia Wang 	.ops = {
561225eaba2fSLydia Wang 		.open = via_pcm_open_close,
561325eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
561425eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
561525eaba2fSLydia Wang 		.close = via_pcm_open_close,
561625eaba2fSLydia Wang 	},
561725eaba2fSLydia Wang };
561825eaba2fSLydia Wang 
561925eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
562025eaba2fSLydia Wang 	.substreams = 1,
562125eaba2fSLydia Wang 	.channels_min = 2,
562225eaba2fSLydia Wang 	.channels_max = 2,
562325eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
562425eaba2fSLydia Wang 	.ops = {
562525eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
562625eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
562725eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
562825eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
562925eaba2fSLydia Wang 	},
563025eaba2fSLydia Wang };
563125eaba2fSLydia Wang 
563225eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
563325eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
563425eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
563525eaba2fSLydia Wang {
563625eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
563725eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
563825eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
563925eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
564025eaba2fSLydia Wang 	return 0;
564125eaba2fSLydia Wang }
564225eaba2fSLydia Wang 
564325eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
564425eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
564525eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
564625eaba2fSLydia Wang {
564725eaba2fSLydia Wang 	int err;
564825eaba2fSLydia Wang 
564925eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
565025eaba2fSLydia Wang 		return -1;
565125eaba2fSLydia Wang 
565225eaba2fSLydia Wang 
565325eaba2fSLydia Wang 	/* Line-Out: PortE */
565425eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
565525eaba2fSLydia Wang 			      "Master Front Playback Volume",
565625eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
565725eaba2fSLydia Wang 	if (err < 0)
565825eaba2fSLydia Wang 		return err;
565925eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
566025eaba2fSLydia Wang 			      "Master Front Playback Switch",
566125eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
566225eaba2fSLydia Wang 	if (err < 0)
566325eaba2fSLydia Wang 		return err;
566425eaba2fSLydia Wang 
566525eaba2fSLydia Wang 	return 0;
566625eaba2fSLydia Wang }
566725eaba2fSLydia Wang 
566825eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
566925eaba2fSLydia Wang {
567025eaba2fSLydia Wang 	int err;
567125eaba2fSLydia Wang 
567225eaba2fSLydia Wang 	if (!pin)
567325eaba2fSLydia Wang 		return 0;
567425eaba2fSLydia Wang 
567525eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
567625eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
567725eaba2fSLydia Wang 
567825eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
567925eaba2fSLydia Wang 			      "Headphone Playback Volume",
568025eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
568125eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
568225eaba2fSLydia Wang 	if (err < 0)
568325eaba2fSLydia Wang 		return err;
568425eaba2fSLydia Wang 
568525eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
568625eaba2fSLydia Wang 			      "Headphone Playback Switch",
568725eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
568825eaba2fSLydia Wang 	if (err < 0)
568925eaba2fSLydia Wang 		return err;
569025eaba2fSLydia Wang 
569125eaba2fSLydia Wang 	create_hp_imux(spec);
569225eaba2fSLydia Wang 	return 0;
569325eaba2fSLydia Wang }
569425eaba2fSLydia Wang 
569525eaba2fSLydia Wang /* create playback/capture controls for input pins */
569625eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec,
569725eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
569825eaba2fSLydia Wang {
569925eaba2fSLydia Wang 	static char *labels[] = {
570025eaba2fSLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
570125eaba2fSLydia Wang 	};
570225eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
570325eaba2fSLydia Wang 	int i, err, idx = 0;
570425eaba2fSLydia Wang 
570525eaba2fSLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
570625eaba2fSLydia Wang 		if (!cfg->input_pins[i])
570725eaba2fSLydia Wang 			continue;
570825eaba2fSLydia Wang 
570925eaba2fSLydia Wang 		switch (cfg->input_pins[i]) {
571025eaba2fSLydia Wang 		case 0x2b: /* Mic */
571125eaba2fSLydia Wang 			idx = 0;
571225eaba2fSLydia Wang 			break;
571325eaba2fSLydia Wang 
571425eaba2fSLydia Wang 		case 0x2a: /* Line In */
571525eaba2fSLydia Wang 			idx = 1;
571625eaba2fSLydia Wang 			break;
571725eaba2fSLydia Wang 
571825eaba2fSLydia Wang 		case 0x29: /* Front Mic */
571925eaba2fSLydia Wang 			idx = 2;
572025eaba2fSLydia Wang 			break;
572125eaba2fSLydia Wang 		}
572225eaba2fSLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
572325eaba2fSLydia Wang 		if (err < 0)
572425eaba2fSLydia Wang 			return err;
572525eaba2fSLydia Wang 		imux->items[imux->num_items].label = labels[i];
572625eaba2fSLydia Wang 		imux->items[imux->num_items].index = idx;
572725eaba2fSLydia Wang 		imux->num_items++;
572825eaba2fSLydia Wang 	}
572925eaba2fSLydia Wang 
573025eaba2fSLydia Wang 	/* build volume/mute control of loopback */
573125eaba2fSLydia Wang 	err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21);
573225eaba2fSLydia Wang 	if (err < 0)
573325eaba2fSLydia Wang 		return err;
573425eaba2fSLydia Wang 
573525eaba2fSLydia Wang 	/* for internal loopback recording select */
573625eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
573725eaba2fSLydia Wang 	imux->items[imux->num_items].index = 3;
573825eaba2fSLydia Wang 	imux->num_items++;
573925eaba2fSLydia Wang 
574025eaba2fSLydia Wang 	/* for digital mic select */
574125eaba2fSLydia Wang 	imux->items[imux->num_items].label = "Digital Mic";
574225eaba2fSLydia Wang 	imux->items[imux->num_items].index = 4;
574325eaba2fSLydia Wang 	imux->num_items++;
574425eaba2fSLydia Wang 
574525eaba2fSLydia Wang 	return 0;
574625eaba2fSLydia Wang }
574725eaba2fSLydia Wang 
574825eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
574925eaba2fSLydia Wang {
575025eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
575125eaba2fSLydia Wang 	int err;
575225eaba2fSLydia Wang 
575325eaba2fSLydia Wang 
575425eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
575525eaba2fSLydia Wang 	if (err < 0)
575625eaba2fSLydia Wang 		return err;
575725eaba2fSLydia Wang 
575825eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
575925eaba2fSLydia Wang 	if (err < 0)
576025eaba2fSLydia Wang 		return err;
576125eaba2fSLydia Wang 
576225eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
576325eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
576425eaba2fSLydia Wang 
576525eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
576625eaba2fSLydia Wang 	if (err < 0)
576725eaba2fSLydia Wang 		return err;
576825eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
576925eaba2fSLydia Wang 	if (err < 0)
577025eaba2fSLydia Wang 		return err;
577125eaba2fSLydia Wang 	err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg);
577225eaba2fSLydia Wang 	if (err < 0)
577325eaba2fSLydia Wang 		return err;
577425eaba2fSLydia Wang 
577525eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
577625eaba2fSLydia Wang 
577725eaba2fSLydia Wang 	fill_dig_outs(codec);
577825eaba2fSLydia Wang 
577925eaba2fSLydia Wang 	if (spec->kctls.list)
578025eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
578125eaba2fSLydia Wang 
578225eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
578325eaba2fSLydia Wang 
578425eaba2fSLydia Wang 	if (spec->hp_mux)
57855b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
578625eaba2fSLydia Wang 
578725eaba2fSLydia Wang 	return 1;
578825eaba2fSLydia Wang }
578925eaba2fSLydia Wang 
579025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
579125eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
579225eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
579325eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
579425eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
579525eaba2fSLydia Wang 	{ } /* end */
579625eaba2fSLydia Wang };
579725eaba2fSLydia Wang #endif
579825eaba2fSLydia Wang 
579925eaba2fSLydia Wang 
580025eaba2fSLydia Wang /* patch for vt2002P */
580125eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
580225eaba2fSLydia Wang {
580325eaba2fSLydia Wang 	struct via_spec *spec;
580425eaba2fSLydia Wang 	int err;
580525eaba2fSLydia Wang 
580625eaba2fSLydia Wang 	/* create a codec specific record */
58075b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
580825eaba2fSLydia Wang 	if (spec == NULL)
580925eaba2fSLydia Wang 		return -ENOMEM;
581025eaba2fSLydia Wang 
581125eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
581225eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
581325eaba2fSLydia Wang 	if (err < 0) {
581425eaba2fSLydia Wang 		via_free(codec);
581525eaba2fSLydia Wang 		return err;
581625eaba2fSLydia Wang 	} else if (!err) {
581725eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
581825eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
581925eaba2fSLydia Wang 	}
582025eaba2fSLydia Wang 
582125eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
582225eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
582325eaba2fSLydia Wang 
582425eaba2fSLydia Wang 	spec->stream_name_analog = "VT2002P Analog";
582525eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
582625eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
582725eaba2fSLydia Wang 
582825eaba2fSLydia Wang 	spec->stream_name_digital = "VT2002P Digital";
582925eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
583025eaba2fSLydia Wang 
583125eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
583225eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
583325eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
583425eaba2fSLydia Wang 		get_mux_nids(codec);
583525eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
583625eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
583725eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
583825eaba2fSLydia Wang 		spec->num_mixers++;
583925eaba2fSLydia Wang 	}
584025eaba2fSLydia Wang 
584125eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
584225eaba2fSLydia Wang 
584325eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
58440f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
584525eaba2fSLydia Wang 
584625eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
584725eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
584825eaba2fSLydia Wang #endif
584925eaba2fSLydia Wang 
585025eaba2fSLydia Wang 	return 0;
585125eaba2fSLydia Wang }
5852ab6734e7SLydia Wang 
5853ab6734e7SLydia Wang /* for vt1812 */
5854ab6734e7SLydia Wang 
5855ab6734e7SLydia Wang /* capture mixer elements */
5856ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5857ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5858ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5859ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5860ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5861ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5862ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5863ab6734e7SLydia Wang 		       HDA_INPUT),
5864ab6734e7SLydia Wang 	{
5865ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5866ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5867ab6734e7SLydia Wang 		 * So call somewhat different..
5868ab6734e7SLydia Wang 		 */
5869ab6734e7SLydia Wang 		.name = "Input Source",
5870ab6734e7SLydia Wang 		.count = 2,
5871ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5872ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5873ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5874ab6734e7SLydia Wang 	},
5875ab6734e7SLydia Wang 	{ } /* end */
5876ab6734e7SLydia Wang };
5877ab6734e7SLydia Wang 
5878ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = {
5879ab6734e7SLydia Wang 	/*
5880ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5881ab6734e7SLydia Wang 	 */
5882ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5883ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5884ab6734e7SLydia Wang 
5885ab6734e7SLydia Wang 
5886ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5887ab6734e7SLydia Wang 	 * mixer widget
5888ab6734e7SLydia Wang 	 */
5889ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5890ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5891ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5892ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5893ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5894ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5895ab6734e7SLydia Wang 
5896ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5897ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5898ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5899ab6734e7SLydia Wang 
5900ab6734e7SLydia Wang 	/* PW9 Output enable */
5901ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5902ab6734e7SLydia Wang 
5903ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5904ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5905ab6734e7SLydia Wang 
5906ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5907ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5908ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5909ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5910ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5911ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5912ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5913ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5914ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5915ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5916ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5917ab6734e7SLydia Wang 
5918ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5919ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5920ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5921ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5922ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5923ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5924ab6734e7SLydia Wang 
5925ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5926ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5927ab6734e7SLydia Wang 	{ }
5928ab6734e7SLydia Wang };
5929ab6734e7SLydia Wang 
5930ab6734e7SLydia Wang 
5931ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = {
5932ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5933ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5934ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5935ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5936ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5937ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5938ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5939ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5940ab6734e7SLydia Wang 	{ }
5941ab6734e7SLydia Wang };
5942ab6734e7SLydia Wang 
5943ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5944ab6734e7SLydia Wang 	.substreams = 2,
5945ab6734e7SLydia Wang 	.channels_min = 2,
5946ab6734e7SLydia Wang 	.channels_max = 2,
5947ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5948ab6734e7SLydia Wang 	.ops = {
5949ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5950ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5951ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5952ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5953ab6734e7SLydia Wang 	},
5954ab6734e7SLydia Wang };
5955ab6734e7SLydia Wang 
5956ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5957ab6734e7SLydia Wang 	.substreams = 2,
5958ab6734e7SLydia Wang 	.channels_min = 2,
5959ab6734e7SLydia Wang 	.channels_max = 2,
5960ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5961ab6734e7SLydia Wang 	.ops = {
5962ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5963ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5964ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5965ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5966ab6734e7SLydia Wang 	},
5967ab6734e7SLydia Wang };
5968ab6734e7SLydia Wang 
5969ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5970ab6734e7SLydia Wang 	.substreams = 1,
5971ab6734e7SLydia Wang 	.channels_min = 2,
5972ab6734e7SLydia Wang 	.channels_max = 2,
5973ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5974ab6734e7SLydia Wang 	.ops = {
5975ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5976ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5977ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5978ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5979ab6734e7SLydia Wang 	},
5980ab6734e7SLydia Wang };
5981ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5982ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5983ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5984ab6734e7SLydia Wang {
5985ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5986ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5987ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5988ab6734e7SLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5989ab6734e7SLydia Wang 	return 0;
5990ab6734e7SLydia Wang }
5991ab6734e7SLydia Wang 
5992ab6734e7SLydia Wang 
5993ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5994ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5995ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5996ab6734e7SLydia Wang {
5997ab6734e7SLydia Wang 	int err;
5998ab6734e7SLydia Wang 
5999ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
6000ab6734e7SLydia Wang 		return -1;
6001ab6734e7SLydia Wang 
6002ab6734e7SLydia Wang 	/* Line-Out: PortE */
6003ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
6004ab6734e7SLydia Wang 			      "Master Front Playback Volume",
6005ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
6006ab6734e7SLydia Wang 	if (err < 0)
6007ab6734e7SLydia Wang 		return err;
6008ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
6009ab6734e7SLydia Wang 			      "Master Front Playback Switch",
6010ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
6011ab6734e7SLydia Wang 	if (err < 0)
6012ab6734e7SLydia Wang 		return err;
6013ab6734e7SLydia Wang 
6014ab6734e7SLydia Wang 	return 0;
6015ab6734e7SLydia Wang }
6016ab6734e7SLydia Wang 
6017ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
6018ab6734e7SLydia Wang {
6019ab6734e7SLydia Wang 	int err;
6020ab6734e7SLydia Wang 
6021ab6734e7SLydia Wang 	if (!pin)
6022ab6734e7SLydia Wang 		return 0;
6023ab6734e7SLydia Wang 
6024ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
6025ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
6026ab6734e7SLydia Wang 
6027ab6734e7SLydia Wang 
6028ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
6029ab6734e7SLydia Wang 			      "Headphone Playback Volume",
6030ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
6031ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
6032ab6734e7SLydia Wang 	if (err < 0)
6033ab6734e7SLydia Wang 		return err;
6034ab6734e7SLydia Wang 
6035ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
6036ab6734e7SLydia Wang 			      "Headphone Playback Switch",
6037ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
6038ab6734e7SLydia Wang 	if (err < 0)
6039ab6734e7SLydia Wang 		return err;
6040ab6734e7SLydia Wang 
6041ab6734e7SLydia Wang 	create_hp_imux(spec);
6042ab6734e7SLydia Wang 	return 0;
6043ab6734e7SLydia Wang }
6044ab6734e7SLydia Wang 
6045ab6734e7SLydia Wang /* create playback/capture controls for input pins */
6046ab6734e7SLydia Wang static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec,
6047ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
6048ab6734e7SLydia Wang {
6049ab6734e7SLydia Wang 	static char *labels[] = {
6050ab6734e7SLydia Wang 		"Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL
6051ab6734e7SLydia Wang 	};
6052ab6734e7SLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
6053ab6734e7SLydia Wang 	int i, err, idx = 0;
6054ab6734e7SLydia Wang 
6055ab6734e7SLydia Wang 	for (i = 0; i < AUTO_PIN_LAST; i++) {
6056ab6734e7SLydia Wang 		if (!cfg->input_pins[i])
6057ab6734e7SLydia Wang 			continue;
6058ab6734e7SLydia Wang 
6059ab6734e7SLydia Wang 		switch (cfg->input_pins[i]) {
6060ab6734e7SLydia Wang 		case 0x2b: /* Mic */
6061ab6734e7SLydia Wang 			idx = 0;
6062ab6734e7SLydia Wang 			break;
6063ab6734e7SLydia Wang 
6064ab6734e7SLydia Wang 		case 0x2a: /* Line In */
6065ab6734e7SLydia Wang 			idx = 1;
6066ab6734e7SLydia Wang 			break;
6067ab6734e7SLydia Wang 
6068ab6734e7SLydia Wang 		case 0x29: /* Front Mic */
6069ab6734e7SLydia Wang 			idx = 2;
6070ab6734e7SLydia Wang 			break;
6071ab6734e7SLydia Wang 		}
6072ab6734e7SLydia Wang 		err = via_new_analog_input(spec, labels[i], idx, 0x21);
6073ab6734e7SLydia Wang 		if (err < 0)
6074ab6734e7SLydia Wang 			return err;
6075ab6734e7SLydia Wang 		imux->items[imux->num_items].label = labels[i];
6076ab6734e7SLydia Wang 		imux->items[imux->num_items].index = idx;
6077ab6734e7SLydia Wang 		imux->num_items++;
6078ab6734e7SLydia Wang 	}
6079ab6734e7SLydia Wang 	/* build volume/mute control of loopback */
6080ab6734e7SLydia Wang 	err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21);
6081ab6734e7SLydia Wang 	if (err < 0)
6082ab6734e7SLydia Wang 		return err;
6083ab6734e7SLydia Wang 
6084ab6734e7SLydia Wang 	/* for internal loopback recording select */
6085ab6734e7SLydia Wang 	imux->items[imux->num_items].label = "Stereo Mixer";
6086ab6734e7SLydia Wang 	imux->items[imux->num_items].index = 5;
6087ab6734e7SLydia Wang 	imux->num_items++;
6088ab6734e7SLydia Wang 
6089ab6734e7SLydia Wang 	/* for digital mic select */
6090ab6734e7SLydia Wang 	imux->items[imux->num_items].label = "Digital Mic";
6091ab6734e7SLydia Wang 	imux->items[imux->num_items].index = 6;
6092ab6734e7SLydia Wang 	imux->num_items++;
6093ab6734e7SLydia Wang 
6094ab6734e7SLydia Wang 	return 0;
6095ab6734e7SLydia Wang }
6096ab6734e7SLydia Wang 
6097ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
6098ab6734e7SLydia Wang {
6099ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
6100ab6734e7SLydia Wang 	int err;
6101ab6734e7SLydia Wang 
6102ab6734e7SLydia Wang 
6103ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
6104ab6734e7SLydia Wang 	if (err < 0)
6105ab6734e7SLydia Wang 		return err;
6106ab6734e7SLydia Wang 	fill_dig_outs(codec);
6107ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
6108ab6734e7SLydia Wang 	if (err < 0)
6109ab6734e7SLydia Wang 		return err;
6110ab6734e7SLydia Wang 
6111ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
6112ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
6113ab6734e7SLydia Wang 
6114ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
6115ab6734e7SLydia Wang 	if (err < 0)
6116ab6734e7SLydia Wang 		return err;
6117ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
6118ab6734e7SLydia Wang 	if (err < 0)
6119ab6734e7SLydia Wang 		return err;
6120ab6734e7SLydia Wang 	err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg);
6121ab6734e7SLydia Wang 	if (err < 0)
6122ab6734e7SLydia Wang 		return err;
6123ab6734e7SLydia Wang 
6124ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
6125ab6734e7SLydia Wang 
6126ab6734e7SLydia Wang 	fill_dig_outs(codec);
6127ab6734e7SLydia Wang 
6128ab6734e7SLydia Wang 	if (spec->kctls.list)
6129ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
6130ab6734e7SLydia Wang 
6131ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
6132ab6734e7SLydia Wang 
6133ab6734e7SLydia Wang 	if (spec->hp_mux)
61345b0cb1d8SJaroslav Kysela 		via_hp_build(spec);
6135ab6734e7SLydia Wang 
6136ab6734e7SLydia Wang 	return 1;
6137ab6734e7SLydia Wang }
6138ab6734e7SLydia Wang 
6139ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6140ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = {
6141ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
6142ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
6143ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
6144ab6734e7SLydia Wang 	{ } /* end */
6145ab6734e7SLydia Wang };
6146ab6734e7SLydia Wang #endif
6147ab6734e7SLydia Wang 
6148ab6734e7SLydia Wang 
6149ab6734e7SLydia Wang /* patch for vt1812 */
6150ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
6151ab6734e7SLydia Wang {
6152ab6734e7SLydia Wang 	struct via_spec *spec;
6153ab6734e7SLydia Wang 	int err;
6154ab6734e7SLydia Wang 
6155ab6734e7SLydia Wang 	/* create a codec specific record */
61565b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
6157ab6734e7SLydia Wang 	if (spec == NULL)
6158ab6734e7SLydia Wang 		return -ENOMEM;
6159ab6734e7SLydia Wang 
6160ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
6161ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
6162ab6734e7SLydia Wang 	if (err < 0) {
6163ab6734e7SLydia Wang 		via_free(codec);
6164ab6734e7SLydia Wang 		return err;
6165ab6734e7SLydia Wang 	} else if (!err) {
6166ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
6167ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
6168ab6734e7SLydia Wang 	}
6169ab6734e7SLydia Wang 
6170ab6734e7SLydia Wang 
6171ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
6172ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6173ab6734e7SLydia Wang 
6174ab6734e7SLydia Wang 	spec->stream_name_analog = "VT1812 Analog";
6175ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6176ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6177ab6734e7SLydia Wang 
6178ab6734e7SLydia Wang 	spec->stream_name_digital = "VT1812 Digital";
6179ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6180ab6734e7SLydia Wang 
6181ab6734e7SLydia Wang 
6182ab6734e7SLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
6183ab6734e7SLydia Wang 		spec->adc_nids = vt1812_adc_nids;
6184ab6734e7SLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6185ab6734e7SLydia Wang 		get_mux_nids(codec);
6186ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
6187ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
6188ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6189ab6734e7SLydia Wang 		spec->num_mixers++;
6190ab6734e7SLydia Wang 	}
6191ab6734e7SLydia Wang 
6192ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
6193ab6734e7SLydia Wang 
6194ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
61950f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
6196ab6734e7SLydia Wang 
6197ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6198ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
6199ab6734e7SLydia Wang #endif
6200ab6734e7SLydia Wang 
6201ab6734e7SLydia Wang 	return 0;
6202ab6734e7SLydia Wang }
6203ab6734e7SLydia Wang 
6204c577b8a1SJoseph Chan /*
6205c577b8a1SJoseph Chan  * patch entries
6206c577b8a1SJoseph Chan  */
62071289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
62083218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
62093218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
62103218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
62113218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
62123218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
6213f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
62143218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
6215f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
62163218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
6217f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
62183218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
6219f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
62203218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
6221f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62223218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
6223f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62243218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
6225f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62263218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
6227f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62283218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
6229f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62303218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
6231f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62323218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
6233f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62343218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6235f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62363218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6237f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62383218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6239f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62403218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6241f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62423218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6243f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62443218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6245d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62463218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6247d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62483218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6249d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62503218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6251d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62523218c178STakashi Iwai 	{ .id = 0x11064397, .name = "VT1708S",
6253d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62543218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6255d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62563218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6257d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62583218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6259d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62603218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6261d949cac1SHarald Welte 	  .patch = patch_vt1702},
62623218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6263d949cac1SHarald Welte 	  .patch = patch_vt1702},
62643218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6265d949cac1SHarald Welte 	  .patch = patch_vt1702},
62663218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6267d949cac1SHarald Welte 	  .patch = patch_vt1702},
62683218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6269d949cac1SHarald Welte 	  .patch = patch_vt1702},
62703218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6271d949cac1SHarald Welte 	  .patch = patch_vt1702},
62723218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6273d949cac1SHarald Welte 	  .patch = patch_vt1702},
62743218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6275d949cac1SHarald Welte 	  .patch = patch_vt1702},
6276eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6277eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6278eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6279eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6280bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6281bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6282bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6283bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6284f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6285f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6286f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6287f3db423dSLydia Wang 	  .patch = patch_vt1716S},
628825eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
628925eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6290ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
629136dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
629236dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
6293c577b8a1SJoseph Chan 	{} /* terminator */
6294c577b8a1SJoseph Chan };
62951289e9e8STakashi Iwai 
62961289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
62971289e9e8STakashi Iwai 
62981289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
62991289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
63001289e9e8STakashi Iwai 	.owner = THIS_MODULE,
63011289e9e8STakashi Iwai };
63021289e9e8STakashi Iwai 
63031289e9e8STakashi Iwai MODULE_LICENSE("GPL");
63041289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
63051289e9e8STakashi Iwai 
63061289e9e8STakashi Iwai static int __init patch_via_init(void)
63071289e9e8STakashi Iwai {
63081289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
63091289e9e8STakashi Iwai }
63101289e9e8STakashi Iwai 
63111289e9e8STakashi Iwai static void __exit patch_via_exit(void)
63121289e9e8STakashi Iwai {
63131289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
63141289e9e8STakashi Iwai }
63151289e9e8STakashi Iwai 
63161289e9e8STakashi Iwai module_init(patch_via_init)
63171289e9e8STakashi Iwai module_exit(patch_via_exit)
6318