xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 82673bc8950b869f01f9fd517f1c2286e0e49f44)
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,
10111890956SLydia Wang 	VT1802,
102d7426329SHarald Welte 	CODEC_TYPES,
103d7426329SHarald Welte };
104d7426329SHarald Welte 
10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
10611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
10711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
10811890956SLydia Wang 	 (spec)->codec_type == VT1802)
10911890956SLydia Wang 
1101f2e99feSLydia Wang struct via_spec {
1111f2e99feSLydia Wang 	/* codec parameterization */
11290dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1131f2e99feSLydia Wang 	unsigned int num_mixers;
1141f2e99feSLydia Wang 
11590dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1161f2e99feSLydia Wang 	unsigned int num_iverbs;
1171f2e99feSLydia Wang 
118*82673bc8STakashi Iwai 	char stream_name_analog[32];
11990dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12090dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1211f2e99feSLydia Wang 
122*82673bc8STakashi Iwai 	char stream_name_digital[32];
12390dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
12490dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1251f2e99feSLydia Wang 
1261f2e99feSLydia Wang 	/* playback */
1271f2e99feSLydia Wang 	struct hda_multi_out multiout;
1281f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1291f2e99feSLydia Wang 
1301f2e99feSLydia Wang 	/* capture */
1311f2e99feSLydia Wang 	unsigned int num_adc_nids;
132a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1331f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
1341f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1351f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1361f2e99feSLydia Wang 
1371f2e99feSLydia Wang 	/* capture source */
1381f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1391f2e99feSLydia Wang 	unsigned int cur_mux[3];
1401f2e99feSLydia Wang 
1411f2e99feSLydia Wang 	/* PCM information */
1421f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1431f2e99feSLydia Wang 
1441f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1451f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1461f2e99feSLydia Wang 	struct snd_array kctls;
1471f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1481f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1491f2e99feSLydia Wang 
1501f2e99feSLydia Wang 	/* HP mode source */
1511f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1521f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1531f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
1541f2e99feSLydia Wang 	unsigned int smart51_enabled;
155f3db423dSLydia Wang 	unsigned int dmic_enabled;
15624088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1571f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1581f2e99feSLydia Wang 
1591f2e99feSLydia Wang 	/* work to check hp jack state */
1601f2e99feSLydia Wang 	struct hda_codec *codec;
1611f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
162e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1631f2e99feSLydia Wang 	int vt1708_hp_present;
1643e95b9abSLydia Wang 
1653e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1663e95b9abSLydia Wang 
1671f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1681f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1691f2e99feSLydia Wang #endif
1701f2e99feSLydia Wang };
1711f2e99feSLydia Wang 
1720341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1735b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1745b0cb1d8SJaroslav Kysela {
1755b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1765b0cb1d8SJaroslav Kysela 
1775b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1785b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1795b0cb1d8SJaroslav Kysela 		return NULL;
1805b0cb1d8SJaroslav Kysela 
1815b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1825b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1830341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1840341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1850341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1860341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1875b0cb1d8SJaroslav Kysela 	return spec;
1885b0cb1d8SJaroslav Kysela }
1895b0cb1d8SJaroslav Kysela 
190744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
191d7426329SHarald Welte {
192744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
193d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
194d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
195d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
196d7426329SHarald Welte 
197d7426329SHarald Welte 	/* get codec type */
198d7426329SHarald Welte 	if (ven_id != 0x1106)
199d7426329SHarald Welte 		codec_type = UNKNOWN;
200d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
201d7426329SHarald Welte 		codec_type = VT1708;
202d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
203d7426329SHarald Welte 		codec_type = VT1709_10CH;
204d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
205d7426329SHarald Welte 		codec_type = VT1709_6CH;
206518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
207d7426329SHarald Welte 		codec_type = VT1708B_8CH;
208518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
209518bf3baSLydia Wang 			codec_type = VT1708BCE;
210518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
211d7426329SHarald Welte 		codec_type = VT1708B_4CH;
212d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
213d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
214d7426329SHarald Welte 		codec_type = VT1708S;
215d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
216d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
217d7426329SHarald Welte 		codec_type = VT1702;
218eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
219eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
220eb7188caSLydia Wang 		codec_type = VT1718S;
221f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
222f3db423dSLydia Wang 		codec_type = VT1716S;
223bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
224bb3c6bfcSLydia Wang 		codec_type = VT1718S;
22525eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
22625eaba2fSLydia Wang 		codec_type = VT2002P;
227ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
228ab6734e7SLydia Wang 		codec_type = VT1812;
22936dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
23036dd5c4aSLydia Wang 		codec_type = VT1708S;
23111890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
23211890956SLydia Wang 		codec_type = VT1802;
233d7426329SHarald Welte 	else
234d7426329SHarald Welte 		codec_type = UNKNOWN;
235d7426329SHarald Welte 	return codec_type;
236d7426329SHarald Welte };
237d7426329SHarald Welte 
238ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
23969e52a80SHarald Welte #define VIA_HP_EVENT		0x01
24069e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
241ec7e7e42SLydia Wang #define VIA_MONO_EVENT		0x03
242ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT	0x04
243ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT	0x05
24469e52a80SHarald Welte 
245c577b8a1SJoseph Chan enum {
246c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
247c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
248f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
24925eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
250c577b8a1SJoseph Chan };
251c577b8a1SJoseph Chan 
252c577b8a1SJoseph Chan enum {
253eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
254c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
255c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
256c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
257c577b8a1SJoseph Chan };
258c577b8a1SJoseph Chan 
259f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2601f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2611f2e99feSLydia Wang 
2621f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2631f2e99feSLydia Wang {
2641f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2651f2e99feSLydia Wang 		return;
2661f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
267e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2681f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2691f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2701f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2711f2e99feSLydia Wang }
2721f2e99feSLydia Wang 
2731f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2741f2e99feSLydia Wang {
2751f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2761f2e99feSLydia Wang 		return;
2771f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2781f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2791f2e99feSLydia Wang 		return;
2801f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
281e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2825b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2831f2e99feSLydia Wang }
284f5271101SLydia Wang 
2853e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2863e95b9abSLydia Wang {
2873e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2883e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2893e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2903e95b9abSLydia Wang }
29125eaba2fSLydia Wang 
292f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
293f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
294f5271101SLydia Wang {
295f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
296f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
297f5271101SLydia Wang 
2983e95b9abSLydia Wang 	set_widgets_power_state(codec);
299f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
3001f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3011f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3021f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3031f2e99feSLydia Wang 		else
3041f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3051f2e99feSLydia Wang 	}
306f5271101SLydia Wang 	return change;
307f5271101SLydia Wang }
308f5271101SLydia Wang 
309f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
310f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
311f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
312f5271101SLydia Wang 			.name = NULL,					\
313f5271101SLydia Wang 			.index = 0,					\
314f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
315f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
316f5271101SLydia Wang 			.put = analog_input_switch_put,			\
317f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
318f5271101SLydia Wang 
31925eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
32025eaba2fSLydia Wang 
32125eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
32225eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
32325eaba2fSLydia Wang {
32425eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
32625eaba2fSLydia Wang 	int i;
32725eaba2fSLydia Wang 	int change = 0;
32825eaba2fSLydia Wang 
32925eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
33025eaba2fSLydia Wang 	int lmute, rmute;
33125eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
33225eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
33325eaba2fSLydia Wang 		return change;
33425eaba2fSLydia Wang 	}
33525eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
33625eaba2fSLydia Wang 					      ucontrol);
33725eaba2fSLydia Wang 	/* Get mute value */
33825eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
33925eaba2fSLydia Wang 	valp++;
34025eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
34125eaba2fSLydia Wang 
34225eaba2fSLydia Wang 	/* Set hp pins */
34325eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
34425eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
34525eaba2fSLydia Wang 			snd_hda_codec_amp_update(
34625eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
34725eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
34825eaba2fSLydia Wang 				lmute);
34925eaba2fSLydia Wang 			snd_hda_codec_amp_update(
35025eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
35125eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35225eaba2fSLydia Wang 				rmute);
35325eaba2fSLydia Wang 		}
35425eaba2fSLydia Wang 	}
35525eaba2fSLydia Wang 
35625eaba2fSLydia Wang 	if (!lmute && !rmute) {
35725eaba2fSLydia Wang 		/* Line Outs */
35825eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
35925eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
36025eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
36125eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
36225eaba2fSLydia Wang 		/* Speakers */
36325eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
36425eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
36525eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
36625eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
36725eaba2fSLydia Wang 		/* unmute */
36825eaba2fSLydia Wang 		via_hp_bind_automute(codec);
36925eaba2fSLydia Wang 
37025eaba2fSLydia Wang 	} else {
37125eaba2fSLydia Wang 		if (lmute) {
37225eaba2fSLydia Wang 			/* Mute all left channels */
37325eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
37425eaba2fSLydia Wang 				snd_hda_codec_amp_update(
37525eaba2fSLydia Wang 					codec,
37625eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
37725eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37825eaba2fSLydia Wang 					lmute);
37925eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
38025eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38125eaba2fSLydia Wang 					codec,
38225eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
38325eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
38425eaba2fSLydia Wang 					lmute);
38525eaba2fSLydia Wang 		}
38625eaba2fSLydia Wang 		if (rmute) {
38725eaba2fSLydia Wang 			/* mute all right channels */
38825eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38925eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39025eaba2fSLydia Wang 					codec,
39125eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
39225eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39325eaba2fSLydia Wang 					rmute);
39425eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
39525eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39625eaba2fSLydia Wang 					codec,
39725eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
39825eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39925eaba2fSLydia Wang 					rmute);
40025eaba2fSLydia Wang 		}
40125eaba2fSLydia Wang 	}
40225eaba2fSLydia Wang 	return change;
40325eaba2fSLydia Wang }
40425eaba2fSLydia Wang 
40525eaba2fSLydia Wang #define BIND_PIN_MUTE							\
40625eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
40725eaba2fSLydia Wang 			.name = NULL,					\
40825eaba2fSLydia Wang 			.index = 0,					\
40925eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
41025eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
41125eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
41225eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
41325eaba2fSLydia Wang 
41490dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
415c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
416c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
417f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
41825eaba2fSLydia Wang 	BIND_PIN_MUTE,
419c577b8a1SJoseph Chan };
420c577b8a1SJoseph Chan 
421ab6734e7SLydia Wang 
422c577b8a1SJoseph Chan /* add dynamic controls */
423291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
424291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
425291c9e33STakashi Iwai 				const char *name)
426c577b8a1SJoseph Chan {
427c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
428c577b8a1SJoseph Chan 
429603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
430603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
431c577b8a1SJoseph Chan 	if (!knew)
432291c9e33STakashi Iwai 		return NULL;
433291c9e33STakashi Iwai 	*knew = *tmpl;
434291c9e33STakashi Iwai 	if (!name)
435291c9e33STakashi Iwai 		name = tmpl->name;
436291c9e33STakashi Iwai 	if (name) {
437c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
438c577b8a1SJoseph Chan 		if (!knew->name)
439291c9e33STakashi Iwai 			return NULL;
440291c9e33STakashi Iwai 	}
441291c9e33STakashi Iwai 	return knew;
442291c9e33STakashi Iwai }
443291c9e33STakashi Iwai 
444291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
445291c9e33STakashi Iwai 			     int idx, unsigned long val)
446291c9e33STakashi Iwai {
447291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
448291c9e33STakashi Iwai 
449291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
450291c9e33STakashi Iwai 	if (!knew)
451c577b8a1SJoseph Chan 		return -ENOMEM;
4524d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4535e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
454c577b8a1SJoseph Chan 	knew->private_value = val;
455c577b8a1SJoseph Chan 	return 0;
456c577b8a1SJoseph Chan }
457c577b8a1SJoseph Chan 
4587b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4597b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4607b315bb4STakashi Iwai 
461291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
4625b0cb1d8SJaroslav Kysela 
463603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
464603c4019STakashi Iwai {
465603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
466603c4019STakashi Iwai 
467603c4019STakashi Iwai 	if (spec->kctls.list) {
468603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
469603c4019STakashi Iwai 		int i;
470603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
471603c4019STakashi Iwai 			kfree(kctl[i].name);
472603c4019STakashi Iwai 	}
473603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
474603c4019STakashi Iwai }
475603c4019STakashi Iwai 
476c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4779510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4787b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
479c577b8a1SJoseph Chan {
480c577b8a1SJoseph Chan 	char name[32];
481c577b8a1SJoseph Chan 	int err;
482c577b8a1SJoseph Chan 
483c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4847b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
485c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
486c577b8a1SJoseph Chan 	if (err < 0)
487c577b8a1SJoseph Chan 		return err;
488c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
4897b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
490c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
491c577b8a1SJoseph Chan 	if (err < 0)
492c577b8a1SJoseph Chan 		return err;
493c577b8a1SJoseph Chan 	return 0;
494c577b8a1SJoseph Chan }
495c577b8a1SJoseph Chan 
496c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
497c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
498c577b8a1SJoseph Chan 					   int dac_idx)
499c577b8a1SJoseph Chan {
500c577b8a1SJoseph Chan 	/* set as output */
501c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
502c577b8a1SJoseph Chan 			    pin_type);
503c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
504c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
505d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
506d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
507d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
508c577b8a1SJoseph Chan }
509c577b8a1SJoseph Chan 
510c577b8a1SJoseph Chan 
511c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
512c577b8a1SJoseph Chan {
513c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
514c577b8a1SJoseph Chan 	int i;
515c577b8a1SJoseph Chan 
516c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
517c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
518c577b8a1SJoseph Chan 		if (nid)
519c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
520c577b8a1SJoseph Chan 	}
521c577b8a1SJoseph Chan }
522c577b8a1SJoseph Chan 
523c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
524c577b8a1SJoseph Chan {
525c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
526c577b8a1SJoseph Chan 	hda_nid_t pin;
52725eaba2fSLydia Wang 	int i;
528c577b8a1SJoseph Chan 
52925eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
53025eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
531c577b8a1SJoseph Chan 		if (pin) /* connect to front */
532c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
533c577b8a1SJoseph Chan 	}
53425eaba2fSLydia Wang }
535c577b8a1SJoseph Chan 
53632e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
53732e0191dSClemens Ladisch 
538c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
539c577b8a1SJoseph Chan {
540c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5417b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
54232e0191dSClemens Ladisch 	unsigned int ctl;
543c577b8a1SJoseph Chan 	int i;
544c577b8a1SJoseph Chan 
5457b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5467b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
54732e0191dSClemens Ladisch 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
54832e0191dSClemens Ladisch 			ctl = PIN_OUT;
54930649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
55032e0191dSClemens Ladisch 			ctl = PIN_VREF50;
55132e0191dSClemens Ladisch 		else
55232e0191dSClemens Ladisch 			ctl = PIN_IN;
553c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
55432e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
555c577b8a1SJoseph Chan 	}
556c577b8a1SJoseph Chan }
557f5271101SLydia Wang 
558f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
559f5271101SLydia Wang 				unsigned int *affected_parm)
560f5271101SLydia Wang {
561f5271101SLydia Wang 	unsigned parm;
562f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
563f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
564f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
565f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
5661564b287SLydia Wang 	struct via_spec *spec = codec->spec;
56724088a58STakashi Iwai 	unsigned present = 0;
56824088a58STakashi Iwai 
56924088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
57024088a58STakashi Iwai 	if (!no_presence)
57124088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
5721564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5731564b287SLydia Wang 	    || ((no_presence || present)
5741564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
575f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
576f5271101SLydia Wang 		parm = AC_PWRST_D0;
577f5271101SLydia Wang 	} else
578f5271101SLydia Wang 		parm = AC_PWRST_D3;
579f5271101SLydia Wang 
580f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
581f5271101SLydia Wang }
582f5271101SLydia Wang 
58324088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
58424088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
58524088a58STakashi Iwai {
58624088a58STakashi Iwai 	static const char * const texts[] = {
58724088a58STakashi Iwai 		"Disabled", "Enabled"
58824088a58STakashi Iwai 	};
58924088a58STakashi Iwai 
59024088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
59124088a58STakashi Iwai 	uinfo->count = 1;
59224088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
59324088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
59424088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
59524088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
59624088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
59724088a58STakashi Iwai 	return 0;
59824088a58STakashi Iwai }
59924088a58STakashi Iwai 
60024088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
60124088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
60224088a58STakashi Iwai {
60324088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
60424088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
60524088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
60624088a58STakashi Iwai 	return 0;
60724088a58STakashi Iwai }
60824088a58STakashi Iwai 
60924088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
61024088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
61124088a58STakashi Iwai {
61224088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
61324088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
61424088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
61524088a58STakashi Iwai 
61624088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
61724088a58STakashi Iwai 		return 0;
61824088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
61924088a58STakashi Iwai 	set_widgets_power_state(codec);
62024088a58STakashi Iwai 	return 1;
62124088a58STakashi Iwai }
62224088a58STakashi Iwai 
62324088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
62424088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
62524088a58STakashi Iwai 	.name = "Dynamic Power-Control",
62624088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
62724088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
62824088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
62924088a58STakashi Iwai };
63024088a58STakashi Iwai 
63124088a58STakashi Iwai 
632c577b8a1SJoseph Chan /*
633c577b8a1SJoseph Chan  * input MUX handling
634c577b8a1SJoseph Chan  */
635c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
636c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
637c577b8a1SJoseph Chan {
638c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
639c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
640c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
641c577b8a1SJoseph Chan }
642c577b8a1SJoseph Chan 
643c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
644c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
645c577b8a1SJoseph Chan {
646c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
647c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
648c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
649c577b8a1SJoseph Chan 
650c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
651c577b8a1SJoseph Chan 	return 0;
652c577b8a1SJoseph Chan }
653c577b8a1SJoseph Chan 
654c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
655c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
656c577b8a1SJoseph Chan {
657c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
658c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
659c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
660bff5fbf5SLydia Wang 	int ret;
661c577b8a1SJoseph Chan 
662337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
663337b9d02STakashi Iwai 		return -EINVAL;
664a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
665a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
666a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
667a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
668a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
669bff5fbf5SLydia Wang 
670bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
671bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
672bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
673a80e6e3cSLydia Wang 	/* update jack power state */
6743e95b9abSLydia Wang 	set_widgets_power_state(codec);
675a80e6e3cSLydia Wang 
676bff5fbf5SLydia Wang 	return ret;
677c577b8a1SJoseph Chan }
678c577b8a1SJoseph Chan 
6790aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6800aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6810aa62aefSHarald Welte {
6820aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6830aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6840aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6850aa62aefSHarald Welte }
6860aa62aefSHarald Welte 
6870aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
6880aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
6890aa62aefSHarald Welte {
6900aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6915b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
692eb7188caSLydia Wang 	unsigned int pinsel;
693eb7188caSLydia Wang 
694eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
695eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
6960aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
6970aa62aefSHarald Welte 				      0x00);
6980aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
6990aa62aefSHarald Welte 
7000aa62aefSHarald Welte 	return 0;
7010aa62aefSHarald Welte }
7020aa62aefSHarald Welte 
7030713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
7040713efebSLydia Wang {
7050713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
7060713efebSLydia Wang 	if (ctl) {
7070713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7080713efebSLydia Wang 		ctl->vd[0].access |= active
7090713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7100713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
7110713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
7120713efebSLydia Wang 	}
7130713efebSLydia Wang }
7140713efebSLydia Wang 
7155b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
7165b0cb1d8SJaroslav Kysela {
7175b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7185b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7195b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7205b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7215b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
722e87885feSLydia Wang 	case VT2002P:		return 0x19;
723e87885feSLydia Wang 	case VT1802:		return 0x15;
724e87885feSLydia Wang 	case VT1812:		return 0x15;
7255b0cb1d8SJaroslav Kysela 	default:		return 0;
7265b0cb1d8SJaroslav Kysela 	}
7275b0cb1d8SJaroslav Kysela }
7285b0cb1d8SJaroslav Kysela 
729cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
730cdc1784dSLydia Wang {
731cdc1784dSLydia Wang 	/* mute side channel */
732cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
733e87885feSLydia Wang 	unsigned int parm;
7345b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
735cdc1784dSLydia Wang 
736e87885feSLydia Wang 	if (sw3) {
737e87885feSLydia Wang 		if (VT2002P_COMPATIBLE(spec))
738e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
739e87885feSLydia Wang 			       AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
740e87885feSLydia Wang 		else
741e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
742e87885feSLydia Wang 			       AMP_OUT_MUTE : AMP_OUT_UNMUTE;
743e87885feSLydia Wang 		snd_hda_codec_write(codec, sw3, 0,
744e87885feSLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, parm);
745e87885feSLydia Wang 		if (spec->codec_type == VT1812)
746e87885feSLydia Wang 			snd_hda_codec_write(codec, 0x1d, 0,
747e87885feSLydia Wang 					    AC_VERB_SET_AMP_GAIN_MUTE, parm);
748e87885feSLydia Wang 	}
749cdc1784dSLydia Wang 	return 0;
750cdc1784dSLydia Wang }
751cdc1784dSLydia Wang 
7520aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7530aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7540aa62aefSHarald Welte {
7550aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7560aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7575b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7580aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
759cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
760cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
761cdc1784dSLydia Wang 		? 1 : 0;
762ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
763ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
764ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
765ce0e5a9eSLydia Wang 	else
766ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
767ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7680aa62aefSHarald Welte 
769ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
770ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
771ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
772cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
773cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
774cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7750aa62aefSHarald Welte 					   0, 0, 0);
7760aa62aefSHarald Welte 
777cdc1784dSLydia Wang 	update_side_mute_status(codec);
7780713efebSLydia Wang 	/* update HP volume/swtich active state */
7790713efebSLydia Wang 	if (spec->codec_type == VT1708S
780eb7188caSLydia Wang 	    || spec->codec_type == VT1702
781f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
78225eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
78311890956SLydia Wang 	    || VT2002P_COMPATIBLE(spec)) {
7840713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7850713efebSLydia Wang 			     spec->hp_independent_mode);
7860713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
7870713efebSLydia Wang 			     spec->hp_independent_mode);
7880713efebSLydia Wang 	}
789ce0e5a9eSLydia Wang 	/* update jack power state */
7903e95b9abSLydia Wang 	set_widgets_power_state(codec);
7910aa62aefSHarald Welte 	return 0;
7920aa62aefSHarald Welte }
7930aa62aefSHarald Welte 
79490dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = {
7950aa62aefSHarald Welte 	{
7960aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7970aa62aefSHarald Welte 		.name = "Independent HP",
7980aa62aefSHarald Welte 		.info = via_independent_hp_info,
7990aa62aefSHarald Welte 		.get = via_independent_hp_get,
8000aa62aefSHarald Welte 		.put = via_independent_hp_put,
8010aa62aefSHarald Welte 	},
8025b0cb1d8SJaroslav Kysela 	{
8035b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
8045b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
8055b0cb1d8SJaroslav Kysela 	},
8060aa62aefSHarald Welte };
8070aa62aefSHarald Welte 
8083d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
8095b0cb1d8SJaroslav Kysela {
8103d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8115b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8125b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8133d83e577STakashi Iwai 	int nums;
8143d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
8155b0cb1d8SJaroslav Kysela 
8165b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
8175b0cb1d8SJaroslav Kysela 	case VT1718S:
8185b0cb1d8SJaroslav Kysela 		nid = 0x34;
8195b0cb1d8SJaroslav Kysela 		break;
8205b0cb1d8SJaroslav Kysela 	case VT2002P:
82111890956SLydia Wang 	case VT1802:
8225b0cb1d8SJaroslav Kysela 		nid = 0x35;
8235b0cb1d8SJaroslav Kysela 		break;
8245b0cb1d8SJaroslav Kysela 	case VT1812:
8255b0cb1d8SJaroslav Kysela 		nid = 0x3d;
8265b0cb1d8SJaroslav Kysela 		break;
8275b0cb1d8SJaroslav Kysela 	default:
8285b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
8295b0cb1d8SJaroslav Kysela 		break;
8305b0cb1d8SJaroslav Kysela 	}
8315b0cb1d8SJaroslav Kysela 
832ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
833ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
834ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8353d83e577STakashi Iwai 		if (nums <= 1)
8363d83e577STakashi Iwai 			return 0;
837ee3c35c0SLydia Wang 	}
8383d83e577STakashi Iwai 
8393d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8403d83e577STakashi Iwai 	if (knew == NULL)
8413d83e577STakashi Iwai 		return -ENOMEM;
8423d83e577STakashi Iwai 
8435b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8445b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8455b0cb1d8SJaroslav Kysela 
8465b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8475b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8485b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8495b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8505b0cb1d8SJaroslav Kysela 
8515b0cb1d8SJaroslav Kysela 	return 0;
8525b0cb1d8SJaroslav Kysela }
8535b0cb1d8SJaroslav Kysela 
8541564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8551564b287SLydia Wang {
8561564b287SLydia Wang 	int i;
8571564b287SLydia Wang 	struct snd_ctl_elem_id id;
858525566cbSLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
859525566cbSLydia Wang 	struct snd_kcontrol *ctl;
8601564b287SLydia Wang 
8611564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8621564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8631564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8641564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
865525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
866525566cbSLydia Wang 		if (ctl)
867525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
868525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
869525566cbSLydia Wang 					&ctl->id);
8701564b287SLydia Wang 	}
8711564b287SLydia Wang }
8721564b287SLydia Wang 
8731564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8741564b287SLydia Wang {
8751564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8761564b287SLydia Wang 	hda_nid_t  nid_mixer;
8771564b287SLydia Wang 	int start_idx;
8781564b287SLydia Wang 	int end_idx;
8791564b287SLydia Wang 	int i;
8801564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8811564b287SLydia Wang 	switch (spec->codec_type) {
8821564b287SLydia Wang 	case VT1708:
8831564b287SLydia Wang 		nid_mixer = 0x17;
8841564b287SLydia Wang 		start_idx = 2;
8851564b287SLydia Wang 		end_idx = 4;
8861564b287SLydia Wang 		break;
8871564b287SLydia Wang 	case VT1709_10CH:
8881564b287SLydia Wang 	case VT1709_6CH:
8891564b287SLydia Wang 		nid_mixer = 0x18;
8901564b287SLydia Wang 		start_idx = 2;
8911564b287SLydia Wang 		end_idx = 4;
8921564b287SLydia Wang 		break;
8931564b287SLydia Wang 	case VT1708B_8CH:
8941564b287SLydia Wang 	case VT1708B_4CH:
8951564b287SLydia Wang 	case VT1708S:
896f3db423dSLydia Wang 	case VT1716S:
8971564b287SLydia Wang 		nid_mixer = 0x16;
8981564b287SLydia Wang 		start_idx = 2;
8991564b287SLydia Wang 		end_idx = 4;
9001564b287SLydia Wang 		break;
901ab657e0cSLydia Wang 	case VT1718S:
902ab657e0cSLydia Wang 		nid_mixer = 0x21;
903ab657e0cSLydia Wang 		start_idx = 1;
904ab657e0cSLydia Wang 		end_idx = 3;
905ab657e0cSLydia Wang 		break;
9061564b287SLydia Wang 	default:
9071564b287SLydia Wang 		return;
9081564b287SLydia Wang 	}
9091564b287SLydia Wang 	/* check AA path's mute status */
9101564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
9111564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
9121564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
9131564b287SLydia Wang 					 HDA_AMP_MUTE, val);
9141564b287SLydia Wang 	}
9151564b287SLydia Wang }
9161564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
9171564b287SLydia Wang {
9187b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9197b315bb4STakashi Iwai 	int i;
9207b315bb4STakashi Iwai 
9217b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9227b315bb4STakashi Iwai 		if (pin == cfg->inputs[i].pin)
92386e2959aSTakashi Iwai 			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
9241564b287SLydia Wang 	}
9257b315bb4STakashi Iwai 	return 0;
9261564b287SLydia Wang }
9271564b287SLydia Wang 
9281564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
9291564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
9301564b287SLydia Wang {
9311564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9321564b287SLydia Wang 	uinfo->count = 1;
9331564b287SLydia Wang 	uinfo->value.integer.min = 0;
9341564b287SLydia Wang 	uinfo->value.integer.max = 1;
9351564b287SLydia Wang 	return 0;
9361564b287SLydia Wang }
9371564b287SLydia Wang 
9381564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9391564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9401564b287SLydia Wang {
9411564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9421564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9437b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9441564b287SLydia Wang 	int on = 1;
9451564b287SLydia Wang 	int i;
9461564b287SLydia Wang 
9477b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9487b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9497b315bb4STakashi Iwai 		int ctl = snd_hda_codec_read(codec, nid, 0,
9507b315bb4STakashi Iwai 					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
95186e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9527b315bb4STakashi Iwai 			continue;
95386e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9547b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9551564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
9567b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9571564b287SLydia Wang 			on = 0;
9581564b287SLydia Wang 	}
9591564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9601564b287SLydia Wang 	return 0;
9611564b287SLydia Wang }
9621564b287SLydia Wang 
9631564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9641564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9651564b287SLydia Wang {
9661564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9671564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9687b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9691564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9701564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9711564b287SLydia Wang 	int i;
9721564b287SLydia Wang 
9737b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9747b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9757b315bb4STakashi Iwai 		unsigned int parm;
9767b315bb4STakashi Iwai 
97786e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9787b315bb4STakashi Iwai 			continue;
97986e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9807b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9811564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
9827b315bb4STakashi Iwai 
9837b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
9841564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9851564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
9861564b287SLydia Wang 		parm |= out_in;
9871564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
9881564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
9891564b287SLydia Wang 				    parm);
9901564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
9911564b287SLydia Wang 			mute_aa_path(codec, 1);
9921564b287SLydia Wang 			notify_aa_path_ctls(codec);
9931564b287SLydia Wang 		}
9947b315bb4STakashi Iwai 		if (spec->codec_type == VT1718S) {
995eb7188caSLydia Wang 			snd_hda_codec_amp_stereo(
996eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
997eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
9981564b287SLydia Wang 		}
99986e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
1000f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1001f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
10021564b287SLydia Wang 				/* input = index 1 (AOW3) */
10031564b287SLydia Wang 				snd_hda_codec_write(
10041564b287SLydia Wang 					codec, nid, 0,
10051564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
10061564b287SLydia Wang 				snd_hda_codec_amp_stereo(
10071564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
10081564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
10091564b287SLydia Wang 			}
10101564b287SLydia Wang 		}
10111564b287SLydia Wang 	}
10121564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
10133e95b9abSLydia Wang 	set_widgets_power_state(codec);
10141564b287SLydia Wang 	return 1;
10151564b287SLydia Wang }
10161564b287SLydia Wang 
10175f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
10181564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10191564b287SLydia Wang 	.name = "Smart 5.1",
10201564b287SLydia Wang 	.count = 1,
10211564b287SLydia Wang 	.info = via_smart51_info,
10221564b287SLydia Wang 	.get = via_smart51_get,
10231564b287SLydia Wang 	.put = via_smart51_put,
10241564b287SLydia Wang };
10251564b287SLydia Wang 
10265b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
10275b0cb1d8SJaroslav Kysela {
10285b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10297b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10305b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10315b0cb1d8SJaroslav Kysela 	int i;
10325b0cb1d8SJaroslav Kysela 
1033cb34c207SLydia Wang 	if (!cfg)
1034cb34c207SLydia Wang 		return 0;
1035cb34c207SLydia Wang 	if (cfg->line_outs > 2)
1036cb34c207SLydia Wang 		return 0;
1037cb34c207SLydia Wang 
10385f4b36d6STakashi Iwai 	knew = via_clone_control(spec, &via_smart51_mixer);
10395b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10405b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10415b0cb1d8SJaroslav Kysela 
10427b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10437b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
104486e2959aSTakashi Iwai 		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
10455f4b36d6STakashi Iwai 			knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
10467b315bb4STakashi Iwai 			break;
10475b0cb1d8SJaroslav Kysela 		}
10485b0cb1d8SJaroslav Kysela 	}
10495b0cb1d8SJaroslav Kysela 
10505b0cb1d8SJaroslav Kysela 	return 0;
10515b0cb1d8SJaroslav Kysela }
10525b0cb1d8SJaroslav Kysela 
1053c577b8a1SJoseph Chan /* capture mixer elements */
105490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = {
1055c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1056c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1057c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1058c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1059c577b8a1SJoseph Chan 	{
1060c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1061c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1062c577b8a1SJoseph Chan 		 * So call somewhat different..
1063c577b8a1SJoseph Chan 		 */
1064c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1065c577b8a1SJoseph Chan 		.name = "Input Source",
1066c577b8a1SJoseph Chan 		.count = 1,
1067c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1068c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1069c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1070c577b8a1SJoseph Chan 	},
1071c577b8a1SJoseph Chan 	{ } /* end */
1072c577b8a1SJoseph Chan };
1073f5271101SLydia Wang 
1074f5271101SLydia Wang /* check AA path's mute statue */
1075f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1076f5271101SLydia Wang {
1077f5271101SLydia Wang 	int mute = 1;
1078f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1079f5271101SLydia Wang 	int start_idx;
1080f5271101SLydia Wang 	int end_idx;
1081f5271101SLydia Wang 	int i;
1082f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1083f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1084f5271101SLydia Wang 	switch (spec->codec_type) {
1085f5271101SLydia Wang 	case VT1708B_8CH:
1086f5271101SLydia Wang 	case VT1708B_4CH:
1087f5271101SLydia Wang 	case VT1708S:
1088f3db423dSLydia Wang 	case VT1716S:
1089f5271101SLydia Wang 		nid_mixer = 0x16;
1090f5271101SLydia Wang 		start_idx = 2;
1091f5271101SLydia Wang 		end_idx = 4;
1092f5271101SLydia Wang 		break;
1093f5271101SLydia Wang 	case VT1702:
1094f5271101SLydia Wang 		nid_mixer = 0x1a;
1095f5271101SLydia Wang 		start_idx = 1;
1096f5271101SLydia Wang 		end_idx = 3;
1097f5271101SLydia Wang 		break;
1098eb7188caSLydia Wang 	case VT1718S:
1099eb7188caSLydia Wang 		nid_mixer = 0x21;
1100eb7188caSLydia Wang 		start_idx = 1;
1101eb7188caSLydia Wang 		end_idx = 3;
1102eb7188caSLydia Wang 		break;
110325eaba2fSLydia Wang 	case VT2002P:
1104ab6734e7SLydia Wang 	case VT1812:
110511890956SLydia Wang 	case VT1802:
110625eaba2fSLydia Wang 		nid_mixer = 0x21;
110725eaba2fSLydia Wang 		start_idx = 0;
110825eaba2fSLydia Wang 		end_idx = 2;
110925eaba2fSLydia Wang 		break;
1110f5271101SLydia Wang 	default:
1111f5271101SLydia Wang 		return 0;
1112f5271101SLydia Wang 	}
1113f5271101SLydia Wang 	/* check AA path's mute status */
1114f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1115f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1116f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1117f5271101SLydia Wang 		int shift = 8 * (i % 4);
1118f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1119f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1120f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1121f5271101SLydia Wang 			/* check mute status while the pin is connected */
1122f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1123f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1124f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1125f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1126f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1127f5271101SLydia Wang 				mute = 0;
1128f5271101SLydia Wang 				break;
1129f5271101SLydia Wang 			}
1130f5271101SLydia Wang 		}
1131f5271101SLydia Wang 	}
1132f5271101SLydia Wang 	return mute;
1133f5271101SLydia Wang }
1134f5271101SLydia Wang 
1135f5271101SLydia Wang /* enter/exit analog low-current mode */
1136f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1137f5271101SLydia Wang {
1138f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1139f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1140f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1141f5271101SLydia Wang 	unsigned int verb = 0;
1142f5271101SLydia Wang 	unsigned int parm = 0;
1143f5271101SLydia Wang 
1144f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1145f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1146f5271101SLydia Wang 	else {
1147f5271101SLydia Wang 		enable = enable && stream_idle;
1148f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1149f5271101SLydia Wang 	}
1150f5271101SLydia Wang 
1151f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1152f5271101SLydia Wang 	switch (spec->codec_type) {
1153f5271101SLydia Wang 	case VT1708B_8CH:
1154f5271101SLydia Wang 	case VT1708B_4CH:
1155f5271101SLydia Wang 		verb = 0xf70;
1156f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1157f5271101SLydia Wang 		break;
1158f5271101SLydia Wang 	case VT1708S:
1159eb7188caSLydia Wang 	case VT1718S:
1160f3db423dSLydia Wang 	case VT1716S:
1161f5271101SLydia Wang 		verb = 0xf73;
1162f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1163f5271101SLydia Wang 		break;
1164f5271101SLydia Wang 	case VT1702:
1165f5271101SLydia Wang 		verb = 0xf73;
1166f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1167f5271101SLydia Wang 		break;
116825eaba2fSLydia Wang 	case VT2002P:
1169ab6734e7SLydia Wang 	case VT1812:
117011890956SLydia Wang 	case VT1802:
117125eaba2fSLydia Wang 		verb = 0xf93;
117225eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
117325eaba2fSLydia Wang 		break;
1174f5271101SLydia Wang 	default:
1175f5271101SLydia Wang 		return;		/* other codecs are not supported */
1176f5271101SLydia Wang 	}
1177f5271101SLydia Wang 	/* send verb */
1178f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1179f5271101SLydia Wang }
1180f5271101SLydia Wang 
1181c577b8a1SJoseph Chan /*
1182c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1183c577b8a1SJoseph Chan  */
118490dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = {
1185c577b8a1SJoseph Chan 	/*
1186c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1187c577b8a1SJoseph Chan 	 */
1188c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1189c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1190c577b8a1SJoseph Chan 
1191c577b8a1SJoseph Chan 
1192f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1193c577b8a1SJoseph Chan 	 * mixer widget
1194c577b8a1SJoseph Chan 	 */
1195c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1196f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1197f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1198f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1199f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1200f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1201c577b8a1SJoseph Chan 
1202c577b8a1SJoseph Chan 	/*
1203c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1204c577b8a1SJoseph Chan 	 */
1205c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1206c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1207c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1208c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1209c577b8a1SJoseph Chan 
1210bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1211bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1212c577b8a1SJoseph Chan 	/* PW9 Output enable */
1213c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1214aa266fccSLydia Wang 	/* power down jack detect function */
1215aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1216f7278fd0SJosepch Chan 	{ }
1217c577b8a1SJoseph Chan };
1218c577b8a1SJoseph Chan 
1219c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1220c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1221c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1222c577b8a1SJoseph Chan {
1223c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
122417314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
122517314379SLydia Wang 		&& substream->ref_count == 0;
122617314379SLydia Wang 	analog_low_current_mode(codec, idle);
12279a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
12289a08160bSTakashi Iwai 					     hinfo);
1229c577b8a1SJoseph Chan }
1230c577b8a1SJoseph Chan 
12310aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
12320aa62aefSHarald Welte 				      unsigned int stream_tag,
12330aa62aefSHarald Welte 				      unsigned int format,
12340aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
12350aa62aefSHarald Welte {
12360aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12370aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1238dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
12390aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12400aa62aefSHarald Welte 	int i;
12417c935976SStephen Warren 	struct hda_spdif_out *spdif =
12427c935976SStephen Warren 		snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid);
12430aa62aefSHarald Welte 
12440aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12450aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12460aa62aefSHarald Welte 		if (chs == 2 &&
12470aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12480aa62aefSHarald Welte 						format) &&
12497c935976SStephen Warren 		    !(spdif->status & IEC958_AES0_NONAUDIO)) {
12500aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12510aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12520aa62aefSHarald Welte 			 * be updated */
12537c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12540aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12550aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12567c935976SStephen Warren 						    spdif->ctls &
12570aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12580aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12590aa62aefSHarald Welte 						   stream_tag, 0, format);
12600aa62aefSHarald Welte 			/* turn on again (if needed) */
12617c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12620aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12630aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12647c935976SStephen Warren 						    spdif->ctls & 0xff);
12650aa62aefSHarald Welte 		} else {
12660aa62aefSHarald Welte 			mout->dig_out_used = 0;
12670aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12680aa62aefSHarald Welte 						   0, 0, 0);
12690aa62aefSHarald Welte 		}
12700aa62aefSHarald Welte 	}
12710aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12720aa62aefSHarald Welte 
12730aa62aefSHarald Welte 	/* front */
12740aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12750aa62aefSHarald Welte 				   0, format);
12760aa62aefSHarald Welte 
1277eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1278eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12790aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12800aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12810aa62aefSHarald Welte 					   0, format);
12820aa62aefSHarald Welte 
12830aa62aefSHarald Welte 	/* extra outputs copied from front */
12840aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12850aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12860aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12870aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12880aa62aefSHarald Welte 						   stream_tag, 0, format);
12890aa62aefSHarald Welte 
12900aa62aefSHarald Welte 	/* surrounds */
12910aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12920aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12930aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12940aa62aefSHarald Welte 						   i * 2, format);
12950aa62aefSHarald Welte 		else /* copy front */
12960aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12970aa62aefSHarald Welte 						   0, format);
12980aa62aefSHarald Welte 	}
12990aa62aefSHarald Welte }
13000aa62aefSHarald Welte 
13010aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
13020aa62aefSHarald Welte 					  struct hda_codec *codec,
13030aa62aefSHarald Welte 					  unsigned int stream_tag,
13040aa62aefSHarald Welte 					  unsigned int format,
13050aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
13060aa62aefSHarald Welte {
13070aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13080aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1309dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13100aa62aefSHarald Welte 
13110aa62aefSHarald Welte 	if (substream->number == 0)
13120aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
13130aa62aefSHarald Welte 					  substream);
13140aa62aefSHarald Welte 	else {
13150aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13160aa62aefSHarald Welte 		    spec->hp_independent_mode)
13170aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13180aa62aefSHarald Welte 						   stream_tag, 0, format);
13190aa62aefSHarald Welte 	}
13201f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
13210aa62aefSHarald Welte 	return 0;
13220aa62aefSHarald Welte }
13230aa62aefSHarald Welte 
13240aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
13250aa62aefSHarald Welte 				    struct hda_codec *codec,
13260aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
13270aa62aefSHarald Welte {
13280aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13290aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1330dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13310aa62aefSHarald Welte 	int i;
13320aa62aefSHarald Welte 
13330aa62aefSHarald Welte 	if (substream->number == 0) {
13340aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
13350aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
13360aa62aefSHarald Welte 
13370aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
13380aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13390aa62aefSHarald Welte 						   0, 0, 0);
13400aa62aefSHarald Welte 
13410aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13420aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
13430aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
13440aa62aefSHarald Welte 							mout->extra_out_nid[i],
13450aa62aefSHarald Welte 							0, 0, 0);
13460aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
13470aa62aefSHarald Welte 		if (mout->dig_out_nid &&
13480aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13490aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13500aa62aefSHarald Welte 						   0, 0, 0);
13510aa62aefSHarald Welte 			mout->dig_out_used = 0;
13520aa62aefSHarald Welte 		}
13530aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
13540aa62aefSHarald Welte 	} else {
13550aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13560aa62aefSHarald Welte 		    spec->hp_independent_mode)
13570aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13580aa62aefSHarald Welte 						   0, 0, 0);
13590aa62aefSHarald Welte 	}
13601f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13610aa62aefSHarald Welte 	return 0;
13620aa62aefSHarald Welte }
13630aa62aefSHarald Welte 
1364c577b8a1SJoseph Chan /*
1365c577b8a1SJoseph Chan  * Digital out
1366c577b8a1SJoseph Chan  */
1367c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1368c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1369c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1370c577b8a1SJoseph Chan {
1371c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1372c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1373c577b8a1SJoseph Chan }
1374c577b8a1SJoseph Chan 
1375c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1376c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1377c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1378c577b8a1SJoseph Chan {
1379c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1380c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1381c577b8a1SJoseph Chan }
1382c577b8a1SJoseph Chan 
13835691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
138498aa34c0SHarald Welte 					struct hda_codec *codec,
138598aa34c0SHarald Welte 					unsigned int stream_tag,
138698aa34c0SHarald Welte 					unsigned int format,
138798aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
138898aa34c0SHarald Welte {
138998aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13909da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13919da29271STakashi Iwai 					     stream_tag, format, substream);
13929da29271STakashi Iwai }
13935691ec7fSHarald Welte 
13949da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
13959da29271STakashi Iwai 					struct hda_codec *codec,
13969da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13979da29271STakashi Iwai {
13989da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
13999da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
140098aa34c0SHarald Welte 	return 0;
140198aa34c0SHarald Welte }
140298aa34c0SHarald Welte 
1403c577b8a1SJoseph Chan /*
1404c577b8a1SJoseph Chan  * Analog capture
1405c577b8a1SJoseph Chan  */
1406c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1407c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1408c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1409c577b8a1SJoseph Chan 				   unsigned int format,
1410c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1411c577b8a1SJoseph Chan {
1412c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1413c577b8a1SJoseph Chan 
1414c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1415c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1416c577b8a1SJoseph Chan 	return 0;
1417c577b8a1SJoseph Chan }
1418c577b8a1SJoseph Chan 
1419c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1420c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1421c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1422c577b8a1SJoseph Chan {
1423c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1424888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1425c577b8a1SJoseph Chan 	return 0;
1426c577b8a1SJoseph Chan }
1427c577b8a1SJoseph Chan 
142890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_playback = {
14290aa62aefSHarald Welte 	.substreams = 2,
1430c577b8a1SJoseph Chan 	.channels_min = 2,
1431c577b8a1SJoseph Chan 	.channels_max = 8,
1432c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1433c577b8a1SJoseph Chan 	.ops = {
1434c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
14350aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
14360aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1437c577b8a1SJoseph Chan 	},
1438c577b8a1SJoseph Chan };
1439c577b8a1SJoseph Chan 
144090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1441c873cc25SLydia Wang 	.substreams = 2,
1442bc9b5623STakashi Iwai 	.channels_min = 2,
1443bc9b5623STakashi Iwai 	.channels_max = 8,
1444bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1445bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1446bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1447bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1448bc9b5623STakashi Iwai 	 */
1449bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1450bc9b5623STakashi Iwai 	.ops = {
1451bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1452c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1453c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1454bc9b5623STakashi Iwai 	},
1455bc9b5623STakashi Iwai };
1456bc9b5623STakashi Iwai 
145790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_capture = {
1458c577b8a1SJoseph Chan 	.substreams = 2,
1459c577b8a1SJoseph Chan 	.channels_min = 2,
1460c577b8a1SJoseph Chan 	.channels_max = 2,
1461c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1462c577b8a1SJoseph Chan 	.ops = {
1463c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1464c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1465c577b8a1SJoseph Chan 	},
1466c577b8a1SJoseph Chan };
1467c577b8a1SJoseph Chan 
146890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_playback = {
1469c577b8a1SJoseph Chan 	.substreams = 1,
1470c577b8a1SJoseph Chan 	.channels_min = 2,
1471c577b8a1SJoseph Chan 	.channels_max = 2,
1472c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1473c577b8a1SJoseph Chan 	.ops = {
1474c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14756b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14769da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14779da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1478c577b8a1SJoseph Chan 	},
1479c577b8a1SJoseph Chan };
1480c577b8a1SJoseph Chan 
148190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_capture = {
1482c577b8a1SJoseph Chan 	.substreams = 1,
1483c577b8a1SJoseph Chan 	.channels_min = 2,
1484c577b8a1SJoseph Chan 	.channels_max = 2,
1485c577b8a1SJoseph Chan };
1486c577b8a1SJoseph Chan 
1487c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1488c577b8a1SJoseph Chan {
1489c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14905b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
149190dd48a1STakashi Iwai 	const struct snd_kcontrol_new *knew;
14925b0cb1d8SJaroslav Kysela 	int err, i;
1493c577b8a1SJoseph Chan 
149424088a58STakashi Iwai 	if (spec->set_widgets_power_state)
149524088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
149624088a58STakashi Iwai 			return -ENOMEM;
149724088a58STakashi Iwai 
1498c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1499c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1500c577b8a1SJoseph Chan 		if (err < 0)
1501c577b8a1SJoseph Chan 			return err;
1502c577b8a1SJoseph Chan 	}
1503c577b8a1SJoseph Chan 
1504c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1505c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
150674b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1507c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1508c577b8a1SJoseph Chan 		if (err < 0)
1509c577b8a1SJoseph Chan 			return err;
15109a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
15119a08160bSTakashi Iwai 						    &spec->multiout);
15129a08160bSTakashi Iwai 		if (err < 0)
15139a08160bSTakashi Iwai 			return err;
15149a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1515c577b8a1SJoseph Chan 	}
1516c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1517c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1518c577b8a1SJoseph Chan 		if (err < 0)
1519c577b8a1SJoseph Chan 			return err;
1520c577b8a1SJoseph Chan 	}
152117314379SLydia Wang 
15225b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15235b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15245b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
152521949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15265b0cb1d8SJaroslav Kysela 		if (err < 0)
15275b0cb1d8SJaroslav Kysela 			return err;
15285b0cb1d8SJaroslav Kysela 	}
15295b0cb1d8SJaroslav Kysela 
15305b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
15315b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
15325b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
15335b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
15345b0cb1d8SJaroslav Kysela 				continue;
15355b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
15365b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
15375b0cb1d8SJaroslav Kysela 				continue;
15385b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
15395b0cb1d8SJaroslav Kysela 					      knew->subdevice);
15405b0cb1d8SJaroslav Kysela 		}
15415b0cb1d8SJaroslav Kysela 	}
15425b0cb1d8SJaroslav Kysela 
154317314379SLydia Wang 	/* init power states */
15443e95b9abSLydia Wang 	set_widgets_power_state(codec);
154517314379SLydia Wang 	analog_low_current_mode(codec, 1);
154617314379SLydia Wang 
1547603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1548c577b8a1SJoseph Chan 	return 0;
1549c577b8a1SJoseph Chan }
1550c577b8a1SJoseph Chan 
1551c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1552c577b8a1SJoseph Chan {
1553c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1554c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1555c577b8a1SJoseph Chan 
1556c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1557c577b8a1SJoseph Chan 	codec->pcm_info = info;
1558c577b8a1SJoseph Chan 
1559*82673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1560*82673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1561c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1562377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1563377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1564377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1565377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1566c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1567c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1568c577b8a1SJoseph Chan 
1569c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1570c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1571c577b8a1SJoseph Chan 
1572c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1573c577b8a1SJoseph Chan 		codec->num_pcms++;
1574c577b8a1SJoseph Chan 		info++;
1575*82673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
1576*82673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
1577*82673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1578c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
15797ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1580c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1581c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1582c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1583c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1584c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1585c577b8a1SJoseph Chan 		}
1586c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1587c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1588c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1589c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1590c577b8a1SJoseph Chan 				spec->dig_in_nid;
1591c577b8a1SJoseph Chan 		}
1592c577b8a1SJoseph Chan 	}
1593c577b8a1SJoseph Chan 
1594c577b8a1SJoseph Chan 	return 0;
1595c577b8a1SJoseph Chan }
1596c577b8a1SJoseph Chan 
1597c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1598c577b8a1SJoseph Chan {
1599c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1600c577b8a1SJoseph Chan 
1601c577b8a1SJoseph Chan 	if (!spec)
1602c577b8a1SJoseph Chan 		return;
1603c577b8a1SJoseph Chan 
1604603c4019STakashi Iwai 	via_free_kctls(codec);
16051f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1606c577b8a1SJoseph Chan 	kfree(codec->spec);
1607c577b8a1SJoseph Chan }
1608c577b8a1SJoseph Chan 
160969e52a80SHarald Welte /* mute internal speaker if HP is plugged */
161069e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
161169e52a80SHarald Welte {
1612dcf34c8cSLydia Wang 	unsigned int present = 0;
161369e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
161469e52a80SHarald Welte 
1615d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1616dcf34c8cSLydia Wang 
1617dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1618dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
1619dcf34c8cSLydia Wang 		/* auto mute */
1620dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
1621dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1622dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1623dcf34c8cSLydia Wang 		/* notify change */
1624dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
1625dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1626dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
1627dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1628dcf34c8cSLydia Wang 			       &id);
1629dcf34c8cSLydia Wang 	}
163069e52a80SHarald Welte }
163169e52a80SHarald Welte 
1632f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1633f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1634f3db423dSLydia Wang {
1635f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1636f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1637f3db423dSLydia Wang 
1638f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1639f3db423dSLydia Wang 		return;
1640f3db423dSLydia Wang 
1641d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1642d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1643f3db423dSLydia Wang 
1644f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1645f3db423dSLydia Wang 	if (lineout_present) {
1646f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1647f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1648f3db423dSLydia Wang 		return;
1649f3db423dSLydia Wang 	}
1650f3db423dSLydia Wang 
1651d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1652f3db423dSLydia Wang 
1653f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
1654f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1655f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1656f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
1657f3db423dSLydia Wang }
1658f3db423dSLydia Wang 
165969e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
166069e52a80SHarald Welte {
166169e52a80SHarald Welte 	unsigned int gpio_data;
166269e52a80SHarald Welte 	unsigned int vol_counter;
166369e52a80SHarald Welte 	unsigned int vol;
166469e52a80SHarald Welte 	unsigned int master_vol;
166569e52a80SHarald Welte 
166669e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
166769e52a80SHarald Welte 
166869e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
166969e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
167069e52a80SHarald Welte 
167169e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
167269e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
167369e52a80SHarald Welte 
167469e52a80SHarald Welte 	vol = vol_counter & 0x1F;
167569e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
167669e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
167769e52a80SHarald Welte 					AC_AMP_GET_INPUT);
167869e52a80SHarald Welte 
167969e52a80SHarald Welte 	if (gpio_data == 0x02) {
168069e52a80SHarald Welte 		/* unmute line out */
168169e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
168269e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
168369e52a80SHarald Welte 
168469e52a80SHarald Welte 		if (vol_counter & 0x20) {
168569e52a80SHarald Welte 			/* decrease volume */
168669e52a80SHarald Welte 			if (vol > master_vol)
168769e52a80SHarald Welte 				vol = master_vol;
168869e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
168969e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
169069e52a80SHarald Welte 						 master_vol-vol);
169169e52a80SHarald Welte 		} else {
169269e52a80SHarald Welte 			/* increase volume */
169369e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
169469e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
169569e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
169669e52a80SHarald Welte 					  (master_vol+vol));
169769e52a80SHarald Welte 		}
169869e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
169969e52a80SHarald Welte 		/* mute line out */
170069e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
170169e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
170269e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
170369e52a80SHarald Welte 					 HDA_AMP_MUTE);
170469e52a80SHarald Welte 	}
170569e52a80SHarald Welte }
170669e52a80SHarald Welte 
170725eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
170825eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
170925eaba2fSLydia Wang {
171025eaba2fSLydia Wang 	unsigned int hp_present;
171125eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
171225eaba2fSLydia Wang 
171327439ce7SLydia Wang 	if (!VT2002P_COMPATIBLE(spec))
171425eaba2fSLydia Wang 		return;
171525eaba2fSLydia Wang 
1716d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
171725eaba2fSLydia Wang 
171825eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
171925eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
172025eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
172125eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
172225eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
172325eaba2fSLydia Wang 		/* notify change */
172425eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
172525eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
172625eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
172725eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
172825eaba2fSLydia Wang 			       &id);
172925eaba2fSLydia Wang 	}
173025eaba2fSLydia Wang }
173125eaba2fSLydia Wang 
173225eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
173325eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
173425eaba2fSLydia Wang {
173501a1796bSakpm@linux-foundation.org 	/* use long instead of int below just to avoid an internal compiler
173601a1796bSakpm@linux-foundation.org 	 * error with gcc 4.0.x
173701a1796bSakpm@linux-foundation.org 	 */
173801a1796bSakpm@linux-foundation.org 	unsigned long hp_present, present = 0;
173925eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
174025eaba2fSLydia Wang 	int i;
174125eaba2fSLydia Wang 
174225eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
174325eaba2fSLydia Wang 		return;
174425eaba2fSLydia Wang 
1745d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
174625eaba2fSLydia Wang 
1747d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
174825eaba2fSLydia Wang 
174925eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
175025eaba2fSLydia Wang 		/* Mute Line-Outs */
175125eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
175225eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
175325eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
175425eaba2fSLydia Wang 				HDA_OUTPUT, 0,
175525eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
175625eaba2fSLydia Wang 		if (hp_present)
175725eaba2fSLydia Wang 			present = hp_present;
175825eaba2fSLydia Wang 	}
175925eaba2fSLydia Wang 	/* Speakers */
176025eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
176125eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
176225eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
176325eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
176425eaba2fSLydia Wang }
176525eaba2fSLydia Wang 
176625eaba2fSLydia Wang 
176769e52a80SHarald Welte /* unsolicited event for jack sensing */
176869e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
176969e52a80SHarald Welte 				  unsigned int res)
177069e52a80SHarald Welte {
177169e52a80SHarald Welte 	res >>= 26;
1772ec7e7e42SLydia Wang 
1773a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
17743e95b9abSLydia Wang 		set_widgets_power_state(codec);
1775ec7e7e42SLydia Wang 
1776ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1777ec7e7e42SLydia Wang 
1778ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1779ec7e7e42SLydia Wang 		via_hp_automute(codec);
1780ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1781ec7e7e42SLydia Wang 		via_gpio_control(codec);
1782ec7e7e42SLydia Wang 	else if (res == VIA_MONO_EVENT)
1783f3db423dSLydia Wang 		via_mono_automute(codec);
1784ec7e7e42SLydia Wang 	else if (res == VIA_SPEAKER_EVENT)
178525eaba2fSLydia Wang 		via_speaker_automute(codec);
1786ec7e7e42SLydia Wang 	else if (res == VIA_BIND_HP_EVENT)
178725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
178869e52a80SHarald Welte }
178969e52a80SHarald Welte 
1790c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1791c577b8a1SJoseph Chan {
1792c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
179369e52a80SHarald Welte 	int i;
179469e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
179569e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
179669e52a80SHarald Welte 
1797f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1798f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
179955d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
180055d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1801f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
180212b74c80STakashi Iwai 					    PIN_OUT);
180355d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1804f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1805f7278fd0SJosepch Chan 		}
180612b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
180712b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
180812b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1809f7278fd0SJosepch Chan 
18109da29271STakashi Iwai 	/* assign slave outs */
18119da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
18129da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
18135691ec7fSHarald Welte 
1814c577b8a1SJoseph Chan 	return 0;
1815c577b8a1SJoseph Chan }
1816c577b8a1SJoseph Chan 
18171f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18181f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
18191f2e99feSLydia Wang {
18201f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
18211f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
18221f2e99feSLydia Wang 	return 0;
18231f2e99feSLydia Wang }
18241f2e99feSLydia Wang #endif
18251f2e99feSLydia Wang 
1826cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1827cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1828cb53c626STakashi Iwai {
1829cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1830cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1831cb53c626STakashi Iwai }
1832cb53c626STakashi Iwai #endif
1833cb53c626STakashi Iwai 
1834c577b8a1SJoseph Chan /*
1835c577b8a1SJoseph Chan  */
183690dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1837c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1838c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1839c577b8a1SJoseph Chan 	.init = via_init,
1840c577b8a1SJoseph Chan 	.free = via_free,
18411f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18421f2e99feSLydia Wang 	.suspend = via_suspend,
18431f2e99feSLydia Wang #endif
1844cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1845cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1846cb53c626STakashi Iwai #endif
1847c577b8a1SJoseph Chan };
1848c577b8a1SJoseph Chan 
1849c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
1850c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1851c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
1852c577b8a1SJoseph Chan {
1853c577b8a1SJoseph Chan 	int i;
1854c577b8a1SJoseph Chan 	hda_nid_t nid;
1855c577b8a1SJoseph Chan 
1856c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
1857c577b8a1SJoseph Chan 
1858c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
1859c577b8a1SJoseph Chan 
1860c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
1861c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1862c577b8a1SJoseph Chan 		if (nid) {
1863c577b8a1SJoseph Chan 			/* config dac list */
1864c577b8a1SJoseph Chan 			switch (i) {
1865c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
1866dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x10;
1867c577b8a1SJoseph Chan 				break;
1868c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
1869dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x12;
1870c577b8a1SJoseph Chan 				break;
1871c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
1872dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x11;
1873c577b8a1SJoseph Chan 				break;
1874c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
1875dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x13;
1876c577b8a1SJoseph Chan 				break;
1877c577b8a1SJoseph Chan 			}
1878c577b8a1SJoseph Chan 		}
1879c577b8a1SJoseph Chan 	}
1880c577b8a1SJoseph Chan 
1881c577b8a1SJoseph Chan 	return 0;
1882c577b8a1SJoseph Chan }
1883c577b8a1SJoseph Chan 
1884c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
1885c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1886c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
1887c577b8a1SJoseph Chan {
1888c577b8a1SJoseph Chan 	char name[32];
1889ea734963STakashi Iwai 	static const char * const chname[4] = {
1890ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
1891ea734963STakashi Iwai 	};
18929645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
1893c577b8a1SJoseph Chan 	int i, err;
1894c577b8a1SJoseph Chan 
1895c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1896c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1897c577b8a1SJoseph Chan 
1898c577b8a1SJoseph Chan 		if (!nid)
1899c577b8a1SJoseph Chan 			continue;
1900c577b8a1SJoseph Chan 
19019645c203SLydia Wang 		nid_vol = nid_vols[i];
1902c577b8a1SJoseph Chan 
1903c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
1904c577b8a1SJoseph Chan 			/* Center/LFE */
1905c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1906c577b8a1SJoseph Chan 					"Center Playback Volume",
1907f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1908f7278fd0SJosepch Chan 							    HDA_OUTPUT));
1909c577b8a1SJoseph Chan 			if (err < 0)
1910c577b8a1SJoseph Chan 				return err;
1911c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1912c577b8a1SJoseph Chan 					      "LFE Playback Volume",
1913f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1914f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1915c577b8a1SJoseph Chan 			if (err < 0)
1916c577b8a1SJoseph Chan 				return err;
1917c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1918c577b8a1SJoseph Chan 					      "Center Playback Switch",
1919f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1920f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1921c577b8a1SJoseph Chan 			if (err < 0)
1922c577b8a1SJoseph Chan 				return err;
1923c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1924c577b8a1SJoseph Chan 					      "LFE Playback Switch",
1925f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1926f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1927c577b8a1SJoseph Chan 			if (err < 0)
1928c577b8a1SJoseph Chan 				return err;
1929c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
1930c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
1931c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1932c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
19339645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1934f7278fd0SJosepch Chan 								  HDA_INPUT));
1935c577b8a1SJoseph Chan 			if (err < 0)
1936c577b8a1SJoseph Chan 				return err;
1937c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1938c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
19399645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1940f7278fd0SJosepch Chan 								  HDA_INPUT));
1941c577b8a1SJoseph Chan 			if (err < 0)
1942c577b8a1SJoseph Chan 				return err;
1943c577b8a1SJoseph Chan 
1944c577b8a1SJoseph Chan 			/* add control to PW3 */
1945c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1946c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1947f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1948f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1949c577b8a1SJoseph Chan 			if (err < 0)
1950c577b8a1SJoseph Chan 				return err;
1951c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1952c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1953f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1954f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1955c577b8a1SJoseph Chan 			if (err < 0)
1956c577b8a1SJoseph Chan 				return err;
1957c577b8a1SJoseph Chan 		} else {
1958c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1959c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1960f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1961f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1962c577b8a1SJoseph Chan 			if (err < 0)
1963c577b8a1SJoseph Chan 				return err;
1964c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1965c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1966f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1967f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1968c577b8a1SJoseph Chan 			if (err < 0)
1969c577b8a1SJoseph Chan 				return err;
1970c577b8a1SJoseph Chan 		}
1971c577b8a1SJoseph Chan 	}
1972c577b8a1SJoseph Chan 
1973c577b8a1SJoseph Chan 	return 0;
1974c577b8a1SJoseph Chan }
1975c577b8a1SJoseph Chan 
19760aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
19770aa62aefSHarald Welte {
19780aa62aefSHarald Welte 	int i;
19790aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
1980ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
19810aa62aefSHarald Welte 
19820aa62aefSHarald Welte 	/* for hp mode select */
198310a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
198410a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
19850aa62aefSHarald Welte 
19860aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
19870aa62aefSHarald Welte }
19880aa62aefSHarald Welte 
1989c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1990c577b8a1SJoseph Chan {
1991c577b8a1SJoseph Chan 	int err;
1992c577b8a1SJoseph Chan 
1993c577b8a1SJoseph Chan 	if (!pin)
1994c577b8a1SJoseph Chan 		return 0;
1995c577b8a1SJoseph Chan 
1996c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
1997cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
1998c577b8a1SJoseph Chan 
1999c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2000c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2001c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2002c577b8a1SJoseph Chan 	if (err < 0)
2003c577b8a1SJoseph Chan 		return err;
2004c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2005c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2006c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2007c577b8a1SJoseph Chan 	if (err < 0)
2008c577b8a1SJoseph Chan 		return err;
2009c577b8a1SJoseph Chan 
20100aa62aefSHarald Welte 	create_hp_imux(spec);
20110aa62aefSHarald Welte 
2012c577b8a1SJoseph Chan 	return 0;
2013c577b8a1SJoseph Chan }
2014c577b8a1SJoseph Chan 
2015a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
2016a766d0d7STakashi Iwai 				hda_nid_t nid)
2017a766d0d7STakashi Iwai {
2018a766d0d7STakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
2019a766d0d7STakashi Iwai 	int i, nums;
2020a766d0d7STakashi Iwai 
2021a766d0d7STakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
2022a766d0d7STakashi Iwai 	for (i = 0; i < nums; i++)
2023a766d0d7STakashi Iwai 		if (conn[i] == nid)
2024a766d0d7STakashi Iwai 			return i;
2025a766d0d7STakashi Iwai 	return -1;
2026a766d0d7STakashi Iwai }
2027a766d0d7STakashi Iwai 
2028a766d0d7STakashi Iwai /* look for ADCs */
2029a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2030a766d0d7STakashi Iwai {
2031a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2032a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2033a766d0d7STakashi Iwai 	int i;
2034a766d0d7STakashi Iwai 
2035a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2036a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2037a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2038a766d0d7STakashi Iwai 			continue;
2039a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2040a766d0d7STakashi Iwai 			continue;
2041a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2042a766d0d7STakashi Iwai 			continue;
2043a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2044a766d0d7STakashi Iwai 			return -ENOMEM;
2045a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2046a766d0d7STakashi Iwai 	}
2047a766d0d7STakashi Iwai 	return 0;
2048a766d0d7STakashi Iwai }
2049a766d0d7STakashi Iwai 
2050a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2051a766d0d7STakashi Iwai 
2052c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
205310a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
2054f3268512STakashi Iwai 					    const struct auto_pin_cfg *cfg,
2055a766d0d7STakashi Iwai 					    hda_nid_t mix_nid)
2056c577b8a1SJoseph Chan {
205710a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
20580aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2059a766d0d7STakashi Iwai 	int i, err, idx, idx2, type, type_idx = 0;
2060a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
2061a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
2062a766d0d7STakashi Iwai 	int num_idxs;
2063a766d0d7STakashi Iwai 
2064a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2065a766d0d7STakashi Iwai 	if (err < 0)
2066a766d0d7STakashi Iwai 		return err;
2067a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2068a766d0d7STakashi Iwai 	if (err < 0)
2069a766d0d7STakashi Iwai 		return err;
2070a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
2071a766d0d7STakashi Iwai 
2072a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
2073a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
2074a766d0d7STakashi Iwai 	if (num_idxs <= 0)
2075a766d0d7STakashi Iwai 		return 0;
2076c577b8a1SJoseph Chan 
2077c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2078f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2079a766d0d7STakashi Iwai 		if (pin_idxs[idx] == mix_nid) {
208010a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2081f3268512STakashi Iwai 			break;
2082f3268512STakashi Iwai 		}
2083f3268512STakashi Iwai 	}
2084c577b8a1SJoseph Chan 
20857b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
208610a20af7STakashi Iwai 		const char *label;
20877b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2088f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
20897b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2090c577b8a1SJoseph Chan 				break;
2091f3268512STakashi Iwai 		if (idx >= num_idxs)
2092f3268512STakashi Iwai 			continue;
20937b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
20947b315bb4STakashi Iwai 			type_idx++;
20957b315bb4STakashi Iwai 		else
20967b315bb4STakashi Iwai 			type_idx = 0;
209710a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2098a766d0d7STakashi Iwai 		idx2 = get_connection_index(codec, mix_nid, pin_idxs[idx]);
2099a766d0d7STakashi Iwai 		if (idx2 >= 0)
210016922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2101a766d0d7STakashi Iwai 						   idx2, mix_nid);
2102c577b8a1SJoseph Chan 		if (err < 0)
2103c577b8a1SJoseph Chan 			return err;
210410a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2105c577b8a1SJoseph Chan 	}
2106c577b8a1SJoseph Chan 	return 0;
2107c577b8a1SJoseph Chan }
2108c577b8a1SJoseph Chan 
2109f3268512STakashi Iwai /* create playback/capture controls for input pins */
211010a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
2111f3268512STakashi Iwai 						const struct auto_pin_cfg *cfg)
2112f3268512STakashi Iwai {
2113a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x17);
2114f3268512STakashi Iwai }
2115f3268512STakashi Iwai 
2116cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
211790dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = {
2118cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2119cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2120cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2121cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2122cb53c626STakashi Iwai 	{ } /* end */
2123cb53c626STakashi Iwai };
2124cb53c626STakashi Iwai #endif
2125cb53c626STakashi Iwai 
212676d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
212776d9b0ddSHarald Welte {
212876d9b0ddSHarald Welte 	unsigned int def_conf;
212976d9b0ddSHarald Welte 	unsigned char seqassoc;
213076d9b0ddSHarald Welte 
21312f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
213276d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
213376d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
213482ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
213582ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
213676d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
21372f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
213876d9b0ddSHarald Welte 	}
213976d9b0ddSHarald Welte 
214076d9b0ddSHarald Welte 	return;
214176d9b0ddSHarald Welte }
214276d9b0ddSHarald Welte 
2143e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
21441f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
21451f2e99feSLydia Wang {
21461f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21471f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
21481f2e99feSLydia Wang 
21491f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21501f2e99feSLydia Wang 		return 0;
2151e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
21521f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2153e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
21541f2e99feSLydia Wang 	return 0;
21551f2e99feSLydia Wang }
21561f2e99feSLydia Wang 
2157e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
21581f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
21591f2e99feSLydia Wang {
21601f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21611f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
21621f2e99feSLydia Wang 	int change;
21631f2e99feSLydia Wang 
21641f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21651f2e99feSLydia Wang 		return 0;
2166e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
21671f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2168e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2169e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
21701f2e99feSLydia Wang 		mute_aa_path(codec, 1);
21711f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
21721f2e99feSLydia Wang 	}
21731f2e99feSLydia Wang 	return change;
21741f2e99feSLydia Wang }
21751f2e99feSLydia Wang 
2176e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
21771f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21781f2e99feSLydia Wang 	.name = "Jack Detect",
21791f2e99feSLydia Wang 	.count = 1,
21801f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2181e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2182e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
21831f2e99feSLydia Wang };
21841f2e99feSLydia Wang 
2185c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2186c577b8a1SJoseph Chan {
2187c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2188c577b8a1SJoseph Chan 	int err;
2189c577b8a1SJoseph Chan 
219076d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
219176d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
219276d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
219376d9b0ddSHarald Welte 
2194c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2195c577b8a1SJoseph Chan 	if (err < 0)
2196c577b8a1SJoseph Chan 		return err;
2197c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2198c577b8a1SJoseph Chan 	if (err < 0)
2199c577b8a1SJoseph Chan 		return err;
2200c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2201c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2202c577b8a1SJoseph Chan 
2203c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2204c577b8a1SJoseph Chan 	if (err < 0)
2205c577b8a1SJoseph Chan 		return err;
2206c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2207c577b8a1SJoseph Chan 	if (err < 0)
2208c577b8a1SJoseph Chan 		return err;
220910a20af7STakashi Iwai 	err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
2210c577b8a1SJoseph Chan 	if (err < 0)
2211c577b8a1SJoseph Chan 		return err;
22121f2e99feSLydia Wang 	/* add jack detect on/off control */
2213e06e5a29STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2214e06e5a29STakashi Iwai 		return -ENOMEM;
2215c577b8a1SJoseph Chan 
2216c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2217c577b8a1SJoseph Chan 
22180852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2219c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
222055d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2221c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2222c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2223c577b8a1SJoseph Chan 
2224603c4019STakashi Iwai 	if (spec->kctls.list)
2225603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2226c577b8a1SJoseph Chan 
222769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2228c577b8a1SJoseph Chan 
22290aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
22300aa62aefSHarald Welte 
2231f8fdd495SHarald Welte 	if (spec->hp_mux)
22323d83e577STakashi Iwai 		via_hp_build(codec);
2233c577b8a1SJoseph Chan 
22345b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2235c577b8a1SJoseph Chan 	return 1;
2236c577b8a1SJoseph Chan }
2237c577b8a1SJoseph Chan 
2238c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2239c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2240c577b8a1SJoseph Chan {
224125eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
224225eaba2fSLydia Wang 
2243c577b8a1SJoseph Chan 	via_init(codec);
2244c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2245c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2246c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
224711890956SLydia Wang 
224811890956SLydia Wang 	if (VT2002P_COMPATIBLE(spec)) {
224925eaba2fSLydia Wang 		via_hp_bind_automute(codec);
225025eaba2fSLydia Wang 	} else {
225125eaba2fSLydia Wang 		via_hp_automute(codec);
225225eaba2fSLydia Wang 		via_speaker_automute(codec);
225325eaba2fSLydia Wang 	}
225425eaba2fSLydia Wang 
2255c577b8a1SJoseph Chan 	return 0;
2256c577b8a1SJoseph Chan }
2257c577b8a1SJoseph Chan 
22581f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
22591f2e99feSLydia Wang {
22601f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
22611f2e99feSLydia Wang 					     vt1708_hp_work.work);
22621f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22631f2e99feSLydia Wang 		return;
22641f2e99feSLydia Wang 	/* if jack state toggled */
22651f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2266d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
22671f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
22681f2e99feSLydia Wang 		via_hp_automute(spec->codec);
22691f2e99feSLydia Wang 	}
22701f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
22711f2e99feSLydia Wang }
22721f2e99feSLydia Wang 
2273337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2274337b9d02STakashi Iwai {
2275337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2276337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2277337b9d02STakashi Iwai 	unsigned int type;
2278337b9d02STakashi Iwai 	int i, n;
2279337b9d02STakashi Iwai 
2280337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2281337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2282337b9d02STakashi Iwai 		while (nid) {
2283a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
22841c55d521STakashi Iwai 			if (type == AC_WID_PIN)
22851c55d521STakashi Iwai 				break;
2286337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2287337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2288337b9d02STakashi Iwai 			if (n <= 0)
2289337b9d02STakashi Iwai 				break;
2290337b9d02STakashi Iwai 			if (n > 1) {
2291337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2292337b9d02STakashi Iwai 				break;
2293337b9d02STakashi Iwai 			}
2294337b9d02STakashi Iwai 			nid = conn[0];
2295337b9d02STakashi Iwai 		}
2296337b9d02STakashi Iwai 	}
22971c55d521STakashi Iwai 	return 0;
2298337b9d02STakashi Iwai }
2299337b9d02STakashi Iwai 
2300c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2301c577b8a1SJoseph Chan {
2302c577b8a1SJoseph Chan 	struct via_spec *spec;
2303c577b8a1SJoseph Chan 	int err;
2304c577b8a1SJoseph Chan 
2305c577b8a1SJoseph Chan 	/* create a codec specific record */
23065b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2307c577b8a1SJoseph Chan 	if (spec == NULL)
2308c577b8a1SJoseph Chan 		return -ENOMEM;
2309c577b8a1SJoseph Chan 
2310c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2311c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2312c577b8a1SJoseph Chan 	if (err < 0) {
2313c577b8a1SJoseph Chan 		via_free(codec);
2314c577b8a1SJoseph Chan 		return err;
2315c577b8a1SJoseph Chan 	} else if (!err) {
2316c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2317c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2318c577b8a1SJoseph Chan 	}
2319c577b8a1SJoseph Chan 
2320c577b8a1SJoseph Chan 
2321c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2322bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2323bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2324bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2325c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2326c577b8a1SJoseph Chan 
2327c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2328c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2329c577b8a1SJoseph Chan 
2330a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2331c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2332c577b8a1SJoseph Chan 		spec->num_mixers++;
2333c577b8a1SJoseph Chan 	}
2334c577b8a1SJoseph Chan 
2335c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2336c577b8a1SJoseph Chan 
2337c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2338cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2339cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2340cb53c626STakashi Iwai #endif
23411f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2342c577b8a1SJoseph Chan 	return 0;
2343c577b8a1SJoseph Chan }
2344c577b8a1SJoseph Chan 
2345c577b8a1SJoseph Chan /* capture mixer elements */
234690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = {
2347c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2348c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2349c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2350c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2351c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2352c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2353c577b8a1SJoseph Chan 	{
2354c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2355c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2356c577b8a1SJoseph Chan 		 * So call somewhat different..
2357c577b8a1SJoseph Chan 		 */
2358c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2359c577b8a1SJoseph Chan 		.name = "Input Source",
2360c577b8a1SJoseph Chan 		.count = 1,
2361c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2362c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2363c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2364c577b8a1SJoseph Chan 	},
2365c577b8a1SJoseph Chan 	{ } /* end */
2366c577b8a1SJoseph Chan };
2367c577b8a1SJoseph Chan 
236890dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = {
2369a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2370a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
237169e52a80SHarald Welte 	{ }
237269e52a80SHarald Welte };
237369e52a80SHarald Welte 
2374c577b8a1SJoseph Chan /*
2375c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2376c577b8a1SJoseph Chan  */
237790dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2378c577b8a1SJoseph Chan 	/*
2379c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2380c577b8a1SJoseph Chan 	 */
2381c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2382c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2383c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2384c577b8a1SJoseph Chan 
2385c577b8a1SJoseph Chan 
2386f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2387c577b8a1SJoseph Chan 	 * mixer widget
2388c577b8a1SJoseph Chan 	 */
2389c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2390f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2391f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2392f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2393f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2394f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2395c577b8a1SJoseph Chan 
2396c577b8a1SJoseph Chan 	/*
2397c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2398c577b8a1SJoseph Chan 	 */
2399c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2400c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2401c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2402c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2403c577b8a1SJoseph Chan 
2404c577b8a1SJoseph Chan 	/*
2405c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2406c577b8a1SJoseph Chan 	 */
2407c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2408c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2409c577b8a1SJoseph Chan 
2410bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2411bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2412c577b8a1SJoseph Chan 	/* PW9 Output enable */
2413c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2414c577b8a1SJoseph Chan 	{ }
2415c577b8a1SJoseph Chan };
2416c577b8a1SJoseph Chan 
241790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2418c577b8a1SJoseph Chan 	.substreams = 1,
2419c577b8a1SJoseph Chan 	.channels_min = 2,
2420c577b8a1SJoseph Chan 	.channels_max = 10,
2421c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2422c577b8a1SJoseph Chan 	.ops = {
2423c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2424c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2425c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2426c577b8a1SJoseph Chan 	},
2427c577b8a1SJoseph Chan };
2428c577b8a1SJoseph Chan 
242990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2430c577b8a1SJoseph Chan 	.substreams = 1,
2431c577b8a1SJoseph Chan 	.channels_min = 2,
2432c577b8a1SJoseph Chan 	.channels_max = 6,
2433c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2434c577b8a1SJoseph Chan 	.ops = {
2435c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2436c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2437c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2438c577b8a1SJoseph Chan 	},
2439c577b8a1SJoseph Chan };
2440c577b8a1SJoseph Chan 
244190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_analog_capture = {
2442c577b8a1SJoseph Chan 	.substreams = 2,
2443c577b8a1SJoseph Chan 	.channels_min = 2,
2444c577b8a1SJoseph Chan 	.channels_max = 2,
2445c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2446c577b8a1SJoseph Chan 	.ops = {
2447c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2448c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2449c577b8a1SJoseph Chan 	},
2450c577b8a1SJoseph Chan };
2451c577b8a1SJoseph Chan 
245290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_playback = {
2453c577b8a1SJoseph Chan 	.substreams = 1,
2454c577b8a1SJoseph Chan 	.channels_min = 2,
2455c577b8a1SJoseph Chan 	.channels_max = 2,
2456c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2457c577b8a1SJoseph Chan 	.ops = {
2458c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2459c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2460c577b8a1SJoseph Chan 	},
2461c577b8a1SJoseph Chan };
2462c577b8a1SJoseph Chan 
246390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_capture = {
2464c577b8a1SJoseph Chan 	.substreams = 1,
2465c577b8a1SJoseph Chan 	.channels_min = 2,
2466c577b8a1SJoseph Chan 	.channels_max = 2,
2467c577b8a1SJoseph Chan };
2468c577b8a1SJoseph Chan 
2469c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2470c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2471c577b8a1SJoseph Chan {
2472c577b8a1SJoseph Chan 	int i;
2473c577b8a1SJoseph Chan 	hda_nid_t nid;
2474c577b8a1SJoseph Chan 
2475c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2476c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2477c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2478c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2479c577b8a1SJoseph Chan 
2480c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2481c577b8a1SJoseph Chan 
2482c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2483c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2484c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2485c577b8a1SJoseph Chan 			if (nid) {
2486c577b8a1SJoseph Chan 				/* config dac list */
2487c577b8a1SJoseph Chan 				switch (i) {
2488c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2489c577b8a1SJoseph Chan 					/* AOW0 */
2490dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x10;
2491c577b8a1SJoseph Chan 					break;
2492c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2493c577b8a1SJoseph Chan 					/* AOW2 */
2494dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x12;
2495c577b8a1SJoseph Chan 					break;
2496c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2497c577b8a1SJoseph Chan 					/* AOW3 */
2498dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x11;
2499c577b8a1SJoseph Chan 					break;
2500c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2501c577b8a1SJoseph Chan 					/* AOW1 */
2502dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x27;
2503c577b8a1SJoseph Chan 					break;
2504c577b8a1SJoseph Chan 				default:
2505c577b8a1SJoseph Chan 					break;
2506c577b8a1SJoseph Chan 				}
2507c577b8a1SJoseph Chan 			}
2508c577b8a1SJoseph Chan 		}
2509dda14410STakashi Iwai 		spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2510c577b8a1SJoseph Chan 
2511c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2512c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2513c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2514c577b8a1SJoseph Chan 			if (nid) {
2515c577b8a1SJoseph Chan 				/* config dac list */
2516c577b8a1SJoseph Chan 				switch (i) {
2517c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2518c577b8a1SJoseph Chan 					/* AOW0 */
2519dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x10;
2520c577b8a1SJoseph Chan 					break;
2521c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2522c577b8a1SJoseph Chan 					/* AOW2 */
2523dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x12;
2524c577b8a1SJoseph Chan 					break;
2525c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2526c577b8a1SJoseph Chan 					/* AOW1 */
2527dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x11;
2528c577b8a1SJoseph Chan 					break;
2529c577b8a1SJoseph Chan 				default:
2530c577b8a1SJoseph Chan 					break;
2531c577b8a1SJoseph Chan 				}
2532c577b8a1SJoseph Chan 			}
2533c577b8a1SJoseph Chan 		}
2534c577b8a1SJoseph Chan 	}
2535c577b8a1SJoseph Chan 
2536c577b8a1SJoseph Chan 	return 0;
2537c577b8a1SJoseph Chan }
2538c577b8a1SJoseph Chan 
2539c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2540c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2541c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2542c577b8a1SJoseph Chan {
2543c577b8a1SJoseph Chan 	char name[32];
2544ea734963STakashi Iwai 	static const char * const chname[4] = {
2545ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
2546ea734963STakashi Iwai 	};
25474483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2548c577b8a1SJoseph Chan 	int i, err;
2549c577b8a1SJoseph Chan 
2550c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2551c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2552c577b8a1SJoseph Chan 
2553c577b8a1SJoseph Chan 		if (!nid)
2554c577b8a1SJoseph Chan 			continue;
2555c577b8a1SJoseph Chan 
25564483a2f5SLydia Wang 		nid_vol = nid_vols[i];
25574483a2f5SLydia Wang 
2558c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2559c577b8a1SJoseph Chan 			/* Center/LFE */
2560c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2561c577b8a1SJoseph Chan 					      "Center Playback Volume",
25624483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2563f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2564c577b8a1SJoseph Chan 			if (err < 0)
2565c577b8a1SJoseph Chan 				return err;
2566c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2567c577b8a1SJoseph Chan 					      "LFE Playback Volume",
25684483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2569f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2570c577b8a1SJoseph Chan 			if (err < 0)
2571c577b8a1SJoseph Chan 				return err;
2572c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2573c577b8a1SJoseph Chan 					      "Center Playback Switch",
25744483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2575f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2576c577b8a1SJoseph Chan 			if (err < 0)
2577c577b8a1SJoseph Chan 				return err;
2578c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2579c577b8a1SJoseph Chan 					      "LFE Playback Switch",
25804483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2581f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2582c577b8a1SJoseph Chan 			if (err < 0)
2583c577b8a1SJoseph Chan 				return err;
2584c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
25854483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2586c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2587c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
25884483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2589f7278fd0SJosepch Chan 								  HDA_INPUT));
2590c577b8a1SJoseph Chan 			if (err < 0)
2591c577b8a1SJoseph Chan 				return err;
2592c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2593c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
25944483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2595f7278fd0SJosepch Chan 								  HDA_INPUT));
2596c577b8a1SJoseph Chan 			if (err < 0)
2597c577b8a1SJoseph Chan 				return err;
2598c577b8a1SJoseph Chan 
2599c577b8a1SJoseph Chan 			/* add control to PW3 */
2600c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2601c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2602f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2603f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2604c577b8a1SJoseph Chan 			if (err < 0)
2605c577b8a1SJoseph Chan 				return err;
2606c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2607c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2608f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2609f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2610c577b8a1SJoseph Chan 			if (err < 0)
2611c577b8a1SJoseph Chan 				return err;
2612c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2613c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2614c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
26154483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2616f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2617c577b8a1SJoseph Chan 			if (err < 0)
2618c577b8a1SJoseph Chan 				return err;
2619c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2620c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
26214483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2622f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2623c577b8a1SJoseph Chan 			if (err < 0)
2624c577b8a1SJoseph Chan 				return err;
2625c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2626c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2627c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
26284483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2629f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2630c577b8a1SJoseph Chan 			if (err < 0)
2631c577b8a1SJoseph Chan 				return err;
2632c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2633c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
26344483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2635f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2636c577b8a1SJoseph Chan 			if (err < 0)
2637c577b8a1SJoseph Chan 				return err;
2638c577b8a1SJoseph Chan 		}
2639c577b8a1SJoseph Chan 	}
2640c577b8a1SJoseph Chan 
2641c577b8a1SJoseph Chan 	return 0;
2642c577b8a1SJoseph Chan }
2643c577b8a1SJoseph Chan 
2644c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2645c577b8a1SJoseph Chan {
2646c577b8a1SJoseph Chan 	int err;
2647c577b8a1SJoseph Chan 
2648c577b8a1SJoseph Chan 	if (!pin)
2649c577b8a1SJoseph Chan 		return 0;
2650c577b8a1SJoseph Chan 
2651c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2652c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2653c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2654c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2655cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2656c577b8a1SJoseph Chan 
2657c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2658c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2659c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2660c577b8a1SJoseph Chan 	if (err < 0)
2661c577b8a1SJoseph Chan 		return err;
2662c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2663c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2664c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2665c577b8a1SJoseph Chan 	if (err < 0)
2666c577b8a1SJoseph Chan 		return err;
2667c577b8a1SJoseph Chan 
2668c577b8a1SJoseph Chan 	return 0;
2669c577b8a1SJoseph Chan }
2670c577b8a1SJoseph Chan 
2671c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
267210a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
2673c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2674c577b8a1SJoseph Chan {
2675a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x18);
2676c577b8a1SJoseph Chan }
2677c577b8a1SJoseph Chan 
2678c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2679c577b8a1SJoseph Chan {
2680c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2681c577b8a1SJoseph Chan 	int err;
2682c577b8a1SJoseph Chan 
2683c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2684c577b8a1SJoseph Chan 	if (err < 0)
2685c577b8a1SJoseph Chan 		return err;
2686c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2687c577b8a1SJoseph Chan 	if (err < 0)
2688c577b8a1SJoseph Chan 		return err;
2689c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2690c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2691c577b8a1SJoseph Chan 
2692c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2693c577b8a1SJoseph Chan 	if (err < 0)
2694c577b8a1SJoseph Chan 		return err;
2695c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2696c577b8a1SJoseph Chan 	if (err < 0)
2697c577b8a1SJoseph Chan 		return err;
269810a20af7STakashi Iwai 	err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
2699c577b8a1SJoseph Chan 	if (err < 0)
2700c577b8a1SJoseph Chan 		return err;
2701c577b8a1SJoseph Chan 
2702c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2703c577b8a1SJoseph Chan 
27040852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2705c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
270655d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2707c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2708c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2709c577b8a1SJoseph Chan 
2710603c4019STakashi Iwai 	if (spec->kctls.list)
2711603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2712c577b8a1SJoseph Chan 
27130aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2714c577b8a1SJoseph Chan 
2715f8fdd495SHarald Welte 	if (spec->hp_mux)
27163d83e577STakashi Iwai 		via_hp_build(codec);
2717f8fdd495SHarald Welte 
27185b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2719c577b8a1SJoseph Chan 	return 1;
2720c577b8a1SJoseph Chan }
2721c577b8a1SJoseph Chan 
2722cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
272390dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = {
2724cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2725cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2726cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2727cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2728cb53c626STakashi Iwai 	{ } /* end */
2729cb53c626STakashi Iwai };
2730cb53c626STakashi Iwai #endif
2731cb53c626STakashi Iwai 
2732c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2733c577b8a1SJoseph Chan {
2734c577b8a1SJoseph Chan 	struct via_spec *spec;
2735c577b8a1SJoseph Chan 	int err;
2736c577b8a1SJoseph Chan 
2737c577b8a1SJoseph Chan 	/* create a codec specific record */
27385b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2739c577b8a1SJoseph Chan 	if (spec == NULL)
2740c577b8a1SJoseph Chan 		return -ENOMEM;
2741c577b8a1SJoseph Chan 
2742c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2743c577b8a1SJoseph Chan 	if (err < 0) {
2744c577b8a1SJoseph Chan 		via_free(codec);
2745c577b8a1SJoseph Chan 		return err;
2746c577b8a1SJoseph Chan 	} else if (!err) {
2747c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2748c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2749c577b8a1SJoseph Chan 	}
2750c577b8a1SJoseph Chan 
275169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
275269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2753c577b8a1SJoseph Chan 
2754c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2755c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2756c577b8a1SJoseph Chan 
2757c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2758c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2759c577b8a1SJoseph Chan 
2760a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2761c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2762c577b8a1SJoseph Chan 		spec->num_mixers++;
2763c577b8a1SJoseph Chan 	}
2764c577b8a1SJoseph Chan 
2765c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2766c577b8a1SJoseph Chan 
2767c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
276869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2769cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2770cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2771cb53c626STakashi Iwai #endif
2772c577b8a1SJoseph Chan 
2773c577b8a1SJoseph Chan 	return 0;
2774c577b8a1SJoseph Chan }
2775c577b8a1SJoseph Chan /*
2776c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2777c577b8a1SJoseph Chan  */
277890dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2779c577b8a1SJoseph Chan 	/*
2780c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2781c577b8a1SJoseph Chan 	 */
2782c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2783c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2784c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2785c577b8a1SJoseph Chan 
2786c577b8a1SJoseph Chan 
2787c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2788c577b8a1SJoseph Chan 	 * mixer widget
2789c577b8a1SJoseph Chan 	 */
2790c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2791c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2792c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2793c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2794c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2795c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2796c577b8a1SJoseph Chan 
2797c577b8a1SJoseph Chan 	/*
2798c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2799c577b8a1SJoseph Chan 	 */
2800c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2801c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2802c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2803c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2804c577b8a1SJoseph Chan 
2805c577b8a1SJoseph Chan 	/*
2806c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2807c577b8a1SJoseph Chan 	 */
2808c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2809c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2810c577b8a1SJoseph Chan 
2811c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2812c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2813c577b8a1SJoseph Chan 	/* PW9 Output enable */
2814c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2815c577b8a1SJoseph Chan 	{ }
2816c577b8a1SJoseph Chan };
2817c577b8a1SJoseph Chan 
2818c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2819c577b8a1SJoseph Chan {
2820c577b8a1SJoseph Chan 	struct via_spec *spec;
2821c577b8a1SJoseph Chan 	int err;
2822c577b8a1SJoseph Chan 
2823c577b8a1SJoseph Chan 	/* create a codec specific record */
28245b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2825c577b8a1SJoseph Chan 	if (spec == NULL)
2826c577b8a1SJoseph Chan 		return -ENOMEM;
2827c577b8a1SJoseph Chan 
2828c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2829c577b8a1SJoseph Chan 	if (err < 0) {
2830c577b8a1SJoseph Chan 		via_free(codec);
2831c577b8a1SJoseph Chan 		return err;
2832c577b8a1SJoseph Chan 	} else if (!err) {
2833c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2834c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2835c577b8a1SJoseph Chan 	}
2836c577b8a1SJoseph Chan 
283769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
283869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2839c577b8a1SJoseph Chan 
2840c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2841c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2842c577b8a1SJoseph Chan 
2843c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2844c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2845c577b8a1SJoseph Chan 
2846a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2847c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2848c577b8a1SJoseph Chan 		spec->num_mixers++;
2849c577b8a1SJoseph Chan 	}
2850c577b8a1SJoseph Chan 
2851c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2852c577b8a1SJoseph Chan 
2853c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
285469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2855cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2856cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2857cb53c626STakashi Iwai #endif
2858f7278fd0SJosepch Chan 	return 0;
2859f7278fd0SJosepch Chan }
2860f7278fd0SJosepch Chan 
2861f7278fd0SJosepch Chan /* capture mixer elements */
286290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2863f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2864f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2865f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2866f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2867f7278fd0SJosepch Chan 	{
2868f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2869f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2870f7278fd0SJosepch Chan 		 * So call somewhat different..
2871f7278fd0SJosepch Chan 		 */
2872f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
2873f7278fd0SJosepch Chan 		.name = "Input Source",
2874f7278fd0SJosepch Chan 		.count = 1,
2875f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
2876f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
2877f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
2878f7278fd0SJosepch Chan 	},
2879f7278fd0SJosepch Chan 	{ } /* end */
2880f7278fd0SJosepch Chan };
2881f7278fd0SJosepch Chan /*
2882f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2883f7278fd0SJosepch Chan  */
288490dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2885f7278fd0SJosepch Chan 	/*
2886f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2887f7278fd0SJosepch Chan 	 */
2888f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2889f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2890f7278fd0SJosepch Chan 
2891f7278fd0SJosepch Chan 
2892f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2893f7278fd0SJosepch Chan 	 * mixer widget
2894f7278fd0SJosepch Chan 	 */
2895f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2896f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2897f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2898f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2899f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2900f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2901f7278fd0SJosepch Chan 
2902f7278fd0SJosepch Chan 	/*
2903f7278fd0SJosepch Chan 	 * Set up output mixers
2904f7278fd0SJosepch Chan 	 */
2905f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2906f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2907f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2908f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2909f7278fd0SJosepch Chan 
2910f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2911bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2912f7278fd0SJosepch Chan 	/* PW9 Output enable */
2913f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2914f7278fd0SJosepch Chan 	/* PW10 Input enable */
2915f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2916f7278fd0SJosepch Chan 	{ }
2917f7278fd0SJosepch Chan };
2918f7278fd0SJosepch Chan 
291990dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2920f7278fd0SJosepch Chan 	/*
2921f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2922f7278fd0SJosepch Chan 	 */
2923f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2924f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2925f7278fd0SJosepch Chan 
2926f7278fd0SJosepch Chan 
2927f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2928f7278fd0SJosepch Chan 	 * mixer widget
2929f7278fd0SJosepch Chan 	 */
2930f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2931f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2932f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2933f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2934f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2935f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2936f7278fd0SJosepch Chan 
2937f7278fd0SJosepch Chan 	/*
2938f7278fd0SJosepch Chan 	 * Set up output mixers
2939f7278fd0SJosepch Chan 	 */
2940f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2941f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2942f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2943f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2944f7278fd0SJosepch Chan 
2945f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2946f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2947f7278fd0SJosepch Chan 	/* PW9 Output enable */
2948f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2949f7278fd0SJosepch Chan 	/* PW10 Input enable */
2950f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2951f7278fd0SJosepch Chan 	{ }
2952f7278fd0SJosepch Chan };
2953f7278fd0SJosepch Chan 
295490dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = {
2955a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2956a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2957a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2958a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2959a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2960a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2961a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2962a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2963a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
296469e52a80SHarald Welte 	{ }
296569e52a80SHarald Welte };
296669e52a80SHarald Welte 
296717314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
296817314379SLydia Wang 			      struct hda_codec *codec,
296917314379SLydia Wang 			      struct snd_pcm_substream *substream)
297017314379SLydia Wang {
297117314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
297217314379SLydia Wang 		&& substream->ref_count == 0;
297317314379SLydia Wang 
297417314379SLydia Wang 	analog_low_current_mode(codec, idle);
297517314379SLydia Wang 	return 0;
297617314379SLydia Wang }
297717314379SLydia Wang 
297890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
29790aa62aefSHarald Welte 	.substreams = 2,
2980f7278fd0SJosepch Chan 	.channels_min = 2,
2981f7278fd0SJosepch Chan 	.channels_max = 8,
2982f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2983f7278fd0SJosepch Chan 	.ops = {
2984f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29850aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
298617314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
298717314379SLydia Wang 		.close = via_pcm_open_close
2988f7278fd0SJosepch Chan 	},
2989f7278fd0SJosepch Chan };
2990f7278fd0SJosepch Chan 
299190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
29920aa62aefSHarald Welte 	.substreams = 2,
2993f7278fd0SJosepch Chan 	.channels_min = 2,
2994f7278fd0SJosepch Chan 	.channels_max = 4,
2995f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2996f7278fd0SJosepch Chan 	.ops = {
2997f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29980aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
29990aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
3000f7278fd0SJosepch Chan 	},
3001f7278fd0SJosepch Chan };
3002f7278fd0SJosepch Chan 
300390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3004f7278fd0SJosepch Chan 	.substreams = 2,
3005f7278fd0SJosepch Chan 	.channels_min = 2,
3006f7278fd0SJosepch Chan 	.channels_max = 2,
3007f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
3008f7278fd0SJosepch Chan 	.ops = {
300917314379SLydia Wang 		.open = via_pcm_open_close,
3010f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
301117314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
301217314379SLydia Wang 		.close = via_pcm_open_close
3013f7278fd0SJosepch Chan 	},
3014f7278fd0SJosepch Chan };
3015f7278fd0SJosepch Chan 
301690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3017f7278fd0SJosepch Chan 	.substreams = 1,
3018f7278fd0SJosepch Chan 	.channels_min = 2,
3019f7278fd0SJosepch Chan 	.channels_max = 2,
3020f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
3021f7278fd0SJosepch Chan 	.ops = {
3022f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
3023f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
30249da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
30259da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3026f7278fd0SJosepch Chan 	},
3027f7278fd0SJosepch Chan };
3028f7278fd0SJosepch Chan 
302990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3030f7278fd0SJosepch Chan 	.substreams = 1,
3031f7278fd0SJosepch Chan 	.channels_min = 2,
3032f7278fd0SJosepch Chan 	.channels_max = 2,
3033f7278fd0SJosepch Chan };
3034f7278fd0SJosepch Chan 
3035f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
3036f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3037f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
3038f7278fd0SJosepch Chan {
3039f7278fd0SJosepch Chan 	int i;
3040f7278fd0SJosepch Chan 	hda_nid_t nid;
3041f7278fd0SJosepch Chan 
3042f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
3043f7278fd0SJosepch Chan 
3044f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3045f7278fd0SJosepch Chan 
3046f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3047f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3048f7278fd0SJosepch Chan 		if (nid) {
3049f7278fd0SJosepch Chan 			/* config dac list */
3050f7278fd0SJosepch Chan 			switch (i) {
3051f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3052dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x10;
3053f7278fd0SJosepch Chan 				break;
3054f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3055dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x24;
3056f7278fd0SJosepch Chan 				break;
3057f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3058dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x11;
3059f7278fd0SJosepch Chan 				break;
3060f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3061dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x25;
3062f7278fd0SJosepch Chan 				break;
3063f7278fd0SJosepch Chan 			}
3064f7278fd0SJosepch Chan 		}
3065f7278fd0SJosepch Chan 	}
3066f7278fd0SJosepch Chan 
3067f7278fd0SJosepch Chan 	return 0;
3068f7278fd0SJosepch Chan }
3069f7278fd0SJosepch Chan 
3070f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3071f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3072f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3073f7278fd0SJosepch Chan {
3074f7278fd0SJosepch Chan 	char name[32];
3075ea734963STakashi Iwai 	static const char * const chname[4] = {
3076ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3077ea734963STakashi Iwai 	};
3078fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3079f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3080f7278fd0SJosepch Chan 	int i, err;
3081f7278fd0SJosepch Chan 
3082f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3083f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3084f7278fd0SJosepch Chan 
3085f7278fd0SJosepch Chan 		if (!nid)
3086f7278fd0SJosepch Chan 			continue;
3087f7278fd0SJosepch Chan 
3088f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3089f7278fd0SJosepch Chan 
3090f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3091f7278fd0SJosepch Chan 			/* Center/LFE */
3092f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3093f7278fd0SJosepch Chan 					      "Center Playback Volume",
3094f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3095f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3096f7278fd0SJosepch Chan 			if (err < 0)
3097f7278fd0SJosepch Chan 				return err;
3098f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3099f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3100f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3101f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3102f7278fd0SJosepch Chan 			if (err < 0)
3103f7278fd0SJosepch Chan 				return err;
3104f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3105f7278fd0SJosepch Chan 					      "Center Playback Switch",
3106f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3107f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3108f7278fd0SJosepch Chan 			if (err < 0)
3109f7278fd0SJosepch Chan 				return err;
3110f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3111f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3112f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3113f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3114f7278fd0SJosepch Chan 			if (err < 0)
3115f7278fd0SJosepch Chan 				return err;
3116f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3117f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3118f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3119f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3120f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3121f7278fd0SJosepch Chan 								  HDA_INPUT));
3122f7278fd0SJosepch Chan 			if (err < 0)
3123f7278fd0SJosepch Chan 				return err;
3124f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3125f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3126f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3127f7278fd0SJosepch Chan 								  HDA_INPUT));
3128f7278fd0SJosepch Chan 			if (err < 0)
3129f7278fd0SJosepch Chan 				return err;
3130f7278fd0SJosepch Chan 
3131f7278fd0SJosepch Chan 			/* add control to PW3 */
3132f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3133f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3134f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3135f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3136f7278fd0SJosepch Chan 			if (err < 0)
3137f7278fd0SJosepch Chan 				return err;
3138f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3139f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3140f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3141f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3142f7278fd0SJosepch Chan 			if (err < 0)
3143f7278fd0SJosepch Chan 				return err;
3144f7278fd0SJosepch Chan 		} else {
3145f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3146f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3147f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3148f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3149f7278fd0SJosepch Chan 			if (err < 0)
3150f7278fd0SJosepch Chan 				return err;
3151f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3152f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3153f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3154f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3155f7278fd0SJosepch Chan 			if (err < 0)
3156f7278fd0SJosepch Chan 				return err;
3157f7278fd0SJosepch Chan 		}
3158f7278fd0SJosepch Chan 	}
3159f7278fd0SJosepch Chan 
3160f7278fd0SJosepch Chan 	return 0;
3161f7278fd0SJosepch Chan }
3162f7278fd0SJosepch Chan 
3163f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3164f7278fd0SJosepch Chan {
3165f7278fd0SJosepch Chan 	int err;
3166f7278fd0SJosepch Chan 
3167f7278fd0SJosepch Chan 	if (!pin)
3168f7278fd0SJosepch Chan 		return 0;
3169f7278fd0SJosepch Chan 
3170f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3171cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3172f7278fd0SJosepch Chan 
3173f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3174f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3175f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3176f7278fd0SJosepch Chan 	if (err < 0)
3177f7278fd0SJosepch Chan 		return err;
3178f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3179f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3180f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3181f7278fd0SJosepch Chan 	if (err < 0)
3182f7278fd0SJosepch Chan 		return err;
3183f7278fd0SJosepch Chan 
31840aa62aefSHarald Welte 	create_hp_imux(spec);
31850aa62aefSHarald Welte 
3186f7278fd0SJosepch Chan 	return 0;
3187f7278fd0SJosepch Chan }
3188f7278fd0SJosepch Chan 
3189f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
319010a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
3191f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3192f7278fd0SJosepch Chan {
3193a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16);
3194f7278fd0SJosepch Chan }
3195f7278fd0SJosepch Chan 
3196f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3197f7278fd0SJosepch Chan {
3198f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3199f7278fd0SJosepch Chan 	int err;
3200f7278fd0SJosepch Chan 
3201f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3202f7278fd0SJosepch Chan 	if (err < 0)
3203f7278fd0SJosepch Chan 		return err;
3204f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3205f7278fd0SJosepch Chan 	if (err < 0)
3206f7278fd0SJosepch Chan 		return err;
3207f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3208f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3209f7278fd0SJosepch Chan 
3210f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3211f7278fd0SJosepch Chan 	if (err < 0)
3212f7278fd0SJosepch Chan 		return err;
3213f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3214f7278fd0SJosepch Chan 	if (err < 0)
3215f7278fd0SJosepch Chan 		return err;
321610a20af7STakashi Iwai 	err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
3217f7278fd0SJosepch Chan 	if (err < 0)
3218f7278fd0SJosepch Chan 		return err;
3219f7278fd0SJosepch Chan 
3220f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3221f7278fd0SJosepch Chan 
32220852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3223f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
322455d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3225f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3226f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3227f7278fd0SJosepch Chan 
3228603c4019STakashi Iwai 	if (spec->kctls.list)
3229603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3230f7278fd0SJosepch Chan 
32310aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
32320aa62aefSHarald Welte 
3233f8fdd495SHarald Welte 	if (spec->hp_mux)
32343d83e577STakashi Iwai 		via_hp_build(codec);
3235f7278fd0SJosepch Chan 
32365b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3237f7278fd0SJosepch Chan 	return 1;
3238f7278fd0SJosepch Chan }
3239f7278fd0SJosepch Chan 
3240f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
324190dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = {
3242f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3243f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3244f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3245f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3246f7278fd0SJosepch Chan 	{ } /* end */
3247f7278fd0SJosepch Chan };
3248f7278fd0SJosepch Chan #endif
32493e95b9abSLydia Wang 
32503e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
32513e95b9abSLydia Wang {
32523e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
32533e95b9abSLydia Wang 	int imux_is_smixer;
32543e95b9abSLydia Wang 	unsigned int parm;
32553e95b9abSLydia Wang 	int is_8ch = 0;
3256bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
3257bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
32583e95b9abSLydia Wang 		is_8ch = 1;
32593e95b9abSLydia Wang 
32603e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
32613e95b9abSLydia Wang 	imux_is_smixer =
32623e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
32633e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
32643e95b9abSLydia Wang 	/* inputs */
32653e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
32663e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32673e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
32683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
32693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
32703e95b9abSLydia Wang 	if (imux_is_smixer)
32713e95b9abSLydia Wang 		parm = AC_PWRST_D0;
32723e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
32733e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
32743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
32753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
32763e95b9abSLydia Wang 
32773e95b9abSLydia Wang 	/* outputs */
32783e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
32793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
32813e95b9abSLydia Wang 	if (spec->smart51_enabled)
32823e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
32833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
32843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
32853e95b9abSLydia Wang 
32863e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
32873e95b9abSLydia Wang 	if (is_8ch) {
32883e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
32903e95b9abSLydia Wang 		if (spec->smart51_enabled)
32913e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
32923e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
32933e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
32953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3296bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
3297bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
3298bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
3299bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
3300bc92df7fSLydia Wang 		if (spec->smart51_enabled)
3301bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
3302bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
3303bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3304bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3305bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
33063e95b9abSLydia Wang 	}
33073e95b9abSLydia Wang 
33083e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
33093e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33103e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
33113e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
33123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
33133e95b9abSLydia Wang 	if (is_8ch)
33143e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
33153e95b9abSLydia Wang 
33163e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
33173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
33183e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
33193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
33203e95b9abSLydia Wang 	if (is_8ch) {
33213e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
33223e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
33233e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
33243e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3325bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
3326bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3327bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
33283e95b9abSLydia Wang }
33293e95b9abSLydia Wang 
3330518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3331f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3332f7278fd0SJosepch Chan {
3333f7278fd0SJosepch Chan 	struct via_spec *spec;
3334f7278fd0SJosepch Chan 	int err;
3335f7278fd0SJosepch Chan 
3336518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3337518bf3baSLydia Wang 		return patch_vt1708S(codec);
3338f7278fd0SJosepch Chan 	/* create a codec specific record */
33395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3340f7278fd0SJosepch Chan 	if (spec == NULL)
3341f7278fd0SJosepch Chan 		return -ENOMEM;
3342f7278fd0SJosepch Chan 
3343f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3344f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3345f7278fd0SJosepch Chan 	if (err < 0) {
3346f7278fd0SJosepch Chan 		via_free(codec);
3347f7278fd0SJosepch Chan 		return err;
3348f7278fd0SJosepch Chan 	} else if (!err) {
3349f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3350f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3351f7278fd0SJosepch Chan 	}
3352f7278fd0SJosepch Chan 
335369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
335469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3355f7278fd0SJosepch Chan 
3356f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3357f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3358f7278fd0SJosepch Chan 
3359f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3360f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3361f7278fd0SJosepch Chan 
3362a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3363f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3364f7278fd0SJosepch Chan 		spec->num_mixers++;
3365f7278fd0SJosepch Chan 	}
3366f7278fd0SJosepch Chan 
3367f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3368f7278fd0SJosepch Chan 
3369f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
337069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3371f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3372f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3373f7278fd0SJosepch Chan #endif
3374f7278fd0SJosepch Chan 
33753e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
33763e95b9abSLydia Wang 
3377f7278fd0SJosepch Chan 	return 0;
3378f7278fd0SJosepch Chan }
3379f7278fd0SJosepch Chan 
3380f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3381f7278fd0SJosepch Chan {
3382f7278fd0SJosepch Chan 	struct via_spec *spec;
3383f7278fd0SJosepch Chan 	int err;
3384f7278fd0SJosepch Chan 
3385f7278fd0SJosepch Chan 	/* create a codec specific record */
33865b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3387f7278fd0SJosepch Chan 	if (spec == NULL)
3388f7278fd0SJosepch Chan 		return -ENOMEM;
3389f7278fd0SJosepch Chan 
3390f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3391f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3392f7278fd0SJosepch Chan 	if (err < 0) {
3393f7278fd0SJosepch Chan 		via_free(codec);
3394f7278fd0SJosepch Chan 		return err;
3395f7278fd0SJosepch Chan 	} else if (!err) {
3396f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3397f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3398f7278fd0SJosepch Chan 	}
3399f7278fd0SJosepch Chan 
340069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
340169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3402f7278fd0SJosepch Chan 
3403f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3404f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3405f7278fd0SJosepch Chan 
3406f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3407f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3408f7278fd0SJosepch Chan 
3409a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3410f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3411f7278fd0SJosepch Chan 		spec->num_mixers++;
3412f7278fd0SJosepch Chan 	}
3413f7278fd0SJosepch Chan 
3414f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3415f7278fd0SJosepch Chan 
3416f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
341769e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3418f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3419f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3420f7278fd0SJosepch Chan #endif
3421c577b8a1SJoseph Chan 
34223e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
34233e95b9abSLydia Wang 
3424c577b8a1SJoseph Chan 	return 0;
3425c577b8a1SJoseph Chan }
3426c577b8a1SJoseph Chan 
3427d949cac1SHarald Welte /* Patch for VT1708S */
3428d949cac1SHarald Welte 
3429d949cac1SHarald Welte /* capture mixer elements */
343090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3431d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3432d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3433d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3434d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
34356369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
34366369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
34376369bcfcSLydia Wang 			 HDA_INPUT),
3438d949cac1SHarald Welte 	{
3439d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3440d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3441d949cac1SHarald Welte 		 * So call somewhat different..
3442d949cac1SHarald Welte 		 */
3443d949cac1SHarald Welte 		/* .name = "Capture Source", */
3444d949cac1SHarald Welte 		.name = "Input Source",
3445d949cac1SHarald Welte 		.count = 1,
3446d949cac1SHarald Welte 		.info = via_mux_enum_info,
3447d949cac1SHarald Welte 		.get = via_mux_enum_get,
3448d949cac1SHarald Welte 		.put = via_mux_enum_put,
3449d949cac1SHarald Welte 	},
3450d949cac1SHarald Welte 	{ } /* end */
3451d949cac1SHarald Welte };
3452d949cac1SHarald Welte 
345390dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = {
3454d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3455d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3456d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3457d949cac1SHarald Welte 
3458d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3459d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3460d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3461d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3462d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3463d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3464d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3465d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3466d949cac1SHarald Welte 
3467d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3468d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
34695691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3470d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
34715691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3472d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3473d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3474bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3475bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3476d949cac1SHarald Welte 	{ }
3477d949cac1SHarald Welte };
3478d949cac1SHarald Welte 
347990dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = {
3480a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3481a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3482a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3483a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3484a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3485a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3486a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3487a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3488a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
348969e52a80SHarald Welte 	{ }
349069e52a80SHarald Welte };
349169e52a80SHarald Welte 
349290dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = {
3493bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3494bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3495bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3496bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3497bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3498bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3499bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3500bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3501bc92df7fSLydia Wang 	{ }
3502bc92df7fSLydia Wang };
3503bc92df7fSLydia Wang 
350490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3505d949cac1SHarald Welte 	.substreams = 2,
3506d949cac1SHarald Welte 	.channels_min = 2,
3507d949cac1SHarald Welte 	.channels_max = 8,
3508d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3509d949cac1SHarald Welte 	.ops = {
3510d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3511c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3512c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
351317314379SLydia Wang 		.close = via_pcm_open_close
3514d949cac1SHarald Welte 	},
3515d949cac1SHarald Welte };
3516d949cac1SHarald Welte 
351790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1705_pcm_analog_playback = {
3518bc92df7fSLydia Wang 	.substreams = 2,
3519bc92df7fSLydia Wang 	.channels_min = 2,
3520bc92df7fSLydia Wang 	.channels_max = 6,
3521bc92df7fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
3522bc92df7fSLydia Wang 	.ops = {
3523bc92df7fSLydia Wang 		.open = via_playback_pcm_open,
3524bc92df7fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3525bc92df7fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
3526bc92df7fSLydia Wang 		.close = via_pcm_open_close
3527bc92df7fSLydia Wang 	},
3528bc92df7fSLydia Wang };
3529bc92df7fSLydia Wang 
353090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3531d949cac1SHarald Welte 	.substreams = 2,
3532d949cac1SHarald Welte 	.channels_min = 2,
3533d949cac1SHarald Welte 	.channels_max = 2,
3534d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3535d949cac1SHarald Welte 	.ops = {
353617314379SLydia Wang 		.open = via_pcm_open_close,
3537d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
353817314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
353917314379SLydia Wang 		.close = via_pcm_open_close
3540d949cac1SHarald Welte 	},
3541d949cac1SHarald Welte };
3542d949cac1SHarald Welte 
354390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_digital_playback = {
35449da29271STakashi Iwai 	.substreams = 1,
3545d949cac1SHarald Welte 	.channels_min = 2,
3546d949cac1SHarald Welte 	.channels_max = 2,
3547d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3548d949cac1SHarald Welte 	.ops = {
3549d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3550d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
35519da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
35529da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3553d949cac1SHarald Welte 	},
3554d949cac1SHarald Welte };
3555d949cac1SHarald Welte 
3556d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3557d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3558d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3559d949cac1SHarald Welte {
3560d949cac1SHarald Welte 	int i;
3561d949cac1SHarald Welte 	hda_nid_t nid;
3562d949cac1SHarald Welte 
3563d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3564d949cac1SHarald Welte 
3565d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3566d949cac1SHarald Welte 
3567d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3568d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3569d949cac1SHarald Welte 		if (nid) {
3570d949cac1SHarald Welte 			/* config dac list */
3571d949cac1SHarald Welte 			switch (i) {
3572d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3573dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x10;
3574d949cac1SHarald Welte 				break;
3575d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3576bc92df7fSLydia Wang 				if (spec->codec->vendor_id == 0x11064397)
3577dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x25;
3578bc92df7fSLydia Wang 				else
3579dda14410STakashi Iwai 					spec->private_dac_nids[i] = 0x24;
3580d949cac1SHarald Welte 				break;
3581d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3582dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x11;
3583d949cac1SHarald Welte 				break;
3584d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3585dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x25;
3586d949cac1SHarald Welte 				break;
3587d949cac1SHarald Welte 			}
3588d949cac1SHarald Welte 		}
3589d949cac1SHarald Welte 	}
3590d949cac1SHarald Welte 
359132e0191dSClemens Ladisch 	/* for Smart 5.1, line/mic inputs double as output pins */
359232e0191dSClemens Ladisch 	if (cfg->line_outs == 1) {
359332e0191dSClemens Ladisch 		spec->multiout.num_dacs = 3;
3594dda14410STakashi Iwai 		spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11;
3595bc92df7fSLydia Wang 		if (spec->codec->vendor_id == 0x11064397)
3596dda14410STakashi Iwai 			spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25;
3597bc92df7fSLydia Wang 		else
3598dda14410STakashi Iwai 			spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24;
359932e0191dSClemens Ladisch 	}
360032e0191dSClemens Ladisch 
3601d949cac1SHarald Welte 	return 0;
3602d949cac1SHarald Welte }
3603d949cac1SHarald Welte 
3604d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3605bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec,
3606d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3607d949cac1SHarald Welte {
3608bc92df7fSLydia Wang 	struct via_spec *spec = codec->spec;
3609d949cac1SHarald Welte 	char name[32];
3610ea734963STakashi Iwai 	static const char * const chname[4] = {
3611ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3612ea734963STakashi Iwai 	};
3613bc92df7fSLydia Wang 	hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25},
3614bc92df7fSLydia Wang 				     {0x10, 0x11, 0x25, 0} };
3615bc92df7fSLydia Wang 	hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27},
3616bc92df7fSLydia Wang 				      {0x1C, 0x18, 0x27, 0} };
3617d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3618d949cac1SHarald Welte 	int i, err;
3619d949cac1SHarald Welte 
3620d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3621d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3622d949cac1SHarald Welte 
362332e0191dSClemens Ladisch 		/* for Smart 5.1, there are always at least six channels */
362432e0191dSClemens Ladisch 		if (!nid && i > AUTO_SEQ_CENLFE)
3625d949cac1SHarald Welte 			continue;
3626d949cac1SHarald Welte 
3627bc92df7fSLydia Wang 		if (codec->vendor_id == 0x11064397) {
3628bc92df7fSLydia Wang 			nid_vol = nid_vols[1][i];
3629bc92df7fSLydia Wang 			nid_mute = nid_mutes[1][i];
3630bc92df7fSLydia Wang 		} else {
3631bc92df7fSLydia Wang 			nid_vol = nid_vols[0][i];
3632bc92df7fSLydia Wang 			nid_mute = nid_mutes[0][i];
3633bc92df7fSLydia Wang 		}
3634bc92df7fSLydia Wang 		if (!nid_vol && !nid_mute)
3635bc92df7fSLydia Wang 			continue;
3636d949cac1SHarald Welte 
3637d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3638d949cac1SHarald Welte 			/* Center/LFE */
3639d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3640d949cac1SHarald Welte 					      "Center Playback Volume",
3641d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3642d949cac1SHarald Welte 								  HDA_OUTPUT));
3643d949cac1SHarald Welte 			if (err < 0)
3644d949cac1SHarald Welte 				return err;
3645d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3646d949cac1SHarald Welte 					      "LFE Playback Volume",
3647d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3648d949cac1SHarald Welte 								  HDA_OUTPUT));
3649d949cac1SHarald Welte 			if (err < 0)
3650d949cac1SHarald Welte 				return err;
3651d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3652d949cac1SHarald Welte 					      "Center Playback Switch",
3653d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3654d949cac1SHarald Welte 								  1, 0,
3655d949cac1SHarald Welte 								  HDA_OUTPUT));
3656d949cac1SHarald Welte 			if (err < 0)
3657d949cac1SHarald Welte 				return err;
3658d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3659d949cac1SHarald Welte 					      "LFE Playback Switch",
3660d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3661d949cac1SHarald Welte 								  2, 0,
3662d949cac1SHarald Welte 								  HDA_OUTPUT));
3663d949cac1SHarald Welte 			if (err < 0)
3664d949cac1SHarald Welte 				return err;
3665d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3666d949cac1SHarald Welte 			/* add control to mixer index 0 */
3667d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3668d949cac1SHarald Welte 					      "Master Front Playback Volume",
3669d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3670d949cac1SHarald Welte 								  HDA_INPUT));
3671d949cac1SHarald Welte 			if (err < 0)
3672d949cac1SHarald Welte 				return err;
3673d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3674d949cac1SHarald Welte 					      "Master Front Playback Switch",
3675d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3676d949cac1SHarald Welte 								  HDA_INPUT));
3677d949cac1SHarald Welte 			if (err < 0)
3678d949cac1SHarald Welte 				return err;
3679d949cac1SHarald Welte 
3680d949cac1SHarald Welte 			/* Front */
3681d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3682d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3683d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3684d949cac1SHarald Welte 								  HDA_OUTPUT));
3685d949cac1SHarald Welte 			if (err < 0)
3686d949cac1SHarald Welte 				return err;
3687d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3688d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3689d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3690d949cac1SHarald Welte 								  3, 0,
3691d949cac1SHarald Welte 								  HDA_OUTPUT));
3692d949cac1SHarald Welte 			if (err < 0)
3693d949cac1SHarald Welte 				return err;
3694d949cac1SHarald Welte 		} else {
3695d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3696d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3697d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3698d949cac1SHarald Welte 								  HDA_OUTPUT));
3699d949cac1SHarald Welte 			if (err < 0)
3700d949cac1SHarald Welte 				return err;
3701d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3702d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3703d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3704d949cac1SHarald Welte 								  3, 0,
3705d949cac1SHarald Welte 								  HDA_OUTPUT));
3706d949cac1SHarald Welte 			if (err < 0)
3707d949cac1SHarald Welte 				return err;
3708d949cac1SHarald Welte 		}
3709d949cac1SHarald Welte 	}
3710d949cac1SHarald Welte 
3711d949cac1SHarald Welte 	return 0;
3712d949cac1SHarald Welte }
3713d949cac1SHarald Welte 
3714d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3715d949cac1SHarald Welte {
3716d949cac1SHarald Welte 	int err;
3717d949cac1SHarald Welte 
3718d949cac1SHarald Welte 	if (!pin)
3719d949cac1SHarald Welte 		return 0;
3720d949cac1SHarald Welte 
3721d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3722cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3723d949cac1SHarald Welte 
3724d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3725d949cac1SHarald Welte 			      "Headphone Playback Volume",
3726d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3727d949cac1SHarald Welte 	if (err < 0)
3728d949cac1SHarald Welte 		return err;
3729d949cac1SHarald Welte 
3730d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3731d949cac1SHarald Welte 			      "Headphone Playback Switch",
3732d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3733d949cac1SHarald Welte 	if (err < 0)
3734d949cac1SHarald Welte 		return err;
3735d949cac1SHarald Welte 
37360aa62aefSHarald Welte 	create_hp_imux(spec);
37370aa62aefSHarald Welte 
3738d949cac1SHarald Welte 	return 0;
3739d949cac1SHarald Welte }
3740d949cac1SHarald Welte 
3741d949cac1SHarald Welte /* create playback/capture controls for input pins */
374210a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
3743d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
3744d949cac1SHarald Welte {
3745a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16);
3746d949cac1SHarald Welte }
3747d949cac1SHarald Welte 
37489da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
37499da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
37509da29271STakashi Iwai {
37519da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
37529da29271STakashi Iwai 	int i;
37539da29271STakashi Iwai 
37549da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
37559da29271STakashi Iwai 		hda_nid_t nid;
37569da29271STakashi Iwai 		int conn;
37579da29271STakashi Iwai 
37589da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
37599da29271STakashi Iwai 		if (!nid)
37609da29271STakashi Iwai 			continue;
37619da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
37629da29271STakashi Iwai 		if (conn < 1)
37639da29271STakashi Iwai 			continue;
37649da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
37659da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
37669da29271STakashi Iwai 		else {
37679da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
37689da29271STakashi Iwai 			break; /* at most two dig outs */
37699da29271STakashi Iwai 		}
37709da29271STakashi Iwai 	}
37719da29271STakashi Iwai }
37729da29271STakashi Iwai 
3773d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3774d949cac1SHarald Welte {
3775d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3776d949cac1SHarald Welte 	int err;
3777d949cac1SHarald Welte 
37789da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3779d949cac1SHarald Welte 	if (err < 0)
3780d949cac1SHarald Welte 		return err;
3781d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3782d949cac1SHarald Welte 	if (err < 0)
3783d949cac1SHarald Welte 		return err;
3784d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3785d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3786d949cac1SHarald Welte 
3787bc92df7fSLydia Wang 	err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg);
3788d949cac1SHarald Welte 	if (err < 0)
3789d949cac1SHarald Welte 		return err;
3790d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3791d949cac1SHarald Welte 	if (err < 0)
3792d949cac1SHarald Welte 		return err;
379310a20af7STakashi Iwai 	err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
3794d949cac1SHarald Welte 	if (err < 0)
3795d949cac1SHarald Welte 		return err;
3796d949cac1SHarald Welte 
3797d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3798d949cac1SHarald Welte 
37999da29271STakashi Iwai 	fill_dig_outs(codec);
380098aa34c0SHarald Welte 
3801603c4019STakashi Iwai 	if (spec->kctls.list)
3802603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3803d949cac1SHarald Welte 
38040aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
38050aa62aefSHarald Welte 
3806f8fdd495SHarald Welte 	if (spec->hp_mux)
38073d83e577STakashi Iwai 		via_hp_build(codec);
3808d949cac1SHarald Welte 
38095b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3810d949cac1SHarald Welte 	return 1;
3811d949cac1SHarald Welte }
3812d949cac1SHarald Welte 
3813d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
381490dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = {
3815d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3816d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3817d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3818d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3819d949cac1SHarald Welte 	{ } /* end */
3820d949cac1SHarald Welte };
3821d949cac1SHarald Welte #endif
3822d949cac1SHarald Welte 
38236369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
38246369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
38256369bcfcSLydia Wang {
38266369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
38276369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
38286369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
38296369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
38306369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
38316369bcfcSLydia Wang }
38326369bcfcSLydia Wang 
3833d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3834d949cac1SHarald Welte {
3835d949cac1SHarald Welte 	struct via_spec *spec;
3836d949cac1SHarald Welte 	int err;
3837d949cac1SHarald Welte 
3838d949cac1SHarald Welte 	/* create a codec specific record */
38395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3840d949cac1SHarald Welte 	if (spec == NULL)
3841d949cac1SHarald Welte 		return -ENOMEM;
3842d949cac1SHarald Welte 
3843d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3844d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3845d949cac1SHarald Welte 	if (err < 0) {
3846d949cac1SHarald Welte 		via_free(codec);
3847d949cac1SHarald Welte 		return err;
3848d949cac1SHarald Welte 	} else if (!err) {
3849d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3850d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3851d949cac1SHarald Welte 	}
3852d949cac1SHarald Welte 
385369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3854bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3855bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3856bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3857bc92df7fSLydia Wang 	else
3858bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3859bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3860d949cac1SHarald Welte 
3861bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3862bc92df7fSLydia Wang 		spec->stream_analog_playback = &vt1705_pcm_analog_playback;
3863bc92df7fSLydia Wang 	else
3864d949cac1SHarald Welte 		spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3865d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3866d949cac1SHarald Welte 
3867d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3868d949cac1SHarald Welte 
3869a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
38706369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
38716369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3872d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3873d949cac1SHarald Welte 		spec->num_mixers++;
3874d949cac1SHarald Welte 	}
3875d949cac1SHarald Welte 
3876d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3877d949cac1SHarald Welte 
3878d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
387969e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3880d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3881d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3882d949cac1SHarald Welte #endif
3883d949cac1SHarald Welte 
3884518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3885518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3886518bf3baSLydia Wang 		kfree(codec->chip_name);
3887518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3888518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3889518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3890518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3891970f630fSLydia Wang 	}
3892bc92df7fSLydia Wang 	/* correct names for VT1705 */
3893bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3894bc92df7fSLydia Wang 		kfree(codec->chip_name);
3895bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3896bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3897bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3898bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3899bc92df7fSLydia Wang 	}
39003e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3901d949cac1SHarald Welte 	return 0;
3902d949cac1SHarald Welte }
3903d949cac1SHarald Welte 
3904d949cac1SHarald Welte /* Patch for VT1702 */
3905d949cac1SHarald Welte 
3906d949cac1SHarald Welte /* capture mixer elements */
390790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = {
3908d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3909d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3910d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3911d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3912d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3913d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3914d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3915d949cac1SHarald Welte 			 HDA_INPUT),
3916d949cac1SHarald Welte 	{
3917d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3918d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3919d949cac1SHarald Welte 		 * So call somewhat different..
3920d949cac1SHarald Welte 		 */
3921d949cac1SHarald Welte 		/* .name = "Capture Source", */
3922d949cac1SHarald Welte 		.name = "Input Source",
3923d949cac1SHarald Welte 		.count = 1,
3924d949cac1SHarald Welte 		.info = via_mux_enum_info,
3925d949cac1SHarald Welte 		.get = via_mux_enum_get,
3926d949cac1SHarald Welte 		.put = via_mux_enum_put,
3927d949cac1SHarald Welte 	},
3928d949cac1SHarald Welte 	{ } /* end */
3929d949cac1SHarald Welte };
3930d949cac1SHarald Welte 
393190dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = {
3932d949cac1SHarald Welte 	/*
3933d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3934d949cac1SHarald Welte 	 */
3935d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3936d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3937d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3938d949cac1SHarald Welte 
3939d949cac1SHarald Welte 
3940d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3941d949cac1SHarald Welte 	 * mixer widget
3942d949cac1SHarald Welte 	 */
3943d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3944d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3945d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3946d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3947d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3948d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3949d949cac1SHarald Welte 
3950d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3951d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3952d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3953d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3954d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3955bc7e7e5cSLydia Wang 	/* mixer enable */
3956bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3957bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3958bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3959d949cac1SHarald Welte 	{ }
3960d949cac1SHarald Welte };
3961d949cac1SHarald Welte 
396290dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = {
3963a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3964a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3965a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3966a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3967a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3968a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
396969e52a80SHarald Welte 	{ }
397069e52a80SHarald Welte };
397169e52a80SHarald Welte 
397290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_playback = {
39730aa62aefSHarald Welte 	.substreams = 2,
3974d949cac1SHarald Welte 	.channels_min = 2,
3975d949cac1SHarald Welte 	.channels_max = 2,
3976d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3977d949cac1SHarald Welte 	.ops = {
3978d949cac1SHarald Welte 		.open = via_playback_pcm_open,
39790aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
398017314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
398117314379SLydia Wang 		.close = via_pcm_open_close
3982d949cac1SHarald Welte 	},
3983d949cac1SHarald Welte };
3984d949cac1SHarald Welte 
398590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_capture = {
3986d949cac1SHarald Welte 	.substreams = 3,
3987d949cac1SHarald Welte 	.channels_min = 2,
3988d949cac1SHarald Welte 	.channels_max = 2,
3989d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
3990d949cac1SHarald Welte 	.ops = {
399117314379SLydia Wang 		.open = via_pcm_open_close,
3992d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
399317314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
399417314379SLydia Wang 		.close = via_pcm_open_close
3995d949cac1SHarald Welte 	},
3996d949cac1SHarald Welte };
3997d949cac1SHarald Welte 
399890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_digital_playback = {
39995691ec7fSHarald Welte 	.substreams = 2,
4000d949cac1SHarald Welte 	.channels_min = 2,
4001d949cac1SHarald Welte 	.channels_max = 2,
4002d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
4003d949cac1SHarald Welte 	.ops = {
4004d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
4005d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
40069da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
40079da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
4008d949cac1SHarald Welte 	},
4009d949cac1SHarald Welte };
4010d949cac1SHarald Welte 
4011d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4012d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4013d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4014d949cac1SHarald Welte {
4015d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4016d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4017d949cac1SHarald Welte 
4018d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4019d949cac1SHarald Welte 		/* config dac list */
4020dda14410STakashi Iwai 		spec->private_dac_nids[0] = 0x10;
4021d949cac1SHarald Welte 	}
4022d949cac1SHarald Welte 
4023d949cac1SHarald Welte 	return 0;
4024d949cac1SHarald Welte }
4025d949cac1SHarald Welte 
4026d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4027d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4028d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4029d949cac1SHarald Welte {
4030d949cac1SHarald Welte 	int err;
4031d949cac1SHarald Welte 
4032d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4033d949cac1SHarald Welte 		return -1;
4034d949cac1SHarald Welte 
4035d949cac1SHarald Welte 	/* add control to mixer index 0 */
4036d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4037d949cac1SHarald Welte 			      "Master Front Playback Volume",
4038d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4039d949cac1SHarald Welte 	if (err < 0)
4040d949cac1SHarald Welte 		return err;
4041d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4042d949cac1SHarald Welte 			      "Master Front Playback Switch",
4043d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4044d949cac1SHarald Welte 	if (err < 0)
4045d949cac1SHarald Welte 		return err;
4046d949cac1SHarald Welte 
4047d949cac1SHarald Welte 	/* Front */
4048d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4049d949cac1SHarald Welte 			      "Front Playback Volume",
4050d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4051d949cac1SHarald Welte 	if (err < 0)
4052d949cac1SHarald Welte 		return err;
4053d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4054d949cac1SHarald Welte 			      "Front Playback Switch",
4055d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4056d949cac1SHarald Welte 	if (err < 0)
4057d949cac1SHarald Welte 		return err;
4058d949cac1SHarald Welte 
4059d949cac1SHarald Welte 	return 0;
4060d949cac1SHarald Welte }
4061d949cac1SHarald Welte 
4062d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4063d949cac1SHarald Welte {
40640713efebSLydia Wang 	int err, i;
40650713efebSLydia Wang 	struct hda_input_mux *imux;
4066ea734963STakashi Iwai 	static const char * const texts[] = { "ON", "OFF", NULL};
4067d949cac1SHarald Welte 	if (!pin)
4068d949cac1SHarald Welte 		return 0;
4069d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4070cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4071d949cac1SHarald Welte 
4072d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4073d949cac1SHarald Welte 			      "Headphone Playback Volume",
4074d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4075d949cac1SHarald Welte 	if (err < 0)
4076d949cac1SHarald Welte 		return err;
4077d949cac1SHarald Welte 
4078d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4079d949cac1SHarald Welte 			      "Headphone Playback Switch",
4080d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4081d949cac1SHarald Welte 	if (err < 0)
4082d949cac1SHarald Welte 		return err;
4083d949cac1SHarald Welte 
40840713efebSLydia Wang 	imux = &spec->private_imux[1];
40850aa62aefSHarald Welte 
40860713efebSLydia Wang 	/* for hp mode select */
408710a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
408810a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
40890713efebSLydia Wang 
40900713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4091d949cac1SHarald Welte 	return 0;
4092d949cac1SHarald Welte }
4093d949cac1SHarald Welte 
4094d949cac1SHarald Welte /* create playback/capture controls for input pins */
409510a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
4096d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4097d949cac1SHarald Welte {
4098a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a);
4099d949cac1SHarald Welte }
4100d949cac1SHarald Welte 
4101d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4102d949cac1SHarald Welte {
4103d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4104d949cac1SHarald Welte 	int err;
4105d949cac1SHarald Welte 
41069da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4107d949cac1SHarald Welte 	if (err < 0)
4108d949cac1SHarald Welte 		return err;
4109d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4110d949cac1SHarald Welte 	if (err < 0)
4111d949cac1SHarald Welte 		return err;
4112d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4113d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4114d949cac1SHarald Welte 
4115d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4116d949cac1SHarald Welte 	if (err < 0)
4117d949cac1SHarald Welte 		return err;
4118d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4119d949cac1SHarald Welte 	if (err < 0)
4120d949cac1SHarald Welte 		return err;
4121c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4122c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4123c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4124c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4125c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4126c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
412710a20af7STakashi Iwai 	err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
4128d949cac1SHarald Welte 	if (err < 0)
4129d949cac1SHarald Welte 		return err;
4130d949cac1SHarald Welte 
4131d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4132d949cac1SHarald Welte 
41339da29271STakashi Iwai 	fill_dig_outs(codec);
413498aa34c0SHarald Welte 
4135603c4019STakashi Iwai 	if (spec->kctls.list)
4136603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4137d949cac1SHarald Welte 
41380aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
41390aa62aefSHarald Welte 
4140f8fdd495SHarald Welte 	if (spec->hp_mux)
41413d83e577STakashi Iwai 		via_hp_build(codec);
4142d949cac1SHarald Welte 
4143d949cac1SHarald Welte 	return 1;
4144d949cac1SHarald Welte }
4145d949cac1SHarald Welte 
4146d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
414790dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = {
4148d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4149d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4150d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4151d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4152d949cac1SHarald Welte 	{ } /* end */
4153d949cac1SHarald Welte };
4154d949cac1SHarald Welte #endif
4155d949cac1SHarald Welte 
41563e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
41573e95b9abSLydia Wang {
41583e95b9abSLydia Wang 	int imux_is_smixer =
41593e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
41603e95b9abSLydia Wang 	unsigned int parm;
41613e95b9abSLydia Wang 	/* inputs */
41623e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
41633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
41653e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
41663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
41673e95b9abSLydia Wang 	if (imux_is_smixer)
41683e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
41693e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
41703e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
41713e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
41723e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
41733e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
41743e95b9abSLydia Wang 
41753e95b9abSLydia Wang 	/* outputs */
41763e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
41773e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41783e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
41793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
41803e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
41813e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
41823e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
41833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
41843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
41853e95b9abSLydia Wang }
41863e95b9abSLydia Wang 
4187d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4188d949cac1SHarald Welte {
4189d949cac1SHarald Welte 	struct via_spec *spec;
4190d949cac1SHarald Welte 	int err;
4191d949cac1SHarald Welte 
4192d949cac1SHarald Welte 	/* create a codec specific record */
41935b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4194d949cac1SHarald Welte 	if (spec == NULL)
4195d949cac1SHarald Welte 		return -ENOMEM;
4196d949cac1SHarald Welte 
4197d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4198d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4199d949cac1SHarald Welte 	if (err < 0) {
4200d949cac1SHarald Welte 		via_free(codec);
4201d949cac1SHarald Welte 		return err;
4202d949cac1SHarald Welte 	} else if (!err) {
4203d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4204d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4205d949cac1SHarald Welte 	}
4206d949cac1SHarald Welte 
420769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
420869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4209d949cac1SHarald Welte 
4210d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4211d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4212d949cac1SHarald Welte 
4213d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4214d949cac1SHarald Welte 
4215a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
4216d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4217d949cac1SHarald Welte 		spec->num_mixers++;
4218d949cac1SHarald Welte 	}
4219d949cac1SHarald Welte 
4220d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4221d949cac1SHarald Welte 
4222d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
422369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4224d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4225d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4226d949cac1SHarald Welte #endif
4227d949cac1SHarald Welte 
42283e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
4229d949cac1SHarald Welte 	return 0;
4230d949cac1SHarald Welte }
4231d949cac1SHarald Welte 
4232eb7188caSLydia Wang /* Patch for VT1718S */
4233eb7188caSLydia Wang 
4234eb7188caSLydia Wang /* capture mixer elements */
423590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4236eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4237eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4238eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4239eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4240eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4241eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4242eb7188caSLydia Wang 			 HDA_INPUT),
4243eb7188caSLydia Wang 	{
4244eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4245eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4246eb7188caSLydia Wang 		 * So call somewhat different..
4247eb7188caSLydia Wang 		 */
4248eb7188caSLydia Wang 		.name = "Input Source",
4249eb7188caSLydia Wang 		.count = 2,
4250eb7188caSLydia Wang 		.info = via_mux_enum_info,
4251eb7188caSLydia Wang 		.get = via_mux_enum_get,
4252eb7188caSLydia Wang 		.put = via_mux_enum_put,
4253eb7188caSLydia Wang 	},
4254eb7188caSLydia Wang 	{ } /* end */
4255eb7188caSLydia Wang };
4256eb7188caSLydia Wang 
425790dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = {
4258eb7188caSLydia Wang 	/*
4259eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4260eb7188caSLydia Wang 	 */
4261eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4262eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4263eb7188caSLydia Wang 
42644ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
42654ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
4266eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4267eb7188caSLydia Wang 	 * mixer widget
4268eb7188caSLydia Wang 	 */
4269eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4270eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4271eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4272eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4273eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
42744ab2d53aSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
4275eb7188caSLydia Wang 
4276eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4277eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4278eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4279eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4280eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4281eb7188caSLydia Wang 	/* PW11 Input enable */
4282eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4283eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4284eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4285eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4286eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4287eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4288eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4289eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4290eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4291eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4292eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4293eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4294eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4295eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4296eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4297eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4298eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4299eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4300eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4301eb7188caSLydia Wang 	{ }
4302eb7188caSLydia Wang };
4303eb7188caSLydia Wang 
4304eb7188caSLydia Wang 
430590dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = {
4306eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4307eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4308eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4309eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4310eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4311eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4312eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4313eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4314eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4315eb7188caSLydia Wang 	{ }
4316eb7188caSLydia Wang };
4317eb7188caSLydia Wang 
431890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4319eb7188caSLydia Wang 	.substreams = 2,
4320eb7188caSLydia Wang 	.channels_min = 2,
4321eb7188caSLydia Wang 	.channels_max = 10,
4322eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4323eb7188caSLydia Wang 	.ops = {
4324eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4325eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4326eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4327eb7188caSLydia Wang 		.close = via_pcm_open_close,
4328eb7188caSLydia Wang 	},
4329eb7188caSLydia Wang };
4330eb7188caSLydia Wang 
433190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4332eb7188caSLydia Wang 	.substreams = 2,
4333eb7188caSLydia Wang 	.channels_min = 2,
4334eb7188caSLydia Wang 	.channels_max = 2,
4335eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4336eb7188caSLydia Wang 	.ops = {
4337eb7188caSLydia Wang 		.open = via_pcm_open_close,
4338eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4339eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4340eb7188caSLydia Wang 		.close = via_pcm_open_close,
4341eb7188caSLydia Wang 	},
4342eb7188caSLydia Wang };
4343eb7188caSLydia Wang 
434490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4345eb7188caSLydia Wang 	.substreams = 2,
4346eb7188caSLydia Wang 	.channels_min = 2,
4347eb7188caSLydia Wang 	.channels_max = 2,
4348eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4349eb7188caSLydia Wang 	.ops = {
4350eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4351eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4352eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4353eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4354eb7188caSLydia Wang 	},
4355eb7188caSLydia Wang };
4356eb7188caSLydia Wang 
435790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4358eb7188caSLydia Wang 	.substreams = 1,
4359eb7188caSLydia Wang 	.channels_min = 2,
4360eb7188caSLydia Wang 	.channels_max = 2,
4361eb7188caSLydia Wang };
4362eb7188caSLydia Wang 
4363eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4364eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4365eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4366eb7188caSLydia Wang {
4367eb7188caSLydia Wang 	int i;
4368eb7188caSLydia Wang 	hda_nid_t nid;
4369eb7188caSLydia Wang 
4370eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4371eb7188caSLydia Wang 
4372eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4373eb7188caSLydia Wang 
4374eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4375eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4376eb7188caSLydia Wang 		if (nid) {
4377eb7188caSLydia Wang 			/* config dac list */
4378eb7188caSLydia Wang 			switch (i) {
4379eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4380dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x8;
4381eb7188caSLydia Wang 				break;
4382eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4383dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0xa;
4384eb7188caSLydia Wang 				break;
4385eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4386dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x9;
4387eb7188caSLydia Wang 				break;
4388eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4389dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0xb;
4390eb7188caSLydia Wang 				break;
4391eb7188caSLydia Wang 			}
4392eb7188caSLydia Wang 		}
4393eb7188caSLydia Wang 	}
4394eb7188caSLydia Wang 
4395eb7188caSLydia Wang 	return 0;
4396eb7188caSLydia Wang }
4397eb7188caSLydia Wang 
4398eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4399eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4400eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4401eb7188caSLydia Wang {
4402eb7188caSLydia Wang 	char name[32];
4403ea734963STakashi Iwai 	static const char * const chname[4] = {
4404ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
4405ea734963STakashi Iwai 	};
4406eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4407eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4408eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4409eb7188caSLydia Wang 	int i, err;
4410eb7188caSLydia Wang 
4411eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4412eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4413eb7188caSLydia Wang 
4414eb7188caSLydia Wang 		if (!nid)
4415eb7188caSLydia Wang 			continue;
4416eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4417eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4418eb7188caSLydia Wang 
4419eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4420eb7188caSLydia Wang 			/* Center/LFE */
4421eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4422eb7188caSLydia Wang 					      "Center Playback Volume",
4423eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4424eb7188caSLydia Wang 								  HDA_OUTPUT));
4425eb7188caSLydia Wang 			if (err < 0)
4426eb7188caSLydia Wang 				return err;
4427eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4428eb7188caSLydia Wang 					      "LFE Playback Volume",
4429eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4430eb7188caSLydia Wang 								  HDA_OUTPUT));
4431eb7188caSLydia Wang 			if (err < 0)
4432eb7188caSLydia Wang 				return err;
4433eb7188caSLydia Wang 			err = via_add_control(
4434eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4435eb7188caSLydia Wang 				"Center Playback Switch",
4436eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4437eb7188caSLydia Wang 						    HDA_OUTPUT));
4438eb7188caSLydia Wang 			if (err < 0)
4439eb7188caSLydia Wang 				return err;
4440eb7188caSLydia Wang 			err = via_add_control(
4441eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4442eb7188caSLydia Wang 				"LFE Playback Switch",
4443eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4444eb7188caSLydia Wang 						    HDA_OUTPUT));
4445eb7188caSLydia Wang 			if (err < 0)
4446eb7188caSLydia Wang 				return err;
4447eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4448eb7188caSLydia Wang 			/* Front */
4449eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4450eb7188caSLydia Wang 			err = via_add_control(
4451eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4452eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4453eb7188caSLydia Wang 			if (err < 0)
4454eb7188caSLydia Wang 				return err;
4455eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4456eb7188caSLydia Wang 			err = via_add_control(
4457eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4458eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4459eb7188caSLydia Wang 						    HDA_OUTPUT));
4460eb7188caSLydia Wang 			if (err < 0)
4461eb7188caSLydia Wang 				return err;
4462eb7188caSLydia Wang 		} else {
4463eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4464eb7188caSLydia Wang 			err = via_add_control(
4465eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4466eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4467eb7188caSLydia Wang 			if (err < 0)
4468eb7188caSLydia Wang 				return err;
4469eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4470eb7188caSLydia Wang 			err = via_add_control(
4471eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4472eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4473eb7188caSLydia Wang 						    HDA_OUTPUT));
4474eb7188caSLydia Wang 			if (err < 0)
4475eb7188caSLydia Wang 				return err;
4476eb7188caSLydia Wang 		}
4477eb7188caSLydia Wang 	}
4478eb7188caSLydia Wang 	return 0;
4479eb7188caSLydia Wang }
4480eb7188caSLydia Wang 
4481eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4482eb7188caSLydia Wang {
4483eb7188caSLydia Wang 	int err;
4484eb7188caSLydia Wang 
4485eb7188caSLydia Wang 	if (!pin)
4486eb7188caSLydia Wang 		return 0;
4487eb7188caSLydia Wang 
4488eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4489eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4490eb7188caSLydia Wang 
4491eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4492eb7188caSLydia Wang 			      "Headphone Playback Volume",
4493eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4494eb7188caSLydia Wang 	if (err < 0)
4495eb7188caSLydia Wang 		return err;
4496eb7188caSLydia Wang 
4497eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4498eb7188caSLydia Wang 			      "Headphone Playback Switch",
4499eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4500eb7188caSLydia Wang 	if (err < 0)
4501eb7188caSLydia Wang 		return err;
4502eb7188caSLydia Wang 
4503eb7188caSLydia Wang 	create_hp_imux(spec);
4504eb7188caSLydia Wang 	return 0;
4505eb7188caSLydia Wang }
4506eb7188caSLydia Wang 
4507eb7188caSLydia Wang /* create playback/capture controls for input pins */
450810a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
4509eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4510eb7188caSLydia Wang {
4511a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21);
4512eb7188caSLydia Wang }
4513eb7188caSLydia Wang 
4514eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4515eb7188caSLydia Wang {
4516eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4517eb7188caSLydia Wang 	int err;
4518eb7188caSLydia Wang 
4519eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4520eb7188caSLydia Wang 
4521eb7188caSLydia Wang 	if (err < 0)
4522eb7188caSLydia Wang 		return err;
4523eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4524eb7188caSLydia Wang 	if (err < 0)
4525eb7188caSLydia Wang 		return err;
4526eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4527eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4528eb7188caSLydia Wang 
4529eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4530eb7188caSLydia Wang 	if (err < 0)
4531eb7188caSLydia Wang 		return err;
4532eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4533eb7188caSLydia Wang 	if (err < 0)
4534eb7188caSLydia Wang 		return err;
453510a20af7STakashi Iwai 	err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
4536eb7188caSLydia Wang 	if (err < 0)
4537eb7188caSLydia Wang 		return err;
4538eb7188caSLydia Wang 
4539eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4540eb7188caSLydia Wang 
4541eb7188caSLydia Wang 	fill_dig_outs(codec);
4542eb7188caSLydia Wang 
4543eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4544eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4545eb7188caSLydia Wang 
4546eb7188caSLydia Wang 	if (spec->kctls.list)
4547eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4548eb7188caSLydia Wang 
4549eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4550eb7188caSLydia Wang 
4551eb7188caSLydia Wang 	if (spec->hp_mux)
45523d83e577STakashi Iwai 		via_hp_build(codec);
4553eb7188caSLydia Wang 
45545b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4555eb7188caSLydia Wang 
4556eb7188caSLydia Wang 	return 1;
4557eb7188caSLydia Wang }
4558eb7188caSLydia Wang 
4559eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
456090dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = {
4561eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4562eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4563eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4564eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4565eb7188caSLydia Wang 	{ } /* end */
4566eb7188caSLydia Wang };
4567eb7188caSLydia Wang #endif
4568eb7188caSLydia Wang 
45693e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
45703e95b9abSLydia Wang {
45713e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
45723e95b9abSLydia Wang 	int imux_is_smixer;
45733e95b9abSLydia Wang 	unsigned int parm;
45743e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
45753e95b9abSLydia Wang 	imux_is_smixer =
45763e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
45773e95b9abSLydia Wang 	/* inputs */
45783e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
45793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
45813e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
45823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
45833e95b9abSLydia Wang 	if (imux_is_smixer)
45843e95b9abSLydia Wang 		parm = AC_PWRST_D0;
45853e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
45863e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
45873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
45883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
45893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
45903e95b9abSLydia Wang 
45913e95b9abSLydia Wang 	/* outputs */
45923e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
45933e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
45953e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
45963e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
45973e95b9abSLydia Wang 
45983e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
45993e95b9abSLydia Wang 	parm = AC_PWRST_D3;
46003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
46013e95b9abSLydia Wang 	if (spec->smart51_enabled)
46023e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
46033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
46043e95b9abSLydia Wang 
46053e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
46063e95b9abSLydia Wang 	parm = AC_PWRST_D3;
46073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
46083e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
46093e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
46103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
46113e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
46123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
46133e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
46143e95b9abSLydia Wang 
46153e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
46163e95b9abSLydia Wang 	parm = AC_PWRST_D3;
46173e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
46183e95b9abSLydia Wang 	if (spec->smart51_enabled)
46193e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
46203e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
46213e95b9abSLydia Wang 
46223e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
46233e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
46243e95b9abSLydia Wang 		parm = AC_PWRST_D3;
46253e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
46263e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
46273e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46283e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
46293e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46303e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
46313e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46323e95b9abSLydia Wang 	}
46333e95b9abSLydia Wang }
46343e95b9abSLydia Wang 
4635eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4636eb7188caSLydia Wang {
4637eb7188caSLydia Wang 	struct via_spec *spec;
4638eb7188caSLydia Wang 	int err;
4639eb7188caSLydia Wang 
4640eb7188caSLydia Wang 	/* create a codec specific record */
46415b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4642eb7188caSLydia Wang 	if (spec == NULL)
4643eb7188caSLydia Wang 		return -ENOMEM;
4644eb7188caSLydia Wang 
4645eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4646eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4647eb7188caSLydia Wang 	if (err < 0) {
4648eb7188caSLydia Wang 		via_free(codec);
4649eb7188caSLydia Wang 		return err;
4650eb7188caSLydia Wang 	} else if (!err) {
4651eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4652eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4653eb7188caSLydia Wang 	}
4654eb7188caSLydia Wang 
4655eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4656eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4657eb7188caSLydia Wang 
4658eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4659eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4660eb7188caSLydia Wang 
4661eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4662bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4663eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4664eb7188caSLydia Wang 
4665a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
4666bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4667bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4668eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4669eb7188caSLydia Wang 		spec->num_mixers++;
4670eb7188caSLydia Wang 	}
4671eb7188caSLydia Wang 
4672eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4673eb7188caSLydia Wang 
4674eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
46750f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4676eb7188caSLydia Wang 
4677eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4678eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4679eb7188caSLydia Wang #endif
4680eb7188caSLydia Wang 
46813e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
46823e95b9abSLydia Wang 
4683eb7188caSLydia Wang 	return 0;
4684eb7188caSLydia Wang }
4685f3db423dSLydia Wang 
4686f3db423dSLydia Wang /* Patch for VT1716S */
4687f3db423dSLydia Wang 
4688f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4689f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4690f3db423dSLydia Wang {
4691f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4692f3db423dSLydia Wang 	uinfo->count = 1;
4693f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4694f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4695f3db423dSLydia Wang 	return 0;
4696f3db423dSLydia Wang }
4697f3db423dSLydia Wang 
4698f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4699f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4700f3db423dSLydia Wang {
4701f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4702f3db423dSLydia Wang 	int index = 0;
4703f3db423dSLydia Wang 
4704f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4705f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4706f3db423dSLydia Wang 	if (index != -1)
4707f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4708f3db423dSLydia Wang 
4709f3db423dSLydia Wang 	return 0;
4710f3db423dSLydia Wang }
4711f3db423dSLydia Wang 
4712f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4713f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4714f3db423dSLydia Wang {
4715f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4716f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4717f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4718f3db423dSLydia Wang 
4719f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4720f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4721f3db423dSLydia Wang 	spec->dmic_enabled = index;
47223e95b9abSLydia Wang 	set_widgets_power_state(codec);
4723f3db423dSLydia Wang 	return 1;
4724f3db423dSLydia Wang }
4725f3db423dSLydia Wang 
4726f3db423dSLydia Wang /* capture mixer elements */
472790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4728f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4729f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4730f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4731f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4732f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4733f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4734f3db423dSLydia Wang 			 HDA_INPUT),
4735f3db423dSLydia Wang 	{
4736f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4737f3db423dSLydia Wang 		.name = "Input Source",
4738f3db423dSLydia Wang 		.count = 1,
4739f3db423dSLydia Wang 		.info = via_mux_enum_info,
4740f3db423dSLydia Wang 		.get = via_mux_enum_get,
4741f3db423dSLydia Wang 		.put = via_mux_enum_put,
4742f3db423dSLydia Wang 	},
4743f3db423dSLydia Wang 	{ } /* end */
4744f3db423dSLydia Wang };
4745f3db423dSLydia Wang 
474690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4747f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4748f3db423dSLydia Wang 	{
4749f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4750f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
47515b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
4752f3db423dSLydia Wang 	 .count = 1,
4753f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
4754f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
4755f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
4756f3db423dSLydia Wang 	 },
4757f3db423dSLydia Wang 	{}			/* end */
4758f3db423dSLydia Wang };
4759f3db423dSLydia Wang 
4760f3db423dSLydia Wang 
4761f3db423dSLydia Wang /* mono-out mixer elements */
476290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4763f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4764f3db423dSLydia Wang 	{ } /* end */
4765f3db423dSLydia Wang };
4766f3db423dSLydia Wang 
476790dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = {
4768f3db423dSLydia Wang 	/*
4769f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4770f3db423dSLydia Wang 	 */
4771f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4772f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4773f3db423dSLydia Wang 
4774f3db423dSLydia Wang 
4775f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4776f3db423dSLydia Wang 	 * mixer widget
4777f3db423dSLydia Wang 	 */
4778f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4779f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4780f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4781f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4782f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4783f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4784f3db423dSLydia Wang 
4785f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4786f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4787f3db423dSLydia Wang 
4788f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4789f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4790f3db423dSLydia Wang 
4791f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4792f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4793f3db423dSLydia Wang 
4794f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4795f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4796f3db423dSLydia Wang 
4797f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4798f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4799f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4800f3db423dSLydia Wang 
4801f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4802f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4803f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4804f3db423dSLydia Wang 	/* PW12 Output enable */
4805f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4806f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4807f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4808f3db423dSLydia Wang 	/* don't bybass mixer */
4809f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4810f3db423dSLydia Wang 	/* Enable mono output */
4811f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4812f3db423dSLydia Wang 	{ }
4813f3db423dSLydia Wang };
4814f3db423dSLydia Wang 
4815f3db423dSLydia Wang 
481690dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = {
4817f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4818f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4819f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4820f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4821f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4822f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4823f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4824f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4825f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4826f3db423dSLydia Wang 	{ }
4827f3db423dSLydia Wang };
4828f3db423dSLydia Wang 
482990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4830f3db423dSLydia Wang 	.substreams = 2,
4831f3db423dSLydia Wang 	.channels_min = 2,
4832f3db423dSLydia Wang 	.channels_max = 6,
4833f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4834f3db423dSLydia Wang 	.ops = {
4835f3db423dSLydia Wang 		.open = via_playback_pcm_open,
4836f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4837f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4838f3db423dSLydia Wang 		.close = via_pcm_open_close,
4839f3db423dSLydia Wang 	},
4840f3db423dSLydia Wang };
4841f3db423dSLydia Wang 
484290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4843f3db423dSLydia Wang 	.substreams = 2,
4844f3db423dSLydia Wang 	.channels_min = 2,
4845f3db423dSLydia Wang 	.channels_max = 2,
4846f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
4847f3db423dSLydia Wang 	.ops = {
4848f3db423dSLydia Wang 		.open = via_pcm_open_close,
4849f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
4850f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4851f3db423dSLydia Wang 		.close = via_pcm_open_close,
4852f3db423dSLydia Wang 	},
4853f3db423dSLydia Wang };
4854f3db423dSLydia Wang 
485590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4856f3db423dSLydia Wang 	.substreams = 2,
4857f3db423dSLydia Wang 	.channels_min = 2,
4858f3db423dSLydia Wang 	.channels_max = 2,
4859f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
4860f3db423dSLydia Wang 	.ops = {
4861f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
4862f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
4863f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4864f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4865f3db423dSLydia Wang 	},
4866f3db423dSLydia Wang };
4867f3db423dSLydia Wang 
4868f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4869f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
4870f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
4871f3db423dSLydia Wang {	int i;
4872f3db423dSLydia Wang 	hda_nid_t nid;
4873f3db423dSLydia Wang 
4874f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4875f3db423dSLydia Wang 
4876f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4877f3db423dSLydia Wang 
4878f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
4879f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4880f3db423dSLydia Wang 		if (nid) {
4881f3db423dSLydia Wang 			/* config dac list */
4882f3db423dSLydia Wang 			switch (i) {
4883f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
4884dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x10;
4885f3db423dSLydia Wang 				break;
4886f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
4887dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x25;
4888f3db423dSLydia Wang 				break;
4889f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
4890dda14410STakashi Iwai 				spec->private_dac_nids[i] = 0x11;
4891f3db423dSLydia Wang 				break;
4892f3db423dSLydia Wang 			}
4893f3db423dSLydia Wang 		}
4894f3db423dSLydia Wang 	}
4895f3db423dSLydia Wang 
4896f3db423dSLydia Wang 	return 0;
4897f3db423dSLydia Wang }
4898f3db423dSLydia Wang 
4899f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
4900f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
4901f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
4902f3db423dSLydia Wang {
4903f3db423dSLydia Wang 	char name[32];
4904ea734963STakashi Iwai 	static const char * const chname[3] = {
4905ea734963STakashi Iwai 		"Front", "Surround", "C/LFE"
4906ea734963STakashi Iwai 	};
4907f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
4908f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
4909f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
4910f3db423dSLydia Wang 	int i, err;
4911f3db423dSLydia Wang 
4912f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
4913f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4914f3db423dSLydia Wang 
4915f3db423dSLydia Wang 		if (!nid)
4916f3db423dSLydia Wang 			continue;
4917f3db423dSLydia Wang 
4918f3db423dSLydia Wang 		nid_vol = nid_vols[i];
4919f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
4920f3db423dSLydia Wang 
4921f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4922f3db423dSLydia Wang 			err = via_add_control(
4923f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4924f3db423dSLydia Wang 				"Center Playback Volume",
4925f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
4926f3db423dSLydia Wang 			if (err < 0)
4927f3db423dSLydia Wang 				return err;
4928f3db423dSLydia Wang 			err = via_add_control(
4929f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4930f3db423dSLydia Wang 				"LFE Playback Volume",
4931f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
4932f3db423dSLydia Wang 			if (err < 0)
4933f3db423dSLydia Wang 				return err;
4934f3db423dSLydia Wang 			err = via_add_control(
4935f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4936f3db423dSLydia Wang 				"Center Playback Switch",
4937f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4938f3db423dSLydia Wang 						    HDA_OUTPUT));
4939f3db423dSLydia Wang 			if (err < 0)
4940f3db423dSLydia Wang 				return err;
4941f3db423dSLydia Wang 			err = via_add_control(
4942f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4943f3db423dSLydia Wang 				"LFE Playback Switch",
4944f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4945f3db423dSLydia Wang 						    HDA_OUTPUT));
4946f3db423dSLydia Wang 			if (err < 0)
4947f3db423dSLydia Wang 				return err;
4948f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4949f3db423dSLydia Wang 
4950f3db423dSLydia Wang 			err = via_add_control(
4951f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4952f3db423dSLydia Wang 				"Master Front Playback Volume",
4953f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4954f3db423dSLydia Wang 			if (err < 0)
4955f3db423dSLydia Wang 				return err;
4956f3db423dSLydia Wang 			err = via_add_control(
4957f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4958f3db423dSLydia Wang 				"Master Front Playback Switch",
4959f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4960f3db423dSLydia Wang 			if (err < 0)
4961f3db423dSLydia Wang 				return err;
4962f3db423dSLydia Wang 
4963f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4964f3db423dSLydia Wang 			err = via_add_control(
4965f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4966f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4967f3db423dSLydia Wang 			if (err < 0)
4968f3db423dSLydia Wang 				return err;
4969f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4970f3db423dSLydia Wang 			err = via_add_control(
4971f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4972f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4973f3db423dSLydia Wang 						    HDA_OUTPUT));
4974f3db423dSLydia Wang 			if (err < 0)
4975f3db423dSLydia Wang 				return err;
4976f3db423dSLydia Wang 		} else {
4977f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4978f3db423dSLydia Wang 			err = via_add_control(
4979f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4980f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4981f3db423dSLydia Wang 			if (err < 0)
4982f3db423dSLydia Wang 				return err;
4983f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4984f3db423dSLydia Wang 			err = via_add_control(
4985f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4986f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4987f3db423dSLydia Wang 						    HDA_OUTPUT));
4988f3db423dSLydia Wang 			if (err < 0)
4989f3db423dSLydia Wang 				return err;
4990f3db423dSLydia Wang 		}
4991f3db423dSLydia Wang 	}
4992f3db423dSLydia Wang 	return 0;
4993f3db423dSLydia Wang }
4994f3db423dSLydia Wang 
4995f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4996f3db423dSLydia Wang {
4997f3db423dSLydia Wang 	int err;
4998f3db423dSLydia Wang 
4999f3db423dSLydia Wang 	if (!pin)
5000f3db423dSLydia Wang 		return 0;
5001f3db423dSLydia Wang 
5002f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5003f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5004f3db423dSLydia Wang 
5005f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5006f3db423dSLydia Wang 			      "Headphone Playback Volume",
5007f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5008f3db423dSLydia Wang 	if (err < 0)
5009f3db423dSLydia Wang 		return err;
5010f3db423dSLydia Wang 
5011f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5012f3db423dSLydia Wang 			      "Headphone Playback Switch",
5013f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5014f3db423dSLydia Wang 	if (err < 0)
5015f3db423dSLydia Wang 		return err;
5016f3db423dSLydia Wang 
5017f3db423dSLydia Wang 	create_hp_imux(spec);
5018f3db423dSLydia Wang 	return 0;
5019f3db423dSLydia Wang }
5020f3db423dSLydia Wang 
5021f3db423dSLydia Wang /* create playback/capture controls for input pins */
502210a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
5023f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5024f3db423dSLydia Wang {
5025a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16);
5026f3db423dSLydia Wang }
5027f3db423dSLydia Wang 
5028f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5029f3db423dSLydia Wang {
5030f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5031f3db423dSLydia Wang 	int err;
5032f3db423dSLydia Wang 
5033f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5034f3db423dSLydia Wang 	if (err < 0)
5035f3db423dSLydia Wang 		return err;
5036f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5037f3db423dSLydia Wang 	if (err < 0)
5038f3db423dSLydia Wang 		return err;
5039f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5040f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5041f3db423dSLydia Wang 
5042f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5043f3db423dSLydia Wang 	if (err < 0)
5044f3db423dSLydia Wang 		return err;
5045f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5046f3db423dSLydia Wang 	if (err < 0)
5047f3db423dSLydia Wang 		return err;
504810a20af7STakashi Iwai 	err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
5049f3db423dSLydia Wang 	if (err < 0)
5050f3db423dSLydia Wang 		return err;
5051f3db423dSLydia Wang 
5052f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5053f3db423dSLydia Wang 
5054f3db423dSLydia Wang 	fill_dig_outs(codec);
5055f3db423dSLydia Wang 
5056f3db423dSLydia Wang 	if (spec->kctls.list)
5057f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5058f3db423dSLydia Wang 
5059f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5060f3db423dSLydia Wang 
5061f3db423dSLydia Wang 	if (spec->hp_mux)
50623d83e577STakashi Iwai 		via_hp_build(codec);
5063f3db423dSLydia Wang 
50645b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
5065f3db423dSLydia Wang 
5066f3db423dSLydia Wang 	return 1;
5067f3db423dSLydia Wang }
5068f3db423dSLydia Wang 
5069f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
507090dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = {
5071f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5072f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5073f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5074f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5075f3db423dSLydia Wang 	{ } /* end */
5076f3db423dSLydia Wang };
5077f3db423dSLydia Wang #endif
5078f3db423dSLydia Wang 
50793e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
50803e95b9abSLydia Wang {
50813e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
50823e95b9abSLydia Wang 	int imux_is_smixer;
50833e95b9abSLydia Wang 	unsigned int parm;
50843e95b9abSLydia Wang 	unsigned int mono_out, present;
50853e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
50863e95b9abSLydia Wang 	imux_is_smixer =
50873e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
50883e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
50893e95b9abSLydia Wang 	/* inputs */
50903e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
50913e95b9abSLydia Wang 	parm = AC_PWRST_D3;
50923e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
50933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
50943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
50953e95b9abSLydia Wang 	if (imux_is_smixer)
50963e95b9abSLydia Wang 		parm = AC_PWRST_D0;
50973e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
50983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
50993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
51003e95b9abSLydia Wang 
51013e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51023e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
51033e95b9abSLydia Wang 	/* PW11 (22h) */
51043e95b9abSLydia Wang 	if (spec->dmic_enabled)
51053e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
51063e95b9abSLydia Wang 	else
51073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
51083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
51093e95b9abSLydia Wang 
51103e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
51113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
51123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
51133e95b9abSLydia Wang 
51143e95b9abSLydia Wang 	/* outputs */
51153e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
51163e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51173e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
51183e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
51193e95b9abSLydia Wang 	if (spec->smart51_enabled)
51203e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
51213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
51223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
51233e95b9abSLydia Wang 
51243e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
51253e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
51273e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
51283e95b9abSLydia Wang 	if (spec->smart51_enabled)
51293e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
51303e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
51313e95b9abSLydia Wang 
51323e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
51333e95b9abSLydia Wang 	if (spec->smart51_enabled)
51343e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
51353e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
51363e95b9abSLydia Wang 
51373e95b9abSLydia Wang 	/* Mono out */
51383e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
51393e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
51403e95b9abSLydia Wang 
51413e95b9abSLydia Wang 	if (present)
51423e95b9abSLydia Wang 		mono_out = 0;
51433e95b9abSLydia Wang 	else {
51443e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
51453e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
51463e95b9abSLydia Wang 			mono_out = 0;
51473e95b9abSLydia Wang 		else
51483e95b9abSLydia Wang 			mono_out = 1;
51493e95b9abSLydia Wang 	}
51503e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
51513e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
51523e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
51533e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
51543e95b9abSLydia Wang 
51553e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
51563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
51583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
51593e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
51603e95b9abSLydia Wang 	if (spec->hp_independent_mode)
51613e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
51623e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
51633e95b9abSLydia Wang 
51643e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
51653e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
51663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
51673e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
51683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
51693e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
51703e95b9abSLydia Wang }
51713e95b9abSLydia Wang 
5172f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5173f3db423dSLydia Wang {
5174f3db423dSLydia Wang 	struct via_spec *spec;
5175f3db423dSLydia Wang 	int err;
5176f3db423dSLydia Wang 
5177f3db423dSLydia Wang 	/* create a codec specific record */
51785b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5179f3db423dSLydia Wang 	if (spec == NULL)
5180f3db423dSLydia Wang 		return -ENOMEM;
5181f3db423dSLydia Wang 
5182f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5183f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5184f3db423dSLydia Wang 	if (err < 0) {
5185f3db423dSLydia Wang 		via_free(codec);
5186f3db423dSLydia Wang 		return err;
5187f3db423dSLydia Wang 	} else if (!err) {
5188f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5189f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5190f3db423dSLydia Wang 	}
5191f3db423dSLydia Wang 
5192f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5193f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5194f3db423dSLydia Wang 
5195f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5196f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5197f3db423dSLydia Wang 
5198f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5199f3db423dSLydia Wang 
5200a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
5201f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5202f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5203f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5204f3db423dSLydia Wang 		spec->num_mixers++;
5205f3db423dSLydia Wang 	}
5206f3db423dSLydia Wang 
5207f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5208f3db423dSLydia Wang 	spec->num_mixers++;
5209f3db423dSLydia Wang 
5210f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5211f3db423dSLydia Wang 
5212f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5213f3db423dSLydia Wang 
5214f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
52150f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5216f3db423dSLydia Wang 
5217f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5218f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5219f3db423dSLydia Wang #endif
5220f3db423dSLydia Wang 
52213e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
5222f3db423dSLydia Wang 	return 0;
5223f3db423dSLydia Wang }
522425eaba2fSLydia Wang 
522525eaba2fSLydia Wang /* for vt2002P */
522625eaba2fSLydia Wang 
522725eaba2fSLydia Wang /* capture mixer elements */
522890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = {
522925eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
523025eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
523125eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
523225eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
523325eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
523425eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
523525eaba2fSLydia Wang 			 HDA_INPUT),
523625eaba2fSLydia Wang 	{
523725eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
523825eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
523925eaba2fSLydia Wang 		 * So call somewhat different..
524025eaba2fSLydia Wang 		 */
524125eaba2fSLydia Wang 		/* .name = "Capture Source", */
524225eaba2fSLydia Wang 		.name = "Input Source",
524325eaba2fSLydia Wang 		.count = 2,
524425eaba2fSLydia Wang 		.info = via_mux_enum_info,
524525eaba2fSLydia Wang 		.get = via_mux_enum_get,
524625eaba2fSLydia Wang 		.put = via_mux_enum_put,
524725eaba2fSLydia Wang 	},
524825eaba2fSLydia Wang 	{ } /* end */
524925eaba2fSLydia Wang };
525025eaba2fSLydia Wang 
525190dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = {
5252eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
5253eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
5254eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
5255eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
525625eaba2fSLydia Wang 	/*
525725eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
525825eaba2fSLydia Wang 	 */
525925eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
526025eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
526125eaba2fSLydia Wang 
526225eaba2fSLydia Wang 
526325eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
526425eaba2fSLydia Wang 	 * mixer widget
526525eaba2fSLydia Wang 	 */
526625eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
526725eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
526825eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
526925eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
527025eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
527125eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
527225eaba2fSLydia Wang 
527325eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
527425eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
527525eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
527625eaba2fSLydia Wang 
527725eaba2fSLydia Wang 	/* PW9 Output enable */
527825eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
527925eaba2fSLydia Wang 
528025eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
528125eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
528225eaba2fSLydia Wang 
528325eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
528425eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528525eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528625eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528725eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528825eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
528925eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
529025eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
529125eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
529225eaba2fSLydia Wang 
529325eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
529425eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
529525eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
529625eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
529725eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
529825eaba2fSLydia Wang 
529925eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
530025eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
530125eaba2fSLydia Wang 
530225eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
530325eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
530425eaba2fSLydia Wang 	{ }
530525eaba2fSLydia Wang };
530690dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = {
530711890956SLydia Wang 	/*
530811890956SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
530911890956SLydia Wang 	 */
531011890956SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
531111890956SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
531211890956SLydia Wang 
531311890956SLydia Wang 
531411890956SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
531511890956SLydia Wang 	 * mixer widget
531611890956SLydia Wang 	 */
531711890956SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
531811890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
531911890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
532011890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
532111890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
532211890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
532311890956SLydia Wang 
532411890956SLydia Wang 	/* MUX Indices: Mic = 0 */
532511890956SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
532611890956SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
532711890956SLydia Wang 
532811890956SLydia Wang 	/* PW9 Output enable */
532911890956SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
533011890956SLydia Wang 
533111890956SLydia Wang 	/* Enable Boost Volume backdoor */
533211890956SLydia Wang 	{0x1, 0xfb9, 0x24},
533311890956SLydia Wang 
533411890956SLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
533511890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533611890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533711890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533811890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533911890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
534011890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
534111890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
534211890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
534311890956SLydia Wang 
534411890956SLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
534511890956SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
534611890956SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
534711890956SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
534811890956SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
534911890956SLydia Wang 
535011890956SLydia Wang 	/* set PW0 index=0 (MW0) */
535111890956SLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
535211890956SLydia Wang 
535311890956SLydia Wang 	/* Enable AOW0 to MW9 */
535411890956SLydia Wang 	{0x1, 0xfb8, 0x88},
535511890956SLydia Wang 	{ }
535611890956SLydia Wang };
535725eaba2fSLydia Wang 
535825eaba2fSLydia Wang 
535990dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = {
536025eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
536125eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
536225eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
536325eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
536425eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
536525eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
536625eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
536725eaba2fSLydia Wang 	{ }
536825eaba2fSLydia Wang };
536990dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = {
537011890956SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
537111890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
537211890956SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
537311890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
537411890956SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
537511890956SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
537611890956SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
537711890956SLydia Wang 	{ }
537811890956SLydia Wang };
537925eaba2fSLydia Wang 
538090dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_playback = {
538125eaba2fSLydia Wang 	.substreams = 2,
538225eaba2fSLydia Wang 	.channels_min = 2,
538325eaba2fSLydia Wang 	.channels_max = 2,
538425eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
538525eaba2fSLydia Wang 	.ops = {
538625eaba2fSLydia Wang 		.open = via_playback_pcm_open,
538725eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
538825eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
538925eaba2fSLydia Wang 		.close = via_pcm_open_close,
539025eaba2fSLydia Wang 	},
539125eaba2fSLydia Wang };
539225eaba2fSLydia Wang 
539390dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_capture = {
539425eaba2fSLydia Wang 	.substreams = 2,
539525eaba2fSLydia Wang 	.channels_min = 2,
539625eaba2fSLydia Wang 	.channels_max = 2,
539725eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
539825eaba2fSLydia Wang 	.ops = {
539925eaba2fSLydia Wang 		.open = via_pcm_open_close,
540025eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
540125eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
540225eaba2fSLydia Wang 		.close = via_pcm_open_close,
540325eaba2fSLydia Wang 	},
540425eaba2fSLydia Wang };
540525eaba2fSLydia Wang 
540690dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_digital_playback = {
540725eaba2fSLydia Wang 	.substreams = 1,
540825eaba2fSLydia Wang 	.channels_min = 2,
540925eaba2fSLydia Wang 	.channels_max = 2,
541025eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
541125eaba2fSLydia Wang 	.ops = {
541225eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
541325eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
541425eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
541525eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
541625eaba2fSLydia Wang 	},
541725eaba2fSLydia Wang };
541825eaba2fSLydia Wang 
541925eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
542025eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
542125eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
542225eaba2fSLydia Wang {
542325eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
542425eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
542525eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
5426dda14410STakashi Iwai 		spec->private_dac_nids[0] = 0x8;
542725eaba2fSLydia Wang 	return 0;
542825eaba2fSLydia Wang }
542925eaba2fSLydia Wang 
543025eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
543125eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
543225eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
543325eaba2fSLydia Wang {
543425eaba2fSLydia Wang 	int err;
543511890956SLydia Wang 	hda_nid_t sw_nid;
543625eaba2fSLydia Wang 
543725eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
543825eaba2fSLydia Wang 		return -1;
543925eaba2fSLydia Wang 
544011890956SLydia Wang 	if (spec->codec_type == VT1802)
544111890956SLydia Wang 		sw_nid = 0x28;
544211890956SLydia Wang 	else
544311890956SLydia Wang 		sw_nid = 0x26;
544425eaba2fSLydia Wang 
544525eaba2fSLydia Wang 	/* Line-Out: PortE */
544625eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
544725eaba2fSLydia Wang 			      "Master Front Playback Volume",
544825eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
544925eaba2fSLydia Wang 	if (err < 0)
545025eaba2fSLydia Wang 		return err;
545125eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
545225eaba2fSLydia Wang 			      "Master Front Playback Switch",
545311890956SLydia Wang 			      HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT));
545425eaba2fSLydia Wang 	if (err < 0)
545525eaba2fSLydia Wang 		return err;
545625eaba2fSLydia Wang 
545725eaba2fSLydia Wang 	return 0;
545825eaba2fSLydia Wang }
545925eaba2fSLydia Wang 
546025eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
546125eaba2fSLydia Wang {
546225eaba2fSLydia Wang 	int err;
546325eaba2fSLydia Wang 
546425eaba2fSLydia Wang 	if (!pin)
546525eaba2fSLydia Wang 		return 0;
546625eaba2fSLydia Wang 
546725eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
546825eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
546925eaba2fSLydia Wang 
547025eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
547125eaba2fSLydia Wang 			      "Headphone Playback Volume",
547225eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
547325eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
547425eaba2fSLydia Wang 	if (err < 0)
547525eaba2fSLydia Wang 		return err;
547625eaba2fSLydia Wang 
547725eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
547825eaba2fSLydia Wang 			      "Headphone Playback Switch",
547925eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
548025eaba2fSLydia Wang 	if (err < 0)
548125eaba2fSLydia Wang 		return err;
548225eaba2fSLydia Wang 
548325eaba2fSLydia Wang 	create_hp_imux(spec);
548425eaba2fSLydia Wang 	return 0;
548525eaba2fSLydia Wang }
548625eaba2fSLydia Wang 
548725eaba2fSLydia Wang /* create playback/capture controls for input pins */
548810a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
548925eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
549025eaba2fSLydia Wang {
5491a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21);
549225eaba2fSLydia Wang }
549325eaba2fSLydia Wang 
549425eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
549525eaba2fSLydia Wang {
549625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
549725eaba2fSLydia Wang 	int err;
549825eaba2fSLydia Wang 
549925eaba2fSLydia Wang 
550025eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
550125eaba2fSLydia Wang 	if (err < 0)
550225eaba2fSLydia Wang 		return err;
550325eaba2fSLydia Wang 
550425eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
550525eaba2fSLydia Wang 	if (err < 0)
550625eaba2fSLydia Wang 		return err;
550725eaba2fSLydia Wang 
550825eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
550925eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
551025eaba2fSLydia Wang 
551125eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
551225eaba2fSLydia Wang 	if (err < 0)
551325eaba2fSLydia Wang 		return err;
551425eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
551525eaba2fSLydia Wang 	if (err < 0)
551625eaba2fSLydia Wang 		return err;
551710a20af7STakashi Iwai 	err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
551825eaba2fSLydia Wang 	if (err < 0)
551925eaba2fSLydia Wang 		return err;
552025eaba2fSLydia Wang 
552125eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
552225eaba2fSLydia Wang 
552325eaba2fSLydia Wang 	fill_dig_outs(codec);
552425eaba2fSLydia Wang 
552525eaba2fSLydia Wang 	if (spec->kctls.list)
552625eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
552725eaba2fSLydia Wang 
552825eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
552925eaba2fSLydia Wang 
553025eaba2fSLydia Wang 	if (spec->hp_mux)
55313d83e577STakashi Iwai 		via_hp_build(codec);
553225eaba2fSLydia Wang 
553325eaba2fSLydia Wang 	return 1;
553425eaba2fSLydia Wang }
553525eaba2fSLydia Wang 
553625eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
553790dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = {
553825eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
553925eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
554025eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
554125eaba2fSLydia Wang 	{ } /* end */
554225eaba2fSLydia Wang };
554325eaba2fSLydia Wang #endif
554425eaba2fSLydia Wang 
55453e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
55463e95b9abSLydia Wang {
55473e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
55483e95b9abSLydia Wang 	int imux_is_smixer;
55493e95b9abSLydia Wang 	unsigned int parm;
55503e95b9abSLydia Wang 	unsigned int present;
55513e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
55523e95b9abSLydia Wang 	imux_is_smixer =
55533e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
55543e95b9abSLydia Wang 	/* inputs */
55553e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
55563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
55573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
55583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
55593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
55603e95b9abSLydia Wang 	parm = AC_PWRST_D0;
55613e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
55623e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
55633e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
55643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
55653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
55663e95b9abSLydia Wang 
55673e95b9abSLydia Wang 	/* outputs */
55683e95b9abSLydia Wang 	/* AOW0 (8h)*/
55693e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
55703e95b9abSLydia Wang 
557111890956SLydia Wang 	if (spec->codec_type == VT1802) {
557211890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
557311890956SLydia Wang 		parm = AC_PWRST_D3;
557411890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
557511890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
557611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
557711890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
557811890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
557911890956SLydia Wang 	} else {
55803e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
55813e95b9abSLydia Wang 		parm = AC_PWRST_D3;
55823e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
55833e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
55843e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
55853e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
55863e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
558711890956SLydia Wang 	}
55883e95b9abSLydia Wang 
558911890956SLydia Wang 	if (spec->codec_type == VT1802) {
559011890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
559111890956SLydia Wang 		parm = AC_PWRST_D3;
559211890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
559311890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
559411890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
559511890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
559611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
559711890956SLydia Wang 	} else {
55983e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
55993e95b9abSLydia Wang 		parm = AC_PWRST_D3;
56003e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
56013e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
56023e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56033e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
56043e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
560511890956SLydia Wang 	}
56063e95b9abSLydia Wang 
56073e95b9abSLydia Wang 	if (spec->hp_independent_mode)
56083e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
56093e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
56103e95b9abSLydia Wang 
56113e95b9abSLydia Wang 	/* Class-D */
56123e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
56133e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
56143e95b9abSLydia Wang 
56153e95b9abSLydia Wang 	parm = AC_PWRST_D3;
56163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
56173e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
561811890956SLydia Wang 	if (spec->codec_type == VT1802)
561911890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
562011890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
562111890956SLydia Wang 	else
56223e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
56233e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56243e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
56253e95b9abSLydia Wang 
56263e95b9abSLydia Wang 	/* Mono Out */
56273e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
56283e95b9abSLydia Wang 
56293e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
563011890956SLydia Wang 	if (spec->codec_type == VT1802) {
563111890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
563211890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
563311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
563411890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
563511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
563611890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
563711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
563811890956SLydia Wang 	} else {
56393e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
56403e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
56413e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56423e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
56433e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
56453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
564611890956SLydia Wang 	}
56473e95b9abSLydia Wang 	/* MW9 (21h) */
56483e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
56493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
56503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
56513e95b9abSLydia Wang 	else
56523e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
56533e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
56543e95b9abSLydia Wang }
565525eaba2fSLydia Wang 
565625eaba2fSLydia Wang /* patch for vt2002P */
565725eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
565825eaba2fSLydia Wang {
565925eaba2fSLydia Wang 	struct via_spec *spec;
566025eaba2fSLydia Wang 	int err;
566125eaba2fSLydia Wang 
566225eaba2fSLydia Wang 	/* create a codec specific record */
56635b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
566425eaba2fSLydia Wang 	if (spec == NULL)
566525eaba2fSLydia Wang 		return -ENOMEM;
566625eaba2fSLydia Wang 
566725eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
566825eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
566925eaba2fSLydia Wang 	if (err < 0) {
567025eaba2fSLydia Wang 		via_free(codec);
567125eaba2fSLydia Wang 		return err;
567225eaba2fSLydia Wang 	} else if (!err) {
567325eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
567425eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
567525eaba2fSLydia Wang 	}
567625eaba2fSLydia Wang 
567711890956SLydia Wang 	if (spec->codec_type == VT1802)
567811890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
567911890956SLydia Wang 			vt1802_volume_init_verbs;
568011890956SLydia Wang 	else
568111890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
568211890956SLydia Wang 			vt2002P_volume_init_verbs;
568325eaba2fSLydia Wang 
568411890956SLydia Wang 	if (spec->codec_type == VT1802)
568511890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
568611890956SLydia Wang 			vt1802_uniwill_init_verbs;
568711890956SLydia Wang 	else
568811890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
568911890956SLydia Wang 			vt2002P_uniwill_init_verbs;
569011890956SLydia Wang 
569125eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
569225eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
569325eaba2fSLydia Wang 
569425eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
569525eaba2fSLydia Wang 
5696a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
569725eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
569825eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
569925eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
570025eaba2fSLydia Wang 		spec->num_mixers++;
570125eaba2fSLydia Wang 	}
570225eaba2fSLydia Wang 
570325eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
570425eaba2fSLydia Wang 
570525eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
57060f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
570725eaba2fSLydia Wang 
570825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
570925eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
571025eaba2fSLydia Wang #endif
571125eaba2fSLydia Wang 
57123e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
571325eaba2fSLydia Wang 	return 0;
571425eaba2fSLydia Wang }
5715ab6734e7SLydia Wang 
5716ab6734e7SLydia Wang /* for vt1812 */
5717ab6734e7SLydia Wang 
5718ab6734e7SLydia Wang /* capture mixer elements */
571990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = {
5720ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5721ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5722ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5723ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5724ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5725ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5726ab6734e7SLydia Wang 		       HDA_INPUT),
5727ab6734e7SLydia Wang 	{
5728ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5729ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5730ab6734e7SLydia Wang 		 * So call somewhat different..
5731ab6734e7SLydia Wang 		 */
5732ab6734e7SLydia Wang 		.name = "Input Source",
5733ab6734e7SLydia Wang 		.count = 2,
5734ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5735ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5736ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5737ab6734e7SLydia Wang 	},
5738ab6734e7SLydia Wang 	{ } /* end */
5739ab6734e7SLydia Wang };
5740ab6734e7SLydia Wang 
574190dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = {
5742ab6734e7SLydia Wang 	/*
5743ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5744ab6734e7SLydia Wang 	 */
5745ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5746ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5747ab6734e7SLydia Wang 
5748ab6734e7SLydia Wang 
5749ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5750ab6734e7SLydia Wang 	 * mixer widget
5751ab6734e7SLydia Wang 	 */
5752ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5753ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5754ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5755ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5756ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5757ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5758ab6734e7SLydia Wang 
5759ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5760ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5761ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5762ab6734e7SLydia Wang 
5763ab6734e7SLydia Wang 	/* PW9 Output enable */
5764ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5765ab6734e7SLydia Wang 
5766ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5767ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5768ab6734e7SLydia Wang 
5769ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5770ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5771ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5772ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5773ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5774ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5775ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5776ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5777ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5778ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5779ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5780ab6734e7SLydia Wang 
5781ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5782ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5783ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5784ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5785ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5786ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5787ab6734e7SLydia Wang 
5788ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5789ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5790ab6734e7SLydia Wang 	{ }
5791ab6734e7SLydia Wang };
5792ab6734e7SLydia Wang 
5793ab6734e7SLydia Wang 
579490dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = {
5795ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5796ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5797ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5798ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5799ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5800ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5801ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5802ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5803ab6734e7SLydia Wang 	{ }
5804ab6734e7SLydia Wang };
5805ab6734e7SLydia Wang 
580690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_playback = {
5807ab6734e7SLydia Wang 	.substreams = 2,
5808ab6734e7SLydia Wang 	.channels_min = 2,
5809ab6734e7SLydia Wang 	.channels_max = 2,
5810ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5811ab6734e7SLydia Wang 	.ops = {
5812ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5813ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5814ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5815ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5816ab6734e7SLydia Wang 	},
5817ab6734e7SLydia Wang };
5818ab6734e7SLydia Wang 
581990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_capture = {
5820ab6734e7SLydia Wang 	.substreams = 2,
5821ab6734e7SLydia Wang 	.channels_min = 2,
5822ab6734e7SLydia Wang 	.channels_max = 2,
5823ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5824ab6734e7SLydia Wang 	.ops = {
5825ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5826ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5827ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5828ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5829ab6734e7SLydia Wang 	},
5830ab6734e7SLydia Wang };
5831ab6734e7SLydia Wang 
583290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_digital_playback = {
5833ab6734e7SLydia Wang 	.substreams = 1,
5834ab6734e7SLydia Wang 	.channels_min = 2,
5835ab6734e7SLydia Wang 	.channels_max = 2,
5836ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5837ab6734e7SLydia Wang 	.ops = {
5838ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5839ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5840ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5841ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5842ab6734e7SLydia Wang 	},
5843ab6734e7SLydia Wang };
5844ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5845ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5846ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5847ab6734e7SLydia Wang {
5848ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5849ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5850ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5851dda14410STakashi Iwai 		spec->private_dac_nids[0] = 0x8;
5852ab6734e7SLydia Wang 	return 0;
5853ab6734e7SLydia Wang }
5854ab6734e7SLydia Wang 
5855ab6734e7SLydia Wang 
5856ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5857ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5858ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5859ab6734e7SLydia Wang {
5860ab6734e7SLydia Wang 	int err;
5861ab6734e7SLydia Wang 
5862ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
5863ab6734e7SLydia Wang 		return -1;
5864ab6734e7SLydia Wang 
5865ab6734e7SLydia Wang 	/* Line-Out: PortE */
5866ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
58673d83e577STakashi Iwai 			      "Front Playback Volume",
5868ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5869ab6734e7SLydia Wang 	if (err < 0)
5870ab6734e7SLydia Wang 		return err;
5871ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
58723d83e577STakashi Iwai 			      "Front Playback Switch",
5873ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5874ab6734e7SLydia Wang 	if (err < 0)
5875ab6734e7SLydia Wang 		return err;
5876ab6734e7SLydia Wang 
5877ab6734e7SLydia Wang 	return 0;
5878ab6734e7SLydia Wang }
5879ab6734e7SLydia Wang 
5880ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5881ab6734e7SLydia Wang {
5882ab6734e7SLydia Wang 	int err;
5883ab6734e7SLydia Wang 
5884ab6734e7SLydia Wang 	if (!pin)
5885ab6734e7SLydia Wang 		return 0;
5886ab6734e7SLydia Wang 
5887ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
5888ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
5889ab6734e7SLydia Wang 
5890ab6734e7SLydia Wang 
5891ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5892ab6734e7SLydia Wang 			      "Headphone Playback Volume",
5893ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5894ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5895ab6734e7SLydia Wang 	if (err < 0)
5896ab6734e7SLydia Wang 		return err;
5897ab6734e7SLydia Wang 
5898ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5899ab6734e7SLydia Wang 			      "Headphone Playback Switch",
5900ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5901ab6734e7SLydia Wang 	if (err < 0)
5902ab6734e7SLydia Wang 		return err;
5903ab6734e7SLydia Wang 
5904ab6734e7SLydia Wang 	create_hp_imux(spec);
5905ab6734e7SLydia Wang 	return 0;
5906ab6734e7SLydia Wang }
5907ab6734e7SLydia Wang 
5908ab6734e7SLydia Wang /* create playback/capture controls for input pins */
590910a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
5910ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
5911ab6734e7SLydia Wang {
5912a766d0d7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21);
5913ab6734e7SLydia Wang }
5914ab6734e7SLydia Wang 
5915ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
5916ab6734e7SLydia Wang {
5917ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
5918ab6734e7SLydia Wang 	int err;
5919ab6734e7SLydia Wang 
5920ab6734e7SLydia Wang 
5921ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5922ab6734e7SLydia Wang 	if (err < 0)
5923ab6734e7SLydia Wang 		return err;
5924ab6734e7SLydia Wang 	fill_dig_outs(codec);
5925ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
5926ab6734e7SLydia Wang 	if (err < 0)
5927ab6734e7SLydia Wang 		return err;
5928ab6734e7SLydia Wang 
5929ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
5930ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
5931ab6734e7SLydia Wang 
5932ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
5933ab6734e7SLydia Wang 	if (err < 0)
5934ab6734e7SLydia Wang 		return err;
5935ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5936ab6734e7SLydia Wang 	if (err < 0)
5937ab6734e7SLydia Wang 		return err;
593810a20af7STakashi Iwai 	err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
5939ab6734e7SLydia Wang 	if (err < 0)
5940ab6734e7SLydia Wang 		return err;
5941ab6734e7SLydia Wang 
5942ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5943ab6734e7SLydia Wang 
5944ab6734e7SLydia Wang 	fill_dig_outs(codec);
5945ab6734e7SLydia Wang 
5946ab6734e7SLydia Wang 	if (spec->kctls.list)
5947ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5948ab6734e7SLydia Wang 
5949ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
5950ab6734e7SLydia Wang 
5951ab6734e7SLydia Wang 	if (spec->hp_mux)
59523d83e577STakashi Iwai 		via_hp_build(codec);
5953ab6734e7SLydia Wang 
5954ab6734e7SLydia Wang 	return 1;
5955ab6734e7SLydia Wang }
5956ab6734e7SLydia Wang 
5957ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
595890dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = {
5959ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
5960ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
5961ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
5962ab6734e7SLydia Wang 	{ } /* end */
5963ab6734e7SLydia Wang };
5964ab6734e7SLydia Wang #endif
5965ab6734e7SLydia Wang 
59663e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
59673e95b9abSLydia Wang {
59683e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
59693e95b9abSLydia Wang 	int imux_is_smixer =
59703e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
59713e95b9abSLydia Wang 	unsigned int parm;
59723e95b9abSLydia Wang 	unsigned int present;
59733e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
59743e95b9abSLydia Wang 	imux_is_smixer =
59753e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
59763e95b9abSLydia Wang 	/* inputs */
59773e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
59783e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
59803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
59813e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
59823e95b9abSLydia Wang 	parm = AC_PWRST_D0;
59833e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
59843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
59853e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
59863e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
59873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
59883e95b9abSLydia Wang 
59893e95b9abSLydia Wang 	/* outputs */
59903e95b9abSLydia Wang 	/* AOW0 (8h)*/
59913e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
59923e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59933e95b9abSLydia Wang 
59943e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
59953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
59973e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
59983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
59993e95b9abSLydia Wang 
60003e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
60013e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60023e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
60033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
60043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
60053e95b9abSLydia Wang 	if (spec->hp_independent_mode)
60063e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
60073e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60083e95b9abSLydia Wang 
60093e95b9abSLydia Wang 	/* Internal Speaker */
60103e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
60113e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
60123e95b9abSLydia Wang 
60133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
60153e95b9abSLydia Wang 	if (present) {
60163e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
60173e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60183e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
60193e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60203e95b9abSLydia Wang 	} else {
60213e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
60223e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60233e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
60243e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60253e95b9abSLydia Wang 	}
60263e95b9abSLydia Wang 
60273e95b9abSLydia Wang 
60283e95b9abSLydia Wang 	/* Mono Out */
60293e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
60303e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
60313e95b9abSLydia Wang 
60323e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60333e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
60343e95b9abSLydia Wang 	if (present) {
60353e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
60363e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60373e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
60383e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60393e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
60403e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60413e95b9abSLydia Wang 	} else {
60423e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
60433e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
60453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
60473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60483e95b9abSLydia Wang 	}
60493e95b9abSLydia Wang 
60503e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
60513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
60533e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
60543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
60553e95b9abSLydia Wang 
60563e95b9abSLydia Wang }
6057ab6734e7SLydia Wang 
6058ab6734e7SLydia Wang /* patch for vt1812 */
6059ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
6060ab6734e7SLydia Wang {
6061ab6734e7SLydia Wang 	struct via_spec *spec;
6062ab6734e7SLydia Wang 	int err;
6063ab6734e7SLydia Wang 
6064ab6734e7SLydia Wang 	/* create a codec specific record */
60655b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
6066ab6734e7SLydia Wang 	if (spec == NULL)
6067ab6734e7SLydia Wang 		return -ENOMEM;
6068ab6734e7SLydia Wang 
6069ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
6070ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
6071ab6734e7SLydia Wang 	if (err < 0) {
6072ab6734e7SLydia Wang 		via_free(codec);
6073ab6734e7SLydia Wang 		return err;
6074ab6734e7SLydia Wang 	} else if (!err) {
6075ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
6076ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
6077ab6734e7SLydia Wang 	}
6078ab6734e7SLydia Wang 
6079ab6734e7SLydia Wang 
6080ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
6081ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6082ab6734e7SLydia Wang 
6083ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6084ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6085ab6734e7SLydia Wang 
6086ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6087ab6734e7SLydia Wang 
6088a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
6089ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
6090ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
6091ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6092ab6734e7SLydia Wang 		spec->num_mixers++;
6093ab6734e7SLydia Wang 	}
6094ab6734e7SLydia Wang 
6095ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
6096ab6734e7SLydia Wang 
6097ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
60980f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
6099ab6734e7SLydia Wang 
6100ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6101ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
6102ab6734e7SLydia Wang #endif
6103ab6734e7SLydia Wang 
61043e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
6105ab6734e7SLydia Wang 	return 0;
6106ab6734e7SLydia Wang }
6107ab6734e7SLydia Wang 
6108c577b8a1SJoseph Chan /*
6109c577b8a1SJoseph Chan  * patch entries
6110c577b8a1SJoseph Chan  */
611190dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
61123218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
61133218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
61143218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
61153218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
61163218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
6117f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61183218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
6119f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61203218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
6121f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61223218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
6123f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61243218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
6125f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61263218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
6127f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61283218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
6129f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61303218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
6131f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
61323218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
6133f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61343218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
6135f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61363218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
6137f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61383218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6139f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
61403218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6141f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61423218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6143f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61443218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6145f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61463218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6147f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
61483218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6149d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61503218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6151d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61523218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6153d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61543218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6155d949cac1SHarald Welte 	  .patch = patch_vt1708S},
6156bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
6157d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61583218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6159d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61603218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6161d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61623218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6163d949cac1SHarald Welte 	  .patch = patch_vt1708S},
61643218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6165d949cac1SHarald Welte 	  .patch = patch_vt1702},
61663218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6167d949cac1SHarald Welte 	  .patch = patch_vt1702},
61683218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6169d949cac1SHarald Welte 	  .patch = patch_vt1702},
61703218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6171d949cac1SHarald Welte 	  .patch = patch_vt1702},
61723218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6173d949cac1SHarald Welte 	  .patch = patch_vt1702},
61743218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6175d949cac1SHarald Welte 	  .patch = patch_vt1702},
61763218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6177d949cac1SHarald Welte 	  .patch = patch_vt1702},
61783218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6179d949cac1SHarald Welte 	  .patch = patch_vt1702},
6180eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6181eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6182eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6183eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6184bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6185bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6186bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6187bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6188f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6189f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6190f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6191f3db423dSLydia Wang 	  .patch = patch_vt1716S},
619225eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
619325eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6194ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
619536dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
619636dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
619711890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
619811890956SLydia Wang 		.patch = patch_vt2002P},
619911890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
620011890956SLydia Wang 		.patch = patch_vt2002P},
6201c577b8a1SJoseph Chan 	{} /* terminator */
6202c577b8a1SJoseph Chan };
62031289e9e8STakashi Iwai 
62041289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
62051289e9e8STakashi Iwai 
62061289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
62071289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
62081289e9e8STakashi Iwai 	.owner = THIS_MODULE,
62091289e9e8STakashi Iwai };
62101289e9e8STakashi Iwai 
62111289e9e8STakashi Iwai MODULE_LICENSE("GPL");
62121289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
62131289e9e8STakashi Iwai 
62141289e9e8STakashi Iwai static int __init patch_via_init(void)
62151289e9e8STakashi Iwai {
62161289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
62171289e9e8STakashi Iwai }
62181289e9e8STakashi Iwai 
62191289e9e8STakashi Iwai static void __exit patch_via_exit(void)
62201289e9e8STakashi Iwai {
62211289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
62221289e9e8STakashi Iwai }
62231289e9e8STakashi Iwai 
62241289e9e8STakashi Iwai module_init(patch_via_init)
62251289e9e8STakashi Iwai module_exit(patch_via_exit)
6226