xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision ec7e7e42da0b33c77f1baafeac93e5128c4eea7a)
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 */
112f3db423dSLydia Wang 	struct snd_kcontrol_new *mixers[6];
1131f2e99feSLydia Wang 	unsigned int num_mixers;
1141f2e99feSLydia Wang 
1151f2e99feSLydia Wang 	struct hda_verb *init_verbs[5];
1161f2e99feSLydia Wang 	unsigned int num_iverbs;
1171f2e99feSLydia Wang 
1181f2e99feSLydia Wang 	char *stream_name_analog;
1191f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_playback;
1201f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_capture;
1211f2e99feSLydia Wang 
1221f2e99feSLydia Wang 	char *stream_name_digital;
1231f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_playback;
1241f2e99feSLydia Wang 	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;
1321f2e99feSLydia Wang 	hda_nid_t *adc_nids;
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;
1561f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1571f2e99feSLydia Wang 
1581f2e99feSLydia Wang 	/* work to check hp jack state */
1591f2e99feSLydia Wang 	struct hda_codec *codec;
1601f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
1611f2e99feSLydia Wang 	int vt1708_jack_detectect;
1621f2e99feSLydia Wang 	int vt1708_hp_present;
1633e95b9abSLydia Wang 
1643e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1653e95b9abSLydia Wang 
1661f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1671f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1681f2e99feSLydia Wang #endif
1691f2e99feSLydia Wang };
1701f2e99feSLydia Wang 
1710341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1725b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1735b0cb1d8SJaroslav Kysela {
1745b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1755b0cb1d8SJaroslav Kysela 
1765b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1775b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1785b0cb1d8SJaroslav Kysela 		return NULL;
1795b0cb1d8SJaroslav Kysela 
1805b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1815b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1820341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1830341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1840341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1850341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1865b0cb1d8SJaroslav Kysela 	return spec;
1875b0cb1d8SJaroslav Kysela }
1885b0cb1d8SJaroslav Kysela 
189744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
190d7426329SHarald Welte {
191744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
192d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
193d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
194d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
195d7426329SHarald Welte 
196d7426329SHarald Welte 	/* get codec type */
197d7426329SHarald Welte 	if (ven_id != 0x1106)
198d7426329SHarald Welte 		codec_type = UNKNOWN;
199d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
200d7426329SHarald Welte 		codec_type = VT1708;
201d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
202d7426329SHarald Welte 		codec_type = VT1709_10CH;
203d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
204d7426329SHarald Welte 		codec_type = VT1709_6CH;
205518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
206d7426329SHarald Welte 		codec_type = VT1708B_8CH;
207518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
208518bf3baSLydia Wang 			codec_type = VT1708BCE;
209518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
210d7426329SHarald Welte 		codec_type = VT1708B_4CH;
211d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
212d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
213d7426329SHarald Welte 		codec_type = VT1708S;
214d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
215d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
216d7426329SHarald Welte 		codec_type = VT1702;
217eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
218eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
219eb7188caSLydia Wang 		codec_type = VT1718S;
220f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
221f3db423dSLydia Wang 		codec_type = VT1716S;
222bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
223bb3c6bfcSLydia Wang 		codec_type = VT1718S;
22425eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
22525eaba2fSLydia Wang 		codec_type = VT2002P;
226ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
227ab6734e7SLydia Wang 		codec_type = VT1812;
22836dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
22936dd5c4aSLydia Wang 		codec_type = VT1708S;
23011890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
23111890956SLydia Wang 		codec_type = VT1802;
232d7426329SHarald Welte 	else
233d7426329SHarald Welte 		codec_type = UNKNOWN;
234d7426329SHarald Welte 	return codec_type;
235d7426329SHarald Welte };
236d7426329SHarald Welte 
237*ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
23869e52a80SHarald Welte #define VIA_HP_EVENT		0x01
23969e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
240*ec7e7e42SLydia Wang #define VIA_MONO_EVENT		0x03
241*ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT	0x04
242*ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT	0x05
24369e52a80SHarald Welte 
244c577b8a1SJoseph Chan enum {
245c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
246c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
247f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
24825eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
249c577b8a1SJoseph Chan };
250c577b8a1SJoseph Chan 
251c577b8a1SJoseph Chan enum {
252eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
253c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
254c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
255c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
256c577b8a1SJoseph Chan };
257c577b8a1SJoseph Chan 
258f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2591f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2601f2e99feSLydia Wang 
2611f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2621f2e99feSLydia Wang {
2631f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2641f2e99feSLydia Wang 		return;
2651f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2661f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2671f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2681f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2691f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2701f2e99feSLydia Wang }
2711f2e99feSLydia Wang 
2721f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2731f2e99feSLydia Wang {
2741f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2751f2e99feSLydia Wang 		return;
2761f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2771f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2781f2e99feSLydia Wang 		return;
2791f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2801f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2815b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2821f2e99feSLydia Wang }
283f5271101SLydia Wang 
2843e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2853e95b9abSLydia Wang {
2863e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2873e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2883e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2893e95b9abSLydia Wang }
29025eaba2fSLydia Wang 
291f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
292f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
293f5271101SLydia Wang {
294f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
295f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
296f5271101SLydia Wang 
2973e95b9abSLydia Wang 	set_widgets_power_state(codec);
298f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2991f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3001f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3011f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3021f2e99feSLydia Wang 		else
3031f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3041f2e99feSLydia Wang 	}
305f5271101SLydia Wang 	return change;
306f5271101SLydia Wang }
307f5271101SLydia Wang 
308f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
309f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
310f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
311f5271101SLydia Wang 			.name = NULL,					\
312f5271101SLydia Wang 			.index = 0,					\
313f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
314f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
315f5271101SLydia Wang 			.put = analog_input_switch_put,			\
316f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
317f5271101SLydia Wang 
31825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
31925eaba2fSLydia Wang 
32025eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
32125eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
32225eaba2fSLydia Wang {
32325eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
32525eaba2fSLydia Wang 	int i;
32625eaba2fSLydia Wang 	int change = 0;
32725eaba2fSLydia Wang 
32825eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
32925eaba2fSLydia Wang 	int lmute, rmute;
33025eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
33125eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
33225eaba2fSLydia Wang 		return change;
33325eaba2fSLydia Wang 	}
33425eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
33525eaba2fSLydia Wang 					      ucontrol);
33625eaba2fSLydia Wang 	/* Get mute value */
33725eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
33825eaba2fSLydia Wang 	valp++;
33925eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
34025eaba2fSLydia Wang 
34125eaba2fSLydia Wang 	/* Set hp pins */
34225eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
34325eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
34425eaba2fSLydia Wang 			snd_hda_codec_amp_update(
34525eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
34625eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
34725eaba2fSLydia Wang 				lmute);
34825eaba2fSLydia Wang 			snd_hda_codec_amp_update(
34925eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
35025eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35125eaba2fSLydia Wang 				rmute);
35225eaba2fSLydia Wang 		}
35325eaba2fSLydia Wang 	}
35425eaba2fSLydia Wang 
35525eaba2fSLydia Wang 	if (!lmute && !rmute) {
35625eaba2fSLydia Wang 		/* Line Outs */
35725eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
35825eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
35925eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
36025eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
36125eaba2fSLydia Wang 		/* Speakers */
36225eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
36325eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
36425eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
36525eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
36625eaba2fSLydia Wang 		/* unmute */
36725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
36825eaba2fSLydia Wang 
36925eaba2fSLydia Wang 	} else {
37025eaba2fSLydia Wang 		if (lmute) {
37125eaba2fSLydia Wang 			/* Mute all left channels */
37225eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
37325eaba2fSLydia Wang 				snd_hda_codec_amp_update(
37425eaba2fSLydia Wang 					codec,
37525eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
37625eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37725eaba2fSLydia Wang 					lmute);
37825eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
37925eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38025eaba2fSLydia Wang 					codec,
38125eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
38225eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
38325eaba2fSLydia Wang 					lmute);
38425eaba2fSLydia Wang 		}
38525eaba2fSLydia Wang 		if (rmute) {
38625eaba2fSLydia Wang 			/* mute all right channels */
38725eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38825eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38925eaba2fSLydia Wang 					codec,
39025eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
39125eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39225eaba2fSLydia Wang 					rmute);
39325eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
39425eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39525eaba2fSLydia Wang 					codec,
39625eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
39725eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39825eaba2fSLydia Wang 					rmute);
39925eaba2fSLydia Wang 		}
40025eaba2fSLydia Wang 	}
40125eaba2fSLydia Wang 	return change;
40225eaba2fSLydia Wang }
40325eaba2fSLydia Wang 
40425eaba2fSLydia Wang #define BIND_PIN_MUTE							\
40525eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
40625eaba2fSLydia Wang 			.name = NULL,					\
40725eaba2fSLydia Wang 			.index = 0,					\
40825eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
40925eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
41025eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
41125eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
41225eaba2fSLydia Wang 
41371eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = {
414c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
415c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
416f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
41725eaba2fSLydia Wang 	BIND_PIN_MUTE,
418c577b8a1SJoseph Chan };
419c577b8a1SJoseph Chan 
420c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
421c577b8a1SJoseph Chan 	/* ADC1-2 */
422c577b8a1SJoseph Chan 	0x15, 0x27
423c577b8a1SJoseph Chan };
424c577b8a1SJoseph Chan 
425c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
426c577b8a1SJoseph Chan 	/* ADC1-2 */
427c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
428c577b8a1SJoseph Chan };
429c577b8a1SJoseph Chan 
430f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
431f7278fd0SJosepch Chan 	/* ADC1-2 */
432f7278fd0SJosepch Chan 	0x13, 0x14
433f7278fd0SJosepch Chan };
434f7278fd0SJosepch Chan 
435d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
436d949cac1SHarald Welte 	/* ADC1-2 */
437d949cac1SHarald Welte 	0x13, 0x14
438d949cac1SHarald Welte };
439d949cac1SHarald Welte 
440d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
441d949cac1SHarald Welte 	/* ADC1-2 */
442d949cac1SHarald Welte 	0x12, 0x20, 0x1F
443d949cac1SHarald Welte };
444d949cac1SHarald Welte 
445eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
446eb7188caSLydia Wang 	/* ADC1-2 */
447eb7188caSLydia Wang 	0x10, 0x11
448eb7188caSLydia Wang };
449eb7188caSLydia Wang 
450f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
451f3db423dSLydia Wang 	/* ADC1-2 */
452f3db423dSLydia Wang 	0x13, 0x14
453f3db423dSLydia Wang };
454f3db423dSLydia Wang 
45525eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
45625eaba2fSLydia Wang 	/* ADC1-2 */
45725eaba2fSLydia Wang 	0x10, 0x11
45825eaba2fSLydia Wang };
45925eaba2fSLydia Wang 
460ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = {
461ab6734e7SLydia Wang 	/* ADC1-2 */
462ab6734e7SLydia Wang 	0x10, 0x11
463ab6734e7SLydia Wang };
464ab6734e7SLydia Wang 
465ab6734e7SLydia Wang 
466c577b8a1SJoseph Chan /* add dynamic controls */
4677b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
4687b315bb4STakashi Iwai 			     int idx, unsigned long val)
469c577b8a1SJoseph Chan {
470c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
471c577b8a1SJoseph Chan 
472603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
473603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
474c577b8a1SJoseph Chan 	if (!knew)
475c577b8a1SJoseph Chan 		return -ENOMEM;
47671eb7dccSLydia Wang 	*knew = via_control_templates[type];
477c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
478c577b8a1SJoseph Chan 	if (!knew->name)
479c577b8a1SJoseph Chan 		return -ENOMEM;
4804d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4815e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
482c577b8a1SJoseph Chan 	knew->private_value = val;
483c577b8a1SJoseph Chan 	return 0;
484c577b8a1SJoseph Chan }
485c577b8a1SJoseph Chan 
4867b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4877b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4887b315bb4STakashi Iwai 
4895b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
4905b0cb1d8SJaroslav Kysela 						struct snd_kcontrol_new *tmpl)
4915b0cb1d8SJaroslav Kysela {
4925b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
4935b0cb1d8SJaroslav Kysela 
4945b0cb1d8SJaroslav Kysela 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
4955b0cb1d8SJaroslav Kysela 	knew = snd_array_new(&spec->kctls);
4965b0cb1d8SJaroslav Kysela 	if (!knew)
4975b0cb1d8SJaroslav Kysela 		return NULL;
4985b0cb1d8SJaroslav Kysela 	*knew = *tmpl;
4995b0cb1d8SJaroslav Kysela 	knew->name = kstrdup(tmpl->name, GFP_KERNEL);
5005b0cb1d8SJaroslav Kysela 	if (!knew->name)
5015b0cb1d8SJaroslav Kysela 		return NULL;
502b331439dSTakashi Iwai 	return knew;
5035b0cb1d8SJaroslav Kysela }
5045b0cb1d8SJaroslav Kysela 
505603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
506603c4019STakashi Iwai {
507603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
508603c4019STakashi Iwai 
509603c4019STakashi Iwai 	if (spec->kctls.list) {
510603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
511603c4019STakashi Iwai 		int i;
512603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
513603c4019STakashi Iwai 			kfree(kctl[i].name);
514603c4019STakashi Iwai 	}
515603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
516603c4019STakashi Iwai }
517603c4019STakashi Iwai 
518c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
5199510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
5207b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
521c577b8a1SJoseph Chan {
522c577b8a1SJoseph Chan 	char name[32];
523c577b8a1SJoseph Chan 	int err;
524c577b8a1SJoseph Chan 
525c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
5267b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
527c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
528c577b8a1SJoseph Chan 	if (err < 0)
529c577b8a1SJoseph Chan 		return err;
530c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5317b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
532c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
533c577b8a1SJoseph Chan 	if (err < 0)
534c577b8a1SJoseph Chan 		return err;
535c577b8a1SJoseph Chan 	return 0;
536c577b8a1SJoseph Chan }
537c577b8a1SJoseph Chan 
538c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
539c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
540c577b8a1SJoseph Chan 					   int dac_idx)
541c577b8a1SJoseph Chan {
542c577b8a1SJoseph Chan 	/* set as output */
543c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
544c577b8a1SJoseph Chan 			    pin_type);
545c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
546c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
547d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
548d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
549d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
550c577b8a1SJoseph Chan }
551c577b8a1SJoseph Chan 
552c577b8a1SJoseph Chan 
553c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
554c577b8a1SJoseph Chan {
555c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
556c577b8a1SJoseph Chan 	int i;
557c577b8a1SJoseph Chan 
558c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
559c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
560c577b8a1SJoseph Chan 		if (nid)
561c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
562c577b8a1SJoseph Chan 	}
563c577b8a1SJoseph Chan }
564c577b8a1SJoseph Chan 
565c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
566c577b8a1SJoseph Chan {
567c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
568c577b8a1SJoseph Chan 	hda_nid_t pin;
56925eaba2fSLydia Wang 	int i;
570c577b8a1SJoseph Chan 
57125eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
57225eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
573c577b8a1SJoseph Chan 		if (pin) /* connect to front */
574c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
575c577b8a1SJoseph Chan 	}
57625eaba2fSLydia Wang }
577c577b8a1SJoseph Chan 
57832e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
57932e0191dSClemens Ladisch 
580c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
581c577b8a1SJoseph Chan {
582c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5837b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
58432e0191dSClemens Ladisch 	unsigned int ctl;
585c577b8a1SJoseph Chan 	int i;
586c577b8a1SJoseph Chan 
5877b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5887b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
58932e0191dSClemens Ladisch 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
59032e0191dSClemens Ladisch 			ctl = PIN_OUT;
59130649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
59232e0191dSClemens Ladisch 			ctl = PIN_VREF50;
59332e0191dSClemens Ladisch 		else
59432e0191dSClemens Ladisch 			ctl = PIN_IN;
595c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
59632e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
597c577b8a1SJoseph Chan 	}
598c577b8a1SJoseph Chan }
599f5271101SLydia Wang 
600f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
601f5271101SLydia Wang 				unsigned int *affected_parm)
602f5271101SLydia Wang {
603f5271101SLydia Wang 	unsigned parm;
604f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
605f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
606f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
607f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
608d56757abSTakashi Iwai 	unsigned present = snd_hda_jack_detect(codec, nid);
6091564b287SLydia Wang 	struct via_spec *spec = codec->spec;
6101564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
6111564b287SLydia Wang 	    || ((no_presence || present)
6121564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
613f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
614f5271101SLydia Wang 		parm = AC_PWRST_D0;
615f5271101SLydia Wang 	} else
616f5271101SLydia Wang 		parm = AC_PWRST_D3;
617f5271101SLydia Wang 
618f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
619f5271101SLydia Wang }
620f5271101SLydia Wang 
621c577b8a1SJoseph Chan /*
622c577b8a1SJoseph Chan  * input MUX handling
623c577b8a1SJoseph Chan  */
624c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
625c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
626c577b8a1SJoseph Chan {
627c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
628c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
629c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
630c577b8a1SJoseph Chan }
631c577b8a1SJoseph Chan 
632c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
633c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
634c577b8a1SJoseph Chan {
635c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
636c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
637c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
638c577b8a1SJoseph Chan 
639c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
640c577b8a1SJoseph Chan 	return 0;
641c577b8a1SJoseph Chan }
642c577b8a1SJoseph Chan 
643c577b8a1SJoseph Chan static int via_mux_enum_put(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);
649bff5fbf5SLydia Wang 	int ret;
650c577b8a1SJoseph Chan 
651337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
652337b9d02STakashi Iwai 		return -EINVAL;
653a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
654a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
655a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
656a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
657a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
658bff5fbf5SLydia Wang 
659bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
660bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
661bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
662a80e6e3cSLydia Wang 	/* update jack power state */
6633e95b9abSLydia Wang 	set_widgets_power_state(codec);
664a80e6e3cSLydia Wang 
665bff5fbf5SLydia Wang 	return ret;
666c577b8a1SJoseph Chan }
667c577b8a1SJoseph Chan 
6680aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6690aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6700aa62aefSHarald Welte {
6710aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6720aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6730aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6740aa62aefSHarald Welte }
6750aa62aefSHarald Welte 
6760aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
6770aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
6780aa62aefSHarald Welte {
6790aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6805b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
681eb7188caSLydia Wang 	unsigned int pinsel;
682eb7188caSLydia Wang 
683eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
684eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
6850aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
6860aa62aefSHarald Welte 				      0x00);
6870aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
6880aa62aefSHarald Welte 
6890aa62aefSHarald Welte 	return 0;
6900aa62aefSHarald Welte }
6910aa62aefSHarald Welte 
6920713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
6930713efebSLydia Wang {
6940713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
6950713efebSLydia Wang 	if (ctl) {
6960713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
6970713efebSLydia Wang 		ctl->vd[0].access |= active
6980713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
6990713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
7000713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
7010713efebSLydia Wang 	}
7020713efebSLydia Wang }
7030713efebSLydia Wang 
7045b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
7055b0cb1d8SJaroslav Kysela {
7065b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7075b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7085b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7095b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7105b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
711e87885feSLydia Wang 	case VT2002P:		return 0x19;
712e87885feSLydia Wang 	case VT1802:		return 0x15;
713e87885feSLydia Wang 	case VT1812:		return 0x15;
7145b0cb1d8SJaroslav Kysela 	default:		return 0;
7155b0cb1d8SJaroslav Kysela 	}
7165b0cb1d8SJaroslav Kysela }
7175b0cb1d8SJaroslav Kysela 
718cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
719cdc1784dSLydia Wang {
720cdc1784dSLydia Wang 	/* mute side channel */
721cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
722e87885feSLydia Wang 	unsigned int parm;
7235b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
724cdc1784dSLydia Wang 
725e87885feSLydia Wang 	if (sw3) {
726e87885feSLydia Wang 		if (VT2002P_COMPATIBLE(spec))
727e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
728e87885feSLydia Wang 			       AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
729e87885feSLydia Wang 		else
730e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
731e87885feSLydia Wang 			       AMP_OUT_MUTE : AMP_OUT_UNMUTE;
732e87885feSLydia Wang 		snd_hda_codec_write(codec, sw3, 0,
733e87885feSLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, parm);
734e87885feSLydia Wang 		if (spec->codec_type == VT1812)
735e87885feSLydia Wang 			snd_hda_codec_write(codec, 0x1d, 0,
736e87885feSLydia Wang 					    AC_VERB_SET_AMP_GAIN_MUTE, parm);
737e87885feSLydia Wang 	}
738cdc1784dSLydia Wang 	return 0;
739cdc1784dSLydia Wang }
740cdc1784dSLydia Wang 
7410aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7420aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7430aa62aefSHarald Welte {
7440aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7450aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7465b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7470aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
748cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
749cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
750cdc1784dSLydia Wang 		? 1 : 0;
751ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
752ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
753ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
754ce0e5a9eSLydia Wang 	else
755ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
756ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7570aa62aefSHarald Welte 
758ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
759ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
760ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
761cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
762cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
763cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7640aa62aefSHarald Welte 					   0, 0, 0);
7650aa62aefSHarald Welte 
766cdc1784dSLydia Wang 	update_side_mute_status(codec);
7670713efebSLydia Wang 	/* update HP volume/swtich active state */
7680713efebSLydia Wang 	if (spec->codec_type == VT1708S
769eb7188caSLydia Wang 	    || spec->codec_type == VT1702
770f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
77125eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
77211890956SLydia Wang 	    || VT2002P_COMPATIBLE(spec)) {
7730713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7740713efebSLydia Wang 			     spec->hp_independent_mode);
7750713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
7760713efebSLydia Wang 			     spec->hp_independent_mode);
7770713efebSLydia Wang 	}
778ce0e5a9eSLydia Wang 	/* update jack power state */
7793e95b9abSLydia Wang 	set_widgets_power_state(codec);
7800aa62aefSHarald Welte 	return 0;
7810aa62aefSHarald Welte }
7820aa62aefSHarald Welte 
7835b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = {
7840aa62aefSHarald Welte 	{
7850aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7860aa62aefSHarald Welte 		.name = "Independent HP",
7870aa62aefSHarald Welte 		.info = via_independent_hp_info,
7880aa62aefSHarald Welte 		.get = via_independent_hp_get,
7890aa62aefSHarald Welte 		.put = via_independent_hp_put,
7900aa62aefSHarald Welte 	},
7915b0cb1d8SJaroslav Kysela 	{
7925b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
7935b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
7945b0cb1d8SJaroslav Kysela 	},
7950aa62aefSHarald Welte };
7960aa62aefSHarald Welte 
7973d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
7985b0cb1d8SJaroslav Kysela {
7993d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8005b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8015b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8023d83e577STakashi Iwai 	int nums;
8033d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
8045b0cb1d8SJaroslav Kysela 
8055b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
8065b0cb1d8SJaroslav Kysela 	case VT1718S:
8075b0cb1d8SJaroslav Kysela 		nid = 0x34;
8085b0cb1d8SJaroslav Kysela 		break;
8095b0cb1d8SJaroslav Kysela 	case VT2002P:
81011890956SLydia Wang 	case VT1802:
8115b0cb1d8SJaroslav Kysela 		nid = 0x35;
8125b0cb1d8SJaroslav Kysela 		break;
8135b0cb1d8SJaroslav Kysela 	case VT1812:
8145b0cb1d8SJaroslav Kysela 		nid = 0x3d;
8155b0cb1d8SJaroslav Kysela 		break;
8165b0cb1d8SJaroslav Kysela 	default:
8175b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
8185b0cb1d8SJaroslav Kysela 		break;
8195b0cb1d8SJaroslav Kysela 	}
8205b0cb1d8SJaroslav Kysela 
821ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
822ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
823ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8243d83e577STakashi Iwai 		if (nums <= 1)
8253d83e577STakashi Iwai 			return 0;
826ee3c35c0SLydia Wang 	}
8273d83e577STakashi Iwai 
8283d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8293d83e577STakashi Iwai 	if (knew == NULL)
8303d83e577STakashi Iwai 		return -ENOMEM;
8313d83e577STakashi Iwai 
8325b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8335b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8345b0cb1d8SJaroslav Kysela 
8355b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8365b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8375b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8385b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8395b0cb1d8SJaroslav Kysela 
8405b0cb1d8SJaroslav Kysela 	return 0;
8415b0cb1d8SJaroslav Kysela }
8425b0cb1d8SJaroslav Kysela 
8431564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8441564b287SLydia Wang {
8451564b287SLydia Wang 	int i;
8461564b287SLydia Wang 	struct snd_ctl_elem_id id;
8471564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
8481564b287SLydia Wang 
8491564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8501564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8511564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8521564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
8531564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
8541564b287SLydia Wang 			       &id);
8551564b287SLydia Wang 	}
8561564b287SLydia Wang }
8571564b287SLydia Wang 
8581564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8591564b287SLydia Wang {
8601564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8611564b287SLydia Wang 	hda_nid_t  nid_mixer;
8621564b287SLydia Wang 	int start_idx;
8631564b287SLydia Wang 	int end_idx;
8641564b287SLydia Wang 	int i;
8651564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8661564b287SLydia Wang 	switch (spec->codec_type) {
8671564b287SLydia Wang 	case VT1708:
8681564b287SLydia Wang 		nid_mixer = 0x17;
8691564b287SLydia Wang 		start_idx = 2;
8701564b287SLydia Wang 		end_idx = 4;
8711564b287SLydia Wang 		break;
8721564b287SLydia Wang 	case VT1709_10CH:
8731564b287SLydia Wang 	case VT1709_6CH:
8741564b287SLydia Wang 		nid_mixer = 0x18;
8751564b287SLydia Wang 		start_idx = 2;
8761564b287SLydia Wang 		end_idx = 4;
8771564b287SLydia Wang 		break;
8781564b287SLydia Wang 	case VT1708B_8CH:
8791564b287SLydia Wang 	case VT1708B_4CH:
8801564b287SLydia Wang 	case VT1708S:
881f3db423dSLydia Wang 	case VT1716S:
8821564b287SLydia Wang 		nid_mixer = 0x16;
8831564b287SLydia Wang 		start_idx = 2;
8841564b287SLydia Wang 		end_idx = 4;
8851564b287SLydia Wang 		break;
886ab657e0cSLydia Wang 	case VT1718S:
887ab657e0cSLydia Wang 		nid_mixer = 0x21;
888ab657e0cSLydia Wang 		start_idx = 1;
889ab657e0cSLydia Wang 		end_idx = 3;
890ab657e0cSLydia Wang 		break;
8911564b287SLydia Wang 	default:
8921564b287SLydia Wang 		return;
8931564b287SLydia Wang 	}
8941564b287SLydia Wang 	/* check AA path's mute status */
8951564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
8961564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
8971564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
8981564b287SLydia Wang 					 HDA_AMP_MUTE, val);
8991564b287SLydia Wang 	}
9001564b287SLydia Wang }
9011564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
9021564b287SLydia Wang {
9037b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9047b315bb4STakashi Iwai 	int i;
9057b315bb4STakashi Iwai 
9067b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9077b315bb4STakashi Iwai 		if (pin == cfg->inputs[i].pin)
90886e2959aSTakashi Iwai 			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
9091564b287SLydia Wang 	}
9107b315bb4STakashi Iwai 	return 0;
9111564b287SLydia Wang }
9121564b287SLydia Wang 
9131564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
9141564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
9151564b287SLydia Wang {
9161564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9171564b287SLydia Wang 	uinfo->count = 1;
9181564b287SLydia Wang 	uinfo->value.integer.min = 0;
9191564b287SLydia Wang 	uinfo->value.integer.max = 1;
9201564b287SLydia Wang 	return 0;
9211564b287SLydia Wang }
9221564b287SLydia Wang 
9231564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9241564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9251564b287SLydia Wang {
9261564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9271564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9287b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9291564b287SLydia Wang 	int on = 1;
9301564b287SLydia Wang 	int i;
9311564b287SLydia Wang 
9327b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9337b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9347b315bb4STakashi Iwai 		int ctl = snd_hda_codec_read(codec, nid, 0,
9357b315bb4STakashi Iwai 					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
93686e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9377b315bb4STakashi Iwai 			continue;
93886e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9397b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9401564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
9417b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9421564b287SLydia Wang 			on = 0;
9431564b287SLydia Wang 	}
9441564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9451564b287SLydia Wang 	return 0;
9461564b287SLydia Wang }
9471564b287SLydia Wang 
9481564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9491564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9501564b287SLydia Wang {
9511564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9521564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9537b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9541564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9551564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9561564b287SLydia Wang 	int i;
9571564b287SLydia Wang 
9587b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9597b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9607b315bb4STakashi Iwai 		unsigned int parm;
9617b315bb4STakashi Iwai 
96286e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9637b315bb4STakashi Iwai 			continue;
96486e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9657b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9661564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
9677b315bb4STakashi Iwai 
9687b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
9691564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9701564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
9711564b287SLydia Wang 		parm |= out_in;
9721564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
9731564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
9741564b287SLydia Wang 				    parm);
9751564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
9761564b287SLydia Wang 			mute_aa_path(codec, 1);
9771564b287SLydia Wang 			notify_aa_path_ctls(codec);
9781564b287SLydia Wang 		}
9797b315bb4STakashi Iwai 		if (spec->codec_type == VT1718S) {
980eb7188caSLydia Wang 			snd_hda_codec_amp_stereo(
981eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
982eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
9831564b287SLydia Wang 		}
98486e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
985f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
986f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
9871564b287SLydia Wang 				/* input = index 1 (AOW3) */
9881564b287SLydia Wang 				snd_hda_codec_write(
9891564b287SLydia Wang 					codec, nid, 0,
9901564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
9911564b287SLydia Wang 				snd_hda_codec_amp_stereo(
9921564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
9931564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
9941564b287SLydia Wang 			}
9951564b287SLydia Wang 		}
9961564b287SLydia Wang 	}
9971564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
9983e95b9abSLydia Wang 	set_widgets_power_state(codec);
9991564b287SLydia Wang 	return 1;
10001564b287SLydia Wang }
10011564b287SLydia Wang 
10025b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = {
10031564b287SLydia Wang 	{
10041564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10051564b287SLydia Wang 	 .name = "Smart 5.1",
10061564b287SLydia Wang 	 .count = 1,
10071564b287SLydia Wang 	 .info = via_smart51_info,
10081564b287SLydia Wang 	 .get = via_smart51_get,
10091564b287SLydia Wang 	 .put = via_smart51_put,
10101564b287SLydia Wang 	 },
10115b0cb1d8SJaroslav Kysela 	{
10125b0cb1d8SJaroslav Kysela 	 .iface = NID_MAPPING,
10135b0cb1d8SJaroslav Kysela 	 .name = "Smart 5.1",
10145b0cb1d8SJaroslav Kysela 	}
10151564b287SLydia Wang };
10161564b287SLydia Wang 
10175b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
10185b0cb1d8SJaroslav Kysela {
10195b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10207b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10215b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10225b0cb1d8SJaroslav Kysela 	int i;
10235b0cb1d8SJaroslav Kysela 
10245b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_smart51_mixer[0]);
10255b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10265b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10275b0cb1d8SJaroslav Kysela 
10287b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10297b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
103086e2959aSTakashi Iwai 		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
10315b0cb1d8SJaroslav Kysela 			knew = via_clone_control(spec, &via_smart51_mixer[1]);
10325b0cb1d8SJaroslav Kysela 			if (knew == NULL)
10335b0cb1d8SJaroslav Kysela 				return -ENOMEM;
10345b0cb1d8SJaroslav Kysela 			knew->subdevice = nid;
10357b315bb4STakashi Iwai 			break;
10365b0cb1d8SJaroslav Kysela 		}
10375b0cb1d8SJaroslav Kysela 	}
10385b0cb1d8SJaroslav Kysela 
10395b0cb1d8SJaroslav Kysela 	return 0;
10405b0cb1d8SJaroslav Kysela }
10415b0cb1d8SJaroslav Kysela 
1042c577b8a1SJoseph Chan /* capture mixer elements */
1043c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1044c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1045c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1046c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1047c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1048c577b8a1SJoseph Chan 	{
1049c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1050c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1051c577b8a1SJoseph Chan 		 * So call somewhat different..
1052c577b8a1SJoseph Chan 		 */
1053c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1054c577b8a1SJoseph Chan 		.name = "Input Source",
1055c577b8a1SJoseph Chan 		.count = 1,
1056c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1057c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1058c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1059c577b8a1SJoseph Chan 	},
1060c577b8a1SJoseph Chan 	{ } /* end */
1061c577b8a1SJoseph Chan };
1062f5271101SLydia Wang 
1063f5271101SLydia Wang /* check AA path's mute statue */
1064f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1065f5271101SLydia Wang {
1066f5271101SLydia Wang 	int mute = 1;
1067f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1068f5271101SLydia Wang 	int start_idx;
1069f5271101SLydia Wang 	int end_idx;
1070f5271101SLydia Wang 	int i;
1071f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1072f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1073f5271101SLydia Wang 	switch (spec->codec_type) {
1074f5271101SLydia Wang 	case VT1708B_8CH:
1075f5271101SLydia Wang 	case VT1708B_4CH:
1076f5271101SLydia Wang 	case VT1708S:
1077f3db423dSLydia Wang 	case VT1716S:
1078f5271101SLydia Wang 		nid_mixer = 0x16;
1079f5271101SLydia Wang 		start_idx = 2;
1080f5271101SLydia Wang 		end_idx = 4;
1081f5271101SLydia Wang 		break;
1082f5271101SLydia Wang 	case VT1702:
1083f5271101SLydia Wang 		nid_mixer = 0x1a;
1084f5271101SLydia Wang 		start_idx = 1;
1085f5271101SLydia Wang 		end_idx = 3;
1086f5271101SLydia Wang 		break;
1087eb7188caSLydia Wang 	case VT1718S:
1088eb7188caSLydia Wang 		nid_mixer = 0x21;
1089eb7188caSLydia Wang 		start_idx = 1;
1090eb7188caSLydia Wang 		end_idx = 3;
1091eb7188caSLydia Wang 		break;
109225eaba2fSLydia Wang 	case VT2002P:
1093ab6734e7SLydia Wang 	case VT1812:
109411890956SLydia Wang 	case VT1802:
109525eaba2fSLydia Wang 		nid_mixer = 0x21;
109625eaba2fSLydia Wang 		start_idx = 0;
109725eaba2fSLydia Wang 		end_idx = 2;
109825eaba2fSLydia Wang 		break;
1099f5271101SLydia Wang 	default:
1100f5271101SLydia Wang 		return 0;
1101f5271101SLydia Wang 	}
1102f5271101SLydia Wang 	/* check AA path's mute status */
1103f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1104f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1105f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1106f5271101SLydia Wang 		int shift = 8 * (i % 4);
1107f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1108f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1109f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1110f5271101SLydia Wang 			/* check mute status while the pin is connected */
1111f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1112f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1113f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1114f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1115f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1116f5271101SLydia Wang 				mute = 0;
1117f5271101SLydia Wang 				break;
1118f5271101SLydia Wang 			}
1119f5271101SLydia Wang 		}
1120f5271101SLydia Wang 	}
1121f5271101SLydia Wang 	return mute;
1122f5271101SLydia Wang }
1123f5271101SLydia Wang 
1124f5271101SLydia Wang /* enter/exit analog low-current mode */
1125f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1126f5271101SLydia Wang {
1127f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1128f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1129f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1130f5271101SLydia Wang 	unsigned int verb = 0;
1131f5271101SLydia Wang 	unsigned int parm = 0;
1132f5271101SLydia Wang 
1133f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1134f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1135f5271101SLydia Wang 	else {
1136f5271101SLydia Wang 		enable = enable && stream_idle;
1137f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1138f5271101SLydia Wang 	}
1139f5271101SLydia Wang 
1140f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1141f5271101SLydia Wang 	switch (spec->codec_type) {
1142f5271101SLydia Wang 	case VT1708B_8CH:
1143f5271101SLydia Wang 	case VT1708B_4CH:
1144f5271101SLydia Wang 		verb = 0xf70;
1145f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1146f5271101SLydia Wang 		break;
1147f5271101SLydia Wang 	case VT1708S:
1148eb7188caSLydia Wang 	case VT1718S:
1149f3db423dSLydia Wang 	case VT1716S:
1150f5271101SLydia Wang 		verb = 0xf73;
1151f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1152f5271101SLydia Wang 		break;
1153f5271101SLydia Wang 	case VT1702:
1154f5271101SLydia Wang 		verb = 0xf73;
1155f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1156f5271101SLydia Wang 		break;
115725eaba2fSLydia Wang 	case VT2002P:
1158ab6734e7SLydia Wang 	case VT1812:
115911890956SLydia Wang 	case VT1802:
116025eaba2fSLydia Wang 		verb = 0xf93;
116125eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
116225eaba2fSLydia Wang 		break;
1163f5271101SLydia Wang 	default:
1164f5271101SLydia Wang 		return;		/* other codecs are not supported */
1165f5271101SLydia Wang 	}
1166f5271101SLydia Wang 	/* send verb */
1167f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1168f5271101SLydia Wang }
1169f5271101SLydia Wang 
1170c577b8a1SJoseph Chan /*
1171c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1172c577b8a1SJoseph Chan  */
1173c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1174c577b8a1SJoseph Chan 	/*
1175c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1176c577b8a1SJoseph Chan 	 */
1177c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1178c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1179c577b8a1SJoseph Chan 
1180c577b8a1SJoseph Chan 
1181f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1182c577b8a1SJoseph Chan 	 * mixer widget
1183c577b8a1SJoseph Chan 	 */
1184c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1185f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1186f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1187f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1188f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1189f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1190c577b8a1SJoseph Chan 
1191c577b8a1SJoseph Chan 	/*
1192c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1193c577b8a1SJoseph Chan 	 */
1194c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1195c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1196c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1197c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1198c577b8a1SJoseph Chan 
1199bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1200bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1201c577b8a1SJoseph Chan 	/* PW9 Output enable */
1202c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1203aa266fccSLydia Wang 	/* power down jack detect function */
1204aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1205f7278fd0SJosepch Chan 	{ }
1206c577b8a1SJoseph Chan };
1207c577b8a1SJoseph Chan 
1208c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1209c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1210c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1211c577b8a1SJoseph Chan {
1212c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
121317314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
121417314379SLydia Wang 		&& substream->ref_count == 0;
121517314379SLydia Wang 	analog_low_current_mode(codec, idle);
12169a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
12179a08160bSTakashi Iwai 					     hinfo);
1218c577b8a1SJoseph Chan }
1219c577b8a1SJoseph Chan 
12200aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
12210aa62aefSHarald Welte 				      unsigned int stream_tag,
12220aa62aefSHarald Welte 				      unsigned int format,
12230aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
12240aa62aefSHarald Welte {
12250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12260aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
12270aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
12280aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12290aa62aefSHarald Welte 	int i;
12300aa62aefSHarald Welte 
12310aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12320aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12330aa62aefSHarald Welte 		if (chs == 2 &&
12340aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12350aa62aefSHarald Welte 						format) &&
12360aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
12370aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12380aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12390aa62aefSHarald Welte 			 * be updated */
12400aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
12410aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12420aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12430aa62aefSHarald Welte 						    codec->spdif_ctls &
12440aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12450aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12460aa62aefSHarald Welte 						   stream_tag, 0, format);
12470aa62aefSHarald Welte 			/* turn on again (if needed) */
12480aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
12490aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12500aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12510aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
12520aa62aefSHarald Welte 		} else {
12530aa62aefSHarald Welte 			mout->dig_out_used = 0;
12540aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12550aa62aefSHarald Welte 						   0, 0, 0);
12560aa62aefSHarald Welte 		}
12570aa62aefSHarald Welte 	}
12580aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12590aa62aefSHarald Welte 
12600aa62aefSHarald Welte 	/* front */
12610aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12620aa62aefSHarald Welte 				   0, format);
12630aa62aefSHarald Welte 
1264eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1265eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12660aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12670aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12680aa62aefSHarald Welte 					   0, format);
12690aa62aefSHarald Welte 
12700aa62aefSHarald Welte 	/* extra outputs copied from front */
12710aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12720aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12730aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12740aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12750aa62aefSHarald Welte 						   stream_tag, 0, format);
12760aa62aefSHarald Welte 
12770aa62aefSHarald Welte 	/* surrounds */
12780aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12790aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12800aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12810aa62aefSHarald Welte 						   i * 2, format);
12820aa62aefSHarald Welte 		else /* copy front */
12830aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12840aa62aefSHarald Welte 						   0, format);
12850aa62aefSHarald Welte 	}
12860aa62aefSHarald Welte }
12870aa62aefSHarald Welte 
12880aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
12890aa62aefSHarald Welte 					  struct hda_codec *codec,
12900aa62aefSHarald Welte 					  unsigned int stream_tag,
12910aa62aefSHarald Welte 					  unsigned int format,
12920aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
12930aa62aefSHarald Welte {
12940aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12950aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
12960aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
12970aa62aefSHarald Welte 
12980aa62aefSHarald Welte 	if (substream->number == 0)
12990aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
13000aa62aefSHarald Welte 					  substream);
13010aa62aefSHarald Welte 	else {
13020aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13030aa62aefSHarald Welte 		    spec->hp_independent_mode)
13040aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13050aa62aefSHarald Welte 						   stream_tag, 0, format);
13060aa62aefSHarald Welte 	}
13071f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
13080aa62aefSHarald Welte 	return 0;
13090aa62aefSHarald Welte }
13100aa62aefSHarald Welte 
13110aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
13120aa62aefSHarald Welte 				    struct hda_codec *codec,
13130aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
13140aa62aefSHarald Welte {
13150aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13160aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
13170aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
13180aa62aefSHarald Welte 	int i;
13190aa62aefSHarald Welte 
13200aa62aefSHarald Welte 	if (substream->number == 0) {
13210aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
13220aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
13230aa62aefSHarald Welte 
13240aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
13250aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13260aa62aefSHarald Welte 						   0, 0, 0);
13270aa62aefSHarald Welte 
13280aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13290aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
13300aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
13310aa62aefSHarald Welte 							mout->extra_out_nid[i],
13320aa62aefSHarald Welte 							0, 0, 0);
13330aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
13340aa62aefSHarald Welte 		if (mout->dig_out_nid &&
13350aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13360aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13370aa62aefSHarald Welte 						   0, 0, 0);
13380aa62aefSHarald Welte 			mout->dig_out_used = 0;
13390aa62aefSHarald Welte 		}
13400aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
13410aa62aefSHarald Welte 	} else {
13420aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13430aa62aefSHarald Welte 		    spec->hp_independent_mode)
13440aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13450aa62aefSHarald Welte 						   0, 0, 0);
13460aa62aefSHarald Welte 	}
13471f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13480aa62aefSHarald Welte 	return 0;
13490aa62aefSHarald Welte }
13500aa62aefSHarald Welte 
1351c577b8a1SJoseph Chan /*
1352c577b8a1SJoseph Chan  * Digital out
1353c577b8a1SJoseph Chan  */
1354c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1355c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1356c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1357c577b8a1SJoseph Chan {
1358c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1359c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1360c577b8a1SJoseph Chan }
1361c577b8a1SJoseph Chan 
1362c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1363c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1364c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1365c577b8a1SJoseph Chan {
1366c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1367c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1368c577b8a1SJoseph Chan }
1369c577b8a1SJoseph Chan 
13705691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
137198aa34c0SHarald Welte 					struct hda_codec *codec,
137298aa34c0SHarald Welte 					unsigned int stream_tag,
137398aa34c0SHarald Welte 					unsigned int format,
137498aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
137598aa34c0SHarald Welte {
137698aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13779da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13789da29271STakashi Iwai 					     stream_tag, format, substream);
13799da29271STakashi Iwai }
13805691ec7fSHarald Welte 
13819da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
13829da29271STakashi Iwai 					struct hda_codec *codec,
13839da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13849da29271STakashi Iwai {
13859da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
13869da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
138798aa34c0SHarald Welte 	return 0;
138898aa34c0SHarald Welte }
138998aa34c0SHarald Welte 
1390c577b8a1SJoseph Chan /*
1391c577b8a1SJoseph Chan  * Analog capture
1392c577b8a1SJoseph Chan  */
1393c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1394c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1395c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1396c577b8a1SJoseph Chan 				   unsigned int format,
1397c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1398c577b8a1SJoseph Chan {
1399c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1400c577b8a1SJoseph Chan 
1401c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1402c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1403c577b8a1SJoseph Chan 	return 0;
1404c577b8a1SJoseph Chan }
1405c577b8a1SJoseph Chan 
1406c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1407c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1408c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1409c577b8a1SJoseph Chan {
1410c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1411888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1412c577b8a1SJoseph Chan 	return 0;
1413c577b8a1SJoseph Chan }
1414c577b8a1SJoseph Chan 
1415c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
14160aa62aefSHarald Welte 	.substreams = 2,
1417c577b8a1SJoseph Chan 	.channels_min = 2,
1418c577b8a1SJoseph Chan 	.channels_max = 8,
1419c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1420c577b8a1SJoseph Chan 	.ops = {
1421c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
14220aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
14230aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1424c577b8a1SJoseph Chan 	},
1425c577b8a1SJoseph Chan };
1426c577b8a1SJoseph Chan 
1427bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1428c873cc25SLydia Wang 	.substreams = 2,
1429bc9b5623STakashi Iwai 	.channels_min = 2,
1430bc9b5623STakashi Iwai 	.channels_max = 8,
1431bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1432bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1433bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1434bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1435bc9b5623STakashi Iwai 	 */
1436bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1437bc9b5623STakashi Iwai 	.ops = {
1438bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1439c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1440c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1441bc9b5623STakashi Iwai 	},
1442bc9b5623STakashi Iwai };
1443bc9b5623STakashi Iwai 
1444c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1445c577b8a1SJoseph Chan 	.substreams = 2,
1446c577b8a1SJoseph Chan 	.channels_min = 2,
1447c577b8a1SJoseph Chan 	.channels_max = 2,
1448c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1449c577b8a1SJoseph Chan 	.ops = {
1450c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1451c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1452c577b8a1SJoseph Chan 	},
1453c577b8a1SJoseph Chan };
1454c577b8a1SJoseph Chan 
1455c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1456c577b8a1SJoseph Chan 	.substreams = 1,
1457c577b8a1SJoseph Chan 	.channels_min = 2,
1458c577b8a1SJoseph Chan 	.channels_max = 2,
1459c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1460c577b8a1SJoseph Chan 	.ops = {
1461c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14626b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14639da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14649da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1465c577b8a1SJoseph Chan 	},
1466c577b8a1SJoseph Chan };
1467c577b8a1SJoseph Chan 
1468c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1469c577b8a1SJoseph Chan 	.substreams = 1,
1470c577b8a1SJoseph Chan 	.channels_min = 2,
1471c577b8a1SJoseph Chan 	.channels_max = 2,
1472c577b8a1SJoseph Chan };
1473c577b8a1SJoseph Chan 
1474c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1475c577b8a1SJoseph Chan {
1476c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14775b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
14785b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
14795b0cb1d8SJaroslav Kysela 	int err, i;
1480c577b8a1SJoseph Chan 
1481c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1482c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1483c577b8a1SJoseph Chan 		if (err < 0)
1484c577b8a1SJoseph Chan 			return err;
1485c577b8a1SJoseph Chan 	}
1486c577b8a1SJoseph Chan 
1487c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1488c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1489c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1490c577b8a1SJoseph Chan 		if (err < 0)
1491c577b8a1SJoseph Chan 			return err;
14929a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
14939a08160bSTakashi Iwai 						    &spec->multiout);
14949a08160bSTakashi Iwai 		if (err < 0)
14959a08160bSTakashi Iwai 			return err;
14969a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1497c577b8a1SJoseph Chan 	}
1498c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1499c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1500c577b8a1SJoseph Chan 		if (err < 0)
1501c577b8a1SJoseph Chan 			return err;
1502c577b8a1SJoseph Chan 	}
150317314379SLydia Wang 
15045b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15055b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15065b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
150721949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15085b0cb1d8SJaroslav Kysela 		if (err < 0)
15095b0cb1d8SJaroslav Kysela 			return err;
15105b0cb1d8SJaroslav Kysela 	}
15115b0cb1d8SJaroslav Kysela 
15125b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
15135b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
15145b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
15155b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
15165b0cb1d8SJaroslav Kysela 				continue;
15175b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
15185b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
15195b0cb1d8SJaroslav Kysela 				continue;
15205b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
15215b0cb1d8SJaroslav Kysela 					      knew->subdevice);
15225b0cb1d8SJaroslav Kysela 		}
15235b0cb1d8SJaroslav Kysela 	}
15245b0cb1d8SJaroslav Kysela 
152517314379SLydia Wang 	/* init power states */
15263e95b9abSLydia Wang 	set_widgets_power_state(codec);
152717314379SLydia Wang 	analog_low_current_mode(codec, 1);
152817314379SLydia Wang 
1529603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1530c577b8a1SJoseph Chan 	return 0;
1531c577b8a1SJoseph Chan }
1532c577b8a1SJoseph Chan 
1533c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1534c577b8a1SJoseph Chan {
1535c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1536c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1537c577b8a1SJoseph Chan 
1538c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1539c577b8a1SJoseph Chan 	codec->pcm_info = info;
1540c577b8a1SJoseph Chan 
1541c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1542377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1543377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1544377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1545377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1546c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1547c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1548c577b8a1SJoseph Chan 
1549c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1550c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1551c577b8a1SJoseph Chan 
1552c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1553c577b8a1SJoseph Chan 		codec->num_pcms++;
1554c577b8a1SJoseph Chan 		info++;
1555c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
15567ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1557c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1558c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1559c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1560c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1561c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1562c577b8a1SJoseph Chan 		}
1563c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1564c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1565c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1566c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1567c577b8a1SJoseph Chan 				spec->dig_in_nid;
1568c577b8a1SJoseph Chan 		}
1569c577b8a1SJoseph Chan 	}
1570c577b8a1SJoseph Chan 
1571c577b8a1SJoseph Chan 	return 0;
1572c577b8a1SJoseph Chan }
1573c577b8a1SJoseph Chan 
1574c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1575c577b8a1SJoseph Chan {
1576c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1577c577b8a1SJoseph Chan 
1578c577b8a1SJoseph Chan 	if (!spec)
1579c577b8a1SJoseph Chan 		return;
1580c577b8a1SJoseph Chan 
1581603c4019STakashi Iwai 	via_free_kctls(codec);
15821f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1583c577b8a1SJoseph Chan 	kfree(codec->spec);
1584c577b8a1SJoseph Chan }
1585c577b8a1SJoseph Chan 
158669e52a80SHarald Welte /* mute internal speaker if HP is plugged */
158769e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
158869e52a80SHarald Welte {
1589dcf34c8cSLydia Wang 	unsigned int present = 0;
159069e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
159169e52a80SHarald Welte 
1592d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1593dcf34c8cSLydia Wang 
1594dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1595dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
1596dcf34c8cSLydia Wang 		/* auto mute */
1597dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
1598dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1599dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1600dcf34c8cSLydia Wang 		/* notify change */
1601dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
1602dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1603dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
1604dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1605dcf34c8cSLydia Wang 			       &id);
1606dcf34c8cSLydia Wang 	}
160769e52a80SHarald Welte }
160869e52a80SHarald Welte 
1609f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1610f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1611f3db423dSLydia Wang {
1612f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1613f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1614f3db423dSLydia Wang 
1615f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1616f3db423dSLydia Wang 		return;
1617f3db423dSLydia Wang 
1618d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1619d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1620f3db423dSLydia Wang 
1621f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1622f3db423dSLydia Wang 	if (lineout_present) {
1623f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1624f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1625f3db423dSLydia Wang 		return;
1626f3db423dSLydia Wang 	}
1627f3db423dSLydia Wang 
1628d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1629f3db423dSLydia Wang 
1630f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
1631f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1632f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1633f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
1634f3db423dSLydia Wang }
1635f3db423dSLydia Wang 
163669e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
163769e52a80SHarald Welte {
163869e52a80SHarald Welte 	unsigned int gpio_data;
163969e52a80SHarald Welte 	unsigned int vol_counter;
164069e52a80SHarald Welte 	unsigned int vol;
164169e52a80SHarald Welte 	unsigned int master_vol;
164269e52a80SHarald Welte 
164369e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
164469e52a80SHarald Welte 
164569e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
164669e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
164769e52a80SHarald Welte 
164869e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
164969e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
165069e52a80SHarald Welte 
165169e52a80SHarald Welte 	vol = vol_counter & 0x1F;
165269e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
165369e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
165469e52a80SHarald Welte 					AC_AMP_GET_INPUT);
165569e52a80SHarald Welte 
165669e52a80SHarald Welte 	if (gpio_data == 0x02) {
165769e52a80SHarald Welte 		/* unmute line out */
165869e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
165969e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
166069e52a80SHarald Welte 
166169e52a80SHarald Welte 		if (vol_counter & 0x20) {
166269e52a80SHarald Welte 			/* decrease volume */
166369e52a80SHarald Welte 			if (vol > master_vol)
166469e52a80SHarald Welte 				vol = master_vol;
166569e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
166669e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
166769e52a80SHarald Welte 						 master_vol-vol);
166869e52a80SHarald Welte 		} else {
166969e52a80SHarald Welte 			/* increase volume */
167069e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
167169e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
167269e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
167369e52a80SHarald Welte 					  (master_vol+vol));
167469e52a80SHarald Welte 		}
167569e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
167669e52a80SHarald Welte 		/* mute line out */
167769e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
167869e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
167969e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
168069e52a80SHarald Welte 					 HDA_AMP_MUTE);
168169e52a80SHarald Welte 	}
168269e52a80SHarald Welte }
168369e52a80SHarald Welte 
168425eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
168525eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
168625eaba2fSLydia Wang {
168725eaba2fSLydia Wang 	unsigned int hp_present;
168825eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
168925eaba2fSLydia Wang 
169027439ce7SLydia Wang 	if (!VT2002P_COMPATIBLE(spec))
169125eaba2fSLydia Wang 		return;
169225eaba2fSLydia Wang 
1693d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
169425eaba2fSLydia Wang 
169525eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
169625eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
169725eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
169825eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
169925eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
170025eaba2fSLydia Wang 		/* notify change */
170125eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
170225eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
170325eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
170425eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
170525eaba2fSLydia Wang 			       &id);
170625eaba2fSLydia Wang 	}
170725eaba2fSLydia Wang }
170825eaba2fSLydia Wang 
170925eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
171025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
171125eaba2fSLydia Wang {
171201a1796bSakpm@linux-foundation.org 	/* use long instead of int below just to avoid an internal compiler
171301a1796bSakpm@linux-foundation.org 	 * error with gcc 4.0.x
171401a1796bSakpm@linux-foundation.org 	 */
171501a1796bSakpm@linux-foundation.org 	unsigned long hp_present, present = 0;
171625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
171725eaba2fSLydia Wang 	int i;
171825eaba2fSLydia Wang 
171925eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
172025eaba2fSLydia Wang 		return;
172125eaba2fSLydia Wang 
1722d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
172325eaba2fSLydia Wang 
1724d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
172525eaba2fSLydia Wang 
172625eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
172725eaba2fSLydia Wang 		/* Mute Line-Outs */
172825eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
172925eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
173025eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
173125eaba2fSLydia Wang 				HDA_OUTPUT, 0,
173225eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
173325eaba2fSLydia Wang 		if (hp_present)
173425eaba2fSLydia Wang 			present = hp_present;
173525eaba2fSLydia Wang 	}
173625eaba2fSLydia Wang 	/* Speakers */
173725eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
173825eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
173925eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
174025eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
174125eaba2fSLydia Wang }
174225eaba2fSLydia Wang 
174325eaba2fSLydia Wang 
174469e52a80SHarald Welte /* unsolicited event for jack sensing */
174569e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
174669e52a80SHarald Welte 				  unsigned int res)
174769e52a80SHarald Welte {
174869e52a80SHarald Welte 	res >>= 26;
1749*ec7e7e42SLydia Wang 
1750a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
17513e95b9abSLydia Wang 		set_widgets_power_state(codec);
1752*ec7e7e42SLydia Wang 
1753*ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1754*ec7e7e42SLydia Wang 
1755*ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1756*ec7e7e42SLydia Wang 		via_hp_automute(codec);
1757*ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1758*ec7e7e42SLydia Wang 		via_gpio_control(codec);
1759*ec7e7e42SLydia Wang 	else if (res == VIA_MONO_EVENT)
1760f3db423dSLydia Wang 		via_mono_automute(codec);
1761*ec7e7e42SLydia Wang 	else if (res == VIA_SPEAKER_EVENT)
176225eaba2fSLydia Wang 		via_speaker_automute(codec);
1763*ec7e7e42SLydia Wang 	else if (res == VIA_BIND_HP_EVENT)
176425eaba2fSLydia Wang 		via_hp_bind_automute(codec);
176569e52a80SHarald Welte }
176669e52a80SHarald Welte 
1767c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1768c577b8a1SJoseph Chan {
1769c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
177069e52a80SHarald Welte 	int i;
177169e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
177269e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
177369e52a80SHarald Welte 
1774f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1775f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
177655d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
177755d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1778f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
177912b74c80STakashi Iwai 					    PIN_OUT);
178055d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1781f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1782f7278fd0SJosepch Chan 		}
178312b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
178412b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
178512b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1786f7278fd0SJosepch Chan 
17879da29271STakashi Iwai 	/* assign slave outs */
17889da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
17899da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
17905691ec7fSHarald Welte 
1791c577b8a1SJoseph Chan 	return 0;
1792c577b8a1SJoseph Chan }
1793c577b8a1SJoseph Chan 
17941f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
17951f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
17961f2e99feSLydia Wang {
17971f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
17981f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
17991f2e99feSLydia Wang 	return 0;
18001f2e99feSLydia Wang }
18011f2e99feSLydia Wang #endif
18021f2e99feSLydia Wang 
1803cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1804cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1805cb53c626STakashi Iwai {
1806cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1807cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1808cb53c626STakashi Iwai }
1809cb53c626STakashi Iwai #endif
1810cb53c626STakashi Iwai 
1811c577b8a1SJoseph Chan /*
1812c577b8a1SJoseph Chan  */
1813c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
1814c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1815c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1816c577b8a1SJoseph Chan 	.init = via_init,
1817c577b8a1SJoseph Chan 	.free = via_free,
18181f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18191f2e99feSLydia Wang 	.suspend = via_suspend,
18201f2e99feSLydia Wang #endif
1821cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1822cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1823cb53c626STakashi Iwai #endif
1824c577b8a1SJoseph Chan };
1825c577b8a1SJoseph Chan 
1826c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
1827c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1828c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
1829c577b8a1SJoseph Chan {
1830c577b8a1SJoseph Chan 	int i;
1831c577b8a1SJoseph Chan 	hda_nid_t nid;
1832c577b8a1SJoseph Chan 
1833c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
1834c577b8a1SJoseph Chan 
1835c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
1836c577b8a1SJoseph Chan 
1837c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
1838c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1839c577b8a1SJoseph Chan 		if (nid) {
1840c577b8a1SJoseph Chan 			/* config dac list */
1841c577b8a1SJoseph Chan 			switch (i) {
1842c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
1843c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
1844c577b8a1SJoseph Chan 				break;
1845c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
1846c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
1847c577b8a1SJoseph Chan 				break;
1848c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
1849fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
1850c577b8a1SJoseph Chan 				break;
1851c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
1852fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
1853c577b8a1SJoseph Chan 				break;
1854c577b8a1SJoseph Chan 			}
1855c577b8a1SJoseph Chan 		}
1856c577b8a1SJoseph Chan 	}
1857c577b8a1SJoseph Chan 
1858c577b8a1SJoseph Chan 	return 0;
1859c577b8a1SJoseph Chan }
1860c577b8a1SJoseph Chan 
1861c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
1862c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1863c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
1864c577b8a1SJoseph Chan {
1865c577b8a1SJoseph Chan 	char name[32];
1866ea734963STakashi Iwai 	static const char * const chname[4] = {
1867ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
1868ea734963STakashi Iwai 	};
18699645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
1870c577b8a1SJoseph Chan 	int i, err;
1871c577b8a1SJoseph Chan 
1872c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1873c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1874c577b8a1SJoseph Chan 
1875c577b8a1SJoseph Chan 		if (!nid)
1876c577b8a1SJoseph Chan 			continue;
1877c577b8a1SJoseph Chan 
18789645c203SLydia Wang 		nid_vol = nid_vols[i];
1879c577b8a1SJoseph Chan 
1880c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
1881c577b8a1SJoseph Chan 			/* Center/LFE */
1882c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1883c577b8a1SJoseph Chan 					"Center Playback Volume",
1884f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1885f7278fd0SJosepch Chan 							    HDA_OUTPUT));
1886c577b8a1SJoseph Chan 			if (err < 0)
1887c577b8a1SJoseph Chan 				return err;
1888c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1889c577b8a1SJoseph Chan 					      "LFE Playback Volume",
1890f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1891f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1892c577b8a1SJoseph Chan 			if (err < 0)
1893c577b8a1SJoseph Chan 				return err;
1894c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1895c577b8a1SJoseph Chan 					      "Center Playback Switch",
1896f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1897f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1898c577b8a1SJoseph Chan 			if (err < 0)
1899c577b8a1SJoseph Chan 				return err;
1900c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1901c577b8a1SJoseph Chan 					      "LFE Playback Switch",
1902f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1903f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1904c577b8a1SJoseph Chan 			if (err < 0)
1905c577b8a1SJoseph Chan 				return err;
1906c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
1907c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
1908c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1909c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
19109645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1911f7278fd0SJosepch Chan 								  HDA_INPUT));
1912c577b8a1SJoseph Chan 			if (err < 0)
1913c577b8a1SJoseph Chan 				return err;
1914c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1915c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
19169645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1917f7278fd0SJosepch Chan 								  HDA_INPUT));
1918c577b8a1SJoseph Chan 			if (err < 0)
1919c577b8a1SJoseph Chan 				return err;
1920c577b8a1SJoseph Chan 
1921c577b8a1SJoseph Chan 			/* add control to PW3 */
1922c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1923c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1924f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1925f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1926c577b8a1SJoseph Chan 			if (err < 0)
1927c577b8a1SJoseph Chan 				return err;
1928c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1929c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1930f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1931f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1932c577b8a1SJoseph Chan 			if (err < 0)
1933c577b8a1SJoseph Chan 				return err;
1934c577b8a1SJoseph Chan 		} else {
1935c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1936c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1937f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1938f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1939c577b8a1SJoseph Chan 			if (err < 0)
1940c577b8a1SJoseph Chan 				return err;
1941c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1942c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1943f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1944f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1945c577b8a1SJoseph Chan 			if (err < 0)
1946c577b8a1SJoseph Chan 				return err;
1947c577b8a1SJoseph Chan 		}
1948c577b8a1SJoseph Chan 	}
1949c577b8a1SJoseph Chan 
1950c577b8a1SJoseph Chan 	return 0;
1951c577b8a1SJoseph Chan }
1952c577b8a1SJoseph Chan 
19530aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
19540aa62aefSHarald Welte {
19550aa62aefSHarald Welte 	int i;
19560aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
1957ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
19580aa62aefSHarald Welte 
19590aa62aefSHarald Welte 	/* for hp mode select */
196010a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
196110a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
19620aa62aefSHarald Welte 
19630aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
19640aa62aefSHarald Welte }
19650aa62aefSHarald Welte 
1966c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1967c577b8a1SJoseph Chan {
1968c577b8a1SJoseph Chan 	int err;
1969c577b8a1SJoseph Chan 
1970c577b8a1SJoseph Chan 	if (!pin)
1971c577b8a1SJoseph Chan 		return 0;
1972c577b8a1SJoseph Chan 
1973c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
1974cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
1975c577b8a1SJoseph Chan 
1976c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1977c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
1978c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1979c577b8a1SJoseph Chan 	if (err < 0)
1980c577b8a1SJoseph Chan 		return err;
1981c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1982c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
1983c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1984c577b8a1SJoseph Chan 	if (err < 0)
1985c577b8a1SJoseph Chan 		return err;
1986c577b8a1SJoseph Chan 
19870aa62aefSHarald Welte 	create_hp_imux(spec);
19880aa62aefSHarald Welte 
1989c577b8a1SJoseph Chan 	return 0;
1990c577b8a1SJoseph Chan }
1991c577b8a1SJoseph Chan 
1992c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
199310a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
1994f3268512STakashi Iwai 					    const struct auto_pin_cfg *cfg,
1995f3268512STakashi Iwai 					    hda_nid_t cap_nid,
1996f3268512STakashi Iwai 					    hda_nid_t pin_idxs[], int num_idxs)
1997c577b8a1SJoseph Chan {
199810a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
19990aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
20007b315bb4STakashi Iwai 	int i, err, idx, type, type_idx = 0;
2001c577b8a1SJoseph Chan 
2002c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2003f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2004f3268512STakashi Iwai 		if (pin_idxs[idx] == 0xff) {
200510a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2006f3268512STakashi Iwai 			break;
2007f3268512STakashi Iwai 		}
2008f3268512STakashi Iwai 	}
2009c577b8a1SJoseph Chan 
20107b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
201110a20af7STakashi Iwai 		const char *label;
20127b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2013f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
20147b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2015c577b8a1SJoseph Chan 				break;
2016f3268512STakashi Iwai 		if (idx >= num_idxs)
2017f3268512STakashi Iwai 			continue;
20187b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
20197b315bb4STakashi Iwai 			type_idx++;
20207b315bb4STakashi Iwai 		else
20217b315bb4STakashi Iwai 			type_idx = 0;
202210a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
202316922281SLydia Wang 		if (spec->codec_type == VT1708S ||
202416922281SLydia Wang 		    spec->codec_type == VT1702 ||
202516922281SLydia Wang 		    spec->codec_type == VT1716S)
202616922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
202716922281SLydia Wang 						   idx+1, cap_nid);
202816922281SLydia Wang 		else
202916922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
203016922281SLydia Wang 						   idx, cap_nid);
2031c577b8a1SJoseph Chan 		if (err < 0)
2032c577b8a1SJoseph Chan 			return err;
203310a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2034c577b8a1SJoseph Chan 	}
2035c577b8a1SJoseph Chan 	return 0;
2036c577b8a1SJoseph Chan }
2037c577b8a1SJoseph Chan 
2038f3268512STakashi Iwai /* create playback/capture controls for input pins */
203910a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
2040f3268512STakashi Iwai 						const struct auto_pin_cfg *cfg)
2041f3268512STakashi Iwai {
2042f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
204310a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
2044f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
2045f3268512STakashi Iwai }
2046f3268512STakashi Iwai 
2047cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2048cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2049cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2050cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2051cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2052cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2053cb53c626STakashi Iwai 	{ } /* end */
2054cb53c626STakashi Iwai };
2055cb53c626STakashi Iwai #endif
2056cb53c626STakashi Iwai 
205776d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
205876d9b0ddSHarald Welte {
205976d9b0ddSHarald Welte 	unsigned int def_conf;
206076d9b0ddSHarald Welte 	unsigned char seqassoc;
206176d9b0ddSHarald Welte 
20622f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
206376d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
206476d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
206582ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
206682ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
206776d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
20682f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
206976d9b0ddSHarald Welte 	}
207076d9b0ddSHarald Welte 
207176d9b0ddSHarald Welte 	return;
207276d9b0ddSHarald Welte }
207376d9b0ddSHarald Welte 
20741f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
20751f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20761f2e99feSLydia Wang {
20771f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20781f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20791f2e99feSLydia Wang 
20801f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20811f2e99feSLydia Wang 		return 0;
20821f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
20831f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
20841f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
20851f2e99feSLydia Wang 	return 0;
20861f2e99feSLydia Wang }
20871f2e99feSLydia Wang 
20881f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
20891f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20901f2e99feSLydia Wang {
20911f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20921f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20931f2e99feSLydia Wang 	int change;
20941f2e99feSLydia Wang 
20951f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20961f2e99feSLydia Wang 		return 0;
20971f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
20981f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
20991f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
21001f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
21011f2e99feSLydia Wang 		mute_aa_path(codec, 1);
21021f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
21031f2e99feSLydia Wang 	}
21041f2e99feSLydia Wang 	return change;
21051f2e99feSLydia Wang }
21061f2e99feSLydia Wang 
21071f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
21081f2e99feSLydia Wang 	{
21091f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21101f2e99feSLydia Wang 		.name = "Jack Detect",
21111f2e99feSLydia Wang 		.count = 1,
21121f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
21131f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
21141f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
21151f2e99feSLydia Wang 	},
21161f2e99feSLydia Wang 	{} /* end */
21171f2e99feSLydia Wang };
21181f2e99feSLydia Wang 
2119c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2120c577b8a1SJoseph Chan {
2121c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2122c577b8a1SJoseph Chan 	int err;
2123c577b8a1SJoseph Chan 
212476d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
212576d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
212676d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
212776d9b0ddSHarald Welte 
2128c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2129c577b8a1SJoseph Chan 	if (err < 0)
2130c577b8a1SJoseph Chan 		return err;
2131c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2132c577b8a1SJoseph Chan 	if (err < 0)
2133c577b8a1SJoseph Chan 		return err;
2134c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2135c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2136c577b8a1SJoseph Chan 
2137c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2138c577b8a1SJoseph Chan 	if (err < 0)
2139c577b8a1SJoseph Chan 		return err;
2140c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2141c577b8a1SJoseph Chan 	if (err < 0)
2142c577b8a1SJoseph Chan 		return err;
214310a20af7STakashi Iwai 	err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
2144c577b8a1SJoseph Chan 	if (err < 0)
2145c577b8a1SJoseph Chan 		return err;
21461f2e99feSLydia Wang 	/* add jack detect on/off control */
21471f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
21481f2e99feSLydia Wang 	if (err < 0)
21491f2e99feSLydia Wang 		return err;
2150c577b8a1SJoseph Chan 
2151c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2152c577b8a1SJoseph Chan 
21530852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2154c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
215555d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2156c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2157c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2158c577b8a1SJoseph Chan 
2159603c4019STakashi Iwai 	if (spec->kctls.list)
2160603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2161c577b8a1SJoseph Chan 
216269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2163c577b8a1SJoseph Chan 
21640aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
21650aa62aefSHarald Welte 
2166f8fdd495SHarald Welte 	if (spec->hp_mux)
21673d83e577STakashi Iwai 		via_hp_build(codec);
2168c577b8a1SJoseph Chan 
21695b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2170c577b8a1SJoseph Chan 	return 1;
2171c577b8a1SJoseph Chan }
2172c577b8a1SJoseph Chan 
2173c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2174c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2175c577b8a1SJoseph Chan {
217625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
217725eaba2fSLydia Wang 
2178c577b8a1SJoseph Chan 	via_init(codec);
2179c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2180c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2181c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
218211890956SLydia Wang 
218311890956SLydia Wang 	if (VT2002P_COMPATIBLE(spec)) {
218425eaba2fSLydia Wang 		via_hp_bind_automute(codec);
218525eaba2fSLydia Wang 	} else {
218625eaba2fSLydia Wang 		via_hp_automute(codec);
218725eaba2fSLydia Wang 		via_speaker_automute(codec);
218825eaba2fSLydia Wang 	}
218925eaba2fSLydia Wang 
2190c577b8a1SJoseph Chan 	return 0;
2191c577b8a1SJoseph Chan }
2192c577b8a1SJoseph Chan 
21931f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
21941f2e99feSLydia Wang {
21951f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
21961f2e99feSLydia Wang 					     vt1708_hp_work.work);
21971f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21981f2e99feSLydia Wang 		return;
21991f2e99feSLydia Wang 	/* if jack state toggled */
22001f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2201d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
22021f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
22031f2e99feSLydia Wang 		via_hp_automute(spec->codec);
22041f2e99feSLydia Wang 	}
22051f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
22061f2e99feSLydia Wang }
22071f2e99feSLydia Wang 
2208337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2209337b9d02STakashi Iwai {
2210337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2211337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2212337b9d02STakashi Iwai 	unsigned int type;
2213337b9d02STakashi Iwai 	int i, n;
2214337b9d02STakashi Iwai 
2215337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2216337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2217337b9d02STakashi Iwai 		while (nid) {
2218a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
22191c55d521STakashi Iwai 			if (type == AC_WID_PIN)
22201c55d521STakashi Iwai 				break;
2221337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2222337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2223337b9d02STakashi Iwai 			if (n <= 0)
2224337b9d02STakashi Iwai 				break;
2225337b9d02STakashi Iwai 			if (n > 1) {
2226337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2227337b9d02STakashi Iwai 				break;
2228337b9d02STakashi Iwai 			}
2229337b9d02STakashi Iwai 			nid = conn[0];
2230337b9d02STakashi Iwai 		}
2231337b9d02STakashi Iwai 	}
22321c55d521STakashi Iwai 	return 0;
2233337b9d02STakashi Iwai }
2234337b9d02STakashi Iwai 
2235c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2236c577b8a1SJoseph Chan {
2237c577b8a1SJoseph Chan 	struct via_spec *spec;
2238c577b8a1SJoseph Chan 	int err;
2239c577b8a1SJoseph Chan 
2240c577b8a1SJoseph Chan 	/* create a codec specific record */
22415b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2242c577b8a1SJoseph Chan 	if (spec == NULL)
2243c577b8a1SJoseph Chan 		return -ENOMEM;
2244c577b8a1SJoseph Chan 
2245c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2246c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2247c577b8a1SJoseph Chan 	if (err < 0) {
2248c577b8a1SJoseph Chan 		via_free(codec);
2249c577b8a1SJoseph Chan 		return err;
2250c577b8a1SJoseph Chan 	} else if (!err) {
2251c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2252c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2253c577b8a1SJoseph Chan 	}
2254c577b8a1SJoseph Chan 
2255c577b8a1SJoseph Chan 
2256c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2257c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2258bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2259bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2260bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2261c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2262c577b8a1SJoseph Chan 
2263c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2264c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2265c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2266c577b8a1SJoseph Chan 
2267c577b8a1SJoseph Chan 
2268c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2269c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2270c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
22710f67a611STakashi Iwai 		get_mux_nids(codec);
2272c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2273c577b8a1SJoseph Chan 		spec->num_mixers++;
2274c577b8a1SJoseph Chan 	}
2275c577b8a1SJoseph Chan 
2276c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2277c577b8a1SJoseph Chan 
2278c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2279cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2280cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2281cb53c626STakashi Iwai #endif
22821f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2283c577b8a1SJoseph Chan 	return 0;
2284c577b8a1SJoseph Chan }
2285c577b8a1SJoseph Chan 
2286c577b8a1SJoseph Chan /* capture mixer elements */
2287c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2288c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2289c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2290c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2291c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2292c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2293c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2294c577b8a1SJoseph Chan 	{
2295c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2296c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2297c577b8a1SJoseph Chan 		 * So call somewhat different..
2298c577b8a1SJoseph Chan 		 */
2299c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2300c577b8a1SJoseph Chan 		.name = "Input Source",
2301c577b8a1SJoseph Chan 		.count = 1,
2302c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2303c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2304c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2305c577b8a1SJoseph Chan 	},
2306c577b8a1SJoseph Chan 	{ } /* end */
2307c577b8a1SJoseph Chan };
2308c577b8a1SJoseph Chan 
230969e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2310a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2311a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
231269e52a80SHarald Welte 	{ }
231369e52a80SHarald Welte };
231469e52a80SHarald Welte 
2315c577b8a1SJoseph Chan /*
2316c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2317c577b8a1SJoseph Chan  */
2318c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2319c577b8a1SJoseph Chan 	/*
2320c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2321c577b8a1SJoseph Chan 	 */
2322c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2323c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2324c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2325c577b8a1SJoseph Chan 
2326c577b8a1SJoseph Chan 
2327f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2328c577b8a1SJoseph Chan 	 * mixer widget
2329c577b8a1SJoseph Chan 	 */
2330c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2331f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2332f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2333f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2334f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2335f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2336c577b8a1SJoseph Chan 
2337c577b8a1SJoseph Chan 	/*
2338c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2339c577b8a1SJoseph Chan 	 */
2340c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2341c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2342c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2343c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2344c577b8a1SJoseph Chan 
2345c577b8a1SJoseph Chan 	/*
2346c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2347c577b8a1SJoseph Chan 	 */
2348c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2349c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2350c577b8a1SJoseph Chan 
2351bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2352bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2353c577b8a1SJoseph Chan 	/* PW9 Output enable */
2354c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2355c577b8a1SJoseph Chan 	{ }
2356c577b8a1SJoseph Chan };
2357c577b8a1SJoseph Chan 
2358c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2359c577b8a1SJoseph Chan 	.substreams = 1,
2360c577b8a1SJoseph Chan 	.channels_min = 2,
2361c577b8a1SJoseph Chan 	.channels_max = 10,
2362c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2363c577b8a1SJoseph Chan 	.ops = {
2364c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2365c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2366c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2367c577b8a1SJoseph Chan 	},
2368c577b8a1SJoseph Chan };
2369c577b8a1SJoseph Chan 
2370c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2371c577b8a1SJoseph Chan 	.substreams = 1,
2372c577b8a1SJoseph Chan 	.channels_min = 2,
2373c577b8a1SJoseph Chan 	.channels_max = 6,
2374c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2375c577b8a1SJoseph Chan 	.ops = {
2376c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2377c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2378c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2379c577b8a1SJoseph Chan 	},
2380c577b8a1SJoseph Chan };
2381c577b8a1SJoseph Chan 
2382c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2383c577b8a1SJoseph Chan 	.substreams = 2,
2384c577b8a1SJoseph Chan 	.channels_min = 2,
2385c577b8a1SJoseph Chan 	.channels_max = 2,
2386c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2387c577b8a1SJoseph Chan 	.ops = {
2388c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2389c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2390c577b8a1SJoseph Chan 	},
2391c577b8a1SJoseph Chan };
2392c577b8a1SJoseph Chan 
2393c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2394c577b8a1SJoseph Chan 	.substreams = 1,
2395c577b8a1SJoseph Chan 	.channels_min = 2,
2396c577b8a1SJoseph Chan 	.channels_max = 2,
2397c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2398c577b8a1SJoseph Chan 	.ops = {
2399c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2400c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2401c577b8a1SJoseph Chan 	},
2402c577b8a1SJoseph Chan };
2403c577b8a1SJoseph Chan 
2404c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2405c577b8a1SJoseph Chan 	.substreams = 1,
2406c577b8a1SJoseph Chan 	.channels_min = 2,
2407c577b8a1SJoseph Chan 	.channels_max = 2,
2408c577b8a1SJoseph Chan };
2409c577b8a1SJoseph Chan 
2410c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2411c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2412c577b8a1SJoseph Chan {
2413c577b8a1SJoseph Chan 	int i;
2414c577b8a1SJoseph Chan 	hda_nid_t nid;
2415c577b8a1SJoseph Chan 
2416c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2417c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2418c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2419c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2420c577b8a1SJoseph Chan 
2421c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2422c577b8a1SJoseph Chan 
2423c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2424c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2425c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2426c577b8a1SJoseph Chan 			if (nid) {
2427c577b8a1SJoseph Chan 				/* config dac list */
2428c577b8a1SJoseph Chan 				switch (i) {
2429c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2430c577b8a1SJoseph Chan 					/* AOW0 */
2431c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2432c577b8a1SJoseph Chan 					break;
2433c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2434c577b8a1SJoseph Chan 					/* AOW2 */
2435c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2436c577b8a1SJoseph Chan 					break;
2437c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2438c577b8a1SJoseph Chan 					/* AOW3 */
2439fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2440c577b8a1SJoseph Chan 					break;
2441c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2442c577b8a1SJoseph Chan 					/* AOW1 */
2443fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2444c577b8a1SJoseph Chan 					break;
2445c577b8a1SJoseph Chan 				default:
2446c577b8a1SJoseph Chan 					break;
2447c577b8a1SJoseph Chan 				}
2448c577b8a1SJoseph Chan 			}
2449c577b8a1SJoseph Chan 		}
2450c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2451c577b8a1SJoseph Chan 
2452c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2453c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2454c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2455c577b8a1SJoseph Chan 			if (nid) {
2456c577b8a1SJoseph Chan 				/* config dac list */
2457c577b8a1SJoseph Chan 				switch (i) {
2458c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2459c577b8a1SJoseph Chan 					/* AOW0 */
2460c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2461c577b8a1SJoseph Chan 					break;
2462c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2463c577b8a1SJoseph Chan 					/* AOW2 */
2464c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2465c577b8a1SJoseph Chan 					break;
2466c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2467c577b8a1SJoseph Chan 					/* AOW1 */
2468c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2469c577b8a1SJoseph Chan 					break;
2470c577b8a1SJoseph Chan 				default:
2471c577b8a1SJoseph Chan 					break;
2472c577b8a1SJoseph Chan 				}
2473c577b8a1SJoseph Chan 			}
2474c577b8a1SJoseph Chan 		}
2475c577b8a1SJoseph Chan 	}
2476c577b8a1SJoseph Chan 
2477c577b8a1SJoseph Chan 	return 0;
2478c577b8a1SJoseph Chan }
2479c577b8a1SJoseph Chan 
2480c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2481c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2482c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2483c577b8a1SJoseph Chan {
2484c577b8a1SJoseph Chan 	char name[32];
2485ea734963STakashi Iwai 	static const char * const chname[4] = {
2486ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
2487ea734963STakashi Iwai 	};
24884483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2489c577b8a1SJoseph Chan 	int i, err;
2490c577b8a1SJoseph Chan 
2491c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2492c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2493c577b8a1SJoseph Chan 
2494c577b8a1SJoseph Chan 		if (!nid)
2495c577b8a1SJoseph Chan 			continue;
2496c577b8a1SJoseph Chan 
24974483a2f5SLydia Wang 		nid_vol = nid_vols[i];
24984483a2f5SLydia Wang 
2499c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2500c577b8a1SJoseph Chan 			/* Center/LFE */
2501c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2502c577b8a1SJoseph Chan 					      "Center Playback Volume",
25034483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2504f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2505c577b8a1SJoseph Chan 			if (err < 0)
2506c577b8a1SJoseph Chan 				return err;
2507c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2508c577b8a1SJoseph Chan 					      "LFE Playback Volume",
25094483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2510f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2511c577b8a1SJoseph Chan 			if (err < 0)
2512c577b8a1SJoseph Chan 				return err;
2513c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2514c577b8a1SJoseph Chan 					      "Center Playback Switch",
25154483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2516f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2517c577b8a1SJoseph Chan 			if (err < 0)
2518c577b8a1SJoseph Chan 				return err;
2519c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2520c577b8a1SJoseph Chan 					      "LFE Playback Switch",
25214483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2522f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2523c577b8a1SJoseph Chan 			if (err < 0)
2524c577b8a1SJoseph Chan 				return err;
2525c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
25264483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2527c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2528c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
25294483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2530f7278fd0SJosepch Chan 								  HDA_INPUT));
2531c577b8a1SJoseph Chan 			if (err < 0)
2532c577b8a1SJoseph Chan 				return err;
2533c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2534c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
25354483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2536f7278fd0SJosepch Chan 								  HDA_INPUT));
2537c577b8a1SJoseph Chan 			if (err < 0)
2538c577b8a1SJoseph Chan 				return err;
2539c577b8a1SJoseph Chan 
2540c577b8a1SJoseph Chan 			/* add control to PW3 */
2541c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2542c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2543f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2544f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2545c577b8a1SJoseph Chan 			if (err < 0)
2546c577b8a1SJoseph Chan 				return err;
2547c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2548c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2549f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2550f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2551c577b8a1SJoseph Chan 			if (err < 0)
2552c577b8a1SJoseph Chan 				return err;
2553c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2554c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2555c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
25564483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2557f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2558c577b8a1SJoseph Chan 			if (err < 0)
2559c577b8a1SJoseph Chan 				return err;
2560c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2561c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
25624483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2563f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2564c577b8a1SJoseph Chan 			if (err < 0)
2565c577b8a1SJoseph Chan 				return err;
2566c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2567c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2568c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
25694483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2570f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2571c577b8a1SJoseph Chan 			if (err < 0)
2572c577b8a1SJoseph Chan 				return err;
2573c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2574c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
25754483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2576f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2577c577b8a1SJoseph Chan 			if (err < 0)
2578c577b8a1SJoseph Chan 				return err;
2579c577b8a1SJoseph Chan 		}
2580c577b8a1SJoseph Chan 	}
2581c577b8a1SJoseph Chan 
2582c577b8a1SJoseph Chan 	return 0;
2583c577b8a1SJoseph Chan }
2584c577b8a1SJoseph Chan 
2585c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2586c577b8a1SJoseph Chan {
2587c577b8a1SJoseph Chan 	int err;
2588c577b8a1SJoseph Chan 
2589c577b8a1SJoseph Chan 	if (!pin)
2590c577b8a1SJoseph Chan 		return 0;
2591c577b8a1SJoseph Chan 
2592c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2593c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2594c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2595c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2596cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2597c577b8a1SJoseph Chan 
2598c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2599c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2600c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2601c577b8a1SJoseph Chan 	if (err < 0)
2602c577b8a1SJoseph Chan 		return err;
2603c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2604c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2605c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2606c577b8a1SJoseph Chan 	if (err < 0)
2607c577b8a1SJoseph Chan 		return err;
2608c577b8a1SJoseph Chan 
2609c577b8a1SJoseph Chan 	return 0;
2610c577b8a1SJoseph Chan }
2611c577b8a1SJoseph Chan 
2612c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
261310a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
2614c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2615c577b8a1SJoseph Chan {
2616f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
261710a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
2618f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
2619c577b8a1SJoseph Chan }
2620c577b8a1SJoseph Chan 
2621c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2622c577b8a1SJoseph Chan {
2623c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2624c577b8a1SJoseph Chan 	int err;
2625c577b8a1SJoseph Chan 
2626c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2627c577b8a1SJoseph Chan 	if (err < 0)
2628c577b8a1SJoseph Chan 		return err;
2629c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2630c577b8a1SJoseph Chan 	if (err < 0)
2631c577b8a1SJoseph Chan 		return err;
2632c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2633c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2634c577b8a1SJoseph Chan 
2635c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2636c577b8a1SJoseph Chan 	if (err < 0)
2637c577b8a1SJoseph Chan 		return err;
2638c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2639c577b8a1SJoseph Chan 	if (err < 0)
2640c577b8a1SJoseph Chan 		return err;
264110a20af7STakashi Iwai 	err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
2642c577b8a1SJoseph Chan 	if (err < 0)
2643c577b8a1SJoseph Chan 		return err;
2644c577b8a1SJoseph Chan 
2645c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2646c577b8a1SJoseph Chan 
26470852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2648c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
264955d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2650c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2651c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2652c577b8a1SJoseph Chan 
2653603c4019STakashi Iwai 	if (spec->kctls.list)
2654603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2655c577b8a1SJoseph Chan 
26560aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2657c577b8a1SJoseph Chan 
2658f8fdd495SHarald Welte 	if (spec->hp_mux)
26593d83e577STakashi Iwai 		via_hp_build(codec);
2660f8fdd495SHarald Welte 
26615b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2662c577b8a1SJoseph Chan 	return 1;
2663c577b8a1SJoseph Chan }
2664c577b8a1SJoseph Chan 
2665cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2666cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
2667cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2668cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2669cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2670cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2671cb53c626STakashi Iwai 	{ } /* end */
2672cb53c626STakashi Iwai };
2673cb53c626STakashi Iwai #endif
2674cb53c626STakashi Iwai 
2675c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2676c577b8a1SJoseph Chan {
2677c577b8a1SJoseph Chan 	struct via_spec *spec;
2678c577b8a1SJoseph Chan 	int err;
2679c577b8a1SJoseph Chan 
2680c577b8a1SJoseph Chan 	/* create a codec specific record */
26815b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2682c577b8a1SJoseph Chan 	if (spec == NULL)
2683c577b8a1SJoseph Chan 		return -ENOMEM;
2684c577b8a1SJoseph Chan 
2685c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2686c577b8a1SJoseph Chan 	if (err < 0) {
2687c577b8a1SJoseph Chan 		via_free(codec);
2688c577b8a1SJoseph Chan 		return err;
2689c577b8a1SJoseph Chan 	} else if (!err) {
2690c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2691c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2692c577b8a1SJoseph Chan 	}
2693c577b8a1SJoseph Chan 
269469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
269569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2696c577b8a1SJoseph Chan 
2697c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
2698c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2699c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2700c577b8a1SJoseph Chan 
2701c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
2702c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2703c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2704c577b8a1SJoseph Chan 
2705c577b8a1SJoseph Chan 
2706c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2707c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
2708c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
2709337b9d02STakashi Iwai 		get_mux_nids(codec);
2710c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2711c577b8a1SJoseph Chan 		spec->num_mixers++;
2712c577b8a1SJoseph Chan 	}
2713c577b8a1SJoseph Chan 
2714c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2715c577b8a1SJoseph Chan 
2716c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
271769e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2718cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2719cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2720cb53c626STakashi Iwai #endif
2721c577b8a1SJoseph Chan 
2722c577b8a1SJoseph Chan 	return 0;
2723c577b8a1SJoseph Chan }
2724c577b8a1SJoseph Chan /*
2725c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2726c577b8a1SJoseph Chan  */
2727c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2728c577b8a1SJoseph Chan 	/*
2729c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2730c577b8a1SJoseph Chan 	 */
2731c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2732c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2733c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2734c577b8a1SJoseph Chan 
2735c577b8a1SJoseph Chan 
2736c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2737c577b8a1SJoseph Chan 	 * mixer widget
2738c577b8a1SJoseph Chan 	 */
2739c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2740c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2741c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2742c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2743c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2744c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2745c577b8a1SJoseph Chan 
2746c577b8a1SJoseph Chan 	/*
2747c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2748c577b8a1SJoseph Chan 	 */
2749c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2750c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2751c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2752c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2753c577b8a1SJoseph Chan 
2754c577b8a1SJoseph Chan 	/*
2755c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2756c577b8a1SJoseph Chan 	 */
2757c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2758c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2759c577b8a1SJoseph Chan 
2760c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2761c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2762c577b8a1SJoseph Chan 	/* PW9 Output enable */
2763c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2764c577b8a1SJoseph Chan 	{ }
2765c577b8a1SJoseph Chan };
2766c577b8a1SJoseph Chan 
2767c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2768c577b8a1SJoseph Chan {
2769c577b8a1SJoseph Chan 	struct via_spec *spec;
2770c577b8a1SJoseph Chan 	int err;
2771c577b8a1SJoseph Chan 
2772c577b8a1SJoseph Chan 	/* create a codec specific record */
27735b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2774c577b8a1SJoseph Chan 	if (spec == NULL)
2775c577b8a1SJoseph Chan 		return -ENOMEM;
2776c577b8a1SJoseph Chan 
2777c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2778c577b8a1SJoseph Chan 	if (err < 0) {
2779c577b8a1SJoseph Chan 		via_free(codec);
2780c577b8a1SJoseph Chan 		return err;
2781c577b8a1SJoseph Chan 	} else if (!err) {
2782c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2783c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2784c577b8a1SJoseph Chan 	}
2785c577b8a1SJoseph Chan 
278669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
278769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2788c577b8a1SJoseph Chan 
2789c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
2790c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2791c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2792c577b8a1SJoseph Chan 
2793c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
2794c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2795c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2796c577b8a1SJoseph Chan 
2797c577b8a1SJoseph Chan 
2798c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2799c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
2800c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
2801337b9d02STakashi Iwai 		get_mux_nids(codec);
2802c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2803c577b8a1SJoseph Chan 		spec->num_mixers++;
2804c577b8a1SJoseph Chan 	}
2805c577b8a1SJoseph Chan 
2806c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2807c577b8a1SJoseph Chan 
2808c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
280969e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2810cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2811cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2812cb53c626STakashi Iwai #endif
2813f7278fd0SJosepch Chan 	return 0;
2814f7278fd0SJosepch Chan }
2815f7278fd0SJosepch Chan 
2816f7278fd0SJosepch Chan /* capture mixer elements */
2817f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2818f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2819f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2820f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2821f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2822f7278fd0SJosepch Chan 	{
2823f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2824f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2825f7278fd0SJosepch Chan 		 * So call somewhat different..
2826f7278fd0SJosepch Chan 		 */
2827f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
2828f7278fd0SJosepch Chan 		.name = "Input Source",
2829f7278fd0SJosepch Chan 		.count = 1,
2830f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
2831f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
2832f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
2833f7278fd0SJosepch Chan 	},
2834f7278fd0SJosepch Chan 	{ } /* end */
2835f7278fd0SJosepch Chan };
2836f7278fd0SJosepch Chan /*
2837f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2838f7278fd0SJosepch Chan  */
2839f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2840f7278fd0SJosepch Chan 	/*
2841f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2842f7278fd0SJosepch Chan 	 */
2843f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2844f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2845f7278fd0SJosepch Chan 
2846f7278fd0SJosepch Chan 
2847f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2848f7278fd0SJosepch Chan 	 * mixer widget
2849f7278fd0SJosepch Chan 	 */
2850f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2851f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2852f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2853f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2854f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2855f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2856f7278fd0SJosepch Chan 
2857f7278fd0SJosepch Chan 	/*
2858f7278fd0SJosepch Chan 	 * Set up output mixers
2859f7278fd0SJosepch Chan 	 */
2860f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2861f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2862f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2863f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2864f7278fd0SJosepch Chan 
2865f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2866bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2867f7278fd0SJosepch Chan 	/* PW9 Output enable */
2868f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2869f7278fd0SJosepch Chan 	/* PW10 Input enable */
2870f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2871f7278fd0SJosepch Chan 	{ }
2872f7278fd0SJosepch Chan };
2873f7278fd0SJosepch Chan 
2874f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2875f7278fd0SJosepch Chan 	/*
2876f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2877f7278fd0SJosepch Chan 	 */
2878f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2879f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2880f7278fd0SJosepch Chan 
2881f7278fd0SJosepch Chan 
2882f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2883f7278fd0SJosepch Chan 	 * mixer widget
2884f7278fd0SJosepch Chan 	 */
2885f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2886f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2887f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2888f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2889f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2890f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2891f7278fd0SJosepch Chan 
2892f7278fd0SJosepch Chan 	/*
2893f7278fd0SJosepch Chan 	 * Set up output mixers
2894f7278fd0SJosepch Chan 	 */
2895f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2896f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2897f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2898f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2899f7278fd0SJosepch Chan 
2900f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2901f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2902f7278fd0SJosepch Chan 	/* PW9 Output enable */
2903f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2904f7278fd0SJosepch Chan 	/* PW10 Input enable */
2905f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2906f7278fd0SJosepch Chan 	{ }
2907f7278fd0SJosepch Chan };
2908f7278fd0SJosepch Chan 
290969e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
2910a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2911a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2912a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2913a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2914a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2915a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2916a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2917a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2918a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
291969e52a80SHarald Welte 	{ }
292069e52a80SHarald Welte };
292169e52a80SHarald Welte 
292217314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
292317314379SLydia Wang 			      struct hda_codec *codec,
292417314379SLydia Wang 			      struct snd_pcm_substream *substream)
292517314379SLydia Wang {
292617314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
292717314379SLydia Wang 		&& substream->ref_count == 0;
292817314379SLydia Wang 
292917314379SLydia Wang 	analog_low_current_mode(codec, idle);
293017314379SLydia Wang 	return 0;
293117314379SLydia Wang }
293217314379SLydia Wang 
2933f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
29340aa62aefSHarald Welte 	.substreams = 2,
2935f7278fd0SJosepch Chan 	.channels_min = 2,
2936f7278fd0SJosepch Chan 	.channels_max = 8,
2937f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2938f7278fd0SJosepch Chan 	.ops = {
2939f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29400aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
294117314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
294217314379SLydia Wang 		.close = via_pcm_open_close
2943f7278fd0SJosepch Chan 	},
2944f7278fd0SJosepch Chan };
2945f7278fd0SJosepch Chan 
2946f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
29470aa62aefSHarald Welte 	.substreams = 2,
2948f7278fd0SJosepch Chan 	.channels_min = 2,
2949f7278fd0SJosepch Chan 	.channels_max = 4,
2950f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2951f7278fd0SJosepch Chan 	.ops = {
2952f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29530aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
29540aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
2955f7278fd0SJosepch Chan 	},
2956f7278fd0SJosepch Chan };
2957f7278fd0SJosepch Chan 
2958f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2959f7278fd0SJosepch Chan 	.substreams = 2,
2960f7278fd0SJosepch Chan 	.channels_min = 2,
2961f7278fd0SJosepch Chan 	.channels_max = 2,
2962f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
2963f7278fd0SJosepch Chan 	.ops = {
296417314379SLydia Wang 		.open = via_pcm_open_close,
2965f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
296617314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
296717314379SLydia Wang 		.close = via_pcm_open_close
2968f7278fd0SJosepch Chan 	},
2969f7278fd0SJosepch Chan };
2970f7278fd0SJosepch Chan 
2971f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2972f7278fd0SJosepch Chan 	.substreams = 1,
2973f7278fd0SJosepch Chan 	.channels_min = 2,
2974f7278fd0SJosepch Chan 	.channels_max = 2,
2975f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
2976f7278fd0SJosepch Chan 	.ops = {
2977f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
2978f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
29799da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
29809da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
2981f7278fd0SJosepch Chan 	},
2982f7278fd0SJosepch Chan };
2983f7278fd0SJosepch Chan 
2984f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2985f7278fd0SJosepch Chan 	.substreams = 1,
2986f7278fd0SJosepch Chan 	.channels_min = 2,
2987f7278fd0SJosepch Chan 	.channels_max = 2,
2988f7278fd0SJosepch Chan };
2989f7278fd0SJosepch Chan 
2990f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
2991f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2992f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
2993f7278fd0SJosepch Chan {
2994f7278fd0SJosepch Chan 	int i;
2995f7278fd0SJosepch Chan 	hda_nid_t nid;
2996f7278fd0SJosepch Chan 
2997f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
2998f7278fd0SJosepch Chan 
2999f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3000f7278fd0SJosepch Chan 
3001f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3002f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3003f7278fd0SJosepch Chan 		if (nid) {
3004f7278fd0SJosepch Chan 			/* config dac list */
3005f7278fd0SJosepch Chan 			switch (i) {
3006f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3007f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
3008f7278fd0SJosepch Chan 				break;
3009f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3010f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
3011f7278fd0SJosepch Chan 				break;
3012f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3013fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3014f7278fd0SJosepch Chan 				break;
3015f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3016fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3017f7278fd0SJosepch Chan 				break;
3018f7278fd0SJosepch Chan 			}
3019f7278fd0SJosepch Chan 		}
3020f7278fd0SJosepch Chan 	}
3021f7278fd0SJosepch Chan 
3022f7278fd0SJosepch Chan 	return 0;
3023f7278fd0SJosepch Chan }
3024f7278fd0SJosepch Chan 
3025f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3026f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3027f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3028f7278fd0SJosepch Chan {
3029f7278fd0SJosepch Chan 	char name[32];
3030ea734963STakashi Iwai 	static const char * const chname[4] = {
3031ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3032ea734963STakashi Iwai 	};
3033fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3034f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3035f7278fd0SJosepch Chan 	int i, err;
3036f7278fd0SJosepch Chan 
3037f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3038f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3039f7278fd0SJosepch Chan 
3040f7278fd0SJosepch Chan 		if (!nid)
3041f7278fd0SJosepch Chan 			continue;
3042f7278fd0SJosepch Chan 
3043f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3044f7278fd0SJosepch Chan 
3045f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3046f7278fd0SJosepch Chan 			/* Center/LFE */
3047f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3048f7278fd0SJosepch Chan 					      "Center Playback Volume",
3049f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3050f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3051f7278fd0SJosepch Chan 			if (err < 0)
3052f7278fd0SJosepch Chan 				return err;
3053f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3054f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3055f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3056f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3057f7278fd0SJosepch Chan 			if (err < 0)
3058f7278fd0SJosepch Chan 				return err;
3059f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3060f7278fd0SJosepch Chan 					      "Center Playback Switch",
3061f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3062f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3063f7278fd0SJosepch Chan 			if (err < 0)
3064f7278fd0SJosepch Chan 				return err;
3065f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3066f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3067f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3068f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3069f7278fd0SJosepch Chan 			if (err < 0)
3070f7278fd0SJosepch Chan 				return err;
3071f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3072f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3073f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3074f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3075f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3076f7278fd0SJosepch Chan 								  HDA_INPUT));
3077f7278fd0SJosepch Chan 			if (err < 0)
3078f7278fd0SJosepch Chan 				return err;
3079f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3080f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3081f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3082f7278fd0SJosepch Chan 								  HDA_INPUT));
3083f7278fd0SJosepch Chan 			if (err < 0)
3084f7278fd0SJosepch Chan 				return err;
3085f7278fd0SJosepch Chan 
3086f7278fd0SJosepch Chan 			/* add control to PW3 */
3087f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3088f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3089f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3090f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3091f7278fd0SJosepch Chan 			if (err < 0)
3092f7278fd0SJosepch Chan 				return err;
3093f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3094f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3095f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3096f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3097f7278fd0SJosepch Chan 			if (err < 0)
3098f7278fd0SJosepch Chan 				return err;
3099f7278fd0SJosepch Chan 		} else {
3100f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3101f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3102f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3103f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3104f7278fd0SJosepch Chan 			if (err < 0)
3105f7278fd0SJosepch Chan 				return err;
3106f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3107f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3108f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3109f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3110f7278fd0SJosepch Chan 			if (err < 0)
3111f7278fd0SJosepch Chan 				return err;
3112f7278fd0SJosepch Chan 		}
3113f7278fd0SJosepch Chan 	}
3114f7278fd0SJosepch Chan 
3115f7278fd0SJosepch Chan 	return 0;
3116f7278fd0SJosepch Chan }
3117f7278fd0SJosepch Chan 
3118f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3119f7278fd0SJosepch Chan {
3120f7278fd0SJosepch Chan 	int err;
3121f7278fd0SJosepch Chan 
3122f7278fd0SJosepch Chan 	if (!pin)
3123f7278fd0SJosepch Chan 		return 0;
3124f7278fd0SJosepch Chan 
3125f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3126cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3127f7278fd0SJosepch Chan 
3128f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3129f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3130f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3131f7278fd0SJosepch Chan 	if (err < 0)
3132f7278fd0SJosepch Chan 		return err;
3133f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3134f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3135f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3136f7278fd0SJosepch Chan 	if (err < 0)
3137f7278fd0SJosepch Chan 		return err;
3138f7278fd0SJosepch Chan 
31390aa62aefSHarald Welte 	create_hp_imux(spec);
31400aa62aefSHarald Welte 
3141f7278fd0SJosepch Chan 	return 0;
3142f7278fd0SJosepch Chan }
3143f7278fd0SJosepch Chan 
3144f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
314510a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
3146f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3147f7278fd0SJosepch Chan {
3148f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
314910a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
3150f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3151f7278fd0SJosepch Chan }
3152f7278fd0SJosepch Chan 
3153f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3154f7278fd0SJosepch Chan {
3155f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3156f7278fd0SJosepch Chan 	int err;
3157f7278fd0SJosepch Chan 
3158f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3159f7278fd0SJosepch Chan 	if (err < 0)
3160f7278fd0SJosepch Chan 		return err;
3161f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3162f7278fd0SJosepch Chan 	if (err < 0)
3163f7278fd0SJosepch Chan 		return err;
3164f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3165f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3166f7278fd0SJosepch Chan 
3167f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3168f7278fd0SJosepch Chan 	if (err < 0)
3169f7278fd0SJosepch Chan 		return err;
3170f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3171f7278fd0SJosepch Chan 	if (err < 0)
3172f7278fd0SJosepch Chan 		return err;
317310a20af7STakashi Iwai 	err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
3174f7278fd0SJosepch Chan 	if (err < 0)
3175f7278fd0SJosepch Chan 		return err;
3176f7278fd0SJosepch Chan 
3177f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3178f7278fd0SJosepch Chan 
31790852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3180f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
318155d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3182f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3183f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3184f7278fd0SJosepch Chan 
3185603c4019STakashi Iwai 	if (spec->kctls.list)
3186603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3187f7278fd0SJosepch Chan 
31880aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
31890aa62aefSHarald Welte 
3190f8fdd495SHarald Welte 	if (spec->hp_mux)
31913d83e577STakashi Iwai 		via_hp_build(codec);
3192f7278fd0SJosepch Chan 
31935b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3194f7278fd0SJosepch Chan 	return 1;
3195f7278fd0SJosepch Chan }
3196f7278fd0SJosepch Chan 
3197f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3198f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3199f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3200f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3201f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3202f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3203f7278fd0SJosepch Chan 	{ } /* end */
3204f7278fd0SJosepch Chan };
3205f7278fd0SJosepch Chan #endif
32063e95b9abSLydia Wang 
32073e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
32083e95b9abSLydia Wang {
32093e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
32103e95b9abSLydia Wang 	int imux_is_smixer;
32113e95b9abSLydia Wang 	unsigned int parm;
32123e95b9abSLydia Wang 	int is_8ch = 0;
3213bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
3214bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
32153e95b9abSLydia Wang 		is_8ch = 1;
32163e95b9abSLydia Wang 
32173e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
32183e95b9abSLydia Wang 	imux_is_smixer =
32193e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
32203e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
32213e95b9abSLydia Wang 	/* inputs */
32223e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
32233e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
32253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
32263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
32273e95b9abSLydia Wang 	if (imux_is_smixer)
32283e95b9abSLydia Wang 		parm = AC_PWRST_D0;
32293e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
32303e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
32313e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
32323e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
32333e95b9abSLydia Wang 
32343e95b9abSLydia Wang 	/* outputs */
32353e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
32363e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
32383e95b9abSLydia Wang 	if (spec->smart51_enabled)
32393e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
32403e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
32413e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
32423e95b9abSLydia Wang 
32433e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
32443e95b9abSLydia Wang 	if (is_8ch) {
32453e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32463e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
32473e95b9abSLydia Wang 		if (spec->smart51_enabled)
32483e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
32493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
32503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32513e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
32523e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3253bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
3254bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
3255bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
3256bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
3257bc92df7fSLydia Wang 		if (spec->smart51_enabled)
3258bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
3259bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
3260bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3261bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3262bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32633e95b9abSLydia Wang 	}
32643e95b9abSLydia Wang 
32653e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
32663e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32673e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
32683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
32693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
32703e95b9abSLydia Wang 	if (is_8ch)
32713e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
32723e95b9abSLydia Wang 
32733e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
32743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
32753e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
32763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
32773e95b9abSLydia Wang 	if (is_8ch) {
32783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
32793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32803e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
32813e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3282bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
3283bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3284bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32853e95b9abSLydia Wang }
32863e95b9abSLydia Wang 
3287518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3288f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3289f7278fd0SJosepch Chan {
3290f7278fd0SJosepch Chan 	struct via_spec *spec;
3291f7278fd0SJosepch Chan 	int err;
3292f7278fd0SJosepch Chan 
3293518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3294518bf3baSLydia Wang 		return patch_vt1708S(codec);
3295f7278fd0SJosepch Chan 	/* create a codec specific record */
32965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3297f7278fd0SJosepch Chan 	if (spec == NULL)
3298f7278fd0SJosepch Chan 		return -ENOMEM;
3299f7278fd0SJosepch Chan 
3300f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3301f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3302f7278fd0SJosepch Chan 	if (err < 0) {
3303f7278fd0SJosepch Chan 		via_free(codec);
3304f7278fd0SJosepch Chan 		return err;
3305f7278fd0SJosepch Chan 	} else if (!err) {
3306f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3307f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3308f7278fd0SJosepch Chan 	}
3309f7278fd0SJosepch Chan 
331069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
331169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3312f7278fd0SJosepch Chan 
3313f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3314f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3315f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3316f7278fd0SJosepch Chan 
3317f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3318f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3319f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3320f7278fd0SJosepch Chan 
3321f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3322f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3323f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3324337b9d02STakashi Iwai 		get_mux_nids(codec);
3325f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3326f7278fd0SJosepch Chan 		spec->num_mixers++;
3327f7278fd0SJosepch Chan 	}
3328f7278fd0SJosepch Chan 
3329f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3330f7278fd0SJosepch Chan 
3331f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
333269e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3333f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3334f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3335f7278fd0SJosepch Chan #endif
3336f7278fd0SJosepch Chan 
33373e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
33383e95b9abSLydia Wang 
3339f7278fd0SJosepch Chan 	return 0;
3340f7278fd0SJosepch Chan }
3341f7278fd0SJosepch Chan 
3342f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3343f7278fd0SJosepch Chan {
3344f7278fd0SJosepch Chan 	struct via_spec *spec;
3345f7278fd0SJosepch Chan 	int err;
3346f7278fd0SJosepch Chan 
3347f7278fd0SJosepch Chan 	/* create a codec specific record */
33485b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3349f7278fd0SJosepch Chan 	if (spec == NULL)
3350f7278fd0SJosepch Chan 		return -ENOMEM;
3351f7278fd0SJosepch Chan 
3352f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3353f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3354f7278fd0SJosepch Chan 	if (err < 0) {
3355f7278fd0SJosepch Chan 		via_free(codec);
3356f7278fd0SJosepch Chan 		return err;
3357f7278fd0SJosepch Chan 	} else if (!err) {
3358f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3359f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3360f7278fd0SJosepch Chan 	}
3361f7278fd0SJosepch Chan 
336269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
336369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3364f7278fd0SJosepch Chan 
3365f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3366f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3367f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3368f7278fd0SJosepch Chan 
3369f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3370f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3371f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3372f7278fd0SJosepch Chan 
3373f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3374f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3375f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3376337b9d02STakashi Iwai 		get_mux_nids(codec);
3377f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3378f7278fd0SJosepch Chan 		spec->num_mixers++;
3379f7278fd0SJosepch Chan 	}
3380f7278fd0SJosepch Chan 
3381f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3382f7278fd0SJosepch Chan 
3383f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
338469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3385f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3386f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3387f7278fd0SJosepch Chan #endif
3388c577b8a1SJoseph Chan 
33893e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
33903e95b9abSLydia Wang 
3391c577b8a1SJoseph Chan 	return 0;
3392c577b8a1SJoseph Chan }
3393c577b8a1SJoseph Chan 
3394d949cac1SHarald Welte /* Patch for VT1708S */
3395d949cac1SHarald Welte 
3396d949cac1SHarald Welte /* capture mixer elements */
3397d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3398d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3399d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3400d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3401d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
34026369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
34036369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
34046369bcfcSLydia Wang 			 HDA_INPUT),
3405d949cac1SHarald Welte 	{
3406d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3407d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3408d949cac1SHarald Welte 		 * So call somewhat different..
3409d949cac1SHarald Welte 		 */
3410d949cac1SHarald Welte 		/* .name = "Capture Source", */
3411d949cac1SHarald Welte 		.name = "Input Source",
3412d949cac1SHarald Welte 		.count = 1,
3413d949cac1SHarald Welte 		.info = via_mux_enum_info,
3414d949cac1SHarald Welte 		.get = via_mux_enum_get,
3415d949cac1SHarald Welte 		.put = via_mux_enum_put,
3416d949cac1SHarald Welte 	},
3417d949cac1SHarald Welte 	{ } /* end */
3418d949cac1SHarald Welte };
3419d949cac1SHarald Welte 
3420d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3421d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3422d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3423d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3424d949cac1SHarald Welte 
3425d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3426d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3427d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3428d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3429d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3430d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3431d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3432d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3433d949cac1SHarald Welte 
3434d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3435d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
34365691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3437d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
34385691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3439d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3440d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3441bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3442bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3443d949cac1SHarald Welte 	{ }
3444d949cac1SHarald Welte };
3445d949cac1SHarald Welte 
344669e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3447a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3448a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3449a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3450a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3451a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3452a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3453a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3454a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3455a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
345669e52a80SHarald Welte 	{ }
345769e52a80SHarald Welte };
345869e52a80SHarald Welte 
3459bc92df7fSLydia Wang static struct hda_verb vt1705_uniwill_init_verbs[] = {
3460bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3461bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3462bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3463bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3464bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3465bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3466bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3467bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3468bc92df7fSLydia Wang 	{ }
3469bc92df7fSLydia Wang };
3470bc92df7fSLydia Wang 
3471d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3472d949cac1SHarald Welte 	.substreams = 2,
3473d949cac1SHarald Welte 	.channels_min = 2,
3474d949cac1SHarald Welte 	.channels_max = 8,
3475d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3476d949cac1SHarald Welte 	.ops = {
3477d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3478c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3479c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
348017314379SLydia Wang 		.close = via_pcm_open_close
3481d949cac1SHarald Welte 	},
3482d949cac1SHarald Welte };
3483d949cac1SHarald Welte 
3484bc92df7fSLydia Wang static struct hda_pcm_stream vt1705_pcm_analog_playback = {
3485bc92df7fSLydia Wang 	.substreams = 2,
3486bc92df7fSLydia Wang 	.channels_min = 2,
3487bc92df7fSLydia Wang 	.channels_max = 6,
3488bc92df7fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
3489bc92df7fSLydia Wang 	.ops = {
3490bc92df7fSLydia Wang 		.open = via_playback_pcm_open,
3491bc92df7fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3492bc92df7fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
3493bc92df7fSLydia Wang 		.close = via_pcm_open_close
3494bc92df7fSLydia Wang 	},
3495bc92df7fSLydia Wang };
3496bc92df7fSLydia Wang 
3497d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3498d949cac1SHarald Welte 	.substreams = 2,
3499d949cac1SHarald Welte 	.channels_min = 2,
3500d949cac1SHarald Welte 	.channels_max = 2,
3501d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3502d949cac1SHarald Welte 	.ops = {
350317314379SLydia Wang 		.open = via_pcm_open_close,
3504d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
350517314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
350617314379SLydia Wang 		.close = via_pcm_open_close
3507d949cac1SHarald Welte 	},
3508d949cac1SHarald Welte };
3509d949cac1SHarald Welte 
3510d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
35119da29271STakashi Iwai 	.substreams = 1,
3512d949cac1SHarald Welte 	.channels_min = 2,
3513d949cac1SHarald Welte 	.channels_max = 2,
3514d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3515d949cac1SHarald Welte 	.ops = {
3516d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3517d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
35189da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
35199da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3520d949cac1SHarald Welte 	},
3521d949cac1SHarald Welte };
3522d949cac1SHarald Welte 
3523d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3524d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3525d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3526d949cac1SHarald Welte {
3527d949cac1SHarald Welte 	int i;
3528d949cac1SHarald Welte 	hda_nid_t nid;
3529d949cac1SHarald Welte 
3530d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3531d949cac1SHarald Welte 
3532d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3533d949cac1SHarald Welte 
3534d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3535d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3536d949cac1SHarald Welte 		if (nid) {
3537d949cac1SHarald Welte 			/* config dac list */
3538d949cac1SHarald Welte 			switch (i) {
3539d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3540d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3541d949cac1SHarald Welte 				break;
3542d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3543bc92df7fSLydia Wang 				if (spec->codec->vendor_id == 0x11064397)
3544bc92df7fSLydia Wang 					spec->multiout.dac_nids[i] = 0x25;
3545bc92df7fSLydia Wang 				else
3546d949cac1SHarald Welte 					spec->multiout.dac_nids[i] = 0x24;
3547d949cac1SHarald Welte 				break;
3548d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3549d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3550d949cac1SHarald Welte 				break;
3551d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3552d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3553d949cac1SHarald Welte 				break;
3554d949cac1SHarald Welte 			}
3555d949cac1SHarald Welte 		}
3556d949cac1SHarald Welte 	}
3557d949cac1SHarald Welte 
355832e0191dSClemens Ladisch 	/* for Smart 5.1, line/mic inputs double as output pins */
355932e0191dSClemens Ladisch 	if (cfg->line_outs == 1) {
356032e0191dSClemens Ladisch 		spec->multiout.num_dacs = 3;
356132e0191dSClemens Ladisch 		spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
3562bc92df7fSLydia Wang 		if (spec->codec->vendor_id == 0x11064397)
3563bc92df7fSLydia Wang 			spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x25;
3564bc92df7fSLydia Wang 		else
356532e0191dSClemens Ladisch 			spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
356632e0191dSClemens Ladisch 	}
356732e0191dSClemens Ladisch 
3568d949cac1SHarald Welte 	return 0;
3569d949cac1SHarald Welte }
3570d949cac1SHarald Welte 
3571d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3572bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec,
3573d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3574d949cac1SHarald Welte {
3575bc92df7fSLydia Wang 	struct via_spec *spec = codec->spec;
3576d949cac1SHarald Welte 	char name[32];
3577ea734963STakashi Iwai 	static const char * const chname[4] = {
3578ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3579ea734963STakashi Iwai 	};
3580bc92df7fSLydia Wang 	hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25},
3581bc92df7fSLydia Wang 				     {0x10, 0x11, 0x25, 0} };
3582bc92df7fSLydia Wang 	hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27},
3583bc92df7fSLydia Wang 				      {0x1C, 0x18, 0x27, 0} };
3584d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3585d949cac1SHarald Welte 	int i, err;
3586d949cac1SHarald Welte 
3587d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3588d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3589d949cac1SHarald Welte 
359032e0191dSClemens Ladisch 		/* for Smart 5.1, there are always at least six channels */
359132e0191dSClemens Ladisch 		if (!nid && i > AUTO_SEQ_CENLFE)
3592d949cac1SHarald Welte 			continue;
3593d949cac1SHarald Welte 
3594bc92df7fSLydia Wang 		if (codec->vendor_id == 0x11064397) {
3595bc92df7fSLydia Wang 			nid_vol = nid_vols[1][i];
3596bc92df7fSLydia Wang 			nid_mute = nid_mutes[1][i];
3597bc92df7fSLydia Wang 		} else {
3598bc92df7fSLydia Wang 			nid_vol = nid_vols[0][i];
3599bc92df7fSLydia Wang 			nid_mute = nid_mutes[0][i];
3600bc92df7fSLydia Wang 		}
3601bc92df7fSLydia Wang 		if (!nid_vol && !nid_mute)
3602bc92df7fSLydia Wang 			continue;
3603d949cac1SHarald Welte 
3604d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3605d949cac1SHarald Welte 			/* Center/LFE */
3606d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3607d949cac1SHarald Welte 					      "Center Playback Volume",
3608d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3609d949cac1SHarald Welte 								  HDA_OUTPUT));
3610d949cac1SHarald Welte 			if (err < 0)
3611d949cac1SHarald Welte 				return err;
3612d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3613d949cac1SHarald Welte 					      "LFE Playback Volume",
3614d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3615d949cac1SHarald Welte 								  HDA_OUTPUT));
3616d949cac1SHarald Welte 			if (err < 0)
3617d949cac1SHarald Welte 				return err;
3618d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3619d949cac1SHarald Welte 					      "Center Playback Switch",
3620d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3621d949cac1SHarald Welte 								  1, 0,
3622d949cac1SHarald Welte 								  HDA_OUTPUT));
3623d949cac1SHarald Welte 			if (err < 0)
3624d949cac1SHarald Welte 				return err;
3625d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3626d949cac1SHarald Welte 					      "LFE Playback Switch",
3627d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3628d949cac1SHarald Welte 								  2, 0,
3629d949cac1SHarald Welte 								  HDA_OUTPUT));
3630d949cac1SHarald Welte 			if (err < 0)
3631d949cac1SHarald Welte 				return err;
3632d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3633d949cac1SHarald Welte 			/* add control to mixer index 0 */
3634d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3635d949cac1SHarald Welte 					      "Master Front Playback Volume",
3636d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3637d949cac1SHarald Welte 								  HDA_INPUT));
3638d949cac1SHarald Welte 			if (err < 0)
3639d949cac1SHarald Welte 				return err;
3640d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3641d949cac1SHarald Welte 					      "Master Front Playback Switch",
3642d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3643d949cac1SHarald Welte 								  HDA_INPUT));
3644d949cac1SHarald Welte 			if (err < 0)
3645d949cac1SHarald Welte 				return err;
3646d949cac1SHarald Welte 
3647d949cac1SHarald Welte 			/* Front */
3648d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3649d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3650d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3651d949cac1SHarald Welte 								  HDA_OUTPUT));
3652d949cac1SHarald Welte 			if (err < 0)
3653d949cac1SHarald Welte 				return err;
3654d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3655d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3656d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3657d949cac1SHarald Welte 								  3, 0,
3658d949cac1SHarald Welte 								  HDA_OUTPUT));
3659d949cac1SHarald Welte 			if (err < 0)
3660d949cac1SHarald Welte 				return err;
3661d949cac1SHarald Welte 		} else {
3662d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3663d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3664d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3665d949cac1SHarald Welte 								  HDA_OUTPUT));
3666d949cac1SHarald Welte 			if (err < 0)
3667d949cac1SHarald Welte 				return err;
3668d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3669d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3670d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3671d949cac1SHarald Welte 								  3, 0,
3672d949cac1SHarald Welte 								  HDA_OUTPUT));
3673d949cac1SHarald Welte 			if (err < 0)
3674d949cac1SHarald Welte 				return err;
3675d949cac1SHarald Welte 		}
3676d949cac1SHarald Welte 	}
3677d949cac1SHarald Welte 
3678d949cac1SHarald Welte 	return 0;
3679d949cac1SHarald Welte }
3680d949cac1SHarald Welte 
3681d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3682d949cac1SHarald Welte {
3683d949cac1SHarald Welte 	int err;
3684d949cac1SHarald Welte 
3685d949cac1SHarald Welte 	if (!pin)
3686d949cac1SHarald Welte 		return 0;
3687d949cac1SHarald Welte 
3688d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3689cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3690d949cac1SHarald Welte 
3691d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3692d949cac1SHarald Welte 			      "Headphone Playback Volume",
3693d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3694d949cac1SHarald Welte 	if (err < 0)
3695d949cac1SHarald Welte 		return err;
3696d949cac1SHarald Welte 
3697d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3698d949cac1SHarald Welte 			      "Headphone Playback Switch",
3699d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3700d949cac1SHarald Welte 	if (err < 0)
3701d949cac1SHarald Welte 		return err;
3702d949cac1SHarald Welte 
37030aa62aefSHarald Welte 	create_hp_imux(spec);
37040aa62aefSHarald Welte 
3705d949cac1SHarald Welte 	return 0;
3706d949cac1SHarald Welte }
3707d949cac1SHarald Welte 
3708d949cac1SHarald Welte /* create playback/capture controls for input pins */
370910a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
3710d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
3711d949cac1SHarald Welte {
3712f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
371310a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
3714f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3715d949cac1SHarald Welte }
3716d949cac1SHarald Welte 
37179da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
37189da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
37199da29271STakashi Iwai {
37209da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
37219da29271STakashi Iwai 	int i;
37229da29271STakashi Iwai 
37239da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
37249da29271STakashi Iwai 		hda_nid_t nid;
37259da29271STakashi Iwai 		int conn;
37269da29271STakashi Iwai 
37279da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
37289da29271STakashi Iwai 		if (!nid)
37299da29271STakashi Iwai 			continue;
37309da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
37319da29271STakashi Iwai 		if (conn < 1)
37329da29271STakashi Iwai 			continue;
37339da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
37349da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
37359da29271STakashi Iwai 		else {
37369da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
37379da29271STakashi Iwai 			break; /* at most two dig outs */
37389da29271STakashi Iwai 		}
37399da29271STakashi Iwai 	}
37409da29271STakashi Iwai }
37419da29271STakashi Iwai 
3742d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3743d949cac1SHarald Welte {
3744d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3745d949cac1SHarald Welte 	int err;
3746d949cac1SHarald Welte 
37479da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3748d949cac1SHarald Welte 	if (err < 0)
3749d949cac1SHarald Welte 		return err;
3750d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3751d949cac1SHarald Welte 	if (err < 0)
3752d949cac1SHarald Welte 		return err;
3753d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3754d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3755d949cac1SHarald Welte 
3756bc92df7fSLydia Wang 	err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg);
3757d949cac1SHarald Welte 	if (err < 0)
3758d949cac1SHarald Welte 		return err;
3759d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3760d949cac1SHarald Welte 	if (err < 0)
3761d949cac1SHarald Welte 		return err;
376210a20af7STakashi Iwai 	err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
3763d949cac1SHarald Welte 	if (err < 0)
3764d949cac1SHarald Welte 		return err;
3765d949cac1SHarald Welte 
3766d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3767d949cac1SHarald Welte 
37689da29271STakashi Iwai 	fill_dig_outs(codec);
376998aa34c0SHarald Welte 
3770603c4019STakashi Iwai 	if (spec->kctls.list)
3771603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3772d949cac1SHarald Welte 
37730aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
37740aa62aefSHarald Welte 
3775f8fdd495SHarald Welte 	if (spec->hp_mux)
37763d83e577STakashi Iwai 		via_hp_build(codec);
3777d949cac1SHarald Welte 
37785b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3779d949cac1SHarald Welte 	return 1;
3780d949cac1SHarald Welte }
3781d949cac1SHarald Welte 
3782d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3783d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
3784d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3785d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3786d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3787d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3788d949cac1SHarald Welte 	{ } /* end */
3789d949cac1SHarald Welte };
3790d949cac1SHarald Welte #endif
3791d949cac1SHarald Welte 
37926369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
37936369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
37946369bcfcSLydia Wang {
37956369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
37966369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
37976369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
37986369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
37996369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
38006369bcfcSLydia Wang }
38016369bcfcSLydia Wang 
3802d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3803d949cac1SHarald Welte {
3804d949cac1SHarald Welte 	struct via_spec *spec;
3805d949cac1SHarald Welte 	int err;
3806d949cac1SHarald Welte 
3807d949cac1SHarald Welte 	/* create a codec specific record */
38085b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3809d949cac1SHarald Welte 	if (spec == NULL)
3810d949cac1SHarald Welte 		return -ENOMEM;
3811d949cac1SHarald Welte 
3812d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3813d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3814d949cac1SHarald Welte 	if (err < 0) {
3815d949cac1SHarald Welte 		via_free(codec);
3816d949cac1SHarald Welte 		return err;
3817d949cac1SHarald Welte 	} else if (!err) {
3818d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3819d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3820d949cac1SHarald Welte 	}
3821d949cac1SHarald Welte 
382269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3823bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3824bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3825bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3826bc92df7fSLydia Wang 	else
3827bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3828bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3829d949cac1SHarald Welte 
383036dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
383136dd5c4aSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
3832bc92df7fSLydia Wang 	else if (codec->vendor_id == 0x11064397)
3833bc92df7fSLydia Wang 		spec->stream_name_analog = "VT1705 Analog";
383436dd5c4aSLydia Wang 	else
3835d949cac1SHarald Welte 		spec->stream_name_analog = "VT1708S Analog";
3836bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3837bc92df7fSLydia Wang 		spec->stream_analog_playback = &vt1705_pcm_analog_playback;
3838bc92df7fSLydia Wang 	else
3839d949cac1SHarald Welte 		spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3840d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3841d949cac1SHarald Welte 
384236dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
384336dd5c4aSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
3844bc92df7fSLydia Wang 	else if (codec->vendor_id == 0x11064397)
3845bc92df7fSLydia Wang 		spec->stream_name_digital = "VT1705 Digital";
384636dd5c4aSLydia Wang 	else
3847d949cac1SHarald Welte 		spec->stream_name_digital = "VT1708S Digital";
3848d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3849d949cac1SHarald Welte 
3850d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
3851d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
3852d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
3853337b9d02STakashi Iwai 		get_mux_nids(codec);
38546369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
38556369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3856d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3857d949cac1SHarald Welte 		spec->num_mixers++;
3858d949cac1SHarald Welte 	}
3859d949cac1SHarald Welte 
3860d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3861d949cac1SHarald Welte 
3862d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
386369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3864d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3865d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3866d949cac1SHarald Welte #endif
3867d949cac1SHarald Welte 
3868518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3869518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3870518bf3baSLydia Wang 		kfree(codec->chip_name);
3871518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3872518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3873518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3874518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3875518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
3876518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
3877518bf3baSLydia Wang 	}
3878970f630fSLydia Wang 	/* correct names for VT1818S */
3879970f630fSLydia Wang 	if (codec->vendor_id == 0x11060440) {
3880970f630fSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
3881970f630fSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
3882970f630fSLydia Wang 	}
3883bc92df7fSLydia Wang 	/* correct names for VT1705 */
3884bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3885bc92df7fSLydia Wang 		kfree(codec->chip_name);
3886bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3887bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3888bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3889bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3890bc92df7fSLydia Wang 	}
38913e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3892d949cac1SHarald Welte 	return 0;
3893d949cac1SHarald Welte }
3894d949cac1SHarald Welte 
3895d949cac1SHarald Welte /* Patch for VT1702 */
3896d949cac1SHarald Welte 
3897d949cac1SHarald Welte /* capture mixer elements */
3898d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3899d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3900d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3901d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3902d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3903d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3904d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3905d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3906d949cac1SHarald Welte 			 HDA_INPUT),
3907d949cac1SHarald Welte 	{
3908d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3909d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3910d949cac1SHarald Welte 		 * So call somewhat different..
3911d949cac1SHarald Welte 		 */
3912d949cac1SHarald Welte 		/* .name = "Capture Source", */
3913d949cac1SHarald Welte 		.name = "Input Source",
3914d949cac1SHarald Welte 		.count = 1,
3915d949cac1SHarald Welte 		.info = via_mux_enum_info,
3916d949cac1SHarald Welte 		.get = via_mux_enum_get,
3917d949cac1SHarald Welte 		.put = via_mux_enum_put,
3918d949cac1SHarald Welte 	},
3919d949cac1SHarald Welte 	{ } /* end */
3920d949cac1SHarald Welte };
3921d949cac1SHarald Welte 
3922d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
3923d949cac1SHarald Welte 	/*
3924d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3925d949cac1SHarald Welte 	 */
3926d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3927d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3928d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3929d949cac1SHarald Welte 
3930d949cac1SHarald Welte 
3931d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3932d949cac1SHarald Welte 	 * mixer widget
3933d949cac1SHarald Welte 	 */
3934d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3935d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3936d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3937d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3938d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3939d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3940d949cac1SHarald Welte 
3941d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3942d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3943d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3944d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3945d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3946bc7e7e5cSLydia Wang 	/* mixer enable */
3947bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3948bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3949bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3950d949cac1SHarald Welte 	{ }
3951d949cac1SHarald Welte };
3952d949cac1SHarald Welte 
395369e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
3954a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3955a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3956a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3957a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3958a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3959a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
396069e52a80SHarald Welte 	{ }
396169e52a80SHarald Welte };
396269e52a80SHarald Welte 
3963d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
39640aa62aefSHarald Welte 	.substreams = 2,
3965d949cac1SHarald Welte 	.channels_min = 2,
3966d949cac1SHarald Welte 	.channels_max = 2,
3967d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3968d949cac1SHarald Welte 	.ops = {
3969d949cac1SHarald Welte 		.open = via_playback_pcm_open,
39700aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
397117314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
397217314379SLydia Wang 		.close = via_pcm_open_close
3973d949cac1SHarald Welte 	},
3974d949cac1SHarald Welte };
3975d949cac1SHarald Welte 
3976d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3977d949cac1SHarald Welte 	.substreams = 3,
3978d949cac1SHarald Welte 	.channels_min = 2,
3979d949cac1SHarald Welte 	.channels_max = 2,
3980d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
3981d949cac1SHarald Welte 	.ops = {
398217314379SLydia Wang 		.open = via_pcm_open_close,
3983d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
398417314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
398517314379SLydia Wang 		.close = via_pcm_open_close
3986d949cac1SHarald Welte 	},
3987d949cac1SHarald Welte };
3988d949cac1SHarald Welte 
3989d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
39905691ec7fSHarald Welte 	.substreams = 2,
3991d949cac1SHarald Welte 	.channels_min = 2,
3992d949cac1SHarald Welte 	.channels_max = 2,
3993d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3994d949cac1SHarald Welte 	.ops = {
3995d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3996d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
39979da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
39989da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3999d949cac1SHarald Welte 	},
4000d949cac1SHarald Welte };
4001d949cac1SHarald Welte 
4002d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4003d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4004d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4005d949cac1SHarald Welte {
4006d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4007d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4008d949cac1SHarald Welte 
4009d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4010d949cac1SHarald Welte 		/* config dac list */
4011d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
4012d949cac1SHarald Welte 	}
4013d949cac1SHarald Welte 
4014d949cac1SHarald Welte 	return 0;
4015d949cac1SHarald Welte }
4016d949cac1SHarald Welte 
4017d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4018d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4019d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4020d949cac1SHarald Welte {
4021d949cac1SHarald Welte 	int err;
4022d949cac1SHarald Welte 
4023d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4024d949cac1SHarald Welte 		return -1;
4025d949cac1SHarald Welte 
4026d949cac1SHarald Welte 	/* add control to mixer index 0 */
4027d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4028d949cac1SHarald Welte 			      "Master Front Playback Volume",
4029d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4030d949cac1SHarald Welte 	if (err < 0)
4031d949cac1SHarald Welte 		return err;
4032d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4033d949cac1SHarald Welte 			      "Master Front Playback Switch",
4034d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4035d949cac1SHarald Welte 	if (err < 0)
4036d949cac1SHarald Welte 		return err;
4037d949cac1SHarald Welte 
4038d949cac1SHarald Welte 	/* Front */
4039d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4040d949cac1SHarald Welte 			      "Front Playback Volume",
4041d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4042d949cac1SHarald Welte 	if (err < 0)
4043d949cac1SHarald Welte 		return err;
4044d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4045d949cac1SHarald Welte 			      "Front Playback Switch",
4046d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4047d949cac1SHarald Welte 	if (err < 0)
4048d949cac1SHarald Welte 		return err;
4049d949cac1SHarald Welte 
4050d949cac1SHarald Welte 	return 0;
4051d949cac1SHarald Welte }
4052d949cac1SHarald Welte 
4053d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4054d949cac1SHarald Welte {
40550713efebSLydia Wang 	int err, i;
40560713efebSLydia Wang 	struct hda_input_mux *imux;
4057ea734963STakashi Iwai 	static const char * const texts[] = { "ON", "OFF", NULL};
4058d949cac1SHarald Welte 	if (!pin)
4059d949cac1SHarald Welte 		return 0;
4060d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4061cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4062d949cac1SHarald Welte 
4063d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4064d949cac1SHarald Welte 			      "Headphone Playback Volume",
4065d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4066d949cac1SHarald Welte 	if (err < 0)
4067d949cac1SHarald Welte 		return err;
4068d949cac1SHarald Welte 
4069d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4070d949cac1SHarald Welte 			      "Headphone Playback Switch",
4071d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4072d949cac1SHarald Welte 	if (err < 0)
4073d949cac1SHarald Welte 		return err;
4074d949cac1SHarald Welte 
40750713efebSLydia Wang 	imux = &spec->private_imux[1];
40760aa62aefSHarald Welte 
40770713efebSLydia Wang 	/* for hp mode select */
407810a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
407910a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
40800713efebSLydia Wang 
40810713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4082d949cac1SHarald Welte 	return 0;
4083d949cac1SHarald Welte }
4084d949cac1SHarald Welte 
4085d949cac1SHarald Welte /* create playback/capture controls for input pins */
408610a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
4087d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4088d949cac1SHarald Welte {
4089f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
409010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
4091f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4092d949cac1SHarald Welte }
4093d949cac1SHarald Welte 
4094d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4095d949cac1SHarald Welte {
4096d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4097d949cac1SHarald Welte 	int err;
4098d949cac1SHarald Welte 
40999da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4100d949cac1SHarald Welte 	if (err < 0)
4101d949cac1SHarald Welte 		return err;
4102d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4103d949cac1SHarald Welte 	if (err < 0)
4104d949cac1SHarald Welte 		return err;
4105d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4106d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4107d949cac1SHarald Welte 
4108d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4109d949cac1SHarald Welte 	if (err < 0)
4110d949cac1SHarald Welte 		return err;
4111d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4112d949cac1SHarald Welte 	if (err < 0)
4113d949cac1SHarald Welte 		return err;
4114c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4115c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4116c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4117c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4118c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4119c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
412010a20af7STakashi Iwai 	err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
4121d949cac1SHarald Welte 	if (err < 0)
4122d949cac1SHarald Welte 		return err;
4123d949cac1SHarald Welte 
4124d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4125d949cac1SHarald Welte 
41269da29271STakashi Iwai 	fill_dig_outs(codec);
412798aa34c0SHarald Welte 
4128603c4019STakashi Iwai 	if (spec->kctls.list)
4129603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4130d949cac1SHarald Welte 
41310aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
41320aa62aefSHarald Welte 
4133f8fdd495SHarald Welte 	if (spec->hp_mux)
41343d83e577STakashi Iwai 		via_hp_build(codec);
4135d949cac1SHarald Welte 
4136d949cac1SHarald Welte 	return 1;
4137d949cac1SHarald Welte }
4138d949cac1SHarald Welte 
4139d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4140d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4141d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4142d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4143d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4144d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4145d949cac1SHarald Welte 	{ } /* end */
4146d949cac1SHarald Welte };
4147d949cac1SHarald Welte #endif
4148d949cac1SHarald Welte 
41493e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
41503e95b9abSLydia Wang {
41513e95b9abSLydia Wang 	int imux_is_smixer =
41523e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
41533e95b9abSLydia Wang 	unsigned int parm;
41543e95b9abSLydia Wang 	/* inputs */
41553e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
41563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
41583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
41593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
41603e95b9abSLydia Wang 	if (imux_is_smixer)
41613e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
41623e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
41633e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
41643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
41653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
41663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
41673e95b9abSLydia Wang 
41683e95b9abSLydia Wang 	/* outputs */
41693e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
41703e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41713e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
41723e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
41733e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
41743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
41753e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
41763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
41773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
41783e95b9abSLydia Wang }
41793e95b9abSLydia Wang 
4180d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4181d949cac1SHarald Welte {
4182d949cac1SHarald Welte 	struct via_spec *spec;
4183d949cac1SHarald Welte 	int err;
4184d949cac1SHarald Welte 
4185d949cac1SHarald Welte 	/* create a codec specific record */
41865b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4187d949cac1SHarald Welte 	if (spec == NULL)
4188d949cac1SHarald Welte 		return -ENOMEM;
4189d949cac1SHarald Welte 
4190d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4191d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4192d949cac1SHarald Welte 	if (err < 0) {
4193d949cac1SHarald Welte 		via_free(codec);
4194d949cac1SHarald Welte 		return err;
4195d949cac1SHarald Welte 	} else if (!err) {
4196d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4197d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4198d949cac1SHarald Welte 	}
4199d949cac1SHarald Welte 
420069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
420169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4202d949cac1SHarald Welte 
4203d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4204d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4205d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4206d949cac1SHarald Welte 
4207d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4208d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4209d949cac1SHarald Welte 
4210d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4211d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4212d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4213337b9d02STakashi Iwai 		get_mux_nids(codec);
4214d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4215d949cac1SHarald Welte 		spec->num_mixers++;
4216d949cac1SHarald Welte 	}
4217d949cac1SHarald Welte 
4218d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4219d949cac1SHarald Welte 
4220d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
422169e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4222d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4223d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4224d949cac1SHarald Welte #endif
4225d949cac1SHarald Welte 
42263e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
4227d949cac1SHarald Welte 	return 0;
4228d949cac1SHarald Welte }
4229d949cac1SHarald Welte 
4230eb7188caSLydia Wang /* Patch for VT1718S */
4231eb7188caSLydia Wang 
4232eb7188caSLydia Wang /* capture mixer elements */
4233eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4234eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4235eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4236eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4237eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4238eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4239eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4240eb7188caSLydia Wang 			 HDA_INPUT),
4241eb7188caSLydia Wang 	{
4242eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4243eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4244eb7188caSLydia Wang 		 * So call somewhat different..
4245eb7188caSLydia Wang 		 */
4246eb7188caSLydia Wang 		.name = "Input Source",
4247eb7188caSLydia Wang 		.count = 2,
4248eb7188caSLydia Wang 		.info = via_mux_enum_info,
4249eb7188caSLydia Wang 		.get = via_mux_enum_get,
4250eb7188caSLydia Wang 		.put = via_mux_enum_put,
4251eb7188caSLydia Wang 	},
4252eb7188caSLydia Wang 	{ } /* end */
4253eb7188caSLydia Wang };
4254eb7188caSLydia Wang 
4255eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4256eb7188caSLydia Wang 	/*
4257eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4258eb7188caSLydia Wang 	 */
4259eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4260eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4261eb7188caSLydia Wang 
42624ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
42634ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
4264eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4265eb7188caSLydia Wang 	 * mixer widget
4266eb7188caSLydia Wang 	 */
4267eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4268eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4269eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4270eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4271eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
42724ab2d53aSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
4273eb7188caSLydia Wang 
4274eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4275eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4276eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4277eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4278eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4279eb7188caSLydia Wang 	/* PW11 Input enable */
4280eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4281eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4282eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4283eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4284eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4285eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4286eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4287eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4288eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4289eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4290eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4291eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4292eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4293eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4294eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4295eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4296eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4297eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4298eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4299eb7188caSLydia Wang 	{ }
4300eb7188caSLydia Wang };
4301eb7188caSLydia Wang 
4302eb7188caSLydia Wang 
4303eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4304eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4305eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4306eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4307eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4308eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4309eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4310eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4311eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4312eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4313eb7188caSLydia Wang 	{ }
4314eb7188caSLydia Wang };
4315eb7188caSLydia Wang 
4316eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4317eb7188caSLydia Wang 	.substreams = 2,
4318eb7188caSLydia Wang 	.channels_min = 2,
4319eb7188caSLydia Wang 	.channels_max = 10,
4320eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4321eb7188caSLydia Wang 	.ops = {
4322eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4323eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4324eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4325eb7188caSLydia Wang 		.close = via_pcm_open_close,
4326eb7188caSLydia Wang 	},
4327eb7188caSLydia Wang };
4328eb7188caSLydia Wang 
4329eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4330eb7188caSLydia Wang 	.substreams = 2,
4331eb7188caSLydia Wang 	.channels_min = 2,
4332eb7188caSLydia Wang 	.channels_max = 2,
4333eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4334eb7188caSLydia Wang 	.ops = {
4335eb7188caSLydia Wang 		.open = via_pcm_open_close,
4336eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4337eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4338eb7188caSLydia Wang 		.close = via_pcm_open_close,
4339eb7188caSLydia Wang 	},
4340eb7188caSLydia Wang };
4341eb7188caSLydia Wang 
4342eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4343eb7188caSLydia Wang 	.substreams = 2,
4344eb7188caSLydia Wang 	.channels_min = 2,
4345eb7188caSLydia Wang 	.channels_max = 2,
4346eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4347eb7188caSLydia Wang 	.ops = {
4348eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4349eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4350eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4351eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4352eb7188caSLydia Wang 	},
4353eb7188caSLydia Wang };
4354eb7188caSLydia Wang 
4355eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4356eb7188caSLydia Wang 	.substreams = 1,
4357eb7188caSLydia Wang 	.channels_min = 2,
4358eb7188caSLydia Wang 	.channels_max = 2,
4359eb7188caSLydia Wang };
4360eb7188caSLydia Wang 
4361eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4362eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4363eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4364eb7188caSLydia Wang {
4365eb7188caSLydia Wang 	int i;
4366eb7188caSLydia Wang 	hda_nid_t nid;
4367eb7188caSLydia Wang 
4368eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4369eb7188caSLydia Wang 
4370eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4371eb7188caSLydia Wang 
4372eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4373eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4374eb7188caSLydia Wang 		if (nid) {
4375eb7188caSLydia Wang 			/* config dac list */
4376eb7188caSLydia Wang 			switch (i) {
4377eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4378eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4379eb7188caSLydia Wang 				break;
4380eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4381eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4382eb7188caSLydia Wang 				break;
4383eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4384eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4385eb7188caSLydia Wang 				break;
4386eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4387eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4388eb7188caSLydia Wang 				break;
4389eb7188caSLydia Wang 			}
4390eb7188caSLydia Wang 		}
4391eb7188caSLydia Wang 	}
4392eb7188caSLydia Wang 
4393eb7188caSLydia Wang 	return 0;
4394eb7188caSLydia Wang }
4395eb7188caSLydia Wang 
4396eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4397eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4398eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4399eb7188caSLydia Wang {
4400eb7188caSLydia Wang 	char name[32];
4401ea734963STakashi Iwai 	static const char * const chname[4] = {
4402ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
4403ea734963STakashi Iwai 	};
4404eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4405eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4406eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4407eb7188caSLydia Wang 	int i, err;
4408eb7188caSLydia Wang 
4409eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4410eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4411eb7188caSLydia Wang 
4412eb7188caSLydia Wang 		if (!nid)
4413eb7188caSLydia Wang 			continue;
4414eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4415eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4416eb7188caSLydia Wang 
4417eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4418eb7188caSLydia Wang 			/* Center/LFE */
4419eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4420eb7188caSLydia Wang 					      "Center Playback Volume",
4421eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4422eb7188caSLydia Wang 								  HDA_OUTPUT));
4423eb7188caSLydia Wang 			if (err < 0)
4424eb7188caSLydia Wang 				return err;
4425eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4426eb7188caSLydia Wang 					      "LFE Playback Volume",
4427eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4428eb7188caSLydia Wang 								  HDA_OUTPUT));
4429eb7188caSLydia Wang 			if (err < 0)
4430eb7188caSLydia Wang 				return err;
4431eb7188caSLydia Wang 			err = via_add_control(
4432eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4433eb7188caSLydia Wang 				"Center Playback Switch",
4434eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4435eb7188caSLydia Wang 						    HDA_OUTPUT));
4436eb7188caSLydia Wang 			if (err < 0)
4437eb7188caSLydia Wang 				return err;
4438eb7188caSLydia Wang 			err = via_add_control(
4439eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4440eb7188caSLydia Wang 				"LFE Playback Switch",
4441eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4442eb7188caSLydia Wang 						    HDA_OUTPUT));
4443eb7188caSLydia Wang 			if (err < 0)
4444eb7188caSLydia Wang 				return err;
4445eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4446eb7188caSLydia Wang 			/* Front */
4447eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4448eb7188caSLydia Wang 			err = via_add_control(
4449eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4450eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4451eb7188caSLydia Wang 			if (err < 0)
4452eb7188caSLydia Wang 				return err;
4453eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4454eb7188caSLydia Wang 			err = via_add_control(
4455eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4456eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4457eb7188caSLydia Wang 						    HDA_OUTPUT));
4458eb7188caSLydia Wang 			if (err < 0)
4459eb7188caSLydia Wang 				return err;
4460eb7188caSLydia Wang 		} else {
4461eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4462eb7188caSLydia Wang 			err = via_add_control(
4463eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4464eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4465eb7188caSLydia Wang 			if (err < 0)
4466eb7188caSLydia Wang 				return err;
4467eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4468eb7188caSLydia Wang 			err = via_add_control(
4469eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4470eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4471eb7188caSLydia Wang 						    HDA_OUTPUT));
4472eb7188caSLydia Wang 			if (err < 0)
4473eb7188caSLydia Wang 				return err;
4474eb7188caSLydia Wang 		}
4475eb7188caSLydia Wang 	}
4476eb7188caSLydia Wang 	return 0;
4477eb7188caSLydia Wang }
4478eb7188caSLydia Wang 
4479eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4480eb7188caSLydia Wang {
4481eb7188caSLydia Wang 	int err;
4482eb7188caSLydia Wang 
4483eb7188caSLydia Wang 	if (!pin)
4484eb7188caSLydia Wang 		return 0;
4485eb7188caSLydia Wang 
4486eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4487eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4488eb7188caSLydia Wang 
4489eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4490eb7188caSLydia Wang 			      "Headphone Playback Volume",
4491eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4492eb7188caSLydia Wang 	if (err < 0)
4493eb7188caSLydia Wang 		return err;
4494eb7188caSLydia Wang 
4495eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4496eb7188caSLydia Wang 			      "Headphone Playback Switch",
4497eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4498eb7188caSLydia Wang 	if (err < 0)
4499eb7188caSLydia Wang 		return err;
4500eb7188caSLydia Wang 
4501eb7188caSLydia Wang 	create_hp_imux(spec);
4502eb7188caSLydia Wang 	return 0;
4503eb7188caSLydia Wang }
4504eb7188caSLydia Wang 
4505eb7188caSLydia Wang /* create playback/capture controls for input pins */
450610a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
4507eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4508eb7188caSLydia Wang {
4509f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
451010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
4511f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
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
4560eb7188caSLydia Wang static 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 
4658bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4659bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4660bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4661bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4662bb3c6bfcSLydia Wang 	else
4663eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4664eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4665eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4666eb7188caSLydia Wang 
4667bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4668bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4669bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4670bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4671bb3c6bfcSLydia Wang 	else
4672eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4673eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4674bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4675eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4676eb7188caSLydia Wang 
4677eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4678eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4679eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4680eb7188caSLydia Wang 		get_mux_nids(codec);
4681bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4682bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4683eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4684eb7188caSLydia Wang 		spec->num_mixers++;
4685eb7188caSLydia Wang 	}
4686eb7188caSLydia Wang 
4687eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4688eb7188caSLydia Wang 
4689eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
46900f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4691eb7188caSLydia Wang 
4692eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4693eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4694eb7188caSLydia Wang #endif
4695eb7188caSLydia Wang 
46963e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
46973e95b9abSLydia Wang 
4698eb7188caSLydia Wang 	return 0;
4699eb7188caSLydia Wang }
4700f3db423dSLydia Wang 
4701f3db423dSLydia Wang /* Patch for VT1716S */
4702f3db423dSLydia Wang 
4703f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4704f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4705f3db423dSLydia Wang {
4706f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4707f3db423dSLydia Wang 	uinfo->count = 1;
4708f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4709f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4710f3db423dSLydia Wang 	return 0;
4711f3db423dSLydia Wang }
4712f3db423dSLydia Wang 
4713f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4714f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4715f3db423dSLydia Wang {
4716f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4717f3db423dSLydia Wang 	int index = 0;
4718f3db423dSLydia Wang 
4719f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4720f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4721f3db423dSLydia Wang 	if (index != -1)
4722f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4723f3db423dSLydia Wang 
4724f3db423dSLydia Wang 	return 0;
4725f3db423dSLydia Wang }
4726f3db423dSLydia Wang 
4727f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4728f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4729f3db423dSLydia Wang {
4730f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4731f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4732f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4733f3db423dSLydia Wang 
4734f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4735f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4736f3db423dSLydia Wang 	spec->dmic_enabled = index;
47373e95b9abSLydia Wang 	set_widgets_power_state(codec);
4738f3db423dSLydia Wang 	return 1;
4739f3db423dSLydia Wang }
4740f3db423dSLydia Wang 
4741f3db423dSLydia Wang /* capture mixer elements */
4742f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4743f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4744f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4745f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4746f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4747f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4748f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4749f3db423dSLydia Wang 			 HDA_INPUT),
4750f3db423dSLydia Wang 	{
4751f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4752f3db423dSLydia Wang 		.name = "Input Source",
4753f3db423dSLydia Wang 		.count = 1,
4754f3db423dSLydia Wang 		.info = via_mux_enum_info,
4755f3db423dSLydia Wang 		.get = via_mux_enum_get,
4756f3db423dSLydia Wang 		.put = via_mux_enum_put,
4757f3db423dSLydia Wang 	},
4758f3db423dSLydia Wang 	{ } /* end */
4759f3db423dSLydia Wang };
4760f3db423dSLydia Wang 
4761f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4762f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4763f3db423dSLydia Wang 	{
4764f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4765f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
47665b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
4767f3db423dSLydia Wang 	 .count = 1,
4768f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
4769f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
4770f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
4771f3db423dSLydia Wang 	 },
4772f3db423dSLydia Wang 	{}			/* end */
4773f3db423dSLydia Wang };
4774f3db423dSLydia Wang 
4775f3db423dSLydia Wang 
4776f3db423dSLydia Wang /* mono-out mixer elements */
4777f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4778f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4779f3db423dSLydia Wang 	{ } /* end */
4780f3db423dSLydia Wang };
4781f3db423dSLydia Wang 
4782f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
4783f3db423dSLydia Wang 	/*
4784f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4785f3db423dSLydia Wang 	 */
4786f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4787f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4788f3db423dSLydia Wang 
4789f3db423dSLydia Wang 
4790f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4791f3db423dSLydia Wang 	 * mixer widget
4792f3db423dSLydia Wang 	 */
4793f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4794f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4795f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4796f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4797f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4798f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4799f3db423dSLydia Wang 
4800f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4801f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4802f3db423dSLydia Wang 
4803f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4804f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4805f3db423dSLydia Wang 
4806f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4807f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4808f3db423dSLydia Wang 
4809f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4810f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4811f3db423dSLydia Wang 
4812f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4813f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4814f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4815f3db423dSLydia Wang 
4816f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4817f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4818f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4819f3db423dSLydia Wang 	/* PW12 Output enable */
4820f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4821f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4822f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4823f3db423dSLydia Wang 	/* don't bybass mixer */
4824f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4825f3db423dSLydia Wang 	/* Enable mono output */
4826f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4827f3db423dSLydia Wang 	{ }
4828f3db423dSLydia Wang };
4829f3db423dSLydia Wang 
4830f3db423dSLydia Wang 
4831f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
4832f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4833f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4834f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4835f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4836f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4837f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4838f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4839f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4840f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4841f3db423dSLydia Wang 	{ }
4842f3db423dSLydia Wang };
4843f3db423dSLydia Wang 
4844f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4845f3db423dSLydia Wang 	.substreams = 2,
4846f3db423dSLydia Wang 	.channels_min = 2,
4847f3db423dSLydia Wang 	.channels_max = 6,
4848f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4849f3db423dSLydia Wang 	.ops = {
4850f3db423dSLydia Wang 		.open = via_playback_pcm_open,
4851f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4852f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4853f3db423dSLydia Wang 		.close = via_pcm_open_close,
4854f3db423dSLydia Wang 	},
4855f3db423dSLydia Wang };
4856f3db423dSLydia Wang 
4857f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4858f3db423dSLydia Wang 	.substreams = 2,
4859f3db423dSLydia Wang 	.channels_min = 2,
4860f3db423dSLydia Wang 	.channels_max = 2,
4861f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
4862f3db423dSLydia Wang 	.ops = {
4863f3db423dSLydia Wang 		.open = via_pcm_open_close,
4864f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
4865f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4866f3db423dSLydia Wang 		.close = via_pcm_open_close,
4867f3db423dSLydia Wang 	},
4868f3db423dSLydia Wang };
4869f3db423dSLydia Wang 
4870f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4871f3db423dSLydia Wang 	.substreams = 2,
4872f3db423dSLydia Wang 	.channels_min = 2,
4873f3db423dSLydia Wang 	.channels_max = 2,
4874f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
4875f3db423dSLydia Wang 	.ops = {
4876f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
4877f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
4878f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4879f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4880f3db423dSLydia Wang 	},
4881f3db423dSLydia Wang };
4882f3db423dSLydia Wang 
4883f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4884f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
4885f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
4886f3db423dSLydia Wang {	int i;
4887f3db423dSLydia Wang 	hda_nid_t nid;
4888f3db423dSLydia Wang 
4889f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4890f3db423dSLydia Wang 
4891f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4892f3db423dSLydia Wang 
4893f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
4894f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4895f3db423dSLydia Wang 		if (nid) {
4896f3db423dSLydia Wang 			/* config dac list */
4897f3db423dSLydia Wang 			switch (i) {
4898f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
4899f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
4900f3db423dSLydia Wang 				break;
4901f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
4902f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
4903f3db423dSLydia Wang 				break;
4904f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
4905f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
4906f3db423dSLydia Wang 				break;
4907f3db423dSLydia Wang 			}
4908f3db423dSLydia Wang 		}
4909f3db423dSLydia Wang 	}
4910f3db423dSLydia Wang 
4911f3db423dSLydia Wang 	return 0;
4912f3db423dSLydia Wang }
4913f3db423dSLydia Wang 
4914f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
4915f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
4916f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
4917f3db423dSLydia Wang {
4918f3db423dSLydia Wang 	char name[32];
4919ea734963STakashi Iwai 	static const char * const chname[3] = {
4920ea734963STakashi Iwai 		"Front", "Surround", "C/LFE"
4921ea734963STakashi Iwai 	};
4922f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
4923f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
4924f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
4925f3db423dSLydia Wang 	int i, err;
4926f3db423dSLydia Wang 
4927f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
4928f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4929f3db423dSLydia Wang 
4930f3db423dSLydia Wang 		if (!nid)
4931f3db423dSLydia Wang 			continue;
4932f3db423dSLydia Wang 
4933f3db423dSLydia Wang 		nid_vol = nid_vols[i];
4934f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
4935f3db423dSLydia Wang 
4936f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4937f3db423dSLydia Wang 			err = via_add_control(
4938f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4939f3db423dSLydia Wang 				"Center Playback Volume",
4940f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
4941f3db423dSLydia Wang 			if (err < 0)
4942f3db423dSLydia Wang 				return err;
4943f3db423dSLydia Wang 			err = via_add_control(
4944f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4945f3db423dSLydia Wang 				"LFE Playback Volume",
4946f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
4947f3db423dSLydia Wang 			if (err < 0)
4948f3db423dSLydia Wang 				return err;
4949f3db423dSLydia Wang 			err = via_add_control(
4950f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4951f3db423dSLydia Wang 				"Center Playback Switch",
4952f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4953f3db423dSLydia Wang 						    HDA_OUTPUT));
4954f3db423dSLydia Wang 			if (err < 0)
4955f3db423dSLydia Wang 				return err;
4956f3db423dSLydia Wang 			err = via_add_control(
4957f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4958f3db423dSLydia Wang 				"LFE Playback Switch",
4959f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4960f3db423dSLydia Wang 						    HDA_OUTPUT));
4961f3db423dSLydia Wang 			if (err < 0)
4962f3db423dSLydia Wang 				return err;
4963f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4964f3db423dSLydia Wang 
4965f3db423dSLydia Wang 			err = via_add_control(
4966f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4967f3db423dSLydia Wang 				"Master Front Playback Volume",
4968f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4969f3db423dSLydia Wang 			if (err < 0)
4970f3db423dSLydia Wang 				return err;
4971f3db423dSLydia Wang 			err = via_add_control(
4972f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4973f3db423dSLydia Wang 				"Master Front Playback Switch",
4974f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4975f3db423dSLydia Wang 			if (err < 0)
4976f3db423dSLydia Wang 				return err;
4977f3db423dSLydia Wang 
4978f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4979f3db423dSLydia Wang 			err = via_add_control(
4980f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4981f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4982f3db423dSLydia Wang 			if (err < 0)
4983f3db423dSLydia Wang 				return err;
4984f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4985f3db423dSLydia Wang 			err = via_add_control(
4986f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4987f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4988f3db423dSLydia Wang 						    HDA_OUTPUT));
4989f3db423dSLydia Wang 			if (err < 0)
4990f3db423dSLydia Wang 				return err;
4991f3db423dSLydia Wang 		} else {
4992f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4993f3db423dSLydia Wang 			err = via_add_control(
4994f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4995f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4996f3db423dSLydia Wang 			if (err < 0)
4997f3db423dSLydia Wang 				return err;
4998f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4999f3db423dSLydia Wang 			err = via_add_control(
5000f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5001f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5002f3db423dSLydia Wang 						    HDA_OUTPUT));
5003f3db423dSLydia Wang 			if (err < 0)
5004f3db423dSLydia Wang 				return err;
5005f3db423dSLydia Wang 		}
5006f3db423dSLydia Wang 	}
5007f3db423dSLydia Wang 	return 0;
5008f3db423dSLydia Wang }
5009f3db423dSLydia Wang 
5010f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5011f3db423dSLydia Wang {
5012f3db423dSLydia Wang 	int err;
5013f3db423dSLydia Wang 
5014f3db423dSLydia Wang 	if (!pin)
5015f3db423dSLydia Wang 		return 0;
5016f3db423dSLydia Wang 
5017f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5018f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5019f3db423dSLydia Wang 
5020f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5021f3db423dSLydia Wang 			      "Headphone Playback Volume",
5022f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5023f3db423dSLydia Wang 	if (err < 0)
5024f3db423dSLydia Wang 		return err;
5025f3db423dSLydia Wang 
5026f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5027f3db423dSLydia Wang 			      "Headphone Playback Switch",
5028f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5029f3db423dSLydia Wang 	if (err < 0)
5030f3db423dSLydia Wang 		return err;
5031f3db423dSLydia Wang 
5032f3db423dSLydia Wang 	create_hp_imux(spec);
5033f3db423dSLydia Wang 	return 0;
5034f3db423dSLydia Wang }
5035f3db423dSLydia Wang 
5036f3db423dSLydia Wang /* create playback/capture controls for input pins */
503710a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
5038f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5039f3db423dSLydia Wang {
5040f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
504110a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
5042f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
5043f3db423dSLydia Wang }
5044f3db423dSLydia Wang 
5045f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5046f3db423dSLydia Wang {
5047f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5048f3db423dSLydia Wang 	int err;
5049f3db423dSLydia Wang 
5050f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5051f3db423dSLydia Wang 	if (err < 0)
5052f3db423dSLydia Wang 		return err;
5053f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5054f3db423dSLydia Wang 	if (err < 0)
5055f3db423dSLydia Wang 		return err;
5056f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5057f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5058f3db423dSLydia Wang 
5059f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5060f3db423dSLydia Wang 	if (err < 0)
5061f3db423dSLydia Wang 		return err;
5062f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5063f3db423dSLydia Wang 	if (err < 0)
5064f3db423dSLydia Wang 		return err;
506510a20af7STakashi Iwai 	err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
5066f3db423dSLydia Wang 	if (err < 0)
5067f3db423dSLydia Wang 		return err;
5068f3db423dSLydia Wang 
5069f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5070f3db423dSLydia Wang 
5071f3db423dSLydia Wang 	fill_dig_outs(codec);
5072f3db423dSLydia Wang 
5073f3db423dSLydia Wang 	if (spec->kctls.list)
5074f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5075f3db423dSLydia Wang 
5076f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5077f3db423dSLydia Wang 
5078f3db423dSLydia Wang 	if (spec->hp_mux)
50793d83e577STakashi Iwai 		via_hp_build(codec);
5080f3db423dSLydia Wang 
50815b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
5082f3db423dSLydia Wang 
5083f3db423dSLydia Wang 	return 1;
5084f3db423dSLydia Wang }
5085f3db423dSLydia Wang 
5086f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5087f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5088f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5089f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5090f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5091f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5092f3db423dSLydia Wang 	{ } /* end */
5093f3db423dSLydia Wang };
5094f3db423dSLydia Wang #endif
5095f3db423dSLydia Wang 
50963e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
50973e95b9abSLydia Wang {
50983e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
50993e95b9abSLydia Wang 	int imux_is_smixer;
51003e95b9abSLydia Wang 	unsigned int parm;
51013e95b9abSLydia Wang 	unsigned int mono_out, present;
51023e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
51033e95b9abSLydia Wang 	imux_is_smixer =
51043e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
51053e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
51063e95b9abSLydia Wang 	/* inputs */
51073e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
51083e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
51103e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
51113e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
51123e95b9abSLydia Wang 	if (imux_is_smixer)
51133e95b9abSLydia Wang 		parm = AC_PWRST_D0;
51143e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
51153e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
51163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
51173e95b9abSLydia Wang 
51183e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
51203e95b9abSLydia Wang 	/* PW11 (22h) */
51213e95b9abSLydia Wang 	if (spec->dmic_enabled)
51223e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
51233e95b9abSLydia Wang 	else
51243e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
51253e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
51263e95b9abSLydia Wang 
51273e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
51283e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
51293e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
51303e95b9abSLydia Wang 
51313e95b9abSLydia Wang 	/* outputs */
51323e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
51333e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
51353e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
51363e95b9abSLydia Wang 	if (spec->smart51_enabled)
51373e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
51383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
51393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
51403e95b9abSLydia Wang 
51413e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
51423e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51433e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
51443e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
51453e95b9abSLydia Wang 	if (spec->smart51_enabled)
51463e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
51473e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
51483e95b9abSLydia Wang 
51493e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
51503e95b9abSLydia Wang 	if (spec->smart51_enabled)
51513e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
51523e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
51533e95b9abSLydia Wang 
51543e95b9abSLydia Wang 	/* Mono out */
51553e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
51563e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
51573e95b9abSLydia Wang 
51583e95b9abSLydia Wang 	if (present)
51593e95b9abSLydia Wang 		mono_out = 0;
51603e95b9abSLydia Wang 	else {
51613e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
51623e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
51633e95b9abSLydia Wang 			mono_out = 0;
51643e95b9abSLydia Wang 		else
51653e95b9abSLydia Wang 			mono_out = 1;
51663e95b9abSLydia Wang 	}
51673e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
51683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
51693e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
51703e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
51713e95b9abSLydia Wang 
51723e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
51733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
51753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
51763e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
51773e95b9abSLydia Wang 	if (spec->hp_independent_mode)
51783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
51793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
51803e95b9abSLydia Wang 
51813e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
51823e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
51833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
51843e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
51853e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
51863e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
51873e95b9abSLydia Wang }
51883e95b9abSLydia Wang 
5189f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5190f3db423dSLydia Wang {
5191f3db423dSLydia Wang 	struct via_spec *spec;
5192f3db423dSLydia Wang 	int err;
5193f3db423dSLydia Wang 
5194f3db423dSLydia Wang 	/* create a codec specific record */
51955b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5196f3db423dSLydia Wang 	if (spec == NULL)
5197f3db423dSLydia Wang 		return -ENOMEM;
5198f3db423dSLydia Wang 
5199f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5200f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5201f3db423dSLydia Wang 	if (err < 0) {
5202f3db423dSLydia Wang 		via_free(codec);
5203f3db423dSLydia Wang 		return err;
5204f3db423dSLydia Wang 	} else if (!err) {
5205f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5206f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5207f3db423dSLydia Wang 	}
5208f3db423dSLydia Wang 
5209f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5210f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5211f3db423dSLydia Wang 
5212f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5213f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5214f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5215f3db423dSLydia Wang 
5216f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5217f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5218f3db423dSLydia Wang 
5219f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5220f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5221f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5222f3db423dSLydia Wang 		get_mux_nids(codec);
5223f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5224f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5225f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5226f3db423dSLydia Wang 		spec->num_mixers++;
5227f3db423dSLydia Wang 	}
5228f3db423dSLydia Wang 
5229f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5230f3db423dSLydia Wang 	spec->num_mixers++;
5231f3db423dSLydia Wang 
5232f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5233f3db423dSLydia Wang 
5234f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5235f3db423dSLydia Wang 
5236f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
52370f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5238f3db423dSLydia Wang 
5239f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5240f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5241f3db423dSLydia Wang #endif
5242f3db423dSLydia Wang 
52433e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
5244f3db423dSLydia Wang 	return 0;
5245f3db423dSLydia Wang }
524625eaba2fSLydia Wang 
524725eaba2fSLydia Wang /* for vt2002P */
524825eaba2fSLydia Wang 
524925eaba2fSLydia Wang /* capture mixer elements */
525025eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
525125eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
525225eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
525325eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
525425eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
525525eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
525625eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
525725eaba2fSLydia Wang 			 HDA_INPUT),
525825eaba2fSLydia Wang 	{
525925eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
526025eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
526125eaba2fSLydia Wang 		 * So call somewhat different..
526225eaba2fSLydia Wang 		 */
526325eaba2fSLydia Wang 		/* .name = "Capture Source", */
526425eaba2fSLydia Wang 		.name = "Input Source",
526525eaba2fSLydia Wang 		.count = 2,
526625eaba2fSLydia Wang 		.info = via_mux_enum_info,
526725eaba2fSLydia Wang 		.get = via_mux_enum_get,
526825eaba2fSLydia Wang 		.put = via_mux_enum_put,
526925eaba2fSLydia Wang 	},
527025eaba2fSLydia Wang 	{ } /* end */
527125eaba2fSLydia Wang };
527225eaba2fSLydia Wang 
527325eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
5274eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
5275eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
5276eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
5277eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
527825eaba2fSLydia Wang 	/*
527925eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
528025eaba2fSLydia Wang 	 */
528125eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528225eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
528325eaba2fSLydia Wang 
528425eaba2fSLydia Wang 
528525eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
528625eaba2fSLydia Wang 	 * mixer widget
528725eaba2fSLydia Wang 	 */
528825eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
528925eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
529025eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
529125eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
529225eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
529325eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
529425eaba2fSLydia Wang 
529525eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
529625eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
529725eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
529825eaba2fSLydia Wang 
529925eaba2fSLydia Wang 	/* PW9 Output enable */
530025eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
530125eaba2fSLydia Wang 
530225eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
530325eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
530425eaba2fSLydia Wang 
530525eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
530625eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
530725eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
530825eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
530925eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
531025eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
531125eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
531225eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
531325eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
531425eaba2fSLydia Wang 
531525eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
531625eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
531725eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
531825eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
531925eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
532025eaba2fSLydia Wang 
532125eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
532225eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
532325eaba2fSLydia Wang 
532425eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
532525eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
532625eaba2fSLydia Wang 	{ }
532725eaba2fSLydia Wang };
532811890956SLydia Wang static struct hda_verb vt1802_volume_init_verbs[] = {
532911890956SLydia Wang 	/*
533011890956SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
533111890956SLydia Wang 	 */
533211890956SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533311890956SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
533411890956SLydia Wang 
533511890956SLydia Wang 
533611890956SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
533711890956SLydia Wang 	 * mixer widget
533811890956SLydia Wang 	 */
533911890956SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
534011890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
534111890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
534211890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
534311890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
534411890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
534511890956SLydia Wang 
534611890956SLydia Wang 	/* MUX Indices: Mic = 0 */
534711890956SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
534811890956SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
534911890956SLydia Wang 
535011890956SLydia Wang 	/* PW9 Output enable */
535111890956SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
535211890956SLydia Wang 
535311890956SLydia Wang 	/* Enable Boost Volume backdoor */
535411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
535511890956SLydia Wang 
535611890956SLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
535711890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
535811890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
535911890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
536011890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
536111890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
536211890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
536311890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
536411890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
536511890956SLydia Wang 
536611890956SLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
536711890956SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
536811890956SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
536911890956SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
537011890956SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
537111890956SLydia Wang 
537211890956SLydia Wang 	/* set PW0 index=0 (MW0) */
537311890956SLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
537411890956SLydia Wang 
537511890956SLydia Wang 	/* Enable AOW0 to MW9 */
537611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
537711890956SLydia Wang 	{ }
537811890956SLydia Wang };
537925eaba2fSLydia Wang 
538025eaba2fSLydia Wang 
538125eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
538225eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
538325eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
538425eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
538525eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
538625eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
538725eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
538825eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
538925eaba2fSLydia Wang 	{ }
539025eaba2fSLydia Wang };
539111890956SLydia Wang static struct hda_verb vt1802_uniwill_init_verbs[] = {
539211890956SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
539311890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
539411890956SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
539511890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
539611890956SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
539711890956SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
539811890956SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
539911890956SLydia Wang 	{ }
540011890956SLydia Wang };
540125eaba2fSLydia Wang 
540225eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
540325eaba2fSLydia Wang 	.substreams = 2,
540425eaba2fSLydia Wang 	.channels_min = 2,
540525eaba2fSLydia Wang 	.channels_max = 2,
540625eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
540725eaba2fSLydia Wang 	.ops = {
540825eaba2fSLydia Wang 		.open = via_playback_pcm_open,
540925eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
541025eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
541125eaba2fSLydia Wang 		.close = via_pcm_open_close,
541225eaba2fSLydia Wang 	},
541325eaba2fSLydia Wang };
541425eaba2fSLydia Wang 
541525eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
541625eaba2fSLydia Wang 	.substreams = 2,
541725eaba2fSLydia Wang 	.channels_min = 2,
541825eaba2fSLydia Wang 	.channels_max = 2,
541925eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
542025eaba2fSLydia Wang 	.ops = {
542125eaba2fSLydia Wang 		.open = via_pcm_open_close,
542225eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
542325eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
542425eaba2fSLydia Wang 		.close = via_pcm_open_close,
542525eaba2fSLydia Wang 	},
542625eaba2fSLydia Wang };
542725eaba2fSLydia Wang 
542825eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
542925eaba2fSLydia Wang 	.substreams = 1,
543025eaba2fSLydia Wang 	.channels_min = 2,
543125eaba2fSLydia Wang 	.channels_max = 2,
543225eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
543325eaba2fSLydia Wang 	.ops = {
543425eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
543525eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
543625eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
543725eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
543825eaba2fSLydia Wang 	},
543925eaba2fSLydia Wang };
544025eaba2fSLydia Wang 
544125eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
544225eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
544325eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
544425eaba2fSLydia Wang {
544525eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
544625eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
544725eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
544825eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
544925eaba2fSLydia Wang 	return 0;
545025eaba2fSLydia Wang }
545125eaba2fSLydia Wang 
545225eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
545325eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
545425eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
545525eaba2fSLydia Wang {
545625eaba2fSLydia Wang 	int err;
545711890956SLydia Wang 	hda_nid_t sw_nid;
545825eaba2fSLydia Wang 
545925eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
546025eaba2fSLydia Wang 		return -1;
546125eaba2fSLydia Wang 
546211890956SLydia Wang 	if (spec->codec_type == VT1802)
546311890956SLydia Wang 		sw_nid = 0x28;
546411890956SLydia Wang 	else
546511890956SLydia Wang 		sw_nid = 0x26;
546625eaba2fSLydia Wang 
546725eaba2fSLydia Wang 	/* Line-Out: PortE */
546825eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
546925eaba2fSLydia Wang 			      "Master Front Playback Volume",
547025eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
547125eaba2fSLydia Wang 	if (err < 0)
547225eaba2fSLydia Wang 		return err;
547325eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
547425eaba2fSLydia Wang 			      "Master Front Playback Switch",
547511890956SLydia Wang 			      HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT));
547625eaba2fSLydia Wang 	if (err < 0)
547725eaba2fSLydia Wang 		return err;
547825eaba2fSLydia Wang 
547925eaba2fSLydia Wang 	return 0;
548025eaba2fSLydia Wang }
548125eaba2fSLydia Wang 
548225eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
548325eaba2fSLydia Wang {
548425eaba2fSLydia Wang 	int err;
548525eaba2fSLydia Wang 
548625eaba2fSLydia Wang 	if (!pin)
548725eaba2fSLydia Wang 		return 0;
548825eaba2fSLydia Wang 
548925eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
549025eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
549125eaba2fSLydia Wang 
549225eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
549325eaba2fSLydia Wang 			      "Headphone Playback Volume",
549425eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
549525eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
549625eaba2fSLydia Wang 	if (err < 0)
549725eaba2fSLydia Wang 		return err;
549825eaba2fSLydia Wang 
549925eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
550025eaba2fSLydia Wang 			      "Headphone Playback Switch",
550125eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
550225eaba2fSLydia Wang 	if (err < 0)
550325eaba2fSLydia Wang 		return err;
550425eaba2fSLydia Wang 
550525eaba2fSLydia Wang 	create_hp_imux(spec);
550625eaba2fSLydia Wang 	return 0;
550725eaba2fSLydia Wang }
550825eaba2fSLydia Wang 
550925eaba2fSLydia Wang /* create playback/capture controls for input pins */
551010a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
551125eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
551225eaba2fSLydia Wang {
551310a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
551425eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5515f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
5516f3268512STakashi Iwai 	int err;
551725eaba2fSLydia Wang 
551810a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5519f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
552025eaba2fSLydia Wang 	if (err < 0)
552125eaba2fSLydia Wang 		return err;
552225eaba2fSLydia Wang 	/* build volume/mute control of loopback */
55237b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
552425eaba2fSLydia Wang 	if (err < 0)
552525eaba2fSLydia Wang 		return err;
552625eaba2fSLydia Wang 
552725eaba2fSLydia Wang 	/* for digital mic select */
552810a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
552925eaba2fSLydia Wang 
553025eaba2fSLydia Wang 	return 0;
553125eaba2fSLydia Wang }
553225eaba2fSLydia Wang 
553325eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
553425eaba2fSLydia Wang {
553525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
553625eaba2fSLydia Wang 	int err;
553725eaba2fSLydia Wang 
553825eaba2fSLydia Wang 
553925eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
554025eaba2fSLydia Wang 	if (err < 0)
554125eaba2fSLydia Wang 		return err;
554225eaba2fSLydia Wang 
554325eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
554425eaba2fSLydia Wang 	if (err < 0)
554525eaba2fSLydia Wang 		return err;
554625eaba2fSLydia Wang 
554725eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
554825eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
554925eaba2fSLydia Wang 
555025eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
555125eaba2fSLydia Wang 	if (err < 0)
555225eaba2fSLydia Wang 		return err;
555325eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
555425eaba2fSLydia Wang 	if (err < 0)
555525eaba2fSLydia Wang 		return err;
555610a20af7STakashi Iwai 	err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
555725eaba2fSLydia Wang 	if (err < 0)
555825eaba2fSLydia Wang 		return err;
555925eaba2fSLydia Wang 
556025eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
556125eaba2fSLydia Wang 
556225eaba2fSLydia Wang 	fill_dig_outs(codec);
556325eaba2fSLydia Wang 
556425eaba2fSLydia Wang 	if (spec->kctls.list)
556525eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
556625eaba2fSLydia Wang 
556725eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
556825eaba2fSLydia Wang 
556925eaba2fSLydia Wang 	if (spec->hp_mux)
55703d83e577STakashi Iwai 		via_hp_build(codec);
557125eaba2fSLydia Wang 
557225eaba2fSLydia Wang 	return 1;
557325eaba2fSLydia Wang }
557425eaba2fSLydia Wang 
557525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
557625eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
557725eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
557825eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
557925eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
558025eaba2fSLydia Wang 	{ } /* end */
558125eaba2fSLydia Wang };
558225eaba2fSLydia Wang #endif
558325eaba2fSLydia Wang 
55843e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
55853e95b9abSLydia Wang {
55863e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
55873e95b9abSLydia Wang 	int imux_is_smixer;
55883e95b9abSLydia Wang 	unsigned int parm;
55893e95b9abSLydia Wang 	unsigned int present;
55903e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
55913e95b9abSLydia Wang 	imux_is_smixer =
55923e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
55933e95b9abSLydia Wang 	/* inputs */
55943e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
55953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
55963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
55973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
55983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
55993e95b9abSLydia Wang 	parm = AC_PWRST_D0;
56003e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
56013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
56023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
56033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
56043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
56053e95b9abSLydia Wang 
56063e95b9abSLydia Wang 	/* outputs */
56073e95b9abSLydia Wang 	/* AOW0 (8h)*/
56083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
56093e95b9abSLydia Wang 
561011890956SLydia Wang 	if (spec->codec_type == VT1802) {
561111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
561211890956SLydia Wang 		parm = AC_PWRST_D3;
561311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
561411890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
561511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
561611890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
561711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
561811890956SLydia Wang 	} else {
56193e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
56203e95b9abSLydia Wang 		parm = AC_PWRST_D3;
56213e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
56223e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
56233e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56243e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
56253e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
562611890956SLydia Wang 	}
56273e95b9abSLydia Wang 
562811890956SLydia Wang 	if (spec->codec_type == VT1802) {
562911890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
563011890956SLydia Wang 		parm = AC_PWRST_D3;
563111890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
563211890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
563311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
563411890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
563511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
563611890956SLydia Wang 	} else {
56373e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
56383e95b9abSLydia Wang 		parm = AC_PWRST_D3;
56393e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
56403e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
56413e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56423e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
56433e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
564411890956SLydia Wang 	}
56453e95b9abSLydia Wang 
56463e95b9abSLydia Wang 	if (spec->hp_independent_mode)
56473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
56483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
56493e95b9abSLydia Wang 
56503e95b9abSLydia Wang 	/* Class-D */
56513e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
56523e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
56533e95b9abSLydia Wang 
56543e95b9abSLydia Wang 	parm = AC_PWRST_D3;
56553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
56563e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
565711890956SLydia Wang 	if (spec->codec_type == VT1802)
565811890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
565911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
566011890956SLydia Wang 	else
56613e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
56623e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56633e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
56643e95b9abSLydia Wang 
56653e95b9abSLydia Wang 	/* Mono Out */
56663e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
56673e95b9abSLydia Wang 
56683e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
566911890956SLydia Wang 	if (spec->codec_type == VT1802) {
567011890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
567111890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
567211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
567311890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
567411890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
567511890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
567611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
567711890956SLydia Wang 	} else {
56783e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
56793e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
56803e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56813e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
56823e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
56833e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
56843e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
568511890956SLydia Wang 	}
56863e95b9abSLydia Wang 	/* MW9 (21h) */
56873e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
56883e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
56893e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
56903e95b9abSLydia Wang 	else
56913e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
56923e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
56933e95b9abSLydia Wang }
569425eaba2fSLydia Wang 
569525eaba2fSLydia Wang /* patch for vt2002P */
569625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
569725eaba2fSLydia Wang {
569825eaba2fSLydia Wang 	struct via_spec *spec;
569925eaba2fSLydia Wang 	int err;
570025eaba2fSLydia Wang 
570125eaba2fSLydia Wang 	/* create a codec specific record */
57025b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
570325eaba2fSLydia Wang 	if (spec == NULL)
570425eaba2fSLydia Wang 		return -ENOMEM;
570525eaba2fSLydia Wang 
570625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
570725eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
570825eaba2fSLydia Wang 	if (err < 0) {
570925eaba2fSLydia Wang 		via_free(codec);
571025eaba2fSLydia Wang 		return err;
571125eaba2fSLydia Wang 	} else if (!err) {
571225eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
571325eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
571425eaba2fSLydia Wang 	}
571525eaba2fSLydia Wang 
571611890956SLydia Wang 	if (spec->codec_type == VT1802)
571711890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
571811890956SLydia Wang 			vt1802_volume_init_verbs;
571911890956SLydia Wang 	else
572011890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
572111890956SLydia Wang 			vt2002P_volume_init_verbs;
572225eaba2fSLydia Wang 
572311890956SLydia Wang 	if (spec->codec_type == VT1802)
572411890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
572511890956SLydia Wang 			vt1802_uniwill_init_verbs;
572611890956SLydia Wang 	else
572711890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
572811890956SLydia Wang 			vt2002P_uniwill_init_verbs;
572911890956SLydia Wang 
573011890956SLydia Wang 	if (spec->codec_type == VT1802)
573111890956SLydia Wang 		spec->stream_name_analog = "VT1802 Analog";
573211890956SLydia Wang 	else
573325eaba2fSLydia Wang 		spec->stream_name_analog = "VT2002P Analog";
573425eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
573525eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
573625eaba2fSLydia Wang 
573711890956SLydia Wang 	if (spec->codec_type == VT1802)
573811890956SLydia Wang 		spec->stream_name_digital = "VT1802 Digital";
573911890956SLydia Wang 	else
574025eaba2fSLydia Wang 		spec->stream_name_digital = "VT2002P Digital";
574125eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
574225eaba2fSLydia Wang 
574325eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
574425eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
574525eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
574625eaba2fSLydia Wang 		get_mux_nids(codec);
574725eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
574825eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
574925eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
575025eaba2fSLydia Wang 		spec->num_mixers++;
575125eaba2fSLydia Wang 	}
575225eaba2fSLydia Wang 
575325eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
575425eaba2fSLydia Wang 
575525eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
57560f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
575725eaba2fSLydia Wang 
575825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
575925eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
576025eaba2fSLydia Wang #endif
576125eaba2fSLydia Wang 
57623e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
576325eaba2fSLydia Wang 	return 0;
576425eaba2fSLydia Wang }
5765ab6734e7SLydia Wang 
5766ab6734e7SLydia Wang /* for vt1812 */
5767ab6734e7SLydia Wang 
5768ab6734e7SLydia Wang /* capture mixer elements */
5769ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5770ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5771ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5772ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5773ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5774ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5775ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5776ab6734e7SLydia Wang 		       HDA_INPUT),
5777ab6734e7SLydia Wang 	{
5778ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5779ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5780ab6734e7SLydia Wang 		 * So call somewhat different..
5781ab6734e7SLydia Wang 		 */
5782ab6734e7SLydia Wang 		.name = "Input Source",
5783ab6734e7SLydia Wang 		.count = 2,
5784ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5785ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5786ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5787ab6734e7SLydia Wang 	},
5788ab6734e7SLydia Wang 	{ } /* end */
5789ab6734e7SLydia Wang };
5790ab6734e7SLydia Wang 
5791ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = {
5792ab6734e7SLydia Wang 	/*
5793ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5794ab6734e7SLydia Wang 	 */
5795ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5796ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5797ab6734e7SLydia Wang 
5798ab6734e7SLydia Wang 
5799ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5800ab6734e7SLydia Wang 	 * mixer widget
5801ab6734e7SLydia Wang 	 */
5802ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5803ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5804ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5805ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5806ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5807ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5808ab6734e7SLydia Wang 
5809ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5810ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5811ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5812ab6734e7SLydia Wang 
5813ab6734e7SLydia Wang 	/* PW9 Output enable */
5814ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5815ab6734e7SLydia Wang 
5816ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5817ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5818ab6734e7SLydia Wang 
5819ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5820ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5821ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5822ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5823ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5824ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5825ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5826ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5827ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5828ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5829ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5830ab6734e7SLydia Wang 
5831ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5832ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5833ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5834ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5835ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5836ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5837ab6734e7SLydia Wang 
5838ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5839ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5840ab6734e7SLydia Wang 	{ }
5841ab6734e7SLydia Wang };
5842ab6734e7SLydia Wang 
5843ab6734e7SLydia Wang 
5844ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = {
5845ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5846ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5847ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5848ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5849ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5850ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5851ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5852ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5853ab6734e7SLydia Wang 	{ }
5854ab6734e7SLydia Wang };
5855ab6734e7SLydia Wang 
5856ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5857ab6734e7SLydia Wang 	.substreams = 2,
5858ab6734e7SLydia Wang 	.channels_min = 2,
5859ab6734e7SLydia Wang 	.channels_max = 2,
5860ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5861ab6734e7SLydia Wang 	.ops = {
5862ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5863ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5864ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5865ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5866ab6734e7SLydia Wang 	},
5867ab6734e7SLydia Wang };
5868ab6734e7SLydia Wang 
5869ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5870ab6734e7SLydia Wang 	.substreams = 2,
5871ab6734e7SLydia Wang 	.channels_min = 2,
5872ab6734e7SLydia Wang 	.channels_max = 2,
5873ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5874ab6734e7SLydia Wang 	.ops = {
5875ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5876ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5877ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5878ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5879ab6734e7SLydia Wang 	},
5880ab6734e7SLydia Wang };
5881ab6734e7SLydia Wang 
5882ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5883ab6734e7SLydia Wang 	.substreams = 1,
5884ab6734e7SLydia Wang 	.channels_min = 2,
5885ab6734e7SLydia Wang 	.channels_max = 2,
5886ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5887ab6734e7SLydia Wang 	.ops = {
5888ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5889ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5890ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5891ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5892ab6734e7SLydia Wang 	},
5893ab6734e7SLydia Wang };
5894ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5895ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5896ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5897ab6734e7SLydia Wang {
5898ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5899ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5900ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5901ab6734e7SLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5902ab6734e7SLydia Wang 	return 0;
5903ab6734e7SLydia Wang }
5904ab6734e7SLydia Wang 
5905ab6734e7SLydia Wang 
5906ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5907ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5908ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5909ab6734e7SLydia Wang {
5910ab6734e7SLydia Wang 	int err;
5911ab6734e7SLydia Wang 
5912ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
5913ab6734e7SLydia Wang 		return -1;
5914ab6734e7SLydia Wang 
5915ab6734e7SLydia Wang 	/* Line-Out: PortE */
5916ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
59173d83e577STakashi Iwai 			      "Front Playback Volume",
5918ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5919ab6734e7SLydia Wang 	if (err < 0)
5920ab6734e7SLydia Wang 		return err;
5921ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
59223d83e577STakashi Iwai 			      "Front Playback Switch",
5923ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5924ab6734e7SLydia Wang 	if (err < 0)
5925ab6734e7SLydia Wang 		return err;
5926ab6734e7SLydia Wang 
5927ab6734e7SLydia Wang 	return 0;
5928ab6734e7SLydia Wang }
5929ab6734e7SLydia Wang 
5930ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5931ab6734e7SLydia Wang {
5932ab6734e7SLydia Wang 	int err;
5933ab6734e7SLydia Wang 
5934ab6734e7SLydia Wang 	if (!pin)
5935ab6734e7SLydia Wang 		return 0;
5936ab6734e7SLydia Wang 
5937ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
5938ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
5939ab6734e7SLydia Wang 
5940ab6734e7SLydia Wang 
5941ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5942ab6734e7SLydia Wang 			      "Headphone Playback Volume",
5943ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5944ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5945ab6734e7SLydia Wang 	if (err < 0)
5946ab6734e7SLydia Wang 		return err;
5947ab6734e7SLydia Wang 
5948ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5949ab6734e7SLydia Wang 			      "Headphone Playback Switch",
5950ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5951ab6734e7SLydia Wang 	if (err < 0)
5952ab6734e7SLydia Wang 		return err;
5953ab6734e7SLydia Wang 
5954ab6734e7SLydia Wang 	create_hp_imux(spec);
5955ab6734e7SLydia Wang 	return 0;
5956ab6734e7SLydia Wang }
5957ab6734e7SLydia Wang 
5958ab6734e7SLydia Wang /* create playback/capture controls for input pins */
595910a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
5960ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
5961ab6734e7SLydia Wang {
596210a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
5963ab6734e7SLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5964f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
5965f3268512STakashi Iwai 	int err;
5966ab6734e7SLydia Wang 
596710a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5968f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
5969ab6734e7SLydia Wang 	if (err < 0)
5970ab6734e7SLydia Wang 		return err;
5971f3268512STakashi Iwai 
5972ab6734e7SLydia Wang 	/* build volume/mute control of loopback */
59737b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
5974ab6734e7SLydia Wang 	if (err < 0)
5975ab6734e7SLydia Wang 		return err;
5976ab6734e7SLydia Wang 
5977ab6734e7SLydia Wang 	/* for digital mic select */
597810a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
5979ab6734e7SLydia Wang 
5980ab6734e7SLydia Wang 	return 0;
5981ab6734e7SLydia Wang }
5982ab6734e7SLydia Wang 
5983ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
5984ab6734e7SLydia Wang {
5985ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
5986ab6734e7SLydia Wang 	int err;
5987ab6734e7SLydia Wang 
5988ab6734e7SLydia Wang 
5989ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5990ab6734e7SLydia Wang 	if (err < 0)
5991ab6734e7SLydia Wang 		return err;
5992ab6734e7SLydia Wang 	fill_dig_outs(codec);
5993ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
5994ab6734e7SLydia Wang 	if (err < 0)
5995ab6734e7SLydia Wang 		return err;
5996ab6734e7SLydia Wang 
5997ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
5998ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
5999ab6734e7SLydia Wang 
6000ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
6001ab6734e7SLydia Wang 	if (err < 0)
6002ab6734e7SLydia Wang 		return err;
6003ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
6004ab6734e7SLydia Wang 	if (err < 0)
6005ab6734e7SLydia Wang 		return err;
600610a20af7STakashi Iwai 	err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
6007ab6734e7SLydia Wang 	if (err < 0)
6008ab6734e7SLydia Wang 		return err;
6009ab6734e7SLydia Wang 
6010ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
6011ab6734e7SLydia Wang 
6012ab6734e7SLydia Wang 	fill_dig_outs(codec);
6013ab6734e7SLydia Wang 
6014ab6734e7SLydia Wang 	if (spec->kctls.list)
6015ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
6016ab6734e7SLydia Wang 
6017ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
6018ab6734e7SLydia Wang 
6019ab6734e7SLydia Wang 	if (spec->hp_mux)
60203d83e577STakashi Iwai 		via_hp_build(codec);
6021ab6734e7SLydia Wang 
6022ab6734e7SLydia Wang 	return 1;
6023ab6734e7SLydia Wang }
6024ab6734e7SLydia Wang 
6025ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6026ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = {
6027ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
6028ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
6029ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
6030ab6734e7SLydia Wang 	{ } /* end */
6031ab6734e7SLydia Wang };
6032ab6734e7SLydia Wang #endif
6033ab6734e7SLydia Wang 
60343e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
60353e95b9abSLydia Wang {
60363e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
60373e95b9abSLydia Wang 	int imux_is_smixer =
60383e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
60393e95b9abSLydia Wang 	unsigned int parm;
60403e95b9abSLydia Wang 	unsigned int present;
60413e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
60423e95b9abSLydia Wang 	imux_is_smixer =
60433e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
60443e95b9abSLydia Wang 	/* inputs */
60453e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
60463e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60473e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
60483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
60493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
60503e95b9abSLydia Wang 	parm = AC_PWRST_D0;
60513e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
60523e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
60533e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
60543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
60553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
60563e95b9abSLydia Wang 
60573e95b9abSLydia Wang 	/* outputs */
60583e95b9abSLydia Wang 	/* AOW0 (8h)*/
60593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
60603e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60613e95b9abSLydia Wang 
60623e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
60633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
60653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
60663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
60673e95b9abSLydia Wang 
60683e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
60693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
60713e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
60723e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
60733e95b9abSLydia Wang 	if (spec->hp_independent_mode)
60743e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
60753e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60763e95b9abSLydia Wang 
60773e95b9abSLydia Wang 	/* Internal Speaker */
60783e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
60793e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
60803e95b9abSLydia Wang 
60813e95b9abSLydia Wang 	parm = AC_PWRST_D3;
60823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
60833e95b9abSLydia Wang 	if (present) {
60843e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
60853e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60863e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
60873e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
60883e95b9abSLydia Wang 	} else {
60893e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
60903e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60913e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
60923e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
60933e95b9abSLydia Wang 	}
60943e95b9abSLydia Wang 
60953e95b9abSLydia Wang 
60963e95b9abSLydia Wang 	/* Mono Out */
60973e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
60983e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
60993e95b9abSLydia Wang 
61003e95b9abSLydia Wang 	parm = AC_PWRST_D3;
61013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
61023e95b9abSLydia Wang 	if (present) {
61033e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
61043e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
61053e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
61063e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
61073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
61083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
61093e95b9abSLydia Wang 	} else {
61103e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
61113e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
61123e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
61133e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
61143e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
61153e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
61163e95b9abSLydia Wang 	}
61173e95b9abSLydia Wang 
61183e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
61193e95b9abSLydia Wang 	parm = AC_PWRST_D3;
61203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
61213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
61223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
61233e95b9abSLydia Wang 
61243e95b9abSLydia Wang }
6125ab6734e7SLydia Wang 
6126ab6734e7SLydia Wang /* patch for vt1812 */
6127ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
6128ab6734e7SLydia Wang {
6129ab6734e7SLydia Wang 	struct via_spec *spec;
6130ab6734e7SLydia Wang 	int err;
6131ab6734e7SLydia Wang 
6132ab6734e7SLydia Wang 	/* create a codec specific record */
61335b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
6134ab6734e7SLydia Wang 	if (spec == NULL)
6135ab6734e7SLydia Wang 		return -ENOMEM;
6136ab6734e7SLydia Wang 
6137ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
6138ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
6139ab6734e7SLydia Wang 	if (err < 0) {
6140ab6734e7SLydia Wang 		via_free(codec);
6141ab6734e7SLydia Wang 		return err;
6142ab6734e7SLydia Wang 	} else if (!err) {
6143ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
6144ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
6145ab6734e7SLydia Wang 	}
6146ab6734e7SLydia Wang 
6147ab6734e7SLydia Wang 
6148ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
6149ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6150ab6734e7SLydia Wang 
6151ab6734e7SLydia Wang 	spec->stream_name_analog = "VT1812 Analog";
6152ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6153ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6154ab6734e7SLydia Wang 
6155ab6734e7SLydia Wang 	spec->stream_name_digital = "VT1812 Digital";
6156ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6157ab6734e7SLydia Wang 
6158ab6734e7SLydia Wang 
6159ab6734e7SLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
6160ab6734e7SLydia Wang 		spec->adc_nids = vt1812_adc_nids;
6161ab6734e7SLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6162ab6734e7SLydia Wang 		get_mux_nids(codec);
6163ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
6164ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
6165ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6166ab6734e7SLydia Wang 		spec->num_mixers++;
6167ab6734e7SLydia Wang 	}
6168ab6734e7SLydia Wang 
6169ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
6170ab6734e7SLydia Wang 
6171ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
61720f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
6173ab6734e7SLydia Wang 
6174ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6175ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
6176ab6734e7SLydia Wang #endif
6177ab6734e7SLydia Wang 
61783e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
6179ab6734e7SLydia Wang 	return 0;
6180ab6734e7SLydia Wang }
6181ab6734e7SLydia Wang 
6182c577b8a1SJoseph Chan /*
6183c577b8a1SJoseph Chan  * patch entries
6184c577b8a1SJoseph Chan  */
61851289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
61863218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
61873218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
61883218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
61893218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
61903218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
6191f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61923218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
6193f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61943218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
6195f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61963218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
6197f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
61983218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
6199f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62003218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
6201f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62023218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
6203f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62043218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
6205f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
62063218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
6207f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62083218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
6209f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62103218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
6211f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62123218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6213f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
62143218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6215f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62163218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6217f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62183218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6219f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62203218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6221f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
62223218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6223d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62243218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6225d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62263218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6227d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62283218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6229d949cac1SHarald Welte 	  .patch = patch_vt1708S},
6230bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
6231d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62323218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6233d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62343218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6235d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62363218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6237d949cac1SHarald Welte 	  .patch = patch_vt1708S},
62383218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6239d949cac1SHarald Welte 	  .patch = patch_vt1702},
62403218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6241d949cac1SHarald Welte 	  .patch = patch_vt1702},
62423218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6243d949cac1SHarald Welte 	  .patch = patch_vt1702},
62443218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6245d949cac1SHarald Welte 	  .patch = patch_vt1702},
62463218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6247d949cac1SHarald Welte 	  .patch = patch_vt1702},
62483218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6249d949cac1SHarald Welte 	  .patch = patch_vt1702},
62503218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6251d949cac1SHarald Welte 	  .patch = patch_vt1702},
62523218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6253d949cac1SHarald Welte 	  .patch = patch_vt1702},
6254eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6255eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6256eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6257eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6258bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6259bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6260bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6261bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6262f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6263f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6264f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6265f3db423dSLydia Wang 	  .patch = patch_vt1716S},
626625eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
626725eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6268ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
626936dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
627036dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
627111890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
627211890956SLydia Wang 		.patch = patch_vt2002P},
627311890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
627411890956SLydia Wang 		.patch = patch_vt2002P},
6275c577b8a1SJoseph Chan 	{} /* terminator */
6276c577b8a1SJoseph Chan };
62771289e9e8STakashi Iwai 
62781289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
62791289e9e8STakashi Iwai 
62801289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
62811289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
62821289e9e8STakashi Iwai 	.owner = THIS_MODULE,
62831289e9e8STakashi Iwai };
62841289e9e8STakashi Iwai 
62851289e9e8STakashi Iwai MODULE_LICENSE("GPL");
62861289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
62871289e9e8STakashi Iwai 
62881289e9e8STakashi Iwai static int __init patch_via_init(void)
62891289e9e8STakashi Iwai {
62901289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
62911289e9e8STakashi Iwai }
62921289e9e8STakashi Iwai 
62931289e9e8STakashi Iwai static void __exit patch_via_exit(void)
62941289e9e8STakashi Iwai {
62951289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
62961289e9e8STakashi Iwai }
62971289e9e8STakashi Iwai 
62981289e9e8STakashi Iwai module_init(patch_via_init)
62991289e9e8STakashi Iwai module_exit(patch_via_exit)
6300