xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision ce0e5a9e81fbb153ee15ca60246c6722f07fc546)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
575b0cb1d8SJaroslav Kysela #define NID_MAPPING		(-1)
585b0cb1d8SJaroslav Kysela 
59c577b8a1SJoseph Chan /* amp values */
60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan /* Pin Widget NID */
64c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
70c577b8a1SJoseph Chan 
71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
75f7278fd0SJosepch Chan 
76f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
80c577b8a1SJoseph Chan 
81d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
83d949cac1SHarald Welte 
84d949cac1SHarald Welte #define VT1702_HP_NID		0x17
85d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
86d949cac1SHarald Welte 
87d7426329SHarald Welte enum VIA_HDA_CODEC {
88d7426329SHarald Welte 	UNKNOWN = -1,
89d7426329SHarald Welte 	VT1708,
90d7426329SHarald Welte 	VT1709_10CH,
91d7426329SHarald Welte 	VT1709_6CH,
92d7426329SHarald Welte 	VT1708B_8CH,
93d7426329SHarald Welte 	VT1708B_4CH,
94d7426329SHarald Welte 	VT1708S,
95518bf3baSLydia Wang 	VT1708BCE,
96d7426329SHarald Welte 	VT1702,
97eb7188caSLydia Wang 	VT1718S,
98f3db423dSLydia Wang 	VT1716S,
9925eaba2fSLydia Wang 	VT2002P,
100ab6734e7SLydia Wang 	VT1812,
101d7426329SHarald Welte 	CODEC_TYPES,
102d7426329SHarald Welte };
103d7426329SHarald Welte 
1041f2e99feSLydia Wang struct via_spec {
1051f2e99feSLydia Wang 	/* codec parameterization */
106f3db423dSLydia Wang 	struct snd_kcontrol_new *mixers[6];
1071f2e99feSLydia Wang 	unsigned int num_mixers;
1081f2e99feSLydia Wang 
1091f2e99feSLydia Wang 	struct hda_verb *init_verbs[5];
1101f2e99feSLydia Wang 	unsigned int num_iverbs;
1111f2e99feSLydia Wang 
1121f2e99feSLydia Wang 	char *stream_name_analog;
1131f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_playback;
1141f2e99feSLydia Wang 	struct hda_pcm_stream *stream_analog_capture;
1151f2e99feSLydia Wang 
1161f2e99feSLydia Wang 	char *stream_name_digital;
1171f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_playback;
1181f2e99feSLydia Wang 	struct hda_pcm_stream *stream_digital_capture;
1191f2e99feSLydia Wang 
1201f2e99feSLydia Wang 	/* playback */
1211f2e99feSLydia Wang 	struct hda_multi_out multiout;
1221f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1231f2e99feSLydia Wang 
1241f2e99feSLydia Wang 	/* capture */
1251f2e99feSLydia Wang 	unsigned int num_adc_nids;
1261f2e99feSLydia Wang 	hda_nid_t *adc_nids;
1271f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
1281f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1291f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1301f2e99feSLydia Wang 
1311f2e99feSLydia Wang 	/* capture source */
1321f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1331f2e99feSLydia Wang 	unsigned int cur_mux[3];
1341f2e99feSLydia Wang 
1351f2e99feSLydia Wang 	/* PCM information */
1361f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1371f2e99feSLydia Wang 
1381f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1391f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1401f2e99feSLydia Wang 	struct snd_array kctls;
1411f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1421f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1431f2e99feSLydia Wang 
1441f2e99feSLydia Wang 	/* HP mode source */
1451f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1461f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1471f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
1481f2e99feSLydia Wang 	unsigned int smart51_enabled;
149f3db423dSLydia Wang 	unsigned int dmic_enabled;
1501f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1511f2e99feSLydia Wang 
1521f2e99feSLydia Wang 	/* work to check hp jack state */
1531f2e99feSLydia Wang 	struct hda_codec *codec;
1541f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
1551f2e99feSLydia Wang 	int vt1708_jack_detectect;
1561f2e99feSLydia Wang 	int vt1708_hp_present;
1571f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1581f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1591f2e99feSLydia Wang #endif
1601f2e99feSLydia Wang };
1611f2e99feSLydia Wang 
1625b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1635b0cb1d8SJaroslav Kysela {
1645b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1655b0cb1d8SJaroslav Kysela 
1665b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1675b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1685b0cb1d8SJaroslav Kysela 		return NULL;
1695b0cb1d8SJaroslav Kysela 
1705b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1715b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1725b0cb1d8SJaroslav Kysela 	return spec;
1735b0cb1d8SJaroslav Kysela }
1745b0cb1d8SJaroslav Kysela 
175744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
176d7426329SHarald Welte {
177744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
178d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
179d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
180d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
181d7426329SHarald Welte 
182d7426329SHarald Welte 	/* get codec type */
183d7426329SHarald Welte 	if (ven_id != 0x1106)
184d7426329SHarald Welte 		codec_type = UNKNOWN;
185d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
186d7426329SHarald Welte 		codec_type = VT1708;
187d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
188d7426329SHarald Welte 		codec_type = VT1709_10CH;
189d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
190d7426329SHarald Welte 		codec_type = VT1709_6CH;
191518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
192d7426329SHarald Welte 		codec_type = VT1708B_8CH;
193518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
194518bf3baSLydia Wang 			codec_type = VT1708BCE;
195518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
196d7426329SHarald Welte 		codec_type = VT1708B_4CH;
197d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
198d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
199d7426329SHarald Welte 		codec_type = VT1708S;
200d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
201d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
202d7426329SHarald Welte 		codec_type = VT1702;
203eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
204eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
205eb7188caSLydia Wang 		codec_type = VT1718S;
206f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
207f3db423dSLydia Wang 		codec_type = VT1716S;
208bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
209bb3c6bfcSLydia Wang 		codec_type = VT1718S;
21025eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
21125eaba2fSLydia Wang 		codec_type = VT2002P;
212ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
213ab6734e7SLydia Wang 		codec_type = VT1812;
21436dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
21536dd5c4aSLydia Wang 		codec_type = VT1708S;
216d7426329SHarald Welte 	else
217d7426329SHarald Welte 		codec_type = UNKNOWN;
218d7426329SHarald Welte 	return codec_type;
219d7426329SHarald Welte };
220d7426329SHarald Welte 
22169e52a80SHarald Welte #define VIA_HP_EVENT		0x01
22269e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
223a34df19aSLydia Wang #define VIA_JACK_EVENT		0x04
224f3db423dSLydia Wang #define VIA_MONO_EVENT		0x08
22525eaba2fSLydia Wang #define VIA_SPEAKER_EVENT	0x10
22625eaba2fSLydia Wang #define VIA_BIND_HP_EVENT	0x20
22769e52a80SHarald Welte 
228c577b8a1SJoseph Chan enum {
229c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
230c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
231f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
23225eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
233c577b8a1SJoseph Chan };
234c577b8a1SJoseph Chan 
235c577b8a1SJoseph Chan enum {
236eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
237c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
238c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
239c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
240c577b8a1SJoseph Chan };
241c577b8a1SJoseph Chan 
242f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
243f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec);
2441f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2451f2e99feSLydia Wang 
2461f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2471f2e99feSLydia Wang {
2481f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2491f2e99feSLydia Wang 		return;
2501f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2511f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2521f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2531f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2541f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2551f2e99feSLydia Wang }
2561f2e99feSLydia Wang 
2571f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2581f2e99feSLydia Wang {
2591f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2601f2e99feSLydia Wang 		return;
2611f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2621f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2631f2e99feSLydia Wang 		return;
2641f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2651f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2665b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2671f2e99feSLydia Wang }
268f5271101SLydia Wang 
26925eaba2fSLydia Wang 
270f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
271f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
272f5271101SLydia Wang {
273f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
274f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
275f5271101SLydia Wang 
276f5271101SLydia Wang 	set_jack_power_state(codec);
277f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2781f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2791f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2801f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2811f2e99feSLydia Wang 		else
2821f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2831f2e99feSLydia Wang 	}
284f5271101SLydia Wang 	return change;
285f5271101SLydia Wang }
286f5271101SLydia Wang 
287f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
288f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
289f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
290f5271101SLydia Wang 			.name = NULL,					\
291f5271101SLydia Wang 			.index = 0,					\
292f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
293f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
294f5271101SLydia Wang 			.put = analog_input_switch_put,			\
295f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
296f5271101SLydia Wang 
29725eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
29825eaba2fSLydia Wang 
29925eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
30025eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
30125eaba2fSLydia Wang {
30225eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
30325eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
30425eaba2fSLydia Wang 	int i;
30525eaba2fSLydia Wang 	int change = 0;
30625eaba2fSLydia Wang 
30725eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
30825eaba2fSLydia Wang 	int lmute, rmute;
30925eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
31025eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
31125eaba2fSLydia Wang 		return change;
31225eaba2fSLydia Wang 	}
31325eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
31425eaba2fSLydia Wang 					      ucontrol);
31525eaba2fSLydia Wang 	/* Get mute value */
31625eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
31725eaba2fSLydia Wang 	valp++;
31825eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
31925eaba2fSLydia Wang 
32025eaba2fSLydia Wang 	/* Set hp pins */
32125eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
32225eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
32325eaba2fSLydia Wang 			snd_hda_codec_amp_update(
32425eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
32525eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
32625eaba2fSLydia Wang 				lmute);
32725eaba2fSLydia Wang 			snd_hda_codec_amp_update(
32825eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
32925eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
33025eaba2fSLydia Wang 				rmute);
33125eaba2fSLydia Wang 		}
33225eaba2fSLydia Wang 	}
33325eaba2fSLydia Wang 
33425eaba2fSLydia Wang 	if (!lmute && !rmute) {
33525eaba2fSLydia Wang 		/* Line Outs */
33625eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
33725eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
33825eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
33925eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
34025eaba2fSLydia Wang 		/* Speakers */
34125eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
34225eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
34325eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
34425eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
34525eaba2fSLydia Wang 		/* unmute */
34625eaba2fSLydia Wang 		via_hp_bind_automute(codec);
34725eaba2fSLydia Wang 
34825eaba2fSLydia Wang 	} else {
34925eaba2fSLydia Wang 		if (lmute) {
35025eaba2fSLydia Wang 			/* Mute all left channels */
35125eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
35225eaba2fSLydia Wang 				snd_hda_codec_amp_update(
35325eaba2fSLydia Wang 					codec,
35425eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
35525eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35625eaba2fSLydia Wang 					lmute);
35725eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
35825eaba2fSLydia Wang 				snd_hda_codec_amp_update(
35925eaba2fSLydia Wang 					codec,
36025eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
36125eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36225eaba2fSLydia Wang 					lmute);
36325eaba2fSLydia Wang 		}
36425eaba2fSLydia Wang 		if (rmute) {
36525eaba2fSLydia Wang 			/* mute all right channels */
36625eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
36725eaba2fSLydia Wang 				snd_hda_codec_amp_update(
36825eaba2fSLydia Wang 					codec,
36925eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
37025eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37125eaba2fSLydia Wang 					rmute);
37225eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
37325eaba2fSLydia Wang 				snd_hda_codec_amp_update(
37425eaba2fSLydia Wang 					codec,
37525eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
37625eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37725eaba2fSLydia Wang 					rmute);
37825eaba2fSLydia Wang 		}
37925eaba2fSLydia Wang 	}
38025eaba2fSLydia Wang 	return change;
38125eaba2fSLydia Wang }
38225eaba2fSLydia Wang 
38325eaba2fSLydia Wang #define BIND_PIN_MUTE							\
38425eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
38525eaba2fSLydia Wang 			.name = NULL,					\
38625eaba2fSLydia Wang 			.index = 0,					\
38725eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
38825eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
38925eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
39025eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
39125eaba2fSLydia Wang 
39271eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = {
393c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
394c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
395f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
39625eaba2fSLydia Wang 	BIND_PIN_MUTE,
397c577b8a1SJoseph Chan };
398c577b8a1SJoseph Chan 
399c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
400c577b8a1SJoseph Chan 	/* ADC1-2 */
401c577b8a1SJoseph Chan 	0x15, 0x27
402c577b8a1SJoseph Chan };
403c577b8a1SJoseph Chan 
404c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
405c577b8a1SJoseph Chan 	/* ADC1-2 */
406c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
407c577b8a1SJoseph Chan };
408c577b8a1SJoseph Chan 
409f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
410f7278fd0SJosepch Chan 	/* ADC1-2 */
411f7278fd0SJosepch Chan 	0x13, 0x14
412f7278fd0SJosepch Chan };
413f7278fd0SJosepch Chan 
414d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
415d949cac1SHarald Welte 	/* ADC1-2 */
416d949cac1SHarald Welte 	0x13, 0x14
417d949cac1SHarald Welte };
418d949cac1SHarald Welte 
419d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
420d949cac1SHarald Welte 	/* ADC1-2 */
421d949cac1SHarald Welte 	0x12, 0x20, 0x1F
422d949cac1SHarald Welte };
423d949cac1SHarald Welte 
424eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
425eb7188caSLydia Wang 	/* ADC1-2 */
426eb7188caSLydia Wang 	0x10, 0x11
427eb7188caSLydia Wang };
428eb7188caSLydia Wang 
429f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
430f3db423dSLydia Wang 	/* ADC1-2 */
431f3db423dSLydia Wang 	0x13, 0x14
432f3db423dSLydia Wang };
433f3db423dSLydia Wang 
43425eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
43525eaba2fSLydia Wang 	/* ADC1-2 */
43625eaba2fSLydia Wang 	0x10, 0x11
43725eaba2fSLydia Wang };
43825eaba2fSLydia Wang 
439ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = {
440ab6734e7SLydia Wang 	/* ADC1-2 */
441ab6734e7SLydia Wang 	0x10, 0x11
442ab6734e7SLydia Wang };
443ab6734e7SLydia Wang 
444ab6734e7SLydia Wang 
445c577b8a1SJoseph Chan /* add dynamic controls */
4467b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
4477b315bb4STakashi Iwai 			     int idx, unsigned long val)
448c577b8a1SJoseph Chan {
449c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
450c577b8a1SJoseph Chan 
451603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
452603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
453c577b8a1SJoseph Chan 	if (!knew)
454c577b8a1SJoseph Chan 		return -ENOMEM;
45571eb7dccSLydia Wang 	*knew = via_control_templates[type];
456c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
457c577b8a1SJoseph Chan 	if (!knew->name)
458c577b8a1SJoseph Chan 		return -ENOMEM;
4594d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4605e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
461c577b8a1SJoseph Chan 	knew->private_value = val;
462c577b8a1SJoseph Chan 	return 0;
463c577b8a1SJoseph Chan }
464c577b8a1SJoseph Chan 
4657b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4667b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4677b315bb4STakashi Iwai 
4685b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
4695b0cb1d8SJaroslav Kysela 						struct snd_kcontrol_new *tmpl)
4705b0cb1d8SJaroslav Kysela {
4715b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
4725b0cb1d8SJaroslav Kysela 
4735b0cb1d8SJaroslav Kysela 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
4745b0cb1d8SJaroslav Kysela 	knew = snd_array_new(&spec->kctls);
4755b0cb1d8SJaroslav Kysela 	if (!knew)
4765b0cb1d8SJaroslav Kysela 		return NULL;
4775b0cb1d8SJaroslav Kysela 	*knew = *tmpl;
4785b0cb1d8SJaroslav Kysela 	knew->name = kstrdup(tmpl->name, GFP_KERNEL);
4795b0cb1d8SJaroslav Kysela 	if (!knew->name)
4805b0cb1d8SJaroslav Kysela 		return NULL;
481b331439dSTakashi Iwai 	return knew;
4825b0cb1d8SJaroslav Kysela }
4835b0cb1d8SJaroslav Kysela 
484603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
485603c4019STakashi Iwai {
486603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
487603c4019STakashi Iwai 
488603c4019STakashi Iwai 	if (spec->kctls.list) {
489603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
490603c4019STakashi Iwai 		int i;
491603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
492603c4019STakashi Iwai 			kfree(kctl[i].name);
493603c4019STakashi Iwai 	}
494603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
495603c4019STakashi Iwai }
496603c4019STakashi Iwai 
497c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4989510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4997b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
500c577b8a1SJoseph Chan {
501c577b8a1SJoseph Chan 	char name[32];
502c577b8a1SJoseph Chan 	int err;
503c577b8a1SJoseph Chan 
504c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
5057b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
506c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
507c577b8a1SJoseph Chan 	if (err < 0)
508c577b8a1SJoseph Chan 		return err;
509c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5107b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
511c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
512c577b8a1SJoseph Chan 	if (err < 0)
513c577b8a1SJoseph Chan 		return err;
514c577b8a1SJoseph Chan 	return 0;
515c577b8a1SJoseph Chan }
516c577b8a1SJoseph Chan 
517c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
518c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
519c577b8a1SJoseph Chan 					   int dac_idx)
520c577b8a1SJoseph Chan {
521c577b8a1SJoseph Chan 	/* set as output */
522c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
523c577b8a1SJoseph Chan 			    pin_type);
524c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
525c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
526d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
527d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
528d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
529c577b8a1SJoseph Chan }
530c577b8a1SJoseph Chan 
531c577b8a1SJoseph Chan 
532c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
533c577b8a1SJoseph Chan {
534c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
535c577b8a1SJoseph Chan 	int i;
536c577b8a1SJoseph Chan 
537c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
538c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
539c577b8a1SJoseph Chan 		if (nid)
540c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
541c577b8a1SJoseph Chan 	}
542c577b8a1SJoseph Chan }
543c577b8a1SJoseph Chan 
544c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
545c577b8a1SJoseph Chan {
546c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
547c577b8a1SJoseph Chan 	hda_nid_t pin;
54825eaba2fSLydia Wang 	int i;
549c577b8a1SJoseph Chan 
55025eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
55125eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
552c577b8a1SJoseph Chan 		if (pin) /* connect to front */
553c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
554c577b8a1SJoseph Chan 	}
55525eaba2fSLydia Wang }
556c577b8a1SJoseph Chan 
55732e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
55832e0191dSClemens Ladisch 
559c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
560c577b8a1SJoseph Chan {
561c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5627b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
56332e0191dSClemens Ladisch 	unsigned int ctl;
564c577b8a1SJoseph Chan 	int i;
565c577b8a1SJoseph Chan 
5667b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5677b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
56832e0191dSClemens Ladisch 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
56932e0191dSClemens Ladisch 			ctl = PIN_OUT;
57030649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
57132e0191dSClemens Ladisch 			ctl = PIN_VREF50;
57232e0191dSClemens Ladisch 		else
57332e0191dSClemens Ladisch 			ctl = PIN_IN;
574c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
57532e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
576c577b8a1SJoseph Chan 	}
577c577b8a1SJoseph Chan }
578f5271101SLydia Wang 
579f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
580f5271101SLydia Wang 				unsigned int *affected_parm)
581f5271101SLydia Wang {
582f5271101SLydia Wang 	unsigned parm;
583f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
584f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
585f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
586f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
587d56757abSTakashi Iwai 	unsigned present = snd_hda_jack_detect(codec, nid);
5881564b287SLydia Wang 	struct via_spec *spec = codec->spec;
5891564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5901564b287SLydia Wang 	    || ((no_presence || present)
5911564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
592f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
593f5271101SLydia Wang 		parm = AC_PWRST_D0;
594f5271101SLydia Wang 	} else
595f5271101SLydia Wang 		parm = AC_PWRST_D3;
596f5271101SLydia Wang 
597f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
598f5271101SLydia Wang }
599f5271101SLydia Wang 
600f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec)
601f5271101SLydia Wang {
602f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
603f5271101SLydia Wang 	int imux_is_smixer;
604f5271101SLydia Wang 	unsigned int parm;
605f5271101SLydia Wang 
606f5271101SLydia Wang 	if (spec->codec_type == VT1702) {
607f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
608f5271101SLydia Wang 			codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
609f5271101SLydia Wang 		/* inputs */
610f5271101SLydia Wang 		/* PW 1/2/5 (14h/15h/18h) */
611f5271101SLydia Wang 		parm = AC_PWRST_D3;
612f5271101SLydia Wang 		set_pin_power_state(codec, 0x14, &parm);
613f5271101SLydia Wang 		set_pin_power_state(codec, 0x15, &parm);
614f5271101SLydia Wang 		set_pin_power_state(codec, 0x18, &parm);
615f5271101SLydia Wang 		if (imux_is_smixer)
616f5271101SLydia Wang 			parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */
617f5271101SLydia Wang 		/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
618f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
619f5271101SLydia Wang 				    parm);
620f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE,
621f5271101SLydia Wang 				    parm);
622f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
623f5271101SLydia Wang 				    parm);
624f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE,
625f5271101SLydia Wang 				    parm);
626f5271101SLydia Wang 
627f5271101SLydia Wang 		/* outputs */
628f5271101SLydia Wang 		/* PW 3/4 (16h/17h) */
629f5271101SLydia Wang 		parm = AC_PWRST_D3;
630f5271101SLydia Wang 		set_pin_power_state(codec, 0x16, &parm);
631f5271101SLydia Wang 		set_pin_power_state(codec, 0x17, &parm);
632f5271101SLydia Wang 		/* MW0 (1ah), AOW 0/1 (10h/1dh) */
633f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
634f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
635f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
636f5271101SLydia Wang 				    parm);
637f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE,
638f5271101SLydia Wang 				    parm);
639f5271101SLydia Wang 	} else if (spec->codec_type == VT1708B_8CH
640f5271101SLydia Wang 		   || spec->codec_type == VT1708B_4CH
641f5271101SLydia Wang 		   || spec->codec_type == VT1708S) {
642f5271101SLydia Wang 		/* SW0 (17h) = stereo mixer */
643f5271101SLydia Wang 		int is_8ch = spec->codec_type != VT1708B_4CH;
644f5271101SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
645f5271101SLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
646f5271101SLydia Wang 			== ((spec->codec_type == VT1708S)  ? 5 : 0);
647f5271101SLydia Wang 		/* inputs */
648f5271101SLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
649f5271101SLydia Wang 		parm = AC_PWRST_D3;
650f5271101SLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
651f5271101SLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
652f5271101SLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
653f5271101SLydia Wang 		if (imux_is_smixer)
654f5271101SLydia Wang 			parm = AC_PWRST_D0;
655f5271101SLydia Wang 		/* SW0 (17h), AIW 0/1 (13h/14h) */
656f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
657f5271101SLydia Wang 				    parm);
658f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
659f5271101SLydia Wang 				    parm);
660f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
661f5271101SLydia Wang 				    parm);
662f5271101SLydia Wang 
663f5271101SLydia Wang 		/* outputs */
664f5271101SLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
665f5271101SLydia Wang 		parm = AC_PWRST_D3;
666f5271101SLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
66732e0191dSClemens Ladisch 		if (spec->smart51_enabled)
66832e0191dSClemens Ladisch 			parm = AC_PWRST_D0;
669f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
670f5271101SLydia Wang 				    parm);
671f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
672f5271101SLydia Wang 				    parm);
673f5271101SLydia Wang 
674f5271101SLydia Wang 		/* PW6 (22h), SW2 (26h), AOW2 (24h) */
675f5271101SLydia Wang 		if (is_8ch) {
676f5271101SLydia Wang 			parm = AC_PWRST_D3;
677f5271101SLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
67832e0191dSClemens Ladisch 			if (spec->smart51_enabled)
67932e0191dSClemens Ladisch 				parm = AC_PWRST_D0;
680f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x26, 0,
681f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
682f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x24, 0,
683f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
684f5271101SLydia Wang 		}
685f5271101SLydia Wang 
686f5271101SLydia Wang 		/* PW 3/4/7 (1ch/1dh/23h) */
687f5271101SLydia Wang 		parm = AC_PWRST_D3;
688f5271101SLydia Wang 		/* force to D0 for internal Speaker */
689f5271101SLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
690f5271101SLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
691f5271101SLydia Wang 		if (is_8ch)
692f5271101SLydia Wang 			set_pin_power_state(codec, 0x23, &parm);
693f5271101SLydia Wang 		/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
694f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
695f5271101SLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
696f5271101SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
697f5271101SLydia Wang 				    parm);
698f5271101SLydia Wang 		if (is_8ch) {
699f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
700f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
701f5271101SLydia Wang 			snd_hda_codec_write(codec, 0x27, 0,
702f5271101SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
703f5271101SLydia Wang 		}
704eb7188caSLydia Wang 	}  else if (spec->codec_type == VT1718S) {
705eb7188caSLydia Wang 		/* MUX6 (1eh) = stereo mixer */
706eb7188caSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
707eb7188caSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
708eb7188caSLydia Wang 		/* inputs */
709eb7188caSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
710eb7188caSLydia Wang 		parm = AC_PWRST_D3;
711eb7188caSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
712eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
713eb7188caSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
714eb7188caSLydia Wang 		if (imux_is_smixer)
715eb7188caSLydia Wang 			parm = AC_PWRST_D0;
716eb7188caSLydia Wang 		/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
717eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE,
718eb7188caSLydia Wang 				    parm);
719eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE,
720eb7188caSLydia Wang 				    parm);
721eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
722eb7188caSLydia Wang 				    parm);
723eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
724eb7188caSLydia Wang 				    parm);
725eb7188caSLydia Wang 
726eb7188caSLydia Wang 		/* outputs */
727eb7188caSLydia Wang 		/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
728eb7188caSLydia Wang 		parm = AC_PWRST_D3;
729eb7188caSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
730eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
731eb7188caSLydia Wang 				    parm);
732eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE,
733eb7188caSLydia Wang 				    parm);
734eb7188caSLydia Wang 
735eb7188caSLydia Wang 		/* PW2 (26h), AOW2 (ah) */
736eb7188caSLydia Wang 		parm = AC_PWRST_D3;
737eb7188caSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
738eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE,
739eb7188caSLydia Wang 				    parm);
740eb7188caSLydia Wang 
741eb7188caSLydia Wang 		/* PW0/1 (24h/25h) */
742eb7188caSLydia Wang 		parm = AC_PWRST_D3;
743eb7188caSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
744eb7188caSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
745eb7188caSLydia Wang 		if (!spec->hp_independent_mode) /* check for redirected HP */
746eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
747eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE,
748eb7188caSLydia Wang 				    parm);
749eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE,
750eb7188caSLydia Wang 				    parm);
751eb7188caSLydia Wang 		/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
752eb7188caSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
753eb7188caSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
754eb7188caSLydia Wang 		if (spec->hp_independent_mode) {
755eb7188caSLydia Wang 			/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
756eb7188caSLydia Wang 			parm = AC_PWRST_D3;
757eb7188caSLydia Wang 			set_pin_power_state(codec, 0x28, &parm);
758eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x1b, 0,
759eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
760eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
761eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
762eb7188caSLydia Wang 			snd_hda_codec_write(codec, 0xc, 0,
763eb7188caSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
764eb7188caSLydia Wang 		}
765f3db423dSLydia Wang 	} else if (spec->codec_type == VT1716S) {
766f3db423dSLydia Wang 		unsigned int mono_out, present;
767f3db423dSLydia Wang 		/* SW0 (17h) = stereo mixer */
768f3db423dSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
769f3db423dSLydia Wang 			codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) ==  5;
770f3db423dSLydia Wang 		/* inputs */
771f3db423dSLydia Wang 		/* PW 1/2/5 (1ah/1bh/1eh) */
772f3db423dSLydia Wang 		parm = AC_PWRST_D3;
773f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
774f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
775f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
776f3db423dSLydia Wang 		if (imux_is_smixer)
777f3db423dSLydia Wang 			parm = AC_PWRST_D0;
778f3db423dSLydia Wang 		/* SW0 (17h), AIW0(13h) */
779f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE,
780f3db423dSLydia Wang 				    parm);
781f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE,
782f3db423dSLydia Wang 				    parm);
783f3db423dSLydia Wang 
784f3db423dSLydia Wang 		parm = AC_PWRST_D3;
785f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
786f3db423dSLydia Wang 		/* PW11 (22h) */
787f3db423dSLydia Wang 		if (spec->dmic_enabled)
788f3db423dSLydia Wang 			set_pin_power_state(codec, 0x22, &parm);
789f3db423dSLydia Wang 		else
790f3db423dSLydia Wang 			snd_hda_codec_write(
791f3db423dSLydia Wang 				codec, 0x22, 0,
792f3db423dSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
793f3db423dSLydia Wang 
794f3db423dSLydia Wang 		/* SW2(26h), AIW1(14h) */
795f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE,
796f3db423dSLydia Wang 				    parm);
797f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE,
798f3db423dSLydia Wang 				    parm);
799f3db423dSLydia Wang 
800f3db423dSLydia Wang 		/* outputs */
801f3db423dSLydia Wang 		/* PW0 (19h), SW1 (18h), AOW1 (11h) */
802f3db423dSLydia Wang 		parm = AC_PWRST_D3;
803f3db423dSLydia Wang 		set_pin_power_state(codec, 0x19, &parm);
804f3db423dSLydia Wang 		/* Smart 5.1 PW2(1bh) */
805f3db423dSLydia Wang 		if (spec->smart51_enabled)
806f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1b, &parm);
807f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE,
808f3db423dSLydia Wang 				    parm);
809f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE,
810f3db423dSLydia Wang 				    parm);
811f3db423dSLydia Wang 
812f3db423dSLydia Wang 		/* PW7 (23h), SW3 (27h), AOW3 (25h) */
813f3db423dSLydia Wang 		parm = AC_PWRST_D3;
814f3db423dSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
815f3db423dSLydia Wang 		/* Smart 5.1 PW1(1ah) */
816f3db423dSLydia Wang 		if (spec->smart51_enabled)
817f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
818f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE,
819f3db423dSLydia Wang 				    parm);
820f3db423dSLydia Wang 
821f3db423dSLydia Wang 		/* Smart 5.1 PW5(1eh) */
822f3db423dSLydia Wang 		if (spec->smart51_enabled)
823f3db423dSLydia Wang 			set_pin_power_state(codec, 0x1e, &parm);
824f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE,
825f3db423dSLydia Wang 				    parm);
826f3db423dSLydia Wang 
827f3db423dSLydia Wang 		/* Mono out */
828f3db423dSLydia Wang 		/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
829d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x1c);
830f3db423dSLydia Wang 		if (present)
831f3db423dSLydia Wang 			mono_out = 0;
832f3db423dSLydia Wang 		else {
833d56757abSTakashi Iwai 			present = snd_hda_jack_detect(codec, 0x1d);
834f3db423dSLydia Wang 			if (!spec->hp_independent_mode && present)
835f3db423dSLydia Wang 				mono_out = 0;
836f3db423dSLydia Wang 			else
837f3db423dSLydia Wang 				mono_out = 1;
838f3db423dSLydia Wang 		}
839f3db423dSLydia Wang 		parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
840f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE,
841f3db423dSLydia Wang 				    parm);
842f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE,
843f3db423dSLydia Wang 				    parm);
844f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE,
845f3db423dSLydia Wang 				    parm);
846f3db423dSLydia Wang 
847f3db423dSLydia Wang 		/* PW 3/4 (1ch/1dh) */
848f3db423dSLydia Wang 		parm = AC_PWRST_D3;
849f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1c, &parm);
850f3db423dSLydia Wang 		set_pin_power_state(codec, 0x1d, &parm);
851f3db423dSLydia Wang 		/* HP Independent Mode, power on AOW3 */
852f3db423dSLydia Wang 		if (spec->hp_independent_mode)
853f3db423dSLydia Wang 			snd_hda_codec_write(codec, 0x25, 0,
854f3db423dSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
855f3db423dSLydia Wang 
856f3db423dSLydia Wang 		/* force to D0 for internal Speaker */
857f3db423dSLydia Wang 		/* MW0 (16h), AOW0 (10h) */
858f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
859f3db423dSLydia Wang 				    imux_is_smixer ? AC_PWRST_D0 : parm);
860f3db423dSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
861f3db423dSLydia Wang 				    mono_out ? AC_PWRST_D0 : parm);
86225eaba2fSLydia Wang 	} else if (spec->codec_type == VT2002P) {
86325eaba2fSLydia Wang 		unsigned int present;
86425eaba2fSLydia Wang 		/* MUX9 (1eh) = stereo mixer */
86525eaba2fSLydia Wang 		imux_is_smixer = snd_hda_codec_read(
86625eaba2fSLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
86725eaba2fSLydia Wang 		/* inputs */
86825eaba2fSLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
86925eaba2fSLydia Wang 		parm = AC_PWRST_D3;
87025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
87125eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
87225eaba2fSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
87325eaba2fSLydia Wang 		if (imux_is_smixer)
87425eaba2fSLydia Wang 			parm = AC_PWRST_D0;
87525eaba2fSLydia Wang 		/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
87625eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
87725eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
87825eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
87925eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
88025eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
88125eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
88225eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
88325eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
88425eaba2fSLydia Wang 
88525eaba2fSLydia Wang 		/* outputs */
88625eaba2fSLydia Wang 		/* AOW0 (8h)*/
88725eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
88825eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
88925eaba2fSLydia Wang 
89025eaba2fSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
89125eaba2fSLydia Wang 		parm = AC_PWRST_D3;
89225eaba2fSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
89325eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
89425eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
89525eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x37,
89625eaba2fSLydia Wang 				    0, AC_VERB_SET_POWER_STATE, parm);
89725eaba2fSLydia Wang 
89825eaba2fSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
89925eaba2fSLydia Wang 		parm = AC_PWRST_D3;
90025eaba2fSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
90125eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
90225eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
90325eaba2fSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
90425eaba2fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
90525eaba2fSLydia Wang 		if (spec->hp_independent_mode)	{
90625eaba2fSLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
90725eaba2fSLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
90825eaba2fSLydia Wang 		}
90925eaba2fSLydia Wang 
91025eaba2fSLydia Wang 		/* Class-D */
91125eaba2fSLydia Wang 		/* PW0 (24h), MW0(18h), MUX0(34h) */
912d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x25);
91325eaba2fSLydia Wang 		parm = AC_PWRST_D3;
91425eaba2fSLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
91525eaba2fSLydia Wang 		if (present) {
91625eaba2fSLydia Wang 			snd_hda_codec_write(
91725eaba2fSLydia Wang 				codec, 0x18, 0,
91825eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
91925eaba2fSLydia Wang 			snd_hda_codec_write(
92025eaba2fSLydia Wang 				codec, 0x34, 0,
92125eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
92225eaba2fSLydia Wang 		} else {
92325eaba2fSLydia Wang 			snd_hda_codec_write(
92425eaba2fSLydia Wang 				codec, 0x18, 0,
92525eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
92625eaba2fSLydia Wang 			snd_hda_codec_write(
92725eaba2fSLydia Wang 				codec, 0x34, 0,
92825eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
92925eaba2fSLydia Wang 		}
93025eaba2fSLydia Wang 
93125eaba2fSLydia Wang 		/* Mono Out */
93225eaba2fSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
933d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x26);
93425eaba2fSLydia Wang 		parm = AC_PWRST_D3;
93525eaba2fSLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
93625eaba2fSLydia Wang 		if (present) {
93725eaba2fSLydia Wang 			snd_hda_codec_write(
93825eaba2fSLydia Wang 				codec, 0x17, 0,
93925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
94025eaba2fSLydia Wang 			snd_hda_codec_write(
94125eaba2fSLydia Wang 				codec, 0x3b, 0,
94225eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
94325eaba2fSLydia Wang 		} else {
94425eaba2fSLydia Wang 			snd_hda_codec_write(
94525eaba2fSLydia Wang 				codec, 0x17, 0,
94625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
94725eaba2fSLydia Wang 			snd_hda_codec_write(
94825eaba2fSLydia Wang 				codec, 0x3b, 0,
94925eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
95025eaba2fSLydia Wang 		}
95125eaba2fSLydia Wang 
95225eaba2fSLydia Wang 		/* MW9 (21h) */
95325eaba2fSLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
95425eaba2fSLydia Wang 			snd_hda_codec_write(
95525eaba2fSLydia Wang 				codec, 0x21, 0,
95625eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
95725eaba2fSLydia Wang 		else
95825eaba2fSLydia Wang 			snd_hda_codec_write(
95925eaba2fSLydia Wang 				codec, 0x21, 0,
96025eaba2fSLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
961ab6734e7SLydia Wang 	} else if (spec->codec_type == VT1812) {
962ab6734e7SLydia Wang 		unsigned int present;
963ab6734e7SLydia Wang 		/* MUX10 (1eh) = stereo mixer */
964ab6734e7SLydia Wang 		imux_is_smixer = snd_hda_codec_read(
965ab6734e7SLydia Wang 			codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
966ab6734e7SLydia Wang 		/* inputs */
967ab6734e7SLydia Wang 		/* PW 5/6/7 (29h/2ah/2bh) */
968ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
969ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x29, &parm);
970ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
971ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
972ab6734e7SLydia Wang 		if (imux_is_smixer)
973ab6734e7SLydia Wang 			parm = AC_PWRST_D0;
974ab6734e7SLydia Wang 		/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
975ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1e, 0,
976ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
977ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1f, 0,
978ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
979ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x10, 0,
980ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
981ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x11, 0,
982ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
983ab6734e7SLydia Wang 
984ab6734e7SLydia Wang 		/* outputs */
985ab6734e7SLydia Wang 		/* AOW0 (8h)*/
986ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x8, 0,
987ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
988ab6734e7SLydia Wang 
989ab6734e7SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
990ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
991ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
992ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
993ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
994ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
995ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
996ab6734e7SLydia Wang 
997ab6734e7SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
998ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
999ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1000ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
1001ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1002ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
1003ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1004ab6734e7SLydia Wang 		if (spec->hp_independent_mode)	{
1005ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x9, 0,
1006ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE, parm);
1007ab6734e7SLydia Wang 		}
1008ab6734e7SLydia Wang 
1009ab6734e7SLydia Wang 		/* Internal Speaker */
1010ab6734e7SLydia Wang 		/* PW0 (24h), MW0(14h), MUX0(34h) */
1011d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x25);
1012ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1013ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x24, &parm);
1014ab6734e7SLydia Wang 		if (present) {
1015ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
1016ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1017ab6734e7SLydia Wang 					    AC_PWRST_D3);
1018ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
1019ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1020ab6734e7SLydia Wang 					    AC_PWRST_D3);
1021ab6734e7SLydia Wang 		} else {
1022ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x14, 0,
1023ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1024ab6734e7SLydia Wang 					    AC_PWRST_D0);
1025ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x34, 0,
1026ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1027ab6734e7SLydia Wang 					    AC_PWRST_D0);
1028ab6734e7SLydia Wang 		}
1029ab6734e7SLydia Wang 		/* Mono Out */
1030ab6734e7SLydia Wang 		/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
1031d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, 0x28);
1032ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1033ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x31, &parm);
1034ab6734e7SLydia Wang 		if (present) {
1035ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
1036ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1037ab6734e7SLydia Wang 					    AC_PWRST_D3);
1038ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1039ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1040ab6734e7SLydia Wang 					    AC_PWRST_D3);
1041ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1042ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1043ab6734e7SLydia Wang 					    AC_PWRST_D3);
1044ab6734e7SLydia Wang 		} else {
1045ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x1c, 0,
1046ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1047ab6734e7SLydia Wang 					    AC_PWRST_D0);
1048ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3c, 0,
1049ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1050ab6734e7SLydia Wang 					    AC_PWRST_D0);
1051ab6734e7SLydia Wang 			snd_hda_codec_write(codec, 0x3e, 0,
1052ab6734e7SLydia Wang 					    AC_VERB_SET_POWER_STATE,
1053ab6734e7SLydia Wang 					    AC_PWRST_D0);
1054ab6734e7SLydia Wang 		}
1055ab6734e7SLydia Wang 
1056ab6734e7SLydia Wang 		/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
1057ab6734e7SLydia Wang 		parm = AC_PWRST_D3;
1058ab6734e7SLydia Wang 		set_pin_power_state(codec, 0x33, &parm);
1059ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x1d, 0,
1060ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1061ab6734e7SLydia Wang 		snd_hda_codec_write(codec, 0x3d, 0,
1062ab6734e7SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
1063ab6734e7SLydia Wang 
1064ab6734e7SLydia Wang 		/* MW9 (21h) */
1065ab6734e7SLydia Wang 		if (imux_is_smixer || !is_aa_path_mute(codec))
1066ab6734e7SLydia Wang 			snd_hda_codec_write(
1067ab6734e7SLydia Wang 				codec, 0x21, 0,
1068ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1069ab6734e7SLydia Wang 		else
1070ab6734e7SLydia Wang 			snd_hda_codec_write(
1071ab6734e7SLydia Wang 				codec, 0x21, 0,
1072ab6734e7SLydia Wang 				AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
1073f5271101SLydia Wang 	}
1074f5271101SLydia Wang }
1075f5271101SLydia Wang 
1076c577b8a1SJoseph Chan /*
1077c577b8a1SJoseph Chan  * input MUX handling
1078c577b8a1SJoseph Chan  */
1079c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1080c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
1081c577b8a1SJoseph Chan {
1082c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1083c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1084c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
1085c577b8a1SJoseph Chan }
1086c577b8a1SJoseph Chan 
1087c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1088c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1089c577b8a1SJoseph Chan {
1090c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1091c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1092c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1093c577b8a1SJoseph Chan 
1094c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
1095c577b8a1SJoseph Chan 	return 0;
1096c577b8a1SJoseph Chan }
1097c577b8a1SJoseph Chan 
1098c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1099c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
1100c577b8a1SJoseph Chan {
1101c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1102c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1103c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1104bff5fbf5SLydia Wang 	int ret;
1105c577b8a1SJoseph Chan 
1106337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
1107337b9d02STakashi Iwai 		return -EINVAL;
1108a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
1109a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
1110a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1111a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
1112a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1113bff5fbf5SLydia Wang 
1114bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
1115bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
1116bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
1117a80e6e3cSLydia Wang 	/* update jack power state */
1118a80e6e3cSLydia Wang 	set_jack_power_state(codec);
1119a80e6e3cSLydia Wang 
1120bff5fbf5SLydia Wang 	return ret;
1121c577b8a1SJoseph Chan }
1122c577b8a1SJoseph Chan 
11230aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
11240aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
11250aa62aefSHarald Welte {
11260aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11270aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11280aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
11290aa62aefSHarald Welte }
11300aa62aefSHarald Welte 
11310aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
11320aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
11330aa62aefSHarald Welte {
11340aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11355b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
1136eb7188caSLydia Wang 	unsigned int pinsel;
1137eb7188caSLydia Wang 
1138eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
1139eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
11400aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
11410aa62aefSHarald Welte 				      0x00);
11420aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
11430aa62aefSHarald Welte 
11440aa62aefSHarald Welte 	return 0;
11450aa62aefSHarald Welte }
11460aa62aefSHarald Welte 
11470713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
11480713efebSLydia Wang {
11490713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
11500713efebSLydia Wang 	if (ctl) {
11510713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11520713efebSLydia Wang 		ctl->vd[0].access |= active
11530713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
11540713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
11550713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
11560713efebSLydia Wang 	}
11570713efebSLydia Wang }
11580713efebSLydia Wang 
11595b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
11605b0cb1d8SJaroslav Kysela {
11615b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
11625b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
11635b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
11645b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
11655b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
11665b0cb1d8SJaroslav Kysela 	default:		return 0;
11675b0cb1d8SJaroslav Kysela 	}
11685b0cb1d8SJaroslav Kysela }
11695b0cb1d8SJaroslav Kysela 
1170cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
1171cdc1784dSLydia Wang {
1172cdc1784dSLydia Wang 	/* mute side channel */
1173cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
1174cdc1784dSLydia Wang 	unsigned int parm = spec->hp_independent_mode
1175cdc1784dSLydia Wang 		? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
11765b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
1177cdc1784dSLydia Wang 
1178cdc1784dSLydia Wang 	if (sw3)
1179cdc1784dSLydia Wang 		snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
1180cdc1784dSLydia Wang 				    parm);
1181cdc1784dSLydia Wang 	return 0;
1182cdc1784dSLydia Wang }
1183cdc1784dSLydia Wang 
11840aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
11850aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
11860aa62aefSHarald Welte {
11870aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
11880aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11895b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
11900aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
1191cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
1192cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
1193cdc1784dSLydia Wang 		? 1 : 0;
1194*ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
1195*ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
1196*ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
1197*ce0e5a9eSLydia Wang 	else
1198*ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
1199*ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
12000aa62aefSHarald Welte 
1201*ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
1202*ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
1203*ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
1204cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
1205cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
1206cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
12070aa62aefSHarald Welte 					   0, 0, 0);
12080aa62aefSHarald Welte 
1209cdc1784dSLydia Wang 	update_side_mute_status(codec);
12100713efebSLydia Wang 	/* update HP volume/swtich active state */
12110713efebSLydia Wang 	if (spec->codec_type == VT1708S
1212eb7188caSLydia Wang 	    || spec->codec_type == VT1702
1213f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
121425eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
1215ab6734e7SLydia Wang 	    || spec->codec_type == VT2002P
1216ab6734e7SLydia Wang 	    || spec->codec_type == VT1812) {
12170713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
12180713efebSLydia Wang 			     spec->hp_independent_mode);
12190713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
12200713efebSLydia Wang 			     spec->hp_independent_mode);
12210713efebSLydia Wang 	}
1222*ce0e5a9eSLydia Wang 	/* update jack power state */
1223*ce0e5a9eSLydia Wang 	set_jack_power_state(codec);
12240aa62aefSHarald Welte 	return 0;
12250aa62aefSHarald Welte }
12260aa62aefSHarald Welte 
12275b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = {
12280aa62aefSHarald Welte 	{
12290aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
12300aa62aefSHarald Welte 		.name = "Independent HP",
12310aa62aefSHarald Welte 		.info = via_independent_hp_info,
12320aa62aefSHarald Welte 		.get = via_independent_hp_get,
12330aa62aefSHarald Welte 		.put = via_independent_hp_put,
12340aa62aefSHarald Welte 	},
12355b0cb1d8SJaroslav Kysela 	{
12365b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
12375b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
12385b0cb1d8SJaroslav Kysela 	},
12390aa62aefSHarald Welte };
12400aa62aefSHarald Welte 
12413d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
12425b0cb1d8SJaroslav Kysela {
12433d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
12445b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
12455b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
12463d83e577STakashi Iwai 	int nums;
12473d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
12485b0cb1d8SJaroslav Kysela 
12495b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
12505b0cb1d8SJaroslav Kysela 	case VT1718S:
12515b0cb1d8SJaroslav Kysela 		nid = 0x34;
12525b0cb1d8SJaroslav Kysela 		break;
12535b0cb1d8SJaroslav Kysela 	case VT2002P:
12545b0cb1d8SJaroslav Kysela 		nid = 0x35;
12555b0cb1d8SJaroslav Kysela 		break;
12565b0cb1d8SJaroslav Kysela 	case VT1812:
12575b0cb1d8SJaroslav Kysela 		nid = 0x3d;
12585b0cb1d8SJaroslav Kysela 		break;
12595b0cb1d8SJaroslav Kysela 	default:
12605b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
12615b0cb1d8SJaroslav Kysela 		break;
12625b0cb1d8SJaroslav Kysela 	}
12635b0cb1d8SJaroslav Kysela 
12643d83e577STakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, HDA_MAX_CONNECTIONS);
12653d83e577STakashi Iwai 	if (nums <= 1)
12663d83e577STakashi Iwai 		return 0;
12673d83e577STakashi Iwai 
12683d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
12693d83e577STakashi Iwai 	if (knew == NULL)
12703d83e577STakashi Iwai 		return -ENOMEM;
12713d83e577STakashi Iwai 
12725b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
12735b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
12745b0cb1d8SJaroslav Kysela 
12755b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
12765b0cb1d8SJaroslav Kysela 	if (knew == NULL)
12775b0cb1d8SJaroslav Kysela 		return -ENOMEM;
12785b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
12795b0cb1d8SJaroslav Kysela 
12805b0cb1d8SJaroslav Kysela 	return 0;
12815b0cb1d8SJaroslav Kysela }
12825b0cb1d8SJaroslav Kysela 
12831564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
12841564b287SLydia Wang {
12851564b287SLydia Wang 	int i;
12861564b287SLydia Wang 	struct snd_ctl_elem_id id;
12871564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
12881564b287SLydia Wang 
12891564b287SLydia Wang 	memset(&id, 0, sizeof(id));
12901564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
12911564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
12921564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
12931564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
12941564b287SLydia Wang 			       &id);
12951564b287SLydia Wang 	}
12961564b287SLydia Wang }
12971564b287SLydia Wang 
12981564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
12991564b287SLydia Wang {
13001564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13011564b287SLydia Wang 	hda_nid_t  nid_mixer;
13021564b287SLydia Wang 	int start_idx;
13031564b287SLydia Wang 	int end_idx;
13041564b287SLydia Wang 	int i;
13051564b287SLydia Wang 	/* get nid of MW0 and start & end index */
13061564b287SLydia Wang 	switch (spec->codec_type) {
13071564b287SLydia Wang 	case VT1708:
13081564b287SLydia Wang 		nid_mixer = 0x17;
13091564b287SLydia Wang 		start_idx = 2;
13101564b287SLydia Wang 		end_idx = 4;
13111564b287SLydia Wang 		break;
13121564b287SLydia Wang 	case VT1709_10CH:
13131564b287SLydia Wang 	case VT1709_6CH:
13141564b287SLydia Wang 		nid_mixer = 0x18;
13151564b287SLydia Wang 		start_idx = 2;
13161564b287SLydia Wang 		end_idx = 4;
13171564b287SLydia Wang 		break;
13181564b287SLydia Wang 	case VT1708B_8CH:
13191564b287SLydia Wang 	case VT1708B_4CH:
13201564b287SLydia Wang 	case VT1708S:
1321f3db423dSLydia Wang 	case VT1716S:
13221564b287SLydia Wang 		nid_mixer = 0x16;
13231564b287SLydia Wang 		start_idx = 2;
13241564b287SLydia Wang 		end_idx = 4;
13251564b287SLydia Wang 		break;
13261564b287SLydia Wang 	default:
13271564b287SLydia Wang 		return;
13281564b287SLydia Wang 	}
13291564b287SLydia Wang 	/* check AA path's mute status */
13301564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
13311564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
13321564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
13331564b287SLydia Wang 					 HDA_AMP_MUTE, val);
13341564b287SLydia Wang 	}
13351564b287SLydia Wang }
13361564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
13371564b287SLydia Wang {
13387b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
13397b315bb4STakashi Iwai 	int i;
13407b315bb4STakashi Iwai 
13417b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
13427b315bb4STakashi Iwai 		if (pin == cfg->inputs[i].pin)
134386e2959aSTakashi Iwai 			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
13441564b287SLydia Wang 	}
13457b315bb4STakashi Iwai 	return 0;
13461564b287SLydia Wang }
13471564b287SLydia Wang 
13481564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
13491564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
13501564b287SLydia Wang {
13511564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
13521564b287SLydia Wang 	uinfo->count = 1;
13531564b287SLydia Wang 	uinfo->value.integer.min = 0;
13541564b287SLydia Wang 	uinfo->value.integer.max = 1;
13551564b287SLydia Wang 	return 0;
13561564b287SLydia Wang }
13571564b287SLydia Wang 
13581564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
13591564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13601564b287SLydia Wang {
13611564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13621564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13637b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
13641564b287SLydia Wang 	int on = 1;
13651564b287SLydia Wang 	int i;
13661564b287SLydia Wang 
13677b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
13687b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
13697b315bb4STakashi Iwai 		int ctl = snd_hda_codec_read(codec, nid, 0,
13707b315bb4STakashi Iwai 					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
137186e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
13727b315bb4STakashi Iwai 			continue;
137386e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
13747b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
13751564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
13767b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
13771564b287SLydia Wang 			on = 0;
13781564b287SLydia Wang 	}
13791564b287SLydia Wang 	*ucontrol->value.integer.value = on;
13801564b287SLydia Wang 	return 0;
13811564b287SLydia Wang }
13821564b287SLydia Wang 
13831564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
13841564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
13851564b287SLydia Wang {
13861564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
13871564b287SLydia Wang 	struct via_spec *spec = codec->spec;
13887b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
13891564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
13901564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
13911564b287SLydia Wang 	int i;
13921564b287SLydia Wang 
13937b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
13947b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
13957b315bb4STakashi Iwai 		unsigned int parm;
13967b315bb4STakashi Iwai 
139786e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
13987b315bb4STakashi Iwai 			continue;
139986e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
14007b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
14011564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
14027b315bb4STakashi Iwai 
14037b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
14041564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
14051564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
14061564b287SLydia Wang 		parm |= out_in;
14071564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
14081564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
14091564b287SLydia Wang 				    parm);
14101564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
14111564b287SLydia Wang 			mute_aa_path(codec, 1);
14121564b287SLydia Wang 			notify_aa_path_ctls(codec);
14131564b287SLydia Wang 		}
14147b315bb4STakashi Iwai 		if (spec->codec_type == VT1718S) {
1415eb7188caSLydia Wang 			snd_hda_codec_amp_stereo(
1416eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1417eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
14181564b287SLydia Wang 		}
141986e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
1420f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1421f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
14221564b287SLydia Wang 				/* input = index 1 (AOW3) */
14231564b287SLydia Wang 				snd_hda_codec_write(
14241564b287SLydia Wang 					codec, nid, 0,
14251564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
14261564b287SLydia Wang 				snd_hda_codec_amp_stereo(
14271564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
14281564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
14291564b287SLydia Wang 			}
14301564b287SLydia Wang 		}
14311564b287SLydia Wang 	}
14321564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
14331564b287SLydia Wang 	set_jack_power_state(codec);
14341564b287SLydia Wang 	return 1;
14351564b287SLydia Wang }
14361564b287SLydia Wang 
14375b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = {
14381564b287SLydia Wang 	{
14391564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
14401564b287SLydia Wang 	 .name = "Smart 5.1",
14411564b287SLydia Wang 	 .count = 1,
14421564b287SLydia Wang 	 .info = via_smart51_info,
14431564b287SLydia Wang 	 .get = via_smart51_get,
14441564b287SLydia Wang 	 .put = via_smart51_put,
14451564b287SLydia Wang 	 },
14465b0cb1d8SJaroslav Kysela 	{
14475b0cb1d8SJaroslav Kysela 	 .iface = NID_MAPPING,
14485b0cb1d8SJaroslav Kysela 	 .name = "Smart 5.1",
14495b0cb1d8SJaroslav Kysela 	}
14501564b287SLydia Wang };
14511564b287SLydia Wang 
14525b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
14535b0cb1d8SJaroslav Kysela {
14545b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
14557b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
14565b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
14575b0cb1d8SJaroslav Kysela 	int i;
14585b0cb1d8SJaroslav Kysela 
14595b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_smart51_mixer[0]);
14605b0cb1d8SJaroslav Kysela 	if (knew == NULL)
14615b0cb1d8SJaroslav Kysela 		return -ENOMEM;
14625b0cb1d8SJaroslav Kysela 
14637b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
14647b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
146586e2959aSTakashi Iwai 		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
14665b0cb1d8SJaroslav Kysela 			knew = via_clone_control(spec, &via_smart51_mixer[1]);
14675b0cb1d8SJaroslav Kysela 			if (knew == NULL)
14685b0cb1d8SJaroslav Kysela 				return -ENOMEM;
14695b0cb1d8SJaroslav Kysela 			knew->subdevice = nid;
14707b315bb4STakashi Iwai 			break;
14715b0cb1d8SJaroslav Kysela 		}
14725b0cb1d8SJaroslav Kysela 	}
14735b0cb1d8SJaroslav Kysela 
14745b0cb1d8SJaroslav Kysela 	return 0;
14755b0cb1d8SJaroslav Kysela }
14765b0cb1d8SJaroslav Kysela 
1477c577b8a1SJoseph Chan /* capture mixer elements */
1478c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1479c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1480c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1481c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1482c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1483c577b8a1SJoseph Chan 	{
1484c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1485c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1486c577b8a1SJoseph Chan 		 * So call somewhat different..
1487c577b8a1SJoseph Chan 		 */
1488c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1489c577b8a1SJoseph Chan 		.name = "Input Source",
1490c577b8a1SJoseph Chan 		.count = 1,
1491c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1492c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1493c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1494c577b8a1SJoseph Chan 	},
1495c577b8a1SJoseph Chan 	{ } /* end */
1496c577b8a1SJoseph Chan };
1497f5271101SLydia Wang 
1498f5271101SLydia Wang /* check AA path's mute statue */
1499f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1500f5271101SLydia Wang {
1501f5271101SLydia Wang 	int mute = 1;
1502f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1503f5271101SLydia Wang 	int start_idx;
1504f5271101SLydia Wang 	int end_idx;
1505f5271101SLydia Wang 	int i;
1506f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1507f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1508f5271101SLydia Wang 	switch (spec->codec_type) {
1509f5271101SLydia Wang 	case VT1708B_8CH:
1510f5271101SLydia Wang 	case VT1708B_4CH:
1511f5271101SLydia Wang 	case VT1708S:
1512f3db423dSLydia Wang 	case VT1716S:
1513f5271101SLydia Wang 		nid_mixer = 0x16;
1514f5271101SLydia Wang 		start_idx = 2;
1515f5271101SLydia Wang 		end_idx = 4;
1516f5271101SLydia Wang 		break;
1517f5271101SLydia Wang 	case VT1702:
1518f5271101SLydia Wang 		nid_mixer = 0x1a;
1519f5271101SLydia Wang 		start_idx = 1;
1520f5271101SLydia Wang 		end_idx = 3;
1521f5271101SLydia Wang 		break;
1522eb7188caSLydia Wang 	case VT1718S:
1523eb7188caSLydia Wang 		nid_mixer = 0x21;
1524eb7188caSLydia Wang 		start_idx = 1;
1525eb7188caSLydia Wang 		end_idx = 3;
1526eb7188caSLydia Wang 		break;
152725eaba2fSLydia Wang 	case VT2002P:
1528ab6734e7SLydia Wang 	case VT1812:
152925eaba2fSLydia Wang 		nid_mixer = 0x21;
153025eaba2fSLydia Wang 		start_idx = 0;
153125eaba2fSLydia Wang 		end_idx = 2;
153225eaba2fSLydia Wang 		break;
1533f5271101SLydia Wang 	default:
1534f5271101SLydia Wang 		return 0;
1535f5271101SLydia Wang 	}
1536f5271101SLydia Wang 	/* check AA path's mute status */
1537f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1538f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1539f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1540f5271101SLydia Wang 		int shift = 8 * (i % 4);
1541f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1542f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1543f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1544f5271101SLydia Wang 			/* check mute status while the pin is connected */
1545f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1546f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1547f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1548f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1549f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1550f5271101SLydia Wang 				mute = 0;
1551f5271101SLydia Wang 				break;
1552f5271101SLydia Wang 			}
1553f5271101SLydia Wang 		}
1554f5271101SLydia Wang 	}
1555f5271101SLydia Wang 	return mute;
1556f5271101SLydia Wang }
1557f5271101SLydia Wang 
1558f5271101SLydia Wang /* enter/exit analog low-current mode */
1559f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1560f5271101SLydia Wang {
1561f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1562f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1563f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1564f5271101SLydia Wang 	unsigned int verb = 0;
1565f5271101SLydia Wang 	unsigned int parm = 0;
1566f5271101SLydia Wang 
1567f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1568f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1569f5271101SLydia Wang 	else {
1570f5271101SLydia Wang 		enable = enable && stream_idle;
1571f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1572f5271101SLydia Wang 	}
1573f5271101SLydia Wang 
1574f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1575f5271101SLydia Wang 	switch (spec->codec_type) {
1576f5271101SLydia Wang 	case VT1708B_8CH:
1577f5271101SLydia Wang 	case VT1708B_4CH:
1578f5271101SLydia Wang 		verb = 0xf70;
1579f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1580f5271101SLydia Wang 		break;
1581f5271101SLydia Wang 	case VT1708S:
1582eb7188caSLydia Wang 	case VT1718S:
1583f3db423dSLydia Wang 	case VT1716S:
1584f5271101SLydia Wang 		verb = 0xf73;
1585f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1586f5271101SLydia Wang 		break;
1587f5271101SLydia Wang 	case VT1702:
1588f5271101SLydia Wang 		verb = 0xf73;
1589f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1590f5271101SLydia Wang 		break;
159125eaba2fSLydia Wang 	case VT2002P:
1592ab6734e7SLydia Wang 	case VT1812:
159325eaba2fSLydia Wang 		verb = 0xf93;
159425eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
159525eaba2fSLydia Wang 		break;
1596f5271101SLydia Wang 	default:
1597f5271101SLydia Wang 		return;		/* other codecs are not supported */
1598f5271101SLydia Wang 	}
1599f5271101SLydia Wang 	/* send verb */
1600f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1601f5271101SLydia Wang }
1602f5271101SLydia Wang 
1603c577b8a1SJoseph Chan /*
1604c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1605c577b8a1SJoseph Chan  */
1606c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1607c577b8a1SJoseph Chan 	/*
1608c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1609c577b8a1SJoseph Chan 	 */
1610c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1611c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1612c577b8a1SJoseph Chan 
1613c577b8a1SJoseph Chan 
1614f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1615c577b8a1SJoseph Chan 	 * mixer widget
1616c577b8a1SJoseph Chan 	 */
1617c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1618f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1619f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1620f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1621f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1622f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1623c577b8a1SJoseph Chan 
1624c577b8a1SJoseph Chan 	/*
1625c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1626c577b8a1SJoseph Chan 	 */
1627c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1628c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1629c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1630c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1631c577b8a1SJoseph Chan 
1632bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1633bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1634c577b8a1SJoseph Chan 	/* PW9 Output enable */
1635c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1636f7278fd0SJosepch Chan 	{ }
1637c577b8a1SJoseph Chan };
1638c577b8a1SJoseph Chan 
1639c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1640c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1641c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1642c577b8a1SJoseph Chan {
1643c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
164417314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
164517314379SLydia Wang 		&& substream->ref_count == 0;
164617314379SLydia Wang 	analog_low_current_mode(codec, idle);
16479a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
16489a08160bSTakashi Iwai 					     hinfo);
1649c577b8a1SJoseph Chan }
1650c577b8a1SJoseph Chan 
16510aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
16520aa62aefSHarald Welte 				      unsigned int stream_tag,
16530aa62aefSHarald Welte 				      unsigned int format,
16540aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
16550aa62aefSHarald Welte {
16560aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
16570aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
16580aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
16590aa62aefSHarald Welte 	int chs = substream->runtime->channels;
16600aa62aefSHarald Welte 	int i;
16610aa62aefSHarald Welte 
16620aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
16630aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
16640aa62aefSHarald Welte 		if (chs == 2 &&
16650aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
16660aa62aefSHarald Welte 						format) &&
16670aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
16680aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
16690aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
16700aa62aefSHarald Welte 			 * be updated */
16710aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
16720aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
16730aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
16740aa62aefSHarald Welte 						    codec->spdif_ctls &
16750aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
16760aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16770aa62aefSHarald Welte 						   stream_tag, 0, format);
16780aa62aefSHarald Welte 			/* turn on again (if needed) */
16790aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
16800aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
16810aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
16820aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
16830aa62aefSHarald Welte 		} else {
16840aa62aefSHarald Welte 			mout->dig_out_used = 0;
16850aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
16860aa62aefSHarald Welte 						   0, 0, 0);
16870aa62aefSHarald Welte 		}
16880aa62aefSHarald Welte 	}
16890aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
16900aa62aefSHarald Welte 
16910aa62aefSHarald Welte 	/* front */
16920aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
16930aa62aefSHarald Welte 				   0, format);
16940aa62aefSHarald Welte 
1695eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1696eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
16970aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
16980aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
16990aa62aefSHarald Welte 					   0, format);
17000aa62aefSHarald Welte 
17010aa62aefSHarald Welte 	/* extra outputs copied from front */
17020aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
17030aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
17040aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
17050aa62aefSHarald Welte 						   mout->extra_out_nid[i],
17060aa62aefSHarald Welte 						   stream_tag, 0, format);
17070aa62aefSHarald Welte 
17080aa62aefSHarald Welte 	/* surrounds */
17090aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
17100aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
17110aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
17120aa62aefSHarald Welte 						   i * 2, format);
17130aa62aefSHarald Welte 		else /* copy front */
17140aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
17150aa62aefSHarald Welte 						   0, format);
17160aa62aefSHarald Welte 	}
17170aa62aefSHarald Welte }
17180aa62aefSHarald Welte 
17190aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
17200aa62aefSHarald Welte 					  struct hda_codec *codec,
17210aa62aefSHarald Welte 					  unsigned int stream_tag,
17220aa62aefSHarald Welte 					  unsigned int format,
17230aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
17240aa62aefSHarald Welte {
17250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
17260aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
17270aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
17280aa62aefSHarald Welte 
17290aa62aefSHarald Welte 	if (substream->number == 0)
17300aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
17310aa62aefSHarald Welte 					  substream);
17320aa62aefSHarald Welte 	else {
17330aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
17340aa62aefSHarald Welte 		    spec->hp_independent_mode)
17350aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17360aa62aefSHarald Welte 						   stream_tag, 0, format);
17370aa62aefSHarald Welte 	}
17381f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
17390aa62aefSHarald Welte 	return 0;
17400aa62aefSHarald Welte }
17410aa62aefSHarald Welte 
17420aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
17430aa62aefSHarald Welte 				    struct hda_codec *codec,
17440aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
17450aa62aefSHarald Welte {
17460aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
17470aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
17480aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
17490aa62aefSHarald Welte 	int i;
17500aa62aefSHarald Welte 
17510aa62aefSHarald Welte 	if (substream->number == 0) {
17520aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
17530aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
17540aa62aefSHarald Welte 
17550aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
17560aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17570aa62aefSHarald Welte 						   0, 0, 0);
17580aa62aefSHarald Welte 
17590aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
17600aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
17610aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
17620aa62aefSHarald Welte 							mout->extra_out_nid[i],
17630aa62aefSHarald Welte 							0, 0, 0);
17640aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
17650aa62aefSHarald Welte 		if (mout->dig_out_nid &&
17660aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
17670aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
17680aa62aefSHarald Welte 						   0, 0, 0);
17690aa62aefSHarald Welte 			mout->dig_out_used = 0;
17700aa62aefSHarald Welte 		}
17710aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
17720aa62aefSHarald Welte 	} else {
17730aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
17740aa62aefSHarald Welte 		    spec->hp_independent_mode)
17750aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
17760aa62aefSHarald Welte 						   0, 0, 0);
17770aa62aefSHarald Welte 	}
17781f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
17790aa62aefSHarald Welte 	return 0;
17800aa62aefSHarald Welte }
17810aa62aefSHarald Welte 
1782c577b8a1SJoseph Chan /*
1783c577b8a1SJoseph Chan  * Digital out
1784c577b8a1SJoseph Chan  */
1785c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1786c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1787c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1788c577b8a1SJoseph Chan {
1789c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1790c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1791c577b8a1SJoseph Chan }
1792c577b8a1SJoseph Chan 
1793c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1794c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1795c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1796c577b8a1SJoseph Chan {
1797c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1798c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1799c577b8a1SJoseph Chan }
1800c577b8a1SJoseph Chan 
18015691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
180298aa34c0SHarald Welte 					struct hda_codec *codec,
180398aa34c0SHarald Welte 					unsigned int stream_tag,
180498aa34c0SHarald Welte 					unsigned int format,
180598aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
180698aa34c0SHarald Welte {
180798aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
18089da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
18099da29271STakashi Iwai 					     stream_tag, format, substream);
18109da29271STakashi Iwai }
18115691ec7fSHarald Welte 
18129da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
18139da29271STakashi Iwai 					struct hda_codec *codec,
18149da29271STakashi Iwai 					struct snd_pcm_substream *substream)
18159da29271STakashi Iwai {
18169da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
18179da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
181898aa34c0SHarald Welte 	return 0;
181998aa34c0SHarald Welte }
182098aa34c0SHarald Welte 
1821c577b8a1SJoseph Chan /*
1822c577b8a1SJoseph Chan  * Analog capture
1823c577b8a1SJoseph Chan  */
1824c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1825c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1826c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1827c577b8a1SJoseph Chan 				   unsigned int format,
1828c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1829c577b8a1SJoseph Chan {
1830c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1831c577b8a1SJoseph Chan 
1832c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1833c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1834c577b8a1SJoseph Chan 	return 0;
1835c577b8a1SJoseph Chan }
1836c577b8a1SJoseph Chan 
1837c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1838c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1839c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1840c577b8a1SJoseph Chan {
1841c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1842888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1843c577b8a1SJoseph Chan 	return 0;
1844c577b8a1SJoseph Chan }
1845c577b8a1SJoseph Chan 
1846c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
18470aa62aefSHarald Welte 	.substreams = 2,
1848c577b8a1SJoseph Chan 	.channels_min = 2,
1849c577b8a1SJoseph Chan 	.channels_max = 8,
1850c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1851c577b8a1SJoseph Chan 	.ops = {
1852c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
18530aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
18540aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1855c577b8a1SJoseph Chan 	},
1856c577b8a1SJoseph Chan };
1857c577b8a1SJoseph Chan 
1858bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1859c873cc25SLydia Wang 	.substreams = 2,
1860bc9b5623STakashi Iwai 	.channels_min = 2,
1861bc9b5623STakashi Iwai 	.channels_max = 8,
1862bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1863bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1864bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1865bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1866bc9b5623STakashi Iwai 	 */
1867bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1868bc9b5623STakashi Iwai 	.ops = {
1869bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1870c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1871c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1872bc9b5623STakashi Iwai 	},
1873bc9b5623STakashi Iwai };
1874bc9b5623STakashi Iwai 
1875c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1876c577b8a1SJoseph Chan 	.substreams = 2,
1877c577b8a1SJoseph Chan 	.channels_min = 2,
1878c577b8a1SJoseph Chan 	.channels_max = 2,
1879c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1880c577b8a1SJoseph Chan 	.ops = {
1881c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1882c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1883c577b8a1SJoseph Chan 	},
1884c577b8a1SJoseph Chan };
1885c577b8a1SJoseph Chan 
1886c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1887c577b8a1SJoseph Chan 	.substreams = 1,
1888c577b8a1SJoseph Chan 	.channels_min = 2,
1889c577b8a1SJoseph Chan 	.channels_max = 2,
1890c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1891c577b8a1SJoseph Chan 	.ops = {
1892c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
18936b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
18949da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
18959da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1896c577b8a1SJoseph Chan 	},
1897c577b8a1SJoseph Chan };
1898c577b8a1SJoseph Chan 
1899c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1900c577b8a1SJoseph Chan 	.substreams = 1,
1901c577b8a1SJoseph Chan 	.channels_min = 2,
1902c577b8a1SJoseph Chan 	.channels_max = 2,
1903c577b8a1SJoseph Chan };
1904c577b8a1SJoseph Chan 
1905c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1906c577b8a1SJoseph Chan {
1907c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
19085b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
19095b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
19105b0cb1d8SJaroslav Kysela 	int err, i;
1911c577b8a1SJoseph Chan 
1912c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1913c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1914c577b8a1SJoseph Chan 		if (err < 0)
1915c577b8a1SJoseph Chan 			return err;
1916c577b8a1SJoseph Chan 	}
1917c577b8a1SJoseph Chan 
1918c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1919c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1920c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1921c577b8a1SJoseph Chan 		if (err < 0)
1922c577b8a1SJoseph Chan 			return err;
19239a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
19249a08160bSTakashi Iwai 						    &spec->multiout);
19259a08160bSTakashi Iwai 		if (err < 0)
19269a08160bSTakashi Iwai 			return err;
19279a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1928c577b8a1SJoseph Chan 	}
1929c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1930c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1931c577b8a1SJoseph Chan 		if (err < 0)
1932c577b8a1SJoseph Chan 			return err;
1933c577b8a1SJoseph Chan 	}
193417314379SLydia Wang 
19355b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
19365b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
19375b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
193821949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
19395b0cb1d8SJaroslav Kysela 		if (err < 0)
19405b0cb1d8SJaroslav Kysela 			return err;
19415b0cb1d8SJaroslav Kysela 	}
19425b0cb1d8SJaroslav Kysela 
19435b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
19445b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
19455b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
19465b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
19475b0cb1d8SJaroslav Kysela 				continue;
19485b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
19495b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
19505b0cb1d8SJaroslav Kysela 				continue;
19515b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
19525b0cb1d8SJaroslav Kysela 					      knew->subdevice);
19535b0cb1d8SJaroslav Kysela 		}
19545b0cb1d8SJaroslav Kysela 	}
19555b0cb1d8SJaroslav Kysela 
195617314379SLydia Wang 	/* init power states */
195717314379SLydia Wang 	set_jack_power_state(codec);
195817314379SLydia Wang 	analog_low_current_mode(codec, 1);
195917314379SLydia Wang 
1960603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1961c577b8a1SJoseph Chan 	return 0;
1962c577b8a1SJoseph Chan }
1963c577b8a1SJoseph Chan 
1964c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1965c577b8a1SJoseph Chan {
1966c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1967c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1968c577b8a1SJoseph Chan 
1969c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1970c577b8a1SJoseph Chan 	codec->pcm_info = info;
1971c577b8a1SJoseph Chan 
1972c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1973377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1974377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1975377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1976377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1977c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1978c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1979c577b8a1SJoseph Chan 
1980c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1981c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1982c577b8a1SJoseph Chan 
1983c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1984c577b8a1SJoseph Chan 		codec->num_pcms++;
1985c577b8a1SJoseph Chan 		info++;
1986c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
19877ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1988c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1989c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1990c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1991c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1992c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1993c577b8a1SJoseph Chan 		}
1994c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1995c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1996c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1997c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1998c577b8a1SJoseph Chan 				spec->dig_in_nid;
1999c577b8a1SJoseph Chan 		}
2000c577b8a1SJoseph Chan 	}
2001c577b8a1SJoseph Chan 
2002c577b8a1SJoseph Chan 	return 0;
2003c577b8a1SJoseph Chan }
2004c577b8a1SJoseph Chan 
2005c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
2006c577b8a1SJoseph Chan {
2007c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2008c577b8a1SJoseph Chan 
2009c577b8a1SJoseph Chan 	if (!spec)
2010c577b8a1SJoseph Chan 		return;
2011c577b8a1SJoseph Chan 
2012603c4019STakashi Iwai 	via_free_kctls(codec);
20131f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
2014c577b8a1SJoseph Chan 	kfree(codec->spec);
2015c577b8a1SJoseph Chan }
2016c577b8a1SJoseph Chan 
201769e52a80SHarald Welte /* mute internal speaker if HP is plugged */
201869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
201969e52a80SHarald Welte {
2020dcf34c8cSLydia Wang 	unsigned int present = 0;
202169e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
202269e52a80SHarald Welte 
2023d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
2024dcf34c8cSLydia Wang 
2025dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
2026dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
2027dcf34c8cSLydia Wang 		/* auto mute */
2028dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
2029dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
2030dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
2031dcf34c8cSLydia Wang 		/* notify change */
2032dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
2033dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
2034dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
2035dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
2036dcf34c8cSLydia Wang 			       &id);
2037dcf34c8cSLydia Wang 	}
203869e52a80SHarald Welte }
203969e52a80SHarald Welte 
2040f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
2041f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
2042f3db423dSLydia Wang {
2043f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
2044f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
2045f3db423dSLydia Wang 
2046f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
2047f3db423dSLydia Wang 		return;
2048f3db423dSLydia Wang 
2049d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
2050d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
2051f3db423dSLydia Wang 
2052f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
2053f3db423dSLydia Wang 	if (lineout_present) {
2054f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
2055f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
2056f3db423dSLydia Wang 		return;
2057f3db423dSLydia Wang 	}
2058f3db423dSLydia Wang 
2059d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
2060f3db423dSLydia Wang 
2061f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
2062f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
2063f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
2064f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
2065f3db423dSLydia Wang }
2066f3db423dSLydia Wang 
206769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
206869e52a80SHarald Welte {
206969e52a80SHarald Welte 	unsigned int gpio_data;
207069e52a80SHarald Welte 	unsigned int vol_counter;
207169e52a80SHarald Welte 	unsigned int vol;
207269e52a80SHarald Welte 	unsigned int master_vol;
207369e52a80SHarald Welte 
207469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
207569e52a80SHarald Welte 
207669e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
207769e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
207869e52a80SHarald Welte 
207969e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
208069e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
208169e52a80SHarald Welte 
208269e52a80SHarald Welte 	vol = vol_counter & 0x1F;
208369e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
208469e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
208569e52a80SHarald Welte 					AC_AMP_GET_INPUT);
208669e52a80SHarald Welte 
208769e52a80SHarald Welte 	if (gpio_data == 0x02) {
208869e52a80SHarald Welte 		/* unmute line out */
208969e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
209069e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
209169e52a80SHarald Welte 
209269e52a80SHarald Welte 		if (vol_counter & 0x20) {
209369e52a80SHarald Welte 			/* decrease volume */
209469e52a80SHarald Welte 			if (vol > master_vol)
209569e52a80SHarald Welte 				vol = master_vol;
209669e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
209769e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
209869e52a80SHarald Welte 						 master_vol-vol);
209969e52a80SHarald Welte 		} else {
210069e52a80SHarald Welte 			/* increase volume */
210169e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
210269e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
210369e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
210469e52a80SHarald Welte 					  (master_vol+vol));
210569e52a80SHarald Welte 		}
210669e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
210769e52a80SHarald Welte 		/* mute line out */
210869e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
210969e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
211069e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
211169e52a80SHarald Welte 					 HDA_AMP_MUTE);
211269e52a80SHarald Welte 	}
211369e52a80SHarald Welte }
211469e52a80SHarald Welte 
211525eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
211625eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
211725eaba2fSLydia Wang {
211825eaba2fSLydia Wang 	unsigned int hp_present;
211925eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
212025eaba2fSLydia Wang 
2121ab6734e7SLydia Wang 	if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
212225eaba2fSLydia Wang 		return;
212325eaba2fSLydia Wang 
2124d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
212525eaba2fSLydia Wang 
212625eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
212725eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
212825eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
212925eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
213025eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
213125eaba2fSLydia Wang 		/* notify change */
213225eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
213325eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
213425eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
213525eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
213625eaba2fSLydia Wang 			       &id);
213725eaba2fSLydia Wang 	}
213825eaba2fSLydia Wang }
213925eaba2fSLydia Wang 
214025eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
214125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
214225eaba2fSLydia Wang {
214301a1796bSakpm@linux-foundation.org 	/* use long instead of int below just to avoid an internal compiler
214401a1796bSakpm@linux-foundation.org 	 * error with gcc 4.0.x
214501a1796bSakpm@linux-foundation.org 	 */
214601a1796bSakpm@linux-foundation.org 	unsigned long hp_present, present = 0;
214725eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
214825eaba2fSLydia Wang 	int i;
214925eaba2fSLydia Wang 
215025eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
215125eaba2fSLydia Wang 		return;
215225eaba2fSLydia Wang 
2153d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
215425eaba2fSLydia Wang 
2155d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
215625eaba2fSLydia Wang 
215725eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
215825eaba2fSLydia Wang 		/* Mute Line-Outs */
215925eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
216025eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
216125eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
216225eaba2fSLydia Wang 				HDA_OUTPUT, 0,
216325eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
216425eaba2fSLydia Wang 		if (hp_present)
216525eaba2fSLydia Wang 			present = hp_present;
216625eaba2fSLydia Wang 	}
216725eaba2fSLydia Wang 	/* Speakers */
216825eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
216925eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
217025eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
217125eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
217225eaba2fSLydia Wang }
217325eaba2fSLydia Wang 
217425eaba2fSLydia Wang 
217569e52a80SHarald Welte /* unsolicited event for jack sensing */
217669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
217769e52a80SHarald Welte 				  unsigned int res)
217869e52a80SHarald Welte {
217969e52a80SHarald Welte 	res >>= 26;
2180a34df19aSLydia Wang 	if (res & VIA_HP_EVENT)
218169e52a80SHarald Welte 		via_hp_automute(codec);
2182a34df19aSLydia Wang 	if (res & VIA_GPIO_EVENT)
218369e52a80SHarald Welte 		via_gpio_control(codec);
2184a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
2185a34df19aSLydia Wang 		set_jack_power_state(codec);
2186f3db423dSLydia Wang 	if (res & VIA_MONO_EVENT)
2187f3db423dSLydia Wang 		via_mono_automute(codec);
218825eaba2fSLydia Wang 	if (res & VIA_SPEAKER_EVENT)
218925eaba2fSLydia Wang 		via_speaker_automute(codec);
219025eaba2fSLydia Wang 	if (res & VIA_BIND_HP_EVENT)
219125eaba2fSLydia Wang 		via_hp_bind_automute(codec);
219269e52a80SHarald Welte }
219369e52a80SHarald Welte 
2194c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
2195c577b8a1SJoseph Chan {
2196c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
219769e52a80SHarald Welte 	int i;
219869e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
219969e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
220069e52a80SHarald Welte 
2201518bf3baSLydia Wang 	spec->codec_type = get_codec_type(codec);
2202518bf3baSLydia Wang 	if (spec->codec_type == VT1708BCE)
2203518bf3baSLydia Wang 		spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost
2204518bf3baSLydia Wang 					       same */
2205f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
2206f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
220755d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
220855d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2209f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
221012b74c80STakashi Iwai 					    PIN_OUT);
221155d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
2212f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
2213f7278fd0SJosepch Chan 		}
221412b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
221512b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
221612b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2217f7278fd0SJosepch Chan 
22189da29271STakashi Iwai 	/* assign slave outs */
22199da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
22209da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
22215691ec7fSHarald Welte 
2222c577b8a1SJoseph Chan 	return 0;
2223c577b8a1SJoseph Chan }
2224c577b8a1SJoseph Chan 
22251f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
22261f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
22271f2e99feSLydia Wang {
22281f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22291f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
22301f2e99feSLydia Wang 	return 0;
22311f2e99feSLydia Wang }
22321f2e99feSLydia Wang #endif
22331f2e99feSLydia Wang 
2234cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2235cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
2236cb53c626STakashi Iwai {
2237cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
2238cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
2239cb53c626STakashi Iwai }
2240cb53c626STakashi Iwai #endif
2241cb53c626STakashi Iwai 
2242c577b8a1SJoseph Chan /*
2243c577b8a1SJoseph Chan  */
2244c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
2245c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
2246c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
2247c577b8a1SJoseph Chan 	.init = via_init,
2248c577b8a1SJoseph Chan 	.free = via_free,
22491f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
22501f2e99feSLydia Wang 	.suspend = via_suspend,
22511f2e99feSLydia Wang #endif
2252cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2253cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
2254cb53c626STakashi Iwai #endif
2255c577b8a1SJoseph Chan };
2256c577b8a1SJoseph Chan 
2257c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
2258c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
2259c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2260c577b8a1SJoseph Chan {
2261c577b8a1SJoseph Chan 	int i;
2262c577b8a1SJoseph Chan 	hda_nid_t nid;
2263c577b8a1SJoseph Chan 
2264c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
2265c577b8a1SJoseph Chan 
2266c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2267c577b8a1SJoseph Chan 
2268c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
2269c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2270c577b8a1SJoseph Chan 		if (nid) {
2271c577b8a1SJoseph Chan 			/* config dac list */
2272c577b8a1SJoseph Chan 			switch (i) {
2273c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
2274c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
2275c577b8a1SJoseph Chan 				break;
2276c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
2277c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
2278c577b8a1SJoseph Chan 				break;
2279c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
2280fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
2281c577b8a1SJoseph Chan 				break;
2282c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
2283fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
2284c577b8a1SJoseph Chan 				break;
2285c577b8a1SJoseph Chan 			}
2286c577b8a1SJoseph Chan 		}
2287c577b8a1SJoseph Chan 	}
2288c577b8a1SJoseph Chan 
2289c577b8a1SJoseph Chan 	return 0;
2290c577b8a1SJoseph Chan }
2291c577b8a1SJoseph Chan 
2292c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2293c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
2294c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2295c577b8a1SJoseph Chan {
2296c577b8a1SJoseph Chan 	char name[32];
2297ea734963STakashi Iwai 	static const char * const chname[4] = {
2298ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
2299ea734963STakashi Iwai 	};
23009645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
2301c577b8a1SJoseph Chan 	int i, err;
2302c577b8a1SJoseph Chan 
2303c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2304c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2305c577b8a1SJoseph Chan 
2306c577b8a1SJoseph Chan 		if (!nid)
2307c577b8a1SJoseph Chan 			continue;
2308c577b8a1SJoseph Chan 
23099645c203SLydia Wang 		nid_vol = nid_vols[i];
2310c577b8a1SJoseph Chan 
2311c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2312c577b8a1SJoseph Chan 			/* Center/LFE */
2313c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2314c577b8a1SJoseph Chan 					"Center Playback Volume",
2315f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2316f7278fd0SJosepch Chan 							    HDA_OUTPUT));
2317c577b8a1SJoseph Chan 			if (err < 0)
2318c577b8a1SJoseph Chan 				return err;
2319c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2320c577b8a1SJoseph Chan 					      "LFE Playback Volume",
2321f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2322f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2323c577b8a1SJoseph Chan 			if (err < 0)
2324c577b8a1SJoseph Chan 				return err;
2325c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2326c577b8a1SJoseph Chan 					      "Center Playback Switch",
2327f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2328f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2329c577b8a1SJoseph Chan 			if (err < 0)
2330c577b8a1SJoseph Chan 				return err;
2331c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2332c577b8a1SJoseph Chan 					      "LFE Playback Switch",
2333f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2334f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2335c577b8a1SJoseph Chan 			if (err < 0)
2336c577b8a1SJoseph Chan 				return err;
2337c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
2338c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
2339c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2340c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
23419645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2342f7278fd0SJosepch Chan 								  HDA_INPUT));
2343c577b8a1SJoseph Chan 			if (err < 0)
2344c577b8a1SJoseph Chan 				return err;
2345c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2346c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
23479645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2348f7278fd0SJosepch Chan 								  HDA_INPUT));
2349c577b8a1SJoseph Chan 			if (err < 0)
2350c577b8a1SJoseph Chan 				return err;
2351c577b8a1SJoseph Chan 
2352c577b8a1SJoseph Chan 			/* add control to PW3 */
2353c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2354c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2355f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2356f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2357c577b8a1SJoseph Chan 			if (err < 0)
2358c577b8a1SJoseph Chan 				return err;
2359c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2360c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2361f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2362f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2363c577b8a1SJoseph Chan 			if (err < 0)
2364c577b8a1SJoseph Chan 				return err;
2365c577b8a1SJoseph Chan 		} else {
2366c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2367c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2368f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2369f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2370c577b8a1SJoseph Chan 			if (err < 0)
2371c577b8a1SJoseph Chan 				return err;
2372c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2373c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2374f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2375f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2376c577b8a1SJoseph Chan 			if (err < 0)
2377c577b8a1SJoseph Chan 				return err;
2378c577b8a1SJoseph Chan 		}
2379c577b8a1SJoseph Chan 	}
2380c577b8a1SJoseph Chan 
2381c577b8a1SJoseph Chan 	return 0;
2382c577b8a1SJoseph Chan }
2383c577b8a1SJoseph Chan 
23840aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
23850aa62aefSHarald Welte {
23860aa62aefSHarald Welte 	int i;
23870aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
2388ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
23890aa62aefSHarald Welte 
23900aa62aefSHarald Welte 	/* for hp mode select */
239110a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
239210a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
23930aa62aefSHarald Welte 
23940aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
23950aa62aefSHarald Welte }
23960aa62aefSHarald Welte 
2397c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2398c577b8a1SJoseph Chan {
2399c577b8a1SJoseph Chan 	int err;
2400c577b8a1SJoseph Chan 
2401c577b8a1SJoseph Chan 	if (!pin)
2402c577b8a1SJoseph Chan 		return 0;
2403c577b8a1SJoseph Chan 
2404c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
2405cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2406c577b8a1SJoseph Chan 
2407c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2408c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2409c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2410c577b8a1SJoseph Chan 	if (err < 0)
2411c577b8a1SJoseph Chan 		return err;
2412c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2413c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2414c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2415c577b8a1SJoseph Chan 	if (err < 0)
2416c577b8a1SJoseph Chan 		return err;
2417c577b8a1SJoseph Chan 
24180aa62aefSHarald Welte 	create_hp_imux(spec);
24190aa62aefSHarald Welte 
2420c577b8a1SJoseph Chan 	return 0;
2421c577b8a1SJoseph Chan }
2422c577b8a1SJoseph Chan 
2423c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
242410a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
2425f3268512STakashi Iwai 					    const struct auto_pin_cfg *cfg,
2426f3268512STakashi Iwai 					    hda_nid_t cap_nid,
2427f3268512STakashi Iwai 					    hda_nid_t pin_idxs[], int num_idxs)
2428c577b8a1SJoseph Chan {
242910a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
24300aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
24317b315bb4STakashi Iwai 	int i, err, idx, type, type_idx = 0;
2432c577b8a1SJoseph Chan 
2433c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2434f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2435f3268512STakashi Iwai 		if (pin_idxs[idx] == 0xff) {
243610a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2437f3268512STakashi Iwai 			break;
2438f3268512STakashi Iwai 		}
2439f3268512STakashi Iwai 	}
2440c577b8a1SJoseph Chan 
24417b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
244210a20af7STakashi Iwai 		const char *label;
24437b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2444f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
24457b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2446c577b8a1SJoseph Chan 				break;
2447f3268512STakashi Iwai 		if (idx >= num_idxs)
2448f3268512STakashi Iwai 			continue;
24497b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
24507b315bb4STakashi Iwai 			type_idx++;
24517b315bb4STakashi Iwai 		else
24527b315bb4STakashi Iwai 			type_idx = 0;
245310a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
245410a20af7STakashi Iwai 		err = via_new_analog_input(spec, label, type_idx, idx, cap_nid);
2455c577b8a1SJoseph Chan 		if (err < 0)
2456c577b8a1SJoseph Chan 			return err;
245710a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2458c577b8a1SJoseph Chan 	}
2459c577b8a1SJoseph Chan 	return 0;
2460c577b8a1SJoseph Chan }
2461c577b8a1SJoseph Chan 
2462f3268512STakashi Iwai /* create playback/capture controls for input pins */
246310a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
2464f3268512STakashi Iwai 						const struct auto_pin_cfg *cfg)
2465f3268512STakashi Iwai {
2466f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
246710a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
2468f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
2469f3268512STakashi Iwai }
2470f3268512STakashi Iwai 
2471cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2472cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2473cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2474cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2475cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2476cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2477cb53c626STakashi Iwai 	{ } /* end */
2478cb53c626STakashi Iwai };
2479cb53c626STakashi Iwai #endif
2480cb53c626STakashi Iwai 
248176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
248276d9b0ddSHarald Welte {
248376d9b0ddSHarald Welte 	unsigned int def_conf;
248476d9b0ddSHarald Welte 	unsigned char seqassoc;
248576d9b0ddSHarald Welte 
24862f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
248776d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
248876d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
248982ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
249082ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
249176d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
24922f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
249376d9b0ddSHarald Welte 	}
249476d9b0ddSHarald Welte 
249576d9b0ddSHarald Welte 	return;
249676d9b0ddSHarald Welte }
249776d9b0ddSHarald Welte 
24981f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
24991f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
25001f2e99feSLydia Wang {
25011f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
25021f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
25031f2e99feSLydia Wang 
25041f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25051f2e99feSLydia Wang 		return 0;
25061f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
25071f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
25081f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
25091f2e99feSLydia Wang 	return 0;
25101f2e99feSLydia Wang }
25111f2e99feSLydia Wang 
25121f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
25131f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
25141f2e99feSLydia Wang {
25151f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
25161f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
25171f2e99feSLydia Wang 	int change;
25181f2e99feSLydia Wang 
25191f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25201f2e99feSLydia Wang 		return 0;
25211f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
25221f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
25231f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
25241f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
25251f2e99feSLydia Wang 		mute_aa_path(codec, 1);
25261f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
25271f2e99feSLydia Wang 	}
25281f2e99feSLydia Wang 	return change;
25291f2e99feSLydia Wang }
25301f2e99feSLydia Wang 
25311f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
25321f2e99feSLydia Wang 	{
25331f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25341f2e99feSLydia Wang 		.name = "Jack Detect",
25351f2e99feSLydia Wang 		.count = 1,
25361f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
25371f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
25381f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
25391f2e99feSLydia Wang 	},
25401f2e99feSLydia Wang 	{} /* end */
25411f2e99feSLydia Wang };
25421f2e99feSLydia Wang 
2543c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2544c577b8a1SJoseph Chan {
2545c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2546c577b8a1SJoseph Chan 	int err;
2547c577b8a1SJoseph Chan 
254876d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
254976d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
255076d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
255176d9b0ddSHarald Welte 
2552c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2553c577b8a1SJoseph Chan 	if (err < 0)
2554c577b8a1SJoseph Chan 		return err;
2555c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2556c577b8a1SJoseph Chan 	if (err < 0)
2557c577b8a1SJoseph Chan 		return err;
2558c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2559c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2560c577b8a1SJoseph Chan 
2561c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2562c577b8a1SJoseph Chan 	if (err < 0)
2563c577b8a1SJoseph Chan 		return err;
2564c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2565c577b8a1SJoseph Chan 	if (err < 0)
2566c577b8a1SJoseph Chan 		return err;
256710a20af7STakashi Iwai 	err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
2568c577b8a1SJoseph Chan 	if (err < 0)
2569c577b8a1SJoseph Chan 		return err;
25701f2e99feSLydia Wang 	/* add jack detect on/off control */
25711f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
25721f2e99feSLydia Wang 	if (err < 0)
25731f2e99feSLydia Wang 		return err;
2574c577b8a1SJoseph Chan 
2575c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2576c577b8a1SJoseph Chan 
25770852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2578c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
257955d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2580c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2581c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2582c577b8a1SJoseph Chan 
2583603c4019STakashi Iwai 	if (spec->kctls.list)
2584603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2585c577b8a1SJoseph Chan 
258669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2587c577b8a1SJoseph Chan 
25880aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
25890aa62aefSHarald Welte 
2590f8fdd495SHarald Welte 	if (spec->hp_mux)
25913d83e577STakashi Iwai 		via_hp_build(codec);
2592c577b8a1SJoseph Chan 
25935b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2594c577b8a1SJoseph Chan 	return 1;
2595c577b8a1SJoseph Chan }
2596c577b8a1SJoseph Chan 
2597c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2598c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2599c577b8a1SJoseph Chan {
260025eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
260125eaba2fSLydia Wang 
2602c577b8a1SJoseph Chan 	via_init(codec);
2603c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2604c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2605c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
2606ab6734e7SLydia Wang 	if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
260725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
260825eaba2fSLydia Wang 	} else {
260925eaba2fSLydia Wang 		via_hp_automute(codec);
261025eaba2fSLydia Wang 		via_speaker_automute(codec);
261125eaba2fSLydia Wang 	}
261225eaba2fSLydia Wang 
2613c577b8a1SJoseph Chan 	return 0;
2614c577b8a1SJoseph Chan }
2615c577b8a1SJoseph Chan 
26161f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
26171f2e99feSLydia Wang {
26181f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
26191f2e99feSLydia Wang 					     vt1708_hp_work.work);
26201f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
26211f2e99feSLydia Wang 		return;
26221f2e99feSLydia Wang 	/* if jack state toggled */
26231f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2624d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
26251f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
26261f2e99feSLydia Wang 		via_hp_automute(spec->codec);
26271f2e99feSLydia Wang 	}
26281f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
26291f2e99feSLydia Wang }
26301f2e99feSLydia Wang 
2631337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2632337b9d02STakashi Iwai {
2633337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2634337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2635337b9d02STakashi Iwai 	unsigned int type;
2636337b9d02STakashi Iwai 	int i, n;
2637337b9d02STakashi Iwai 
2638337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2639337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2640337b9d02STakashi Iwai 		while (nid) {
2641a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
26421c55d521STakashi Iwai 			if (type == AC_WID_PIN)
26431c55d521STakashi Iwai 				break;
2644337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2645337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2646337b9d02STakashi Iwai 			if (n <= 0)
2647337b9d02STakashi Iwai 				break;
2648337b9d02STakashi Iwai 			if (n > 1) {
2649337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2650337b9d02STakashi Iwai 				break;
2651337b9d02STakashi Iwai 			}
2652337b9d02STakashi Iwai 			nid = conn[0];
2653337b9d02STakashi Iwai 		}
2654337b9d02STakashi Iwai 	}
26551c55d521STakashi Iwai 	return 0;
2656337b9d02STakashi Iwai }
2657337b9d02STakashi Iwai 
2658c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2659c577b8a1SJoseph Chan {
2660c577b8a1SJoseph Chan 	struct via_spec *spec;
2661c577b8a1SJoseph Chan 	int err;
2662c577b8a1SJoseph Chan 
2663c577b8a1SJoseph Chan 	/* create a codec specific record */
26645b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2665c577b8a1SJoseph Chan 	if (spec == NULL)
2666c577b8a1SJoseph Chan 		return -ENOMEM;
2667c577b8a1SJoseph Chan 
2668c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2669c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2670c577b8a1SJoseph Chan 	if (err < 0) {
2671c577b8a1SJoseph Chan 		via_free(codec);
2672c577b8a1SJoseph Chan 		return err;
2673c577b8a1SJoseph Chan 	} else if (!err) {
2674c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2675c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2676c577b8a1SJoseph Chan 	}
2677c577b8a1SJoseph Chan 
2678c577b8a1SJoseph Chan 
2679c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2680c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2681bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2682bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2683bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2684c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2685c577b8a1SJoseph Chan 
2686c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2687c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2688c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2689c577b8a1SJoseph Chan 
2690c577b8a1SJoseph Chan 
2691c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2692c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2693c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
26940f67a611STakashi Iwai 		get_mux_nids(codec);
2695c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2696c577b8a1SJoseph Chan 		spec->num_mixers++;
2697c577b8a1SJoseph Chan 	}
2698c577b8a1SJoseph Chan 
2699c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2700c577b8a1SJoseph Chan 
2701c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2702cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2703cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2704cb53c626STakashi Iwai #endif
27051f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2706c577b8a1SJoseph Chan 	return 0;
2707c577b8a1SJoseph Chan }
2708c577b8a1SJoseph Chan 
2709c577b8a1SJoseph Chan /* capture mixer elements */
2710c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2711c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2712c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2713c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2714c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2715c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2716c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2717c577b8a1SJoseph Chan 	{
2718c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2719c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2720c577b8a1SJoseph Chan 		 * So call somewhat different..
2721c577b8a1SJoseph Chan 		 */
2722c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2723c577b8a1SJoseph Chan 		.name = "Input Source",
2724c577b8a1SJoseph Chan 		.count = 1,
2725c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2726c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2727c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2728c577b8a1SJoseph Chan 	},
2729c577b8a1SJoseph Chan 	{ } /* end */
2730c577b8a1SJoseph Chan };
2731c577b8a1SJoseph Chan 
273269e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2733a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2734a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
273569e52a80SHarald Welte 	{ }
273669e52a80SHarald Welte };
273769e52a80SHarald Welte 
2738c577b8a1SJoseph Chan /*
2739c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2740c577b8a1SJoseph Chan  */
2741c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2742c577b8a1SJoseph Chan 	/*
2743c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2744c577b8a1SJoseph Chan 	 */
2745c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2746c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2747c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2748c577b8a1SJoseph Chan 
2749c577b8a1SJoseph Chan 
2750f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2751c577b8a1SJoseph Chan 	 * mixer widget
2752c577b8a1SJoseph Chan 	 */
2753c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2754f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2755f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2756f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2757f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2758f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2759c577b8a1SJoseph Chan 
2760c577b8a1SJoseph Chan 	/*
2761c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2762c577b8a1SJoseph Chan 	 */
2763c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2764c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2765c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2766c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2767c577b8a1SJoseph Chan 
2768c577b8a1SJoseph Chan 	/*
2769c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2770c577b8a1SJoseph Chan 	 */
2771c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2772c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2773c577b8a1SJoseph Chan 
2774bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2775bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2776c577b8a1SJoseph Chan 	/* PW9 Output enable */
2777c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2778c577b8a1SJoseph Chan 	{ }
2779c577b8a1SJoseph Chan };
2780c577b8a1SJoseph Chan 
2781c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2782c577b8a1SJoseph Chan 	.substreams = 1,
2783c577b8a1SJoseph Chan 	.channels_min = 2,
2784c577b8a1SJoseph Chan 	.channels_max = 10,
2785c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2786c577b8a1SJoseph Chan 	.ops = {
2787c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2788c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2789c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2790c577b8a1SJoseph Chan 	},
2791c577b8a1SJoseph Chan };
2792c577b8a1SJoseph Chan 
2793c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2794c577b8a1SJoseph Chan 	.substreams = 1,
2795c577b8a1SJoseph Chan 	.channels_min = 2,
2796c577b8a1SJoseph Chan 	.channels_max = 6,
2797c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2798c577b8a1SJoseph Chan 	.ops = {
2799c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2800c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2801c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2802c577b8a1SJoseph Chan 	},
2803c577b8a1SJoseph Chan };
2804c577b8a1SJoseph Chan 
2805c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2806c577b8a1SJoseph Chan 	.substreams = 2,
2807c577b8a1SJoseph Chan 	.channels_min = 2,
2808c577b8a1SJoseph Chan 	.channels_max = 2,
2809c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2810c577b8a1SJoseph Chan 	.ops = {
2811c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2812c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2813c577b8a1SJoseph Chan 	},
2814c577b8a1SJoseph Chan };
2815c577b8a1SJoseph Chan 
2816c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2817c577b8a1SJoseph Chan 	.substreams = 1,
2818c577b8a1SJoseph Chan 	.channels_min = 2,
2819c577b8a1SJoseph Chan 	.channels_max = 2,
2820c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2821c577b8a1SJoseph Chan 	.ops = {
2822c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2823c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2824c577b8a1SJoseph Chan 	},
2825c577b8a1SJoseph Chan };
2826c577b8a1SJoseph Chan 
2827c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2828c577b8a1SJoseph Chan 	.substreams = 1,
2829c577b8a1SJoseph Chan 	.channels_min = 2,
2830c577b8a1SJoseph Chan 	.channels_max = 2,
2831c577b8a1SJoseph Chan };
2832c577b8a1SJoseph Chan 
2833c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2834c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2835c577b8a1SJoseph Chan {
2836c577b8a1SJoseph Chan 	int i;
2837c577b8a1SJoseph Chan 	hda_nid_t nid;
2838c577b8a1SJoseph Chan 
2839c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2840c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2841c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2842c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2843c577b8a1SJoseph Chan 
2844c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2845c577b8a1SJoseph Chan 
2846c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2847c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2848c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2849c577b8a1SJoseph Chan 			if (nid) {
2850c577b8a1SJoseph Chan 				/* config dac list */
2851c577b8a1SJoseph Chan 				switch (i) {
2852c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2853c577b8a1SJoseph Chan 					/* AOW0 */
2854c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2855c577b8a1SJoseph Chan 					break;
2856c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2857c577b8a1SJoseph Chan 					/* AOW2 */
2858c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2859c577b8a1SJoseph Chan 					break;
2860c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2861c577b8a1SJoseph Chan 					/* AOW3 */
2862fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2863c577b8a1SJoseph Chan 					break;
2864c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2865c577b8a1SJoseph Chan 					/* AOW1 */
2866fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2867c577b8a1SJoseph Chan 					break;
2868c577b8a1SJoseph Chan 				default:
2869c577b8a1SJoseph Chan 					break;
2870c577b8a1SJoseph Chan 				}
2871c577b8a1SJoseph Chan 			}
2872c577b8a1SJoseph Chan 		}
2873c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2874c577b8a1SJoseph Chan 
2875c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2876c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2877c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2878c577b8a1SJoseph Chan 			if (nid) {
2879c577b8a1SJoseph Chan 				/* config dac list */
2880c577b8a1SJoseph Chan 				switch (i) {
2881c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2882c577b8a1SJoseph Chan 					/* AOW0 */
2883c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2884c577b8a1SJoseph Chan 					break;
2885c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2886c577b8a1SJoseph Chan 					/* AOW2 */
2887c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2888c577b8a1SJoseph Chan 					break;
2889c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2890c577b8a1SJoseph Chan 					/* AOW1 */
2891c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2892c577b8a1SJoseph Chan 					break;
2893c577b8a1SJoseph Chan 				default:
2894c577b8a1SJoseph Chan 					break;
2895c577b8a1SJoseph Chan 				}
2896c577b8a1SJoseph Chan 			}
2897c577b8a1SJoseph Chan 		}
2898c577b8a1SJoseph Chan 	}
2899c577b8a1SJoseph Chan 
2900c577b8a1SJoseph Chan 	return 0;
2901c577b8a1SJoseph Chan }
2902c577b8a1SJoseph Chan 
2903c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2904c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2905c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2906c577b8a1SJoseph Chan {
2907c577b8a1SJoseph Chan 	char name[32];
2908ea734963STakashi Iwai 	static const char * const chname[4] = {
2909ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
2910ea734963STakashi Iwai 	};
29114483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2912c577b8a1SJoseph Chan 	int i, err;
2913c577b8a1SJoseph Chan 
2914c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2915c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2916c577b8a1SJoseph Chan 
2917c577b8a1SJoseph Chan 		if (!nid)
2918c577b8a1SJoseph Chan 			continue;
2919c577b8a1SJoseph Chan 
29204483a2f5SLydia Wang 		nid_vol = nid_vols[i];
29214483a2f5SLydia Wang 
2922c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2923c577b8a1SJoseph Chan 			/* Center/LFE */
2924c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2925c577b8a1SJoseph Chan 					      "Center Playback Volume",
29264483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2927f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2928c577b8a1SJoseph Chan 			if (err < 0)
2929c577b8a1SJoseph Chan 				return err;
2930c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2931c577b8a1SJoseph Chan 					      "LFE Playback Volume",
29324483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2933f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2934c577b8a1SJoseph Chan 			if (err < 0)
2935c577b8a1SJoseph Chan 				return err;
2936c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2937c577b8a1SJoseph Chan 					      "Center Playback Switch",
29384483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2939f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2940c577b8a1SJoseph Chan 			if (err < 0)
2941c577b8a1SJoseph Chan 				return err;
2942c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2943c577b8a1SJoseph Chan 					      "LFE Playback Switch",
29444483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2945f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2946c577b8a1SJoseph Chan 			if (err < 0)
2947c577b8a1SJoseph Chan 				return err;
2948c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
29494483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2950c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2951c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
29524483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2953f7278fd0SJosepch Chan 								  HDA_INPUT));
2954c577b8a1SJoseph Chan 			if (err < 0)
2955c577b8a1SJoseph Chan 				return err;
2956c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2957c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
29584483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2959f7278fd0SJosepch Chan 								  HDA_INPUT));
2960c577b8a1SJoseph Chan 			if (err < 0)
2961c577b8a1SJoseph Chan 				return err;
2962c577b8a1SJoseph Chan 
2963c577b8a1SJoseph Chan 			/* add control to PW3 */
2964c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2965c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2966f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2967f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2968c577b8a1SJoseph Chan 			if (err < 0)
2969c577b8a1SJoseph Chan 				return err;
2970c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2971c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2972f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2973f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2974c577b8a1SJoseph Chan 			if (err < 0)
2975c577b8a1SJoseph Chan 				return err;
2976c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2977c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2978c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
29794483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2980f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2981c577b8a1SJoseph Chan 			if (err < 0)
2982c577b8a1SJoseph Chan 				return err;
2983c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2984c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
29854483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2986f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2987c577b8a1SJoseph Chan 			if (err < 0)
2988c577b8a1SJoseph Chan 				return err;
2989c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2990c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2991c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
29924483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2993f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2994c577b8a1SJoseph Chan 			if (err < 0)
2995c577b8a1SJoseph Chan 				return err;
2996c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2997c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
29984483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2999f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3000c577b8a1SJoseph Chan 			if (err < 0)
3001c577b8a1SJoseph Chan 				return err;
3002c577b8a1SJoseph Chan 		}
3003c577b8a1SJoseph Chan 	}
3004c577b8a1SJoseph Chan 
3005c577b8a1SJoseph Chan 	return 0;
3006c577b8a1SJoseph Chan }
3007c577b8a1SJoseph Chan 
3008c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3009c577b8a1SJoseph Chan {
3010c577b8a1SJoseph Chan 	int err;
3011c577b8a1SJoseph Chan 
3012c577b8a1SJoseph Chan 	if (!pin)
3013c577b8a1SJoseph Chan 		return 0;
3014c577b8a1SJoseph Chan 
3015c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
3016c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
3017c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
3018c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
3019cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3020c577b8a1SJoseph Chan 
3021c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3022c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
3023c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3024c577b8a1SJoseph Chan 	if (err < 0)
3025c577b8a1SJoseph Chan 		return err;
3026c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3027c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
3028c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3029c577b8a1SJoseph Chan 	if (err < 0)
3030c577b8a1SJoseph Chan 		return err;
3031c577b8a1SJoseph Chan 
3032c577b8a1SJoseph Chan 	return 0;
3033c577b8a1SJoseph Chan }
3034c577b8a1SJoseph Chan 
3035c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
303610a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
3037c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
3038c577b8a1SJoseph Chan {
3039f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
304010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
3041f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3042c577b8a1SJoseph Chan }
3043c577b8a1SJoseph Chan 
3044c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
3045c577b8a1SJoseph Chan {
3046c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
3047c577b8a1SJoseph Chan 	int err;
3048c577b8a1SJoseph Chan 
3049c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3050c577b8a1SJoseph Chan 	if (err < 0)
3051c577b8a1SJoseph Chan 		return err;
3052c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
3053c577b8a1SJoseph Chan 	if (err < 0)
3054c577b8a1SJoseph Chan 		return err;
3055c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3056c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
3057c577b8a1SJoseph Chan 
3058c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
3059c577b8a1SJoseph Chan 	if (err < 0)
3060c577b8a1SJoseph Chan 		return err;
3061c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3062c577b8a1SJoseph Chan 	if (err < 0)
3063c577b8a1SJoseph Chan 		return err;
306410a20af7STakashi Iwai 	err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
3065c577b8a1SJoseph Chan 	if (err < 0)
3066c577b8a1SJoseph Chan 		return err;
3067c577b8a1SJoseph Chan 
3068c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3069c577b8a1SJoseph Chan 
30700852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3071c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
307255d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
3073c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
3074c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
3075c577b8a1SJoseph Chan 
3076603c4019STakashi Iwai 	if (spec->kctls.list)
3077603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3078c577b8a1SJoseph Chan 
30790aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
3080c577b8a1SJoseph Chan 
3081f8fdd495SHarald Welte 	if (spec->hp_mux)
30823d83e577STakashi Iwai 		via_hp_build(codec);
3083f8fdd495SHarald Welte 
30845b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3085c577b8a1SJoseph Chan 	return 1;
3086c577b8a1SJoseph Chan }
3087c577b8a1SJoseph Chan 
3088cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3089cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
3090cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
3091cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
3092cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
3093cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
3094cb53c626STakashi Iwai 	{ } /* end */
3095cb53c626STakashi Iwai };
3096cb53c626STakashi Iwai #endif
3097cb53c626STakashi Iwai 
3098c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
3099c577b8a1SJoseph Chan {
3100c577b8a1SJoseph Chan 	struct via_spec *spec;
3101c577b8a1SJoseph Chan 	int err;
3102c577b8a1SJoseph Chan 
3103c577b8a1SJoseph Chan 	/* create a codec specific record */
31045b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3105c577b8a1SJoseph Chan 	if (spec == NULL)
3106c577b8a1SJoseph Chan 		return -ENOMEM;
3107c577b8a1SJoseph Chan 
3108c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3109c577b8a1SJoseph Chan 	if (err < 0) {
3110c577b8a1SJoseph Chan 		via_free(codec);
3111c577b8a1SJoseph Chan 		return err;
3112c577b8a1SJoseph Chan 	} else if (!err) {
3113c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3114c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3115c577b8a1SJoseph Chan 	}
3116c577b8a1SJoseph Chan 
311769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
311869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3119c577b8a1SJoseph Chan 
3120c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3121c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
3122c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3123c577b8a1SJoseph Chan 
3124c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3125c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3126c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3127c577b8a1SJoseph Chan 
3128c577b8a1SJoseph Chan 
3129c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3130c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3131c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3132337b9d02STakashi Iwai 		get_mux_nids(codec);
3133c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3134c577b8a1SJoseph Chan 		spec->num_mixers++;
3135c577b8a1SJoseph Chan 	}
3136c577b8a1SJoseph Chan 
3137c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3138c577b8a1SJoseph Chan 
3139c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
314069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3141cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3142cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3143cb53c626STakashi Iwai #endif
3144c577b8a1SJoseph Chan 
3145c577b8a1SJoseph Chan 	return 0;
3146c577b8a1SJoseph Chan }
3147c577b8a1SJoseph Chan /*
3148c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
3149c577b8a1SJoseph Chan  */
3150c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
3151c577b8a1SJoseph Chan 	/*
3152c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
3153c577b8a1SJoseph Chan 	 */
3154c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3155c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3156c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3157c577b8a1SJoseph Chan 
3158c577b8a1SJoseph Chan 
3159c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3160c577b8a1SJoseph Chan 	 * mixer widget
3161c577b8a1SJoseph Chan 	 */
3162c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3163c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3164c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3165c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3166c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3167c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3168c577b8a1SJoseph Chan 
3169c577b8a1SJoseph Chan 	/*
3170c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
3171c577b8a1SJoseph Chan 	 */
3172c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
3173c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3174c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3175c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3176c577b8a1SJoseph Chan 
3177c577b8a1SJoseph Chan 	/*
3178c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
3179c577b8a1SJoseph Chan 	 */
3180c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3181c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3182c577b8a1SJoseph Chan 
3183c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
3184c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
3185c577b8a1SJoseph Chan 	/* PW9 Output enable */
3186c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3187c577b8a1SJoseph Chan 	{ }
3188c577b8a1SJoseph Chan };
3189c577b8a1SJoseph Chan 
3190c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
3191c577b8a1SJoseph Chan {
3192c577b8a1SJoseph Chan 	struct via_spec *spec;
3193c577b8a1SJoseph Chan 	int err;
3194c577b8a1SJoseph Chan 
3195c577b8a1SJoseph Chan 	/* create a codec specific record */
31965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3197c577b8a1SJoseph Chan 	if (spec == NULL)
3198c577b8a1SJoseph Chan 		return -ENOMEM;
3199c577b8a1SJoseph Chan 
3200c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
3201c577b8a1SJoseph Chan 	if (err < 0) {
3202c577b8a1SJoseph Chan 		via_free(codec);
3203c577b8a1SJoseph Chan 		return err;
3204c577b8a1SJoseph Chan 	} else if (!err) {
3205c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
3206c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
3207c577b8a1SJoseph Chan 	}
3208c577b8a1SJoseph Chan 
320969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
321069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
3211c577b8a1SJoseph Chan 
3212c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
3213c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
3214c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
3215c577b8a1SJoseph Chan 
3216c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
3217c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
3218c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
3219c577b8a1SJoseph Chan 
3220c577b8a1SJoseph Chan 
3221c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
3222c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
3223c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
3224337b9d02STakashi Iwai 		get_mux_nids(codec);
3225c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
3226c577b8a1SJoseph Chan 		spec->num_mixers++;
3227c577b8a1SJoseph Chan 	}
3228c577b8a1SJoseph Chan 
3229c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
3230c577b8a1SJoseph Chan 
3231c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
323269e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3233cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
3234cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
3235cb53c626STakashi Iwai #endif
3236f7278fd0SJosepch Chan 	return 0;
3237f7278fd0SJosepch Chan }
3238f7278fd0SJosepch Chan 
3239f7278fd0SJosepch Chan /* capture mixer elements */
3240f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
3241f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3242f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3243f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3244f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3245f7278fd0SJosepch Chan 	{
3246f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3247f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
3248f7278fd0SJosepch Chan 		 * So call somewhat different..
3249f7278fd0SJosepch Chan 		 */
3250f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
3251f7278fd0SJosepch Chan 		.name = "Input Source",
3252f7278fd0SJosepch Chan 		.count = 1,
3253f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
3254f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
3255f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
3256f7278fd0SJosepch Chan 	},
3257f7278fd0SJosepch Chan 	{ } /* end */
3258f7278fd0SJosepch Chan };
3259f7278fd0SJosepch Chan /*
3260f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
3261f7278fd0SJosepch Chan  */
3262f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
3263f7278fd0SJosepch Chan 	/*
3264f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3265f7278fd0SJosepch Chan 	 */
3266f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3267f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3268f7278fd0SJosepch Chan 
3269f7278fd0SJosepch Chan 
3270f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3271f7278fd0SJosepch Chan 	 * mixer widget
3272f7278fd0SJosepch Chan 	 */
3273f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3274f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3275f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3276f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3277f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3278f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3279f7278fd0SJosepch Chan 
3280f7278fd0SJosepch Chan 	/*
3281f7278fd0SJosepch Chan 	 * Set up output mixers
3282f7278fd0SJosepch Chan 	 */
3283f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3284f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3285f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3286f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3287f7278fd0SJosepch Chan 
3288f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
3289bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
3290f7278fd0SJosepch Chan 	/* PW9 Output enable */
3291f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3292f7278fd0SJosepch Chan 	/* PW10 Input enable */
3293f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3294f7278fd0SJosepch Chan 	{ }
3295f7278fd0SJosepch Chan };
3296f7278fd0SJosepch Chan 
3297f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
3298f7278fd0SJosepch Chan 	/*
3299f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
3300f7278fd0SJosepch Chan 	 */
3301f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3302f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3303f7278fd0SJosepch Chan 
3304f7278fd0SJosepch Chan 
3305f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3306f7278fd0SJosepch Chan 	 * mixer widget
3307f7278fd0SJosepch Chan 	 */
3308f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3309f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3310f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3311f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3312f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3313f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3314f7278fd0SJosepch Chan 
3315f7278fd0SJosepch Chan 	/*
3316f7278fd0SJosepch Chan 	 * Set up output mixers
3317f7278fd0SJosepch Chan 	 */
3318f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
3319f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3320f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3321f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
3322f7278fd0SJosepch Chan 
3323f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
3324f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3325f7278fd0SJosepch Chan 	/* PW9 Output enable */
3326f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3327f7278fd0SJosepch Chan 	/* PW10 Input enable */
3328f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
3329f7278fd0SJosepch Chan 	{ }
3330f7278fd0SJosepch Chan };
3331f7278fd0SJosepch Chan 
333269e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
3333a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3334a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3335a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3336a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3337a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3338a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3339a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3340a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3341a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
334269e52a80SHarald Welte 	{ }
334369e52a80SHarald Welte };
334469e52a80SHarald Welte 
334517314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
334617314379SLydia Wang 			      struct hda_codec *codec,
334717314379SLydia Wang 			      struct snd_pcm_substream *substream)
334817314379SLydia Wang {
334917314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
335017314379SLydia Wang 		&& substream->ref_count == 0;
335117314379SLydia Wang 
335217314379SLydia Wang 	analog_low_current_mode(codec, idle);
335317314379SLydia Wang 	return 0;
335417314379SLydia Wang }
335517314379SLydia Wang 
3356f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
33570aa62aefSHarald Welte 	.substreams = 2,
3358f7278fd0SJosepch Chan 	.channels_min = 2,
3359f7278fd0SJosepch Chan 	.channels_max = 8,
3360f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3361f7278fd0SJosepch Chan 	.ops = {
3362f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33630aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
336417314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
336517314379SLydia Wang 		.close = via_pcm_open_close
3366f7278fd0SJosepch Chan 	},
3367f7278fd0SJosepch Chan };
3368f7278fd0SJosepch Chan 
3369f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
33700aa62aefSHarald Welte 	.substreams = 2,
3371f7278fd0SJosepch Chan 	.channels_min = 2,
3372f7278fd0SJosepch Chan 	.channels_max = 4,
3373f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
3374f7278fd0SJosepch Chan 	.ops = {
3375f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
33760aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
33770aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
3378f7278fd0SJosepch Chan 	},
3379f7278fd0SJosepch Chan };
3380f7278fd0SJosepch Chan 
3381f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
3382f7278fd0SJosepch Chan 	.substreams = 2,
3383f7278fd0SJosepch Chan 	.channels_min = 2,
3384f7278fd0SJosepch Chan 	.channels_max = 2,
3385f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
3386f7278fd0SJosepch Chan 	.ops = {
338717314379SLydia Wang 		.open = via_pcm_open_close,
3388f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
338917314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
339017314379SLydia Wang 		.close = via_pcm_open_close
3391f7278fd0SJosepch Chan 	},
3392f7278fd0SJosepch Chan };
3393f7278fd0SJosepch Chan 
3394f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
3395f7278fd0SJosepch Chan 	.substreams = 1,
3396f7278fd0SJosepch Chan 	.channels_min = 2,
3397f7278fd0SJosepch Chan 	.channels_max = 2,
3398f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
3399f7278fd0SJosepch Chan 	.ops = {
3400f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
3401f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
34029da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
34039da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3404f7278fd0SJosepch Chan 	},
3405f7278fd0SJosepch Chan };
3406f7278fd0SJosepch Chan 
3407f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
3408f7278fd0SJosepch Chan 	.substreams = 1,
3409f7278fd0SJosepch Chan 	.channels_min = 2,
3410f7278fd0SJosepch Chan 	.channels_max = 2,
3411f7278fd0SJosepch Chan };
3412f7278fd0SJosepch Chan 
3413f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
3414f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
3415f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
3416f7278fd0SJosepch Chan {
3417f7278fd0SJosepch Chan 	int i;
3418f7278fd0SJosepch Chan 	hda_nid_t nid;
3419f7278fd0SJosepch Chan 
3420f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
3421f7278fd0SJosepch Chan 
3422f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
3423f7278fd0SJosepch Chan 
3424f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
3425f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3426f7278fd0SJosepch Chan 		if (nid) {
3427f7278fd0SJosepch Chan 			/* config dac list */
3428f7278fd0SJosepch Chan 			switch (i) {
3429f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
3430f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
3431f7278fd0SJosepch Chan 				break;
3432f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
3433f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
3434f7278fd0SJosepch Chan 				break;
3435f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
3436fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3437f7278fd0SJosepch Chan 				break;
3438f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
3439fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3440f7278fd0SJosepch Chan 				break;
3441f7278fd0SJosepch Chan 			}
3442f7278fd0SJosepch Chan 		}
3443f7278fd0SJosepch Chan 	}
3444f7278fd0SJosepch Chan 
3445f7278fd0SJosepch Chan 	return 0;
3446f7278fd0SJosepch Chan }
3447f7278fd0SJosepch Chan 
3448f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
3449f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
3450f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
3451f7278fd0SJosepch Chan {
3452f7278fd0SJosepch Chan 	char name[32];
3453ea734963STakashi Iwai 	static const char * const chname[4] = {
3454ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3455ea734963STakashi Iwai 	};
3456fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3457f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3458f7278fd0SJosepch Chan 	int i, err;
3459f7278fd0SJosepch Chan 
3460f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3461f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3462f7278fd0SJosepch Chan 
3463f7278fd0SJosepch Chan 		if (!nid)
3464f7278fd0SJosepch Chan 			continue;
3465f7278fd0SJosepch Chan 
3466f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3467f7278fd0SJosepch Chan 
3468f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3469f7278fd0SJosepch Chan 			/* Center/LFE */
3470f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3471f7278fd0SJosepch Chan 					      "Center Playback Volume",
3472f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3473f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3474f7278fd0SJosepch Chan 			if (err < 0)
3475f7278fd0SJosepch Chan 				return err;
3476f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3477f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3478f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3479f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3480f7278fd0SJosepch Chan 			if (err < 0)
3481f7278fd0SJosepch Chan 				return err;
3482f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3483f7278fd0SJosepch Chan 					      "Center Playback Switch",
3484f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3485f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3486f7278fd0SJosepch Chan 			if (err < 0)
3487f7278fd0SJosepch Chan 				return err;
3488f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3489f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3490f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3491f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3492f7278fd0SJosepch Chan 			if (err < 0)
3493f7278fd0SJosepch Chan 				return err;
3494f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3495f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3496f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3497f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3498f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3499f7278fd0SJosepch Chan 								  HDA_INPUT));
3500f7278fd0SJosepch Chan 			if (err < 0)
3501f7278fd0SJosepch Chan 				return err;
3502f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3503f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3504f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3505f7278fd0SJosepch Chan 								  HDA_INPUT));
3506f7278fd0SJosepch Chan 			if (err < 0)
3507f7278fd0SJosepch Chan 				return err;
3508f7278fd0SJosepch Chan 
3509f7278fd0SJosepch Chan 			/* add control to PW3 */
3510f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3511f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3512f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3513f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3514f7278fd0SJosepch Chan 			if (err < 0)
3515f7278fd0SJosepch Chan 				return err;
3516f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3517f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3518f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3519f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3520f7278fd0SJosepch Chan 			if (err < 0)
3521f7278fd0SJosepch Chan 				return err;
3522f7278fd0SJosepch Chan 		} else {
3523f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3524f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3525f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3526f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3527f7278fd0SJosepch Chan 			if (err < 0)
3528f7278fd0SJosepch Chan 				return err;
3529f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3530f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3531f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3532f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3533f7278fd0SJosepch Chan 			if (err < 0)
3534f7278fd0SJosepch Chan 				return err;
3535f7278fd0SJosepch Chan 		}
3536f7278fd0SJosepch Chan 	}
3537f7278fd0SJosepch Chan 
3538f7278fd0SJosepch Chan 	return 0;
3539f7278fd0SJosepch Chan }
3540f7278fd0SJosepch Chan 
3541f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3542f7278fd0SJosepch Chan {
3543f7278fd0SJosepch Chan 	int err;
3544f7278fd0SJosepch Chan 
3545f7278fd0SJosepch Chan 	if (!pin)
3546f7278fd0SJosepch Chan 		return 0;
3547f7278fd0SJosepch Chan 
3548f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3549cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3550f7278fd0SJosepch Chan 
3551f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3552f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3553f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3554f7278fd0SJosepch Chan 	if (err < 0)
3555f7278fd0SJosepch Chan 		return err;
3556f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3557f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3558f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3559f7278fd0SJosepch Chan 	if (err < 0)
3560f7278fd0SJosepch Chan 		return err;
3561f7278fd0SJosepch Chan 
35620aa62aefSHarald Welte 	create_hp_imux(spec);
35630aa62aefSHarald Welte 
3564f7278fd0SJosepch Chan 	return 0;
3565f7278fd0SJosepch Chan }
3566f7278fd0SJosepch Chan 
3567f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
356810a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
3569f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3570f7278fd0SJosepch Chan {
3571f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
357210a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
3573f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3574f7278fd0SJosepch Chan }
3575f7278fd0SJosepch Chan 
3576f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3577f7278fd0SJosepch Chan {
3578f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3579f7278fd0SJosepch Chan 	int err;
3580f7278fd0SJosepch Chan 
3581f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3582f7278fd0SJosepch Chan 	if (err < 0)
3583f7278fd0SJosepch Chan 		return err;
3584f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3585f7278fd0SJosepch Chan 	if (err < 0)
3586f7278fd0SJosepch Chan 		return err;
3587f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3588f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3589f7278fd0SJosepch Chan 
3590f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3591f7278fd0SJosepch Chan 	if (err < 0)
3592f7278fd0SJosepch Chan 		return err;
3593f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3594f7278fd0SJosepch Chan 	if (err < 0)
3595f7278fd0SJosepch Chan 		return err;
359610a20af7STakashi Iwai 	err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
3597f7278fd0SJosepch Chan 	if (err < 0)
3598f7278fd0SJosepch Chan 		return err;
3599f7278fd0SJosepch Chan 
3600f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3601f7278fd0SJosepch Chan 
36020852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3603f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
360455d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3605f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3606f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3607f7278fd0SJosepch Chan 
3608603c4019STakashi Iwai 	if (spec->kctls.list)
3609603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3610f7278fd0SJosepch Chan 
36110aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
36120aa62aefSHarald Welte 
3613f8fdd495SHarald Welte 	if (spec->hp_mux)
36143d83e577STakashi Iwai 		via_hp_build(codec);
3615f7278fd0SJosepch Chan 
36165b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3617f7278fd0SJosepch Chan 	return 1;
3618f7278fd0SJosepch Chan }
3619f7278fd0SJosepch Chan 
3620f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3621f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3622f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3623f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3624f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3625f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3626f7278fd0SJosepch Chan 	{ } /* end */
3627f7278fd0SJosepch Chan };
3628f7278fd0SJosepch Chan #endif
3629518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3630f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3631f7278fd0SJosepch Chan {
3632f7278fd0SJosepch Chan 	struct via_spec *spec;
3633f7278fd0SJosepch Chan 	int err;
3634f7278fd0SJosepch Chan 
3635518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3636518bf3baSLydia Wang 		return patch_vt1708S(codec);
3637f7278fd0SJosepch Chan 	/* create a codec specific record */
36385b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3639f7278fd0SJosepch Chan 	if (spec == NULL)
3640f7278fd0SJosepch Chan 		return -ENOMEM;
3641f7278fd0SJosepch Chan 
3642f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3643f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3644f7278fd0SJosepch Chan 	if (err < 0) {
3645f7278fd0SJosepch Chan 		via_free(codec);
3646f7278fd0SJosepch Chan 		return err;
3647f7278fd0SJosepch Chan 	} else if (!err) {
3648f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3649f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3650f7278fd0SJosepch Chan 	}
3651f7278fd0SJosepch Chan 
365269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
365369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3654f7278fd0SJosepch Chan 
3655f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3656f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3657f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3658f7278fd0SJosepch Chan 
3659f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3660f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3661f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3662f7278fd0SJosepch Chan 
3663f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3664f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3665f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3666337b9d02STakashi Iwai 		get_mux_nids(codec);
3667f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3668f7278fd0SJosepch Chan 		spec->num_mixers++;
3669f7278fd0SJosepch Chan 	}
3670f7278fd0SJosepch Chan 
3671f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3672f7278fd0SJosepch Chan 
3673f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
367469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3675f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3676f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3677f7278fd0SJosepch Chan #endif
3678f7278fd0SJosepch Chan 
3679f7278fd0SJosepch Chan 	return 0;
3680f7278fd0SJosepch Chan }
3681f7278fd0SJosepch Chan 
3682f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3683f7278fd0SJosepch Chan {
3684f7278fd0SJosepch Chan 	struct via_spec *spec;
3685f7278fd0SJosepch Chan 	int err;
3686f7278fd0SJosepch Chan 
3687f7278fd0SJosepch Chan 	/* create a codec specific record */
36885b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3689f7278fd0SJosepch Chan 	if (spec == NULL)
3690f7278fd0SJosepch Chan 		return -ENOMEM;
3691f7278fd0SJosepch Chan 
3692f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3693f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3694f7278fd0SJosepch Chan 	if (err < 0) {
3695f7278fd0SJosepch Chan 		via_free(codec);
3696f7278fd0SJosepch Chan 		return err;
3697f7278fd0SJosepch Chan 	} else if (!err) {
3698f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3699f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3700f7278fd0SJosepch Chan 	}
3701f7278fd0SJosepch Chan 
370269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
370369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3704f7278fd0SJosepch Chan 
3705f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3706f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3707f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3708f7278fd0SJosepch Chan 
3709f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3710f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3711f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3712f7278fd0SJosepch Chan 
3713f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3714f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3715f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3716337b9d02STakashi Iwai 		get_mux_nids(codec);
3717f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3718f7278fd0SJosepch Chan 		spec->num_mixers++;
3719f7278fd0SJosepch Chan 	}
3720f7278fd0SJosepch Chan 
3721f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3722f7278fd0SJosepch Chan 
3723f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
372469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3725f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3726f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3727f7278fd0SJosepch Chan #endif
3728c577b8a1SJoseph Chan 
3729c577b8a1SJoseph Chan 	return 0;
3730c577b8a1SJoseph Chan }
3731c577b8a1SJoseph Chan 
3732d949cac1SHarald Welte /* Patch for VT1708S */
3733d949cac1SHarald Welte 
3734d949cac1SHarald Welte /* capture mixer elements */
3735d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3736d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3737d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3738d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3739d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
37406369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
37416369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
37426369bcfcSLydia Wang 			 HDA_INPUT),
3743d949cac1SHarald Welte 	{
3744d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3745d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3746d949cac1SHarald Welte 		 * So call somewhat different..
3747d949cac1SHarald Welte 		 */
3748d949cac1SHarald Welte 		/* .name = "Capture Source", */
3749d949cac1SHarald Welte 		.name = "Input Source",
3750d949cac1SHarald Welte 		.count = 1,
3751d949cac1SHarald Welte 		.info = via_mux_enum_info,
3752d949cac1SHarald Welte 		.get = via_mux_enum_get,
3753d949cac1SHarald Welte 		.put = via_mux_enum_put,
3754d949cac1SHarald Welte 	},
3755d949cac1SHarald Welte 	{ } /* end */
3756d949cac1SHarald Welte };
3757d949cac1SHarald Welte 
3758d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3759d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3760d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3761d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3762d949cac1SHarald Welte 
3763d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3764d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3765d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3766d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3767d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3768d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3769d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3770d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3771d949cac1SHarald Welte 
3772d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3773d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
37745691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3775d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
37765691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3777d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3778d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3779bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3780bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3781d949cac1SHarald Welte 	{ }
3782d949cac1SHarald Welte };
3783d949cac1SHarald Welte 
378469e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3785a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3786a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3787a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3788a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3789a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3790a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3791a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3792a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3793a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
379469e52a80SHarald Welte 	{ }
379569e52a80SHarald Welte };
379669e52a80SHarald Welte 
3797d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3798d949cac1SHarald Welte 	.substreams = 2,
3799d949cac1SHarald Welte 	.channels_min = 2,
3800d949cac1SHarald Welte 	.channels_max = 8,
3801d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3802d949cac1SHarald Welte 	.ops = {
3803d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3804c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3805c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
380617314379SLydia Wang 		.close = via_pcm_open_close
3807d949cac1SHarald Welte 	},
3808d949cac1SHarald Welte };
3809d949cac1SHarald Welte 
3810d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3811d949cac1SHarald Welte 	.substreams = 2,
3812d949cac1SHarald Welte 	.channels_min = 2,
3813d949cac1SHarald Welte 	.channels_max = 2,
3814d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3815d949cac1SHarald Welte 	.ops = {
381617314379SLydia Wang 		.open = via_pcm_open_close,
3817d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
381817314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
381917314379SLydia Wang 		.close = via_pcm_open_close
3820d949cac1SHarald Welte 	},
3821d949cac1SHarald Welte };
3822d949cac1SHarald Welte 
3823d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
38249da29271STakashi Iwai 	.substreams = 1,
3825d949cac1SHarald Welte 	.channels_min = 2,
3826d949cac1SHarald Welte 	.channels_max = 2,
3827d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3828d949cac1SHarald Welte 	.ops = {
3829d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3830d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
38319da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
38329da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3833d949cac1SHarald Welte 	},
3834d949cac1SHarald Welte };
3835d949cac1SHarald Welte 
3836d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3837d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3838d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3839d949cac1SHarald Welte {
3840d949cac1SHarald Welte 	int i;
3841d949cac1SHarald Welte 	hda_nid_t nid;
3842d949cac1SHarald Welte 
3843d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3844d949cac1SHarald Welte 
3845d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3846d949cac1SHarald Welte 
3847d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3848d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3849d949cac1SHarald Welte 		if (nid) {
3850d949cac1SHarald Welte 			/* config dac list */
3851d949cac1SHarald Welte 			switch (i) {
3852d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3853d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3854d949cac1SHarald Welte 				break;
3855d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3856d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x24;
3857d949cac1SHarald Welte 				break;
3858d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3859d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3860d949cac1SHarald Welte 				break;
3861d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3862d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3863d949cac1SHarald Welte 				break;
3864d949cac1SHarald Welte 			}
3865d949cac1SHarald Welte 		}
3866d949cac1SHarald Welte 	}
3867d949cac1SHarald Welte 
386832e0191dSClemens Ladisch 	/* for Smart 5.1, line/mic inputs double as output pins */
386932e0191dSClemens Ladisch 	if (cfg->line_outs == 1) {
387032e0191dSClemens Ladisch 		spec->multiout.num_dacs = 3;
387132e0191dSClemens Ladisch 		spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
387232e0191dSClemens Ladisch 		spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
387332e0191dSClemens Ladisch 	}
387432e0191dSClemens Ladisch 
3875d949cac1SHarald Welte 	return 0;
3876d949cac1SHarald Welte }
3877d949cac1SHarald Welte 
3878d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3879d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec,
3880d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3881d949cac1SHarald Welte {
3882d949cac1SHarald Welte 	char name[32];
3883ea734963STakashi Iwai 	static const char * const chname[4] = {
3884ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3885ea734963STakashi Iwai 	};
3886d949cac1SHarald Welte 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25};
3887d949cac1SHarald Welte 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27};
3888d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3889d949cac1SHarald Welte 	int i, err;
3890d949cac1SHarald Welte 
3891d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3892d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3893d949cac1SHarald Welte 
389432e0191dSClemens Ladisch 		/* for Smart 5.1, there are always at least six channels */
389532e0191dSClemens Ladisch 		if (!nid && i > AUTO_SEQ_CENLFE)
3896d949cac1SHarald Welte 			continue;
3897d949cac1SHarald Welte 
3898d949cac1SHarald Welte 		nid_vol = nid_vols[i];
3899d949cac1SHarald Welte 		nid_mute = nid_mutes[i];
3900d949cac1SHarald Welte 
3901d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3902d949cac1SHarald Welte 			/* Center/LFE */
3903d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3904d949cac1SHarald Welte 					      "Center Playback Volume",
3905d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3906d949cac1SHarald Welte 								  HDA_OUTPUT));
3907d949cac1SHarald Welte 			if (err < 0)
3908d949cac1SHarald Welte 				return err;
3909d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3910d949cac1SHarald Welte 					      "LFE Playback Volume",
3911d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3912d949cac1SHarald Welte 								  HDA_OUTPUT));
3913d949cac1SHarald Welte 			if (err < 0)
3914d949cac1SHarald Welte 				return err;
3915d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3916d949cac1SHarald Welte 					      "Center Playback Switch",
3917d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3918d949cac1SHarald Welte 								  1, 0,
3919d949cac1SHarald Welte 								  HDA_OUTPUT));
3920d949cac1SHarald Welte 			if (err < 0)
3921d949cac1SHarald Welte 				return err;
3922d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3923d949cac1SHarald Welte 					      "LFE Playback Switch",
3924d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3925d949cac1SHarald Welte 								  2, 0,
3926d949cac1SHarald Welte 								  HDA_OUTPUT));
3927d949cac1SHarald Welte 			if (err < 0)
3928d949cac1SHarald Welte 				return err;
3929d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3930d949cac1SHarald Welte 			/* add control to mixer index 0 */
3931d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3932d949cac1SHarald Welte 					      "Master Front Playback Volume",
3933d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3934d949cac1SHarald Welte 								  HDA_INPUT));
3935d949cac1SHarald Welte 			if (err < 0)
3936d949cac1SHarald Welte 				return err;
3937d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3938d949cac1SHarald Welte 					      "Master Front Playback Switch",
3939d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3940d949cac1SHarald Welte 								  HDA_INPUT));
3941d949cac1SHarald Welte 			if (err < 0)
3942d949cac1SHarald Welte 				return err;
3943d949cac1SHarald Welte 
3944d949cac1SHarald Welte 			/* Front */
3945d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3946d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3947d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3948d949cac1SHarald Welte 								  HDA_OUTPUT));
3949d949cac1SHarald Welte 			if (err < 0)
3950d949cac1SHarald Welte 				return err;
3951d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3952d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3953d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3954d949cac1SHarald Welte 								  3, 0,
3955d949cac1SHarald Welte 								  HDA_OUTPUT));
3956d949cac1SHarald Welte 			if (err < 0)
3957d949cac1SHarald Welte 				return err;
3958d949cac1SHarald Welte 		} else {
3959d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3960d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3961d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3962d949cac1SHarald Welte 								  HDA_OUTPUT));
3963d949cac1SHarald Welte 			if (err < 0)
3964d949cac1SHarald Welte 				return err;
3965d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3966d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3967d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3968d949cac1SHarald Welte 								  3, 0,
3969d949cac1SHarald Welte 								  HDA_OUTPUT));
3970d949cac1SHarald Welte 			if (err < 0)
3971d949cac1SHarald Welte 				return err;
3972d949cac1SHarald Welte 		}
3973d949cac1SHarald Welte 	}
3974d949cac1SHarald Welte 
3975d949cac1SHarald Welte 	return 0;
3976d949cac1SHarald Welte }
3977d949cac1SHarald Welte 
3978d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3979d949cac1SHarald Welte {
3980d949cac1SHarald Welte 	int err;
3981d949cac1SHarald Welte 
3982d949cac1SHarald Welte 	if (!pin)
3983d949cac1SHarald Welte 		return 0;
3984d949cac1SHarald Welte 
3985d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3986cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3987d949cac1SHarald Welte 
3988d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3989d949cac1SHarald Welte 			      "Headphone Playback Volume",
3990d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3991d949cac1SHarald Welte 	if (err < 0)
3992d949cac1SHarald Welte 		return err;
3993d949cac1SHarald Welte 
3994d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3995d949cac1SHarald Welte 			      "Headphone Playback Switch",
3996d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3997d949cac1SHarald Welte 	if (err < 0)
3998d949cac1SHarald Welte 		return err;
3999d949cac1SHarald Welte 
40000aa62aefSHarald Welte 	create_hp_imux(spec);
40010aa62aefSHarald Welte 
4002d949cac1SHarald Welte 	return 0;
4003d949cac1SHarald Welte }
4004d949cac1SHarald Welte 
4005d949cac1SHarald Welte /* create playback/capture controls for input pins */
400610a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
4007d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4008d949cac1SHarald Welte {
4009f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
401010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
4011f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4012d949cac1SHarald Welte }
4013d949cac1SHarald Welte 
40149da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
40159da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
40169da29271STakashi Iwai {
40179da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
40189da29271STakashi Iwai 	int i;
40199da29271STakashi Iwai 
40209da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
40219da29271STakashi Iwai 		hda_nid_t nid;
40229da29271STakashi Iwai 		int conn;
40239da29271STakashi Iwai 
40249da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
40259da29271STakashi Iwai 		if (!nid)
40269da29271STakashi Iwai 			continue;
40279da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
40289da29271STakashi Iwai 		if (conn < 1)
40299da29271STakashi Iwai 			continue;
40309da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
40319da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
40329da29271STakashi Iwai 		else {
40339da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
40349da29271STakashi Iwai 			break; /* at most two dig outs */
40359da29271STakashi Iwai 		}
40369da29271STakashi Iwai 	}
40379da29271STakashi Iwai }
40389da29271STakashi Iwai 
4039d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
4040d949cac1SHarald Welte {
4041d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4042d949cac1SHarald Welte 	int err;
4043d949cac1SHarald Welte 
40449da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4045d949cac1SHarald Welte 	if (err < 0)
4046d949cac1SHarald Welte 		return err;
4047d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
4048d949cac1SHarald Welte 	if (err < 0)
4049d949cac1SHarald Welte 		return err;
4050d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4051d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4052d949cac1SHarald Welte 
4053d949cac1SHarald Welte 	err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4054d949cac1SHarald Welte 	if (err < 0)
4055d949cac1SHarald Welte 		return err;
4056d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4057d949cac1SHarald Welte 	if (err < 0)
4058d949cac1SHarald Welte 		return err;
405910a20af7STakashi Iwai 	err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
4060d949cac1SHarald Welte 	if (err < 0)
4061d949cac1SHarald Welte 		return err;
4062d949cac1SHarald Welte 
4063d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4064d949cac1SHarald Welte 
40659da29271STakashi Iwai 	fill_dig_outs(codec);
406698aa34c0SHarald Welte 
4067603c4019STakashi Iwai 	if (spec->kctls.list)
4068603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4069d949cac1SHarald Welte 
40700aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
40710aa62aefSHarald Welte 
4072f8fdd495SHarald Welte 	if (spec->hp_mux)
40733d83e577STakashi Iwai 		via_hp_build(codec);
4074d949cac1SHarald Welte 
40755b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4076d949cac1SHarald Welte 	return 1;
4077d949cac1SHarald Welte }
4078d949cac1SHarald Welte 
4079d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4080d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
4081d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
4082d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
4083d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
4084d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
4085d949cac1SHarald Welte 	{ } /* end */
4086d949cac1SHarald Welte };
4087d949cac1SHarald Welte #endif
4088d949cac1SHarald Welte 
40896369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
40906369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
40916369bcfcSLydia Wang {
40926369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
40936369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
40946369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
40956369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
40966369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
40976369bcfcSLydia Wang }
40986369bcfcSLydia Wang 
4099d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
4100d949cac1SHarald Welte {
4101d949cac1SHarald Welte 	struct via_spec *spec;
4102d949cac1SHarald Welte 	int err;
4103d949cac1SHarald Welte 
4104d949cac1SHarald Welte 	/* create a codec specific record */
41055b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4106d949cac1SHarald Welte 	if (spec == NULL)
4107d949cac1SHarald Welte 		return -ENOMEM;
4108d949cac1SHarald Welte 
4109d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4110d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
4111d949cac1SHarald Welte 	if (err < 0) {
4112d949cac1SHarald Welte 		via_free(codec);
4113d949cac1SHarald Welte 		return err;
4114d949cac1SHarald Welte 	} else if (!err) {
4115d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4116d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4117d949cac1SHarald Welte 	}
4118d949cac1SHarald Welte 
411969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
412069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs;
4121d949cac1SHarald Welte 
412236dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
412336dd5c4aSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
412436dd5c4aSLydia Wang 	else
4125d949cac1SHarald Welte 		spec->stream_name_analog = "VT1708S Analog";
4126d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
4127d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
4128d949cac1SHarald Welte 
412936dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
413036dd5c4aSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
413136dd5c4aSLydia Wang 	else
4132d949cac1SHarald Welte 		spec->stream_name_digital = "VT1708S Digital";
4133d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
4134d949cac1SHarald Welte 
4135d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4136d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
4137d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
4138337b9d02STakashi Iwai 		get_mux_nids(codec);
41396369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
41406369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
4141d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
4142d949cac1SHarald Welte 		spec->num_mixers++;
4143d949cac1SHarald Welte 	}
4144d949cac1SHarald Welte 
4145d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4146d949cac1SHarald Welte 
4147d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
414869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4149d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4150d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
4151d949cac1SHarald Welte #endif
4152d949cac1SHarald Welte 
4153518bf3baSLydia Wang 	/* correct names for VT1708BCE */
4154518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
4155518bf3baSLydia Wang 		kfree(codec->chip_name);
4156518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
4157518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
4158518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
4159518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
4160518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
4161518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
4162518bf3baSLydia Wang 	}
4163d949cac1SHarald Welte 	return 0;
4164d949cac1SHarald Welte }
4165d949cac1SHarald Welte 
4166d949cac1SHarald Welte /* Patch for VT1702 */
4167d949cac1SHarald Welte 
4168d949cac1SHarald Welte /* capture mixer elements */
4169d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
4170d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
4171d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
4172d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
4173d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
4174d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
4175d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
4176d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
4177d949cac1SHarald Welte 			 HDA_INPUT),
4178d949cac1SHarald Welte 	{
4179d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4180d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
4181d949cac1SHarald Welte 		 * So call somewhat different..
4182d949cac1SHarald Welte 		 */
4183d949cac1SHarald Welte 		/* .name = "Capture Source", */
4184d949cac1SHarald Welte 		.name = "Input Source",
4185d949cac1SHarald Welte 		.count = 1,
4186d949cac1SHarald Welte 		.info = via_mux_enum_info,
4187d949cac1SHarald Welte 		.get = via_mux_enum_get,
4188d949cac1SHarald Welte 		.put = via_mux_enum_put,
4189d949cac1SHarald Welte 	},
4190d949cac1SHarald Welte 	{ } /* end */
4191d949cac1SHarald Welte };
4192d949cac1SHarald Welte 
4193d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
4194d949cac1SHarald Welte 	/*
4195d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
4196d949cac1SHarald Welte 	 */
4197d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4198d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4199d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4200d949cac1SHarald Welte 
4201d949cac1SHarald Welte 
4202d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4203d949cac1SHarald Welte 	 * mixer widget
4204d949cac1SHarald Welte 	 */
4205d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
4206d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4207d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4208d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
4209d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
4210d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4211d949cac1SHarald Welte 
4212d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
4213d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
4214d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
4215d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4216d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4217bc7e7e5cSLydia Wang 	/* mixer enable */
4218bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
4219bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
4220bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
4221d949cac1SHarald Welte 	{ }
4222d949cac1SHarald Welte };
4223d949cac1SHarald Welte 
422469e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
4225a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
4226a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4227a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4228a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4229a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4230a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
423169e52a80SHarald Welte 	{ }
423269e52a80SHarald Welte };
423369e52a80SHarald Welte 
4234d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
42350aa62aefSHarald Welte 	.substreams = 2,
4236d949cac1SHarald Welte 	.channels_min = 2,
4237d949cac1SHarald Welte 	.channels_max = 2,
4238d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
4239d949cac1SHarald Welte 	.ops = {
4240d949cac1SHarald Welte 		.open = via_playback_pcm_open,
42410aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
424217314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
424317314379SLydia Wang 		.close = via_pcm_open_close
4244d949cac1SHarald Welte 	},
4245d949cac1SHarald Welte };
4246d949cac1SHarald Welte 
4247d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
4248d949cac1SHarald Welte 	.substreams = 3,
4249d949cac1SHarald Welte 	.channels_min = 2,
4250d949cac1SHarald Welte 	.channels_max = 2,
4251d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
4252d949cac1SHarald Welte 	.ops = {
425317314379SLydia Wang 		.open = via_pcm_open_close,
4254d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
425517314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
425617314379SLydia Wang 		.close = via_pcm_open_close
4257d949cac1SHarald Welte 	},
4258d949cac1SHarald Welte };
4259d949cac1SHarald Welte 
4260d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
42615691ec7fSHarald Welte 	.substreams = 2,
4262d949cac1SHarald Welte 	.channels_min = 2,
4263d949cac1SHarald Welte 	.channels_max = 2,
4264d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
4265d949cac1SHarald Welte 	.ops = {
4266d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
4267d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
42689da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
42699da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
4270d949cac1SHarald Welte 	},
4271d949cac1SHarald Welte };
4272d949cac1SHarald Welte 
4273d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
4274d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
4275d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
4276d949cac1SHarald Welte {
4277d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
4278d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
4279d949cac1SHarald Welte 
4280d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
4281d949cac1SHarald Welte 		/* config dac list */
4282d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
4283d949cac1SHarald Welte 	}
4284d949cac1SHarald Welte 
4285d949cac1SHarald Welte 	return 0;
4286d949cac1SHarald Welte }
4287d949cac1SHarald Welte 
4288d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
4289d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
4290d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
4291d949cac1SHarald Welte {
4292d949cac1SHarald Welte 	int err;
4293d949cac1SHarald Welte 
4294d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
4295d949cac1SHarald Welte 		return -1;
4296d949cac1SHarald Welte 
4297d949cac1SHarald Welte 	/* add control to mixer index 0 */
4298d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4299d949cac1SHarald Welte 			      "Master Front Playback Volume",
4300d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4301d949cac1SHarald Welte 	if (err < 0)
4302d949cac1SHarald Welte 		return err;
4303d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4304d949cac1SHarald Welte 			      "Master Front Playback Switch",
4305d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4306d949cac1SHarald Welte 	if (err < 0)
4307d949cac1SHarald Welte 		return err;
4308d949cac1SHarald Welte 
4309d949cac1SHarald Welte 	/* Front */
4310d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4311d949cac1SHarald Welte 			      "Front Playback Volume",
4312d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4313d949cac1SHarald Welte 	if (err < 0)
4314d949cac1SHarald Welte 		return err;
4315d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4316d949cac1SHarald Welte 			      "Front Playback Switch",
4317d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4318d949cac1SHarald Welte 	if (err < 0)
4319d949cac1SHarald Welte 		return err;
4320d949cac1SHarald Welte 
4321d949cac1SHarald Welte 	return 0;
4322d949cac1SHarald Welte }
4323d949cac1SHarald Welte 
4324d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4325d949cac1SHarald Welte {
43260713efebSLydia Wang 	int err, i;
43270713efebSLydia Wang 	struct hda_input_mux *imux;
4328ea734963STakashi Iwai 	static const char * const texts[] = { "ON", "OFF", NULL};
4329d949cac1SHarald Welte 	if (!pin)
4330d949cac1SHarald Welte 		return 0;
4331d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4332cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4333d949cac1SHarald Welte 
4334d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4335d949cac1SHarald Welte 			      "Headphone Playback Volume",
4336d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4337d949cac1SHarald Welte 	if (err < 0)
4338d949cac1SHarald Welte 		return err;
4339d949cac1SHarald Welte 
4340d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4341d949cac1SHarald Welte 			      "Headphone Playback Switch",
4342d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4343d949cac1SHarald Welte 	if (err < 0)
4344d949cac1SHarald Welte 		return err;
4345d949cac1SHarald Welte 
43460713efebSLydia Wang 	imux = &spec->private_imux[1];
43470aa62aefSHarald Welte 
43480713efebSLydia Wang 	/* for hp mode select */
434910a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
435010a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
43510713efebSLydia Wang 
43520713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4353d949cac1SHarald Welte 	return 0;
4354d949cac1SHarald Welte }
4355d949cac1SHarald Welte 
4356d949cac1SHarald Welte /* create playback/capture controls for input pins */
435710a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
4358d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4359d949cac1SHarald Welte {
4360f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
436110a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
4362f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4363d949cac1SHarald Welte }
4364d949cac1SHarald Welte 
4365d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4366d949cac1SHarald Welte {
4367d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4368d949cac1SHarald Welte 	int err;
4369d949cac1SHarald Welte 
43709da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4371d949cac1SHarald Welte 	if (err < 0)
4372d949cac1SHarald Welte 		return err;
4373d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4374d949cac1SHarald Welte 	if (err < 0)
4375d949cac1SHarald Welte 		return err;
4376d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4377d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4378d949cac1SHarald Welte 
4379d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4380d949cac1SHarald Welte 	if (err < 0)
4381d949cac1SHarald Welte 		return err;
4382d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4383d949cac1SHarald Welte 	if (err < 0)
4384d949cac1SHarald Welte 		return err;
4385c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4386c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4387c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4388c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4389c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4390c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
439110a20af7STakashi Iwai 	err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
4392d949cac1SHarald Welte 	if (err < 0)
4393d949cac1SHarald Welte 		return err;
4394d949cac1SHarald Welte 
4395d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4396d949cac1SHarald Welte 
43979da29271STakashi Iwai 	fill_dig_outs(codec);
439898aa34c0SHarald Welte 
4399603c4019STakashi Iwai 	if (spec->kctls.list)
4400603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4401d949cac1SHarald Welte 
44020aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
44030aa62aefSHarald Welte 
4404f8fdd495SHarald Welte 	if (spec->hp_mux)
44053d83e577STakashi Iwai 		via_hp_build(codec);
4406d949cac1SHarald Welte 
4407d949cac1SHarald Welte 	return 1;
4408d949cac1SHarald Welte }
4409d949cac1SHarald Welte 
4410d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4411d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4412d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4413d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4414d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4415d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4416d949cac1SHarald Welte 	{ } /* end */
4417d949cac1SHarald Welte };
4418d949cac1SHarald Welte #endif
4419d949cac1SHarald Welte 
4420d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4421d949cac1SHarald Welte {
4422d949cac1SHarald Welte 	struct via_spec *spec;
4423d949cac1SHarald Welte 	int err;
4424d949cac1SHarald Welte 
4425d949cac1SHarald Welte 	/* create a codec specific record */
44265b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4427d949cac1SHarald Welte 	if (spec == NULL)
4428d949cac1SHarald Welte 		return -ENOMEM;
4429d949cac1SHarald Welte 
4430d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4431d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4432d949cac1SHarald Welte 	if (err < 0) {
4433d949cac1SHarald Welte 		via_free(codec);
4434d949cac1SHarald Welte 		return err;
4435d949cac1SHarald Welte 	} else if (!err) {
4436d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4437d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4438d949cac1SHarald Welte 	}
4439d949cac1SHarald Welte 
444069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
444169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4442d949cac1SHarald Welte 
4443d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4444d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4445d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4446d949cac1SHarald Welte 
4447d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4448d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4449d949cac1SHarald Welte 
4450d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4451d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4452d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4453337b9d02STakashi Iwai 		get_mux_nids(codec);
4454d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4455d949cac1SHarald Welte 		spec->num_mixers++;
4456d949cac1SHarald Welte 	}
4457d949cac1SHarald Welte 
4458d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4459d949cac1SHarald Welte 
4460d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
446169e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4462d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4463d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4464d949cac1SHarald Welte #endif
4465d949cac1SHarald Welte 
4466d949cac1SHarald Welte 	return 0;
4467d949cac1SHarald Welte }
4468d949cac1SHarald Welte 
4469eb7188caSLydia Wang /* Patch for VT1718S */
4470eb7188caSLydia Wang 
4471eb7188caSLydia Wang /* capture mixer elements */
4472eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4473eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4474eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4475eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4476eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4477eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4478eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4479eb7188caSLydia Wang 			 HDA_INPUT),
4480eb7188caSLydia Wang 	{
4481eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4482eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4483eb7188caSLydia Wang 		 * So call somewhat different..
4484eb7188caSLydia Wang 		 */
4485eb7188caSLydia Wang 		.name = "Input Source",
4486eb7188caSLydia Wang 		.count = 2,
4487eb7188caSLydia Wang 		.info = via_mux_enum_info,
4488eb7188caSLydia Wang 		.get = via_mux_enum_get,
4489eb7188caSLydia Wang 		.put = via_mux_enum_put,
4490eb7188caSLydia Wang 	},
4491eb7188caSLydia Wang 	{ } /* end */
4492eb7188caSLydia Wang };
4493eb7188caSLydia Wang 
4494eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4495eb7188caSLydia Wang 	/*
4496eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4497eb7188caSLydia Wang 	 */
4498eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4499eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4500eb7188caSLydia Wang 
4501eb7188caSLydia Wang 
4502eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4503eb7188caSLydia Wang 	 * mixer widget
4504eb7188caSLydia Wang 	 */
4505eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4506eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4507eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4508eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4509eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4510eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4511eb7188caSLydia Wang 
4512eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4513eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4514eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4515eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4516eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4517eb7188caSLydia Wang 	/* PW11 Input enable */
4518eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4519eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4520eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4521eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4522eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4523eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4524eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4525eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4526eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4527eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4528eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4529eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4530eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4531eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4532eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4533eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4534eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4535eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4536eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4537eb7188caSLydia Wang 	{ }
4538eb7188caSLydia Wang };
4539eb7188caSLydia Wang 
4540eb7188caSLydia Wang 
4541eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4542eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4543eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4544eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4545eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4546eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4547eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4548eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4549eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4550eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4551eb7188caSLydia Wang 	{ }
4552eb7188caSLydia Wang };
4553eb7188caSLydia Wang 
4554eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4555eb7188caSLydia Wang 	.substreams = 2,
4556eb7188caSLydia Wang 	.channels_min = 2,
4557eb7188caSLydia Wang 	.channels_max = 10,
4558eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4559eb7188caSLydia Wang 	.ops = {
4560eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4561eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4562eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4563eb7188caSLydia Wang 		.close = via_pcm_open_close,
4564eb7188caSLydia Wang 	},
4565eb7188caSLydia Wang };
4566eb7188caSLydia Wang 
4567eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4568eb7188caSLydia Wang 	.substreams = 2,
4569eb7188caSLydia Wang 	.channels_min = 2,
4570eb7188caSLydia Wang 	.channels_max = 2,
4571eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4572eb7188caSLydia Wang 	.ops = {
4573eb7188caSLydia Wang 		.open = via_pcm_open_close,
4574eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4575eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4576eb7188caSLydia Wang 		.close = via_pcm_open_close,
4577eb7188caSLydia Wang 	},
4578eb7188caSLydia Wang };
4579eb7188caSLydia Wang 
4580eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4581eb7188caSLydia Wang 	.substreams = 2,
4582eb7188caSLydia Wang 	.channels_min = 2,
4583eb7188caSLydia Wang 	.channels_max = 2,
4584eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4585eb7188caSLydia Wang 	.ops = {
4586eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4587eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4588eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4589eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4590eb7188caSLydia Wang 	},
4591eb7188caSLydia Wang };
4592eb7188caSLydia Wang 
4593eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4594eb7188caSLydia Wang 	.substreams = 1,
4595eb7188caSLydia Wang 	.channels_min = 2,
4596eb7188caSLydia Wang 	.channels_max = 2,
4597eb7188caSLydia Wang };
4598eb7188caSLydia Wang 
4599eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4600eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4601eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4602eb7188caSLydia Wang {
4603eb7188caSLydia Wang 	int i;
4604eb7188caSLydia Wang 	hda_nid_t nid;
4605eb7188caSLydia Wang 
4606eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4607eb7188caSLydia Wang 
4608eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4609eb7188caSLydia Wang 
4610eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4611eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4612eb7188caSLydia Wang 		if (nid) {
4613eb7188caSLydia Wang 			/* config dac list */
4614eb7188caSLydia Wang 			switch (i) {
4615eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4616eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4617eb7188caSLydia Wang 				break;
4618eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4619eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4620eb7188caSLydia Wang 				break;
4621eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4622eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4623eb7188caSLydia Wang 				break;
4624eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4625eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4626eb7188caSLydia Wang 				break;
4627eb7188caSLydia Wang 			}
4628eb7188caSLydia Wang 		}
4629eb7188caSLydia Wang 	}
4630eb7188caSLydia Wang 
4631eb7188caSLydia Wang 	return 0;
4632eb7188caSLydia Wang }
4633eb7188caSLydia Wang 
4634eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4635eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4636eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4637eb7188caSLydia Wang {
4638eb7188caSLydia Wang 	char name[32];
4639ea734963STakashi Iwai 	static const char * const chname[4] = {
4640ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
4641ea734963STakashi Iwai 	};
4642eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4643eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4644eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4645eb7188caSLydia Wang 	int i, err;
4646eb7188caSLydia Wang 
4647eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4648eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4649eb7188caSLydia Wang 
4650eb7188caSLydia Wang 		if (!nid)
4651eb7188caSLydia Wang 			continue;
4652eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4653eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4654eb7188caSLydia Wang 
4655eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4656eb7188caSLydia Wang 			/* Center/LFE */
4657eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4658eb7188caSLydia Wang 					      "Center Playback Volume",
4659eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4660eb7188caSLydia Wang 								  HDA_OUTPUT));
4661eb7188caSLydia Wang 			if (err < 0)
4662eb7188caSLydia Wang 				return err;
4663eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4664eb7188caSLydia Wang 					      "LFE Playback Volume",
4665eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4666eb7188caSLydia Wang 								  HDA_OUTPUT));
4667eb7188caSLydia Wang 			if (err < 0)
4668eb7188caSLydia Wang 				return err;
4669eb7188caSLydia Wang 			err = via_add_control(
4670eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4671eb7188caSLydia Wang 				"Center Playback Switch",
4672eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4673eb7188caSLydia Wang 						    HDA_OUTPUT));
4674eb7188caSLydia Wang 			if (err < 0)
4675eb7188caSLydia Wang 				return err;
4676eb7188caSLydia Wang 			err = via_add_control(
4677eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4678eb7188caSLydia Wang 				"LFE Playback Switch",
4679eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4680eb7188caSLydia Wang 						    HDA_OUTPUT));
4681eb7188caSLydia Wang 			if (err < 0)
4682eb7188caSLydia Wang 				return err;
4683eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4684eb7188caSLydia Wang 			/* Front */
4685eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4686eb7188caSLydia Wang 			err = via_add_control(
4687eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4688eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4689eb7188caSLydia Wang 			if (err < 0)
4690eb7188caSLydia Wang 				return err;
4691eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4692eb7188caSLydia Wang 			err = via_add_control(
4693eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4694eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4695eb7188caSLydia Wang 						    HDA_OUTPUT));
4696eb7188caSLydia Wang 			if (err < 0)
4697eb7188caSLydia Wang 				return err;
4698eb7188caSLydia Wang 		} else {
4699eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4700eb7188caSLydia Wang 			err = via_add_control(
4701eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4702eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4703eb7188caSLydia Wang 			if (err < 0)
4704eb7188caSLydia Wang 				return err;
4705eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4706eb7188caSLydia Wang 			err = via_add_control(
4707eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4708eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4709eb7188caSLydia Wang 						    HDA_OUTPUT));
4710eb7188caSLydia Wang 			if (err < 0)
4711eb7188caSLydia Wang 				return err;
4712eb7188caSLydia Wang 		}
4713eb7188caSLydia Wang 	}
4714eb7188caSLydia Wang 	return 0;
4715eb7188caSLydia Wang }
4716eb7188caSLydia Wang 
4717eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4718eb7188caSLydia Wang {
4719eb7188caSLydia Wang 	int err;
4720eb7188caSLydia Wang 
4721eb7188caSLydia Wang 	if (!pin)
4722eb7188caSLydia Wang 		return 0;
4723eb7188caSLydia Wang 
4724eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4725eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4726eb7188caSLydia Wang 
4727eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4728eb7188caSLydia Wang 			      "Headphone Playback Volume",
4729eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4730eb7188caSLydia Wang 	if (err < 0)
4731eb7188caSLydia Wang 		return err;
4732eb7188caSLydia Wang 
4733eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4734eb7188caSLydia Wang 			      "Headphone Playback Switch",
4735eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4736eb7188caSLydia Wang 	if (err < 0)
4737eb7188caSLydia Wang 		return err;
4738eb7188caSLydia Wang 
4739eb7188caSLydia Wang 	create_hp_imux(spec);
4740eb7188caSLydia Wang 	return 0;
4741eb7188caSLydia Wang }
4742eb7188caSLydia Wang 
4743eb7188caSLydia Wang /* create playback/capture controls for input pins */
474410a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
4745eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4746eb7188caSLydia Wang {
4747f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
474810a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
4749f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4750eb7188caSLydia Wang }
4751eb7188caSLydia Wang 
4752eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4753eb7188caSLydia Wang {
4754eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4755eb7188caSLydia Wang 	int err;
4756eb7188caSLydia Wang 
4757eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4758eb7188caSLydia Wang 
4759eb7188caSLydia Wang 	if (err < 0)
4760eb7188caSLydia Wang 		return err;
4761eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4762eb7188caSLydia Wang 	if (err < 0)
4763eb7188caSLydia Wang 		return err;
4764eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4765eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4766eb7188caSLydia Wang 
4767eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4768eb7188caSLydia Wang 	if (err < 0)
4769eb7188caSLydia Wang 		return err;
4770eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4771eb7188caSLydia Wang 	if (err < 0)
4772eb7188caSLydia Wang 		return err;
477310a20af7STakashi Iwai 	err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
4774eb7188caSLydia Wang 	if (err < 0)
4775eb7188caSLydia Wang 		return err;
4776eb7188caSLydia Wang 
4777eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4778eb7188caSLydia Wang 
4779eb7188caSLydia Wang 	fill_dig_outs(codec);
4780eb7188caSLydia Wang 
4781eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4782eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4783eb7188caSLydia Wang 
4784eb7188caSLydia Wang 	if (spec->kctls.list)
4785eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4786eb7188caSLydia Wang 
4787eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4788eb7188caSLydia Wang 
4789eb7188caSLydia Wang 	if (spec->hp_mux)
47903d83e577STakashi Iwai 		via_hp_build(codec);
4791eb7188caSLydia Wang 
47925b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4793eb7188caSLydia Wang 
4794eb7188caSLydia Wang 	return 1;
4795eb7188caSLydia Wang }
4796eb7188caSLydia Wang 
4797eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4798eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = {
4799eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4800eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4801eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4802eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4803eb7188caSLydia Wang 	{ } /* end */
4804eb7188caSLydia Wang };
4805eb7188caSLydia Wang #endif
4806eb7188caSLydia Wang 
4807eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4808eb7188caSLydia Wang {
4809eb7188caSLydia Wang 	struct via_spec *spec;
4810eb7188caSLydia Wang 	int err;
4811eb7188caSLydia Wang 
4812eb7188caSLydia Wang 	/* create a codec specific record */
48135b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4814eb7188caSLydia Wang 	if (spec == NULL)
4815eb7188caSLydia Wang 		return -ENOMEM;
4816eb7188caSLydia Wang 
4817eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4818eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4819eb7188caSLydia Wang 	if (err < 0) {
4820eb7188caSLydia Wang 		via_free(codec);
4821eb7188caSLydia Wang 		return err;
4822eb7188caSLydia Wang 	} else if (!err) {
4823eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4824eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4825eb7188caSLydia Wang 	}
4826eb7188caSLydia Wang 
4827eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4828eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4829eb7188caSLydia Wang 
4830bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4831bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4832bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4833bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4834bb3c6bfcSLydia Wang 	else
4835eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4836eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4837eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4838eb7188caSLydia Wang 
4839bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4840bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4841bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4842bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4843bb3c6bfcSLydia Wang 	else
4844eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4845eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4846bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4847eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4848eb7188caSLydia Wang 
4849eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4850eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4851eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4852eb7188caSLydia Wang 		get_mux_nids(codec);
4853bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4854bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4855eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4856eb7188caSLydia Wang 		spec->num_mixers++;
4857eb7188caSLydia Wang 	}
4858eb7188caSLydia Wang 
4859eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4860eb7188caSLydia Wang 
4861eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
48620f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4863eb7188caSLydia Wang 
4864eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4865eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4866eb7188caSLydia Wang #endif
4867eb7188caSLydia Wang 
4868eb7188caSLydia Wang 	return 0;
4869eb7188caSLydia Wang }
4870f3db423dSLydia Wang 
4871f3db423dSLydia Wang /* Patch for VT1716S */
4872f3db423dSLydia Wang 
4873f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4874f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4875f3db423dSLydia Wang {
4876f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4877f3db423dSLydia Wang 	uinfo->count = 1;
4878f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4879f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4880f3db423dSLydia Wang 	return 0;
4881f3db423dSLydia Wang }
4882f3db423dSLydia Wang 
4883f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4884f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4885f3db423dSLydia Wang {
4886f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4887f3db423dSLydia Wang 	int index = 0;
4888f3db423dSLydia Wang 
4889f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4890f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4891f3db423dSLydia Wang 	if (index != -1)
4892f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4893f3db423dSLydia Wang 
4894f3db423dSLydia Wang 	return 0;
4895f3db423dSLydia Wang }
4896f3db423dSLydia Wang 
4897f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4898f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4899f3db423dSLydia Wang {
4900f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4901f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4902f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4903f3db423dSLydia Wang 
4904f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4905f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4906f3db423dSLydia Wang 	spec->dmic_enabled = index;
4907f3db423dSLydia Wang 	set_jack_power_state(codec);
4908f3db423dSLydia Wang 
4909f3db423dSLydia Wang 	return 1;
4910f3db423dSLydia Wang }
4911f3db423dSLydia Wang 
4912f3db423dSLydia Wang /* capture mixer elements */
4913f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4914f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4915f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4916f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4917f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4918f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4919f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4920f3db423dSLydia Wang 			 HDA_INPUT),
4921f3db423dSLydia Wang 	{
4922f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4923f3db423dSLydia Wang 		.name = "Input Source",
4924f3db423dSLydia Wang 		.count = 1,
4925f3db423dSLydia Wang 		.info = via_mux_enum_info,
4926f3db423dSLydia Wang 		.get = via_mux_enum_get,
4927f3db423dSLydia Wang 		.put = via_mux_enum_put,
4928f3db423dSLydia Wang 	},
4929f3db423dSLydia Wang 	{ } /* end */
4930f3db423dSLydia Wang };
4931f3db423dSLydia Wang 
4932f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4933f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4934f3db423dSLydia Wang 	{
4935f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4936f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
49375b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
4938f3db423dSLydia Wang 	 .count = 1,
4939f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
4940f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
4941f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
4942f3db423dSLydia Wang 	 },
4943f3db423dSLydia Wang 	{}			/* end */
4944f3db423dSLydia Wang };
4945f3db423dSLydia Wang 
4946f3db423dSLydia Wang 
4947f3db423dSLydia Wang /* mono-out mixer elements */
4948f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4949f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4950f3db423dSLydia Wang 	{ } /* end */
4951f3db423dSLydia Wang };
4952f3db423dSLydia Wang 
4953f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
4954f3db423dSLydia Wang 	/*
4955f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4956f3db423dSLydia Wang 	 */
4957f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4958f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4959f3db423dSLydia Wang 
4960f3db423dSLydia Wang 
4961f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4962f3db423dSLydia Wang 	 * mixer widget
4963f3db423dSLydia Wang 	 */
4964f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4965f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4966f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4967f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4968f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4969f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4970f3db423dSLydia Wang 
4971f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4972f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4973f3db423dSLydia Wang 
4974f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4975f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4976f3db423dSLydia Wang 
4977f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4978f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4979f3db423dSLydia Wang 
4980f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4981f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4982f3db423dSLydia Wang 
4983f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4984f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4985f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4986f3db423dSLydia Wang 
4987f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4988f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4989f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4990f3db423dSLydia Wang 	/* PW12 Output enable */
4991f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4992f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4993f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4994f3db423dSLydia Wang 	/* don't bybass mixer */
4995f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4996f3db423dSLydia Wang 	/* Enable mono output */
4997f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4998f3db423dSLydia Wang 	{ }
4999f3db423dSLydia Wang };
5000f3db423dSLydia Wang 
5001f3db423dSLydia Wang 
5002f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
5003f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
5004f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
5005f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5006f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5007f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5008f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
5009f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
5010f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5011f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5012f3db423dSLydia Wang 	{ }
5013f3db423dSLydia Wang };
5014f3db423dSLydia Wang 
5015f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
5016f3db423dSLydia Wang 	.substreams = 2,
5017f3db423dSLydia Wang 	.channels_min = 2,
5018f3db423dSLydia Wang 	.channels_max = 6,
5019f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5020f3db423dSLydia Wang 	.ops = {
5021f3db423dSLydia Wang 		.open = via_playback_pcm_open,
5022f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5023f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5024f3db423dSLydia Wang 		.close = via_pcm_open_close,
5025f3db423dSLydia Wang 	},
5026f3db423dSLydia Wang };
5027f3db423dSLydia Wang 
5028f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
5029f3db423dSLydia Wang 	.substreams = 2,
5030f3db423dSLydia Wang 	.channels_min = 2,
5031f3db423dSLydia Wang 	.channels_max = 2,
5032f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
5033f3db423dSLydia Wang 	.ops = {
5034f3db423dSLydia Wang 		.open = via_pcm_open_close,
5035f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
5036f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5037f3db423dSLydia Wang 		.close = via_pcm_open_close,
5038f3db423dSLydia Wang 	},
5039f3db423dSLydia Wang };
5040f3db423dSLydia Wang 
5041f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
5042f3db423dSLydia Wang 	.substreams = 2,
5043f3db423dSLydia Wang 	.channels_min = 2,
5044f3db423dSLydia Wang 	.channels_max = 2,
5045f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
5046f3db423dSLydia Wang 	.ops = {
5047f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
5048f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
5049f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5050f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5051f3db423dSLydia Wang 	},
5052f3db423dSLydia Wang };
5053f3db423dSLydia Wang 
5054f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5055f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
5056f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
5057f3db423dSLydia Wang {	int i;
5058f3db423dSLydia Wang 	hda_nid_t nid;
5059f3db423dSLydia Wang 
5060f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
5061f3db423dSLydia Wang 
5062f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5063f3db423dSLydia Wang 
5064f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
5065f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5066f3db423dSLydia Wang 		if (nid) {
5067f3db423dSLydia Wang 			/* config dac list */
5068f3db423dSLydia Wang 			switch (i) {
5069f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
5070f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
5071f3db423dSLydia Wang 				break;
5072f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
5073f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
5074f3db423dSLydia Wang 				break;
5075f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
5076f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
5077f3db423dSLydia Wang 				break;
5078f3db423dSLydia Wang 			}
5079f3db423dSLydia Wang 		}
5080f3db423dSLydia Wang 	}
5081f3db423dSLydia Wang 
5082f3db423dSLydia Wang 	return 0;
5083f3db423dSLydia Wang }
5084f3db423dSLydia Wang 
5085f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
5086f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
5087f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
5088f3db423dSLydia Wang {
5089f3db423dSLydia Wang 	char name[32];
5090ea734963STakashi Iwai 	static const char * const chname[3] = {
5091ea734963STakashi Iwai 		"Front", "Surround", "C/LFE"
5092ea734963STakashi Iwai 	};
5093f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
5094f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
5095f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
5096f3db423dSLydia Wang 	int i, err;
5097f3db423dSLydia Wang 
5098f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
5099f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
5100f3db423dSLydia Wang 
5101f3db423dSLydia Wang 		if (!nid)
5102f3db423dSLydia Wang 			continue;
5103f3db423dSLydia Wang 
5104f3db423dSLydia Wang 		nid_vol = nid_vols[i];
5105f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
5106f3db423dSLydia Wang 
5107f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
5108f3db423dSLydia Wang 			err = via_add_control(
5109f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5110f3db423dSLydia Wang 				"Center Playback Volume",
5111f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
5112f3db423dSLydia Wang 			if (err < 0)
5113f3db423dSLydia Wang 				return err;
5114f3db423dSLydia Wang 			err = via_add_control(
5115f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5116f3db423dSLydia Wang 				"LFE Playback Volume",
5117f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
5118f3db423dSLydia Wang 			if (err < 0)
5119f3db423dSLydia Wang 				return err;
5120f3db423dSLydia Wang 			err = via_add_control(
5121f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5122f3db423dSLydia Wang 				"Center Playback Switch",
5123f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
5124f3db423dSLydia Wang 						    HDA_OUTPUT));
5125f3db423dSLydia Wang 			if (err < 0)
5126f3db423dSLydia Wang 				return err;
5127f3db423dSLydia Wang 			err = via_add_control(
5128f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5129f3db423dSLydia Wang 				"LFE Playback Switch",
5130f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
5131f3db423dSLydia Wang 						    HDA_OUTPUT));
5132f3db423dSLydia Wang 			if (err < 0)
5133f3db423dSLydia Wang 				return err;
5134f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
5135f3db423dSLydia Wang 
5136f3db423dSLydia Wang 			err = via_add_control(
5137f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
5138f3db423dSLydia Wang 				"Master Front Playback Volume",
5139f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5140f3db423dSLydia Wang 			if (err < 0)
5141f3db423dSLydia Wang 				return err;
5142f3db423dSLydia Wang 			err = via_add_control(
5143f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
5144f3db423dSLydia Wang 				"Master Front Playback Switch",
5145f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
5146f3db423dSLydia Wang 			if (err < 0)
5147f3db423dSLydia Wang 				return err;
5148f3db423dSLydia Wang 
5149f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5150f3db423dSLydia Wang 			err = via_add_control(
5151f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5152f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5153f3db423dSLydia Wang 			if (err < 0)
5154f3db423dSLydia Wang 				return err;
5155f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5156f3db423dSLydia Wang 			err = via_add_control(
5157f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5158f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5159f3db423dSLydia Wang 						    HDA_OUTPUT));
5160f3db423dSLydia Wang 			if (err < 0)
5161f3db423dSLydia Wang 				return err;
5162f3db423dSLydia Wang 		} else {
5163f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
5164f3db423dSLydia Wang 			err = via_add_control(
5165f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
5166f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
5167f3db423dSLydia Wang 			if (err < 0)
5168f3db423dSLydia Wang 				return err;
5169f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
5170f3db423dSLydia Wang 			err = via_add_control(
5171f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
5172f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
5173f3db423dSLydia Wang 						    HDA_OUTPUT));
5174f3db423dSLydia Wang 			if (err < 0)
5175f3db423dSLydia Wang 				return err;
5176f3db423dSLydia Wang 		}
5177f3db423dSLydia Wang 	}
5178f3db423dSLydia Wang 	return 0;
5179f3db423dSLydia Wang }
5180f3db423dSLydia Wang 
5181f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5182f3db423dSLydia Wang {
5183f3db423dSLydia Wang 	int err;
5184f3db423dSLydia Wang 
5185f3db423dSLydia Wang 	if (!pin)
5186f3db423dSLydia Wang 		return 0;
5187f3db423dSLydia Wang 
5188f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
5189f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
5190f3db423dSLydia Wang 
5191f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5192f3db423dSLydia Wang 			      "Headphone Playback Volume",
5193f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
5194f3db423dSLydia Wang 	if (err < 0)
5195f3db423dSLydia Wang 		return err;
5196f3db423dSLydia Wang 
5197f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5198f3db423dSLydia Wang 			      "Headphone Playback Switch",
5199f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5200f3db423dSLydia Wang 	if (err < 0)
5201f3db423dSLydia Wang 		return err;
5202f3db423dSLydia Wang 
5203f3db423dSLydia Wang 	create_hp_imux(spec);
5204f3db423dSLydia Wang 	return 0;
5205f3db423dSLydia Wang }
5206f3db423dSLydia Wang 
5207f3db423dSLydia Wang /* create playback/capture controls for input pins */
520810a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
5209f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5210f3db423dSLydia Wang {
5211f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
521210a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
5213f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
5214f3db423dSLydia Wang }
5215f3db423dSLydia Wang 
5216f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5217f3db423dSLydia Wang {
5218f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5219f3db423dSLydia Wang 	int err;
5220f3db423dSLydia Wang 
5221f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5222f3db423dSLydia Wang 	if (err < 0)
5223f3db423dSLydia Wang 		return err;
5224f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5225f3db423dSLydia Wang 	if (err < 0)
5226f3db423dSLydia Wang 		return err;
5227f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5228f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5229f3db423dSLydia Wang 
5230f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5231f3db423dSLydia Wang 	if (err < 0)
5232f3db423dSLydia Wang 		return err;
5233f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5234f3db423dSLydia Wang 	if (err < 0)
5235f3db423dSLydia Wang 		return err;
523610a20af7STakashi Iwai 	err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
5237f3db423dSLydia Wang 	if (err < 0)
5238f3db423dSLydia Wang 		return err;
5239f3db423dSLydia Wang 
5240f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5241f3db423dSLydia Wang 
5242f3db423dSLydia Wang 	fill_dig_outs(codec);
5243f3db423dSLydia Wang 
5244f3db423dSLydia Wang 	if (spec->kctls.list)
5245f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5246f3db423dSLydia Wang 
5247f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5248f3db423dSLydia Wang 
5249f3db423dSLydia Wang 	if (spec->hp_mux)
52503d83e577STakashi Iwai 		via_hp_build(codec);
5251f3db423dSLydia Wang 
52525b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
5253f3db423dSLydia Wang 
5254f3db423dSLydia Wang 	return 1;
5255f3db423dSLydia Wang }
5256f3db423dSLydia Wang 
5257f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5258f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5259f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5260f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5261f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5262f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5263f3db423dSLydia Wang 	{ } /* end */
5264f3db423dSLydia Wang };
5265f3db423dSLydia Wang #endif
5266f3db423dSLydia Wang 
5267f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5268f3db423dSLydia Wang {
5269f3db423dSLydia Wang 	struct via_spec *spec;
5270f3db423dSLydia Wang 	int err;
5271f3db423dSLydia Wang 
5272f3db423dSLydia Wang 	/* create a codec specific record */
52735b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5274f3db423dSLydia Wang 	if (spec == NULL)
5275f3db423dSLydia Wang 		return -ENOMEM;
5276f3db423dSLydia Wang 
5277f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5278f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5279f3db423dSLydia Wang 	if (err < 0) {
5280f3db423dSLydia Wang 		via_free(codec);
5281f3db423dSLydia Wang 		return err;
5282f3db423dSLydia Wang 	} else if (!err) {
5283f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5284f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5285f3db423dSLydia Wang 	}
5286f3db423dSLydia Wang 
5287f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5288f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5289f3db423dSLydia Wang 
5290f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5291f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5292f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5293f3db423dSLydia Wang 
5294f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5295f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5296f3db423dSLydia Wang 
5297f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5298f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5299f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5300f3db423dSLydia Wang 		get_mux_nids(codec);
5301f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5302f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5303f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5304f3db423dSLydia Wang 		spec->num_mixers++;
5305f3db423dSLydia Wang 	}
5306f3db423dSLydia Wang 
5307f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5308f3db423dSLydia Wang 	spec->num_mixers++;
5309f3db423dSLydia Wang 
5310f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5311f3db423dSLydia Wang 
5312f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5313f3db423dSLydia Wang 
5314f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
53150f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5316f3db423dSLydia Wang 
5317f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5318f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5319f3db423dSLydia Wang #endif
5320f3db423dSLydia Wang 
5321f3db423dSLydia Wang 	return 0;
5322f3db423dSLydia Wang }
532325eaba2fSLydia Wang 
532425eaba2fSLydia Wang /* for vt2002P */
532525eaba2fSLydia Wang 
532625eaba2fSLydia Wang /* capture mixer elements */
532725eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
532825eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
532925eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
533025eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
533125eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
533225eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
533325eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
533425eaba2fSLydia Wang 			 HDA_INPUT),
533525eaba2fSLydia Wang 	{
533625eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
533725eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
533825eaba2fSLydia Wang 		 * So call somewhat different..
533925eaba2fSLydia Wang 		 */
534025eaba2fSLydia Wang 		/* .name = "Capture Source", */
534125eaba2fSLydia Wang 		.name = "Input Source",
534225eaba2fSLydia Wang 		.count = 2,
534325eaba2fSLydia Wang 		.info = via_mux_enum_info,
534425eaba2fSLydia Wang 		.get = via_mux_enum_get,
534525eaba2fSLydia Wang 		.put = via_mux_enum_put,
534625eaba2fSLydia Wang 	},
534725eaba2fSLydia Wang 	{ } /* end */
534825eaba2fSLydia Wang };
534925eaba2fSLydia Wang 
535025eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
535125eaba2fSLydia Wang 	/*
535225eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
535325eaba2fSLydia Wang 	 */
535425eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
535525eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
535625eaba2fSLydia Wang 
535725eaba2fSLydia Wang 
535825eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
535925eaba2fSLydia Wang 	 * mixer widget
536025eaba2fSLydia Wang 	 */
536125eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
536225eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
536325eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
536425eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
536525eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
536625eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
536725eaba2fSLydia Wang 
536825eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
536925eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
537025eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
537125eaba2fSLydia Wang 
537225eaba2fSLydia Wang 	/* PW9 Output enable */
537325eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
537425eaba2fSLydia Wang 
537525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
537625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
537725eaba2fSLydia Wang 
537825eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
537925eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
538025eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
538125eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
538225eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
538325eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
538425eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
538525eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
538625eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
538725eaba2fSLydia Wang 
538825eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
538925eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
539025eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
539125eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
539225eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
539325eaba2fSLydia Wang 
539425eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
539525eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
539625eaba2fSLydia Wang 
539725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
539825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
539925eaba2fSLydia Wang 	{ }
540025eaba2fSLydia Wang };
540125eaba2fSLydia Wang 
540225eaba2fSLydia Wang 
540325eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
540425eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
540525eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
540625eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
540725eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
540825eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
540925eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
541025eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
541125eaba2fSLydia Wang 	{ }
541225eaba2fSLydia Wang };
541325eaba2fSLydia Wang 
541425eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
541525eaba2fSLydia Wang 	.substreams = 2,
541625eaba2fSLydia Wang 	.channels_min = 2,
541725eaba2fSLydia Wang 	.channels_max = 2,
541825eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
541925eaba2fSLydia Wang 	.ops = {
542025eaba2fSLydia Wang 		.open = via_playback_pcm_open,
542125eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
542225eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
542325eaba2fSLydia Wang 		.close = via_pcm_open_close,
542425eaba2fSLydia Wang 	},
542525eaba2fSLydia Wang };
542625eaba2fSLydia Wang 
542725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
542825eaba2fSLydia Wang 	.substreams = 2,
542925eaba2fSLydia Wang 	.channels_min = 2,
543025eaba2fSLydia Wang 	.channels_max = 2,
543125eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
543225eaba2fSLydia Wang 	.ops = {
543325eaba2fSLydia Wang 		.open = via_pcm_open_close,
543425eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
543525eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
543625eaba2fSLydia Wang 		.close = via_pcm_open_close,
543725eaba2fSLydia Wang 	},
543825eaba2fSLydia Wang };
543925eaba2fSLydia Wang 
544025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
544125eaba2fSLydia Wang 	.substreams = 1,
544225eaba2fSLydia Wang 	.channels_min = 2,
544325eaba2fSLydia Wang 	.channels_max = 2,
544425eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
544525eaba2fSLydia Wang 	.ops = {
544625eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
544725eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
544825eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
544925eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
545025eaba2fSLydia Wang 	},
545125eaba2fSLydia Wang };
545225eaba2fSLydia Wang 
545325eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
545425eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
545525eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
545625eaba2fSLydia Wang {
545725eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
545825eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
545925eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
546025eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
546125eaba2fSLydia Wang 	return 0;
546225eaba2fSLydia Wang }
546325eaba2fSLydia Wang 
546425eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
546525eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
546625eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
546725eaba2fSLydia Wang {
546825eaba2fSLydia Wang 	int err;
546925eaba2fSLydia Wang 
547025eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
547125eaba2fSLydia Wang 		return -1;
547225eaba2fSLydia Wang 
547325eaba2fSLydia Wang 
547425eaba2fSLydia Wang 	/* Line-Out: PortE */
547525eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
547625eaba2fSLydia Wang 			      "Master Front Playback Volume",
547725eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
547825eaba2fSLydia Wang 	if (err < 0)
547925eaba2fSLydia Wang 		return err;
548025eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
548125eaba2fSLydia Wang 			      "Master Front Playback Switch",
548225eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
548325eaba2fSLydia Wang 	if (err < 0)
548425eaba2fSLydia Wang 		return err;
548525eaba2fSLydia Wang 
548625eaba2fSLydia Wang 	return 0;
548725eaba2fSLydia Wang }
548825eaba2fSLydia Wang 
548925eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
549025eaba2fSLydia Wang {
549125eaba2fSLydia Wang 	int err;
549225eaba2fSLydia Wang 
549325eaba2fSLydia Wang 	if (!pin)
549425eaba2fSLydia Wang 		return 0;
549525eaba2fSLydia Wang 
549625eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
549725eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
549825eaba2fSLydia Wang 
549925eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
550025eaba2fSLydia Wang 			      "Headphone Playback Volume",
550125eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
550225eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
550325eaba2fSLydia Wang 	if (err < 0)
550425eaba2fSLydia Wang 		return err;
550525eaba2fSLydia Wang 
550625eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
550725eaba2fSLydia Wang 			      "Headphone Playback Switch",
550825eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
550925eaba2fSLydia Wang 	if (err < 0)
551025eaba2fSLydia Wang 		return err;
551125eaba2fSLydia Wang 
551225eaba2fSLydia Wang 	create_hp_imux(spec);
551325eaba2fSLydia Wang 	return 0;
551425eaba2fSLydia Wang }
551525eaba2fSLydia Wang 
551625eaba2fSLydia Wang /* create playback/capture controls for input pins */
551710a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
551825eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
551925eaba2fSLydia Wang {
552010a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
552125eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5522f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
5523f3268512STakashi Iwai 	int err;
552425eaba2fSLydia Wang 
552510a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5526f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
552725eaba2fSLydia Wang 	if (err < 0)
552825eaba2fSLydia Wang 		return err;
552925eaba2fSLydia Wang 	/* build volume/mute control of loopback */
55307b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
553125eaba2fSLydia Wang 	if (err < 0)
553225eaba2fSLydia Wang 		return err;
553325eaba2fSLydia Wang 
553425eaba2fSLydia Wang 	/* for digital mic select */
553510a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
553625eaba2fSLydia Wang 
553725eaba2fSLydia Wang 	return 0;
553825eaba2fSLydia Wang }
553925eaba2fSLydia Wang 
554025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
554125eaba2fSLydia Wang {
554225eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
554325eaba2fSLydia Wang 	int err;
554425eaba2fSLydia Wang 
554525eaba2fSLydia Wang 
554625eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
554725eaba2fSLydia Wang 	if (err < 0)
554825eaba2fSLydia Wang 		return err;
554925eaba2fSLydia Wang 
555025eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
555125eaba2fSLydia Wang 	if (err < 0)
555225eaba2fSLydia Wang 		return err;
555325eaba2fSLydia Wang 
555425eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
555525eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
555625eaba2fSLydia Wang 
555725eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
555825eaba2fSLydia Wang 	if (err < 0)
555925eaba2fSLydia Wang 		return err;
556025eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
556125eaba2fSLydia Wang 	if (err < 0)
556225eaba2fSLydia Wang 		return err;
556310a20af7STakashi Iwai 	err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
556425eaba2fSLydia Wang 	if (err < 0)
556525eaba2fSLydia Wang 		return err;
556625eaba2fSLydia Wang 
556725eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
556825eaba2fSLydia Wang 
556925eaba2fSLydia Wang 	fill_dig_outs(codec);
557025eaba2fSLydia Wang 
557125eaba2fSLydia Wang 	if (spec->kctls.list)
557225eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
557325eaba2fSLydia Wang 
557425eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
557525eaba2fSLydia Wang 
557625eaba2fSLydia Wang 	if (spec->hp_mux)
55773d83e577STakashi Iwai 		via_hp_build(codec);
557825eaba2fSLydia Wang 
557925eaba2fSLydia Wang 	return 1;
558025eaba2fSLydia Wang }
558125eaba2fSLydia Wang 
558225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
558325eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
558425eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
558525eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
558625eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
558725eaba2fSLydia Wang 	{ } /* end */
558825eaba2fSLydia Wang };
558925eaba2fSLydia Wang #endif
559025eaba2fSLydia Wang 
559125eaba2fSLydia Wang 
559225eaba2fSLydia Wang /* patch for vt2002P */
559325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
559425eaba2fSLydia Wang {
559525eaba2fSLydia Wang 	struct via_spec *spec;
559625eaba2fSLydia Wang 	int err;
559725eaba2fSLydia Wang 
559825eaba2fSLydia Wang 	/* create a codec specific record */
55995b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
560025eaba2fSLydia Wang 	if (spec == NULL)
560125eaba2fSLydia Wang 		return -ENOMEM;
560225eaba2fSLydia Wang 
560325eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
560425eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
560525eaba2fSLydia Wang 	if (err < 0) {
560625eaba2fSLydia Wang 		via_free(codec);
560725eaba2fSLydia Wang 		return err;
560825eaba2fSLydia Wang 	} else if (!err) {
560925eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
561025eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
561125eaba2fSLydia Wang 	}
561225eaba2fSLydia Wang 
561325eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
561425eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
561525eaba2fSLydia Wang 
561625eaba2fSLydia Wang 	spec->stream_name_analog = "VT2002P Analog";
561725eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
561825eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
561925eaba2fSLydia Wang 
562025eaba2fSLydia Wang 	spec->stream_name_digital = "VT2002P Digital";
562125eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
562225eaba2fSLydia Wang 
562325eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
562425eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
562525eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
562625eaba2fSLydia Wang 		get_mux_nids(codec);
562725eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
562825eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
562925eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
563025eaba2fSLydia Wang 		spec->num_mixers++;
563125eaba2fSLydia Wang 	}
563225eaba2fSLydia Wang 
563325eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
563425eaba2fSLydia Wang 
563525eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
56360f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
563725eaba2fSLydia Wang 
563825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
563925eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
564025eaba2fSLydia Wang #endif
564125eaba2fSLydia Wang 
564225eaba2fSLydia Wang 	return 0;
564325eaba2fSLydia Wang }
5644ab6734e7SLydia Wang 
5645ab6734e7SLydia Wang /* for vt1812 */
5646ab6734e7SLydia Wang 
5647ab6734e7SLydia Wang /* capture mixer elements */
5648ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5649ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5650ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5651ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5652ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5653ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5654ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5655ab6734e7SLydia Wang 		       HDA_INPUT),
5656ab6734e7SLydia Wang 	{
5657ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5658ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5659ab6734e7SLydia Wang 		 * So call somewhat different..
5660ab6734e7SLydia Wang 		 */
5661ab6734e7SLydia Wang 		.name = "Input Source",
5662ab6734e7SLydia Wang 		.count = 2,
5663ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5664ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5665ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5666ab6734e7SLydia Wang 	},
5667ab6734e7SLydia Wang 	{ } /* end */
5668ab6734e7SLydia Wang };
5669ab6734e7SLydia Wang 
5670ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = {
5671ab6734e7SLydia Wang 	/*
5672ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5673ab6734e7SLydia Wang 	 */
5674ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5675ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5676ab6734e7SLydia Wang 
5677ab6734e7SLydia Wang 
5678ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5679ab6734e7SLydia Wang 	 * mixer widget
5680ab6734e7SLydia Wang 	 */
5681ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5682ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5683ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5684ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5685ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5686ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5687ab6734e7SLydia Wang 
5688ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5689ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5690ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5691ab6734e7SLydia Wang 
5692ab6734e7SLydia Wang 	/* PW9 Output enable */
5693ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5694ab6734e7SLydia Wang 
5695ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5696ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5697ab6734e7SLydia Wang 
5698ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5699ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5700ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5701ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5702ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5703ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5704ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5705ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5706ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5707ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5708ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5709ab6734e7SLydia Wang 
5710ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5711ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5712ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5713ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5714ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5715ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5716ab6734e7SLydia Wang 
5717ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5718ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5719ab6734e7SLydia Wang 	{ }
5720ab6734e7SLydia Wang };
5721ab6734e7SLydia Wang 
5722ab6734e7SLydia Wang 
5723ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = {
5724ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5725ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5726ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5727ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5728ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5729ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5730ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5731ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5732ab6734e7SLydia Wang 	{ }
5733ab6734e7SLydia Wang };
5734ab6734e7SLydia Wang 
5735ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5736ab6734e7SLydia Wang 	.substreams = 2,
5737ab6734e7SLydia Wang 	.channels_min = 2,
5738ab6734e7SLydia Wang 	.channels_max = 2,
5739ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5740ab6734e7SLydia Wang 	.ops = {
5741ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5742ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5743ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5744ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5745ab6734e7SLydia Wang 	},
5746ab6734e7SLydia Wang };
5747ab6734e7SLydia Wang 
5748ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5749ab6734e7SLydia Wang 	.substreams = 2,
5750ab6734e7SLydia Wang 	.channels_min = 2,
5751ab6734e7SLydia Wang 	.channels_max = 2,
5752ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5753ab6734e7SLydia Wang 	.ops = {
5754ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5755ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5756ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5757ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5758ab6734e7SLydia Wang 	},
5759ab6734e7SLydia Wang };
5760ab6734e7SLydia Wang 
5761ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5762ab6734e7SLydia Wang 	.substreams = 1,
5763ab6734e7SLydia Wang 	.channels_min = 2,
5764ab6734e7SLydia Wang 	.channels_max = 2,
5765ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5766ab6734e7SLydia Wang 	.ops = {
5767ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5768ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5769ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5770ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5771ab6734e7SLydia Wang 	},
5772ab6734e7SLydia Wang };
5773ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5774ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5775ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5776ab6734e7SLydia Wang {
5777ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5778ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5779ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5780ab6734e7SLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5781ab6734e7SLydia Wang 	return 0;
5782ab6734e7SLydia Wang }
5783ab6734e7SLydia Wang 
5784ab6734e7SLydia Wang 
5785ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5786ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5787ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5788ab6734e7SLydia Wang {
5789ab6734e7SLydia Wang 	int err;
5790ab6734e7SLydia Wang 
5791ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
5792ab6734e7SLydia Wang 		return -1;
5793ab6734e7SLydia Wang 
5794ab6734e7SLydia Wang 	/* Line-Out: PortE */
5795ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
57963d83e577STakashi Iwai 			      "Front Playback Volume",
5797ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5798ab6734e7SLydia Wang 	if (err < 0)
5799ab6734e7SLydia Wang 		return err;
5800ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
58013d83e577STakashi Iwai 			      "Front Playback Switch",
5802ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5803ab6734e7SLydia Wang 	if (err < 0)
5804ab6734e7SLydia Wang 		return err;
5805ab6734e7SLydia Wang 
5806ab6734e7SLydia Wang 	return 0;
5807ab6734e7SLydia Wang }
5808ab6734e7SLydia Wang 
5809ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5810ab6734e7SLydia Wang {
5811ab6734e7SLydia Wang 	int err;
5812ab6734e7SLydia Wang 
5813ab6734e7SLydia Wang 	if (!pin)
5814ab6734e7SLydia Wang 		return 0;
5815ab6734e7SLydia Wang 
5816ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
5817ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
5818ab6734e7SLydia Wang 
5819ab6734e7SLydia Wang 
5820ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5821ab6734e7SLydia Wang 			      "Headphone Playback Volume",
5822ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5823ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5824ab6734e7SLydia Wang 	if (err < 0)
5825ab6734e7SLydia Wang 		return err;
5826ab6734e7SLydia Wang 
5827ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5828ab6734e7SLydia Wang 			      "Headphone Playback Switch",
5829ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5830ab6734e7SLydia Wang 	if (err < 0)
5831ab6734e7SLydia Wang 		return err;
5832ab6734e7SLydia Wang 
5833ab6734e7SLydia Wang 	create_hp_imux(spec);
5834ab6734e7SLydia Wang 	return 0;
5835ab6734e7SLydia Wang }
5836ab6734e7SLydia Wang 
5837ab6734e7SLydia Wang /* create playback/capture controls for input pins */
583810a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
5839ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
5840ab6734e7SLydia Wang {
584110a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
5842ab6734e7SLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5843f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
5844f3268512STakashi Iwai 	int err;
5845ab6734e7SLydia Wang 
584610a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5847f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
5848ab6734e7SLydia Wang 	if (err < 0)
5849ab6734e7SLydia Wang 		return err;
5850f3268512STakashi Iwai 
5851ab6734e7SLydia Wang 	/* build volume/mute control of loopback */
58527b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
5853ab6734e7SLydia Wang 	if (err < 0)
5854ab6734e7SLydia Wang 		return err;
5855ab6734e7SLydia Wang 
5856ab6734e7SLydia Wang 	/* for digital mic select */
585710a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
5858ab6734e7SLydia Wang 
5859ab6734e7SLydia Wang 	return 0;
5860ab6734e7SLydia Wang }
5861ab6734e7SLydia Wang 
5862ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
5863ab6734e7SLydia Wang {
5864ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
5865ab6734e7SLydia Wang 	int err;
5866ab6734e7SLydia Wang 
5867ab6734e7SLydia Wang 
5868ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5869ab6734e7SLydia Wang 	if (err < 0)
5870ab6734e7SLydia Wang 		return err;
5871ab6734e7SLydia Wang 	fill_dig_outs(codec);
5872ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
5873ab6734e7SLydia Wang 	if (err < 0)
5874ab6734e7SLydia Wang 		return err;
5875ab6734e7SLydia Wang 
5876ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
5877ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
5878ab6734e7SLydia Wang 
5879ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
5880ab6734e7SLydia Wang 	if (err < 0)
5881ab6734e7SLydia Wang 		return err;
5882ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5883ab6734e7SLydia Wang 	if (err < 0)
5884ab6734e7SLydia Wang 		return err;
588510a20af7STakashi Iwai 	err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
5886ab6734e7SLydia Wang 	if (err < 0)
5887ab6734e7SLydia Wang 		return err;
5888ab6734e7SLydia Wang 
5889ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5890ab6734e7SLydia Wang 
5891ab6734e7SLydia Wang 	fill_dig_outs(codec);
5892ab6734e7SLydia Wang 
5893ab6734e7SLydia Wang 	if (spec->kctls.list)
5894ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5895ab6734e7SLydia Wang 
5896ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
5897ab6734e7SLydia Wang 
5898ab6734e7SLydia Wang 	if (spec->hp_mux)
58993d83e577STakashi Iwai 		via_hp_build(codec);
5900ab6734e7SLydia Wang 
5901ab6734e7SLydia Wang 	return 1;
5902ab6734e7SLydia Wang }
5903ab6734e7SLydia Wang 
5904ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5905ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = {
5906ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
5907ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
5908ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
5909ab6734e7SLydia Wang 	{ } /* end */
5910ab6734e7SLydia Wang };
5911ab6734e7SLydia Wang #endif
5912ab6734e7SLydia Wang 
5913ab6734e7SLydia Wang 
5914ab6734e7SLydia Wang /* patch for vt1812 */
5915ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
5916ab6734e7SLydia Wang {
5917ab6734e7SLydia Wang 	struct via_spec *spec;
5918ab6734e7SLydia Wang 	int err;
5919ab6734e7SLydia Wang 
5920ab6734e7SLydia Wang 	/* create a codec specific record */
59215b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5922ab6734e7SLydia Wang 	if (spec == NULL)
5923ab6734e7SLydia Wang 		return -ENOMEM;
5924ab6734e7SLydia Wang 
5925ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
5926ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
5927ab6734e7SLydia Wang 	if (err < 0) {
5928ab6734e7SLydia Wang 		via_free(codec);
5929ab6734e7SLydia Wang 		return err;
5930ab6734e7SLydia Wang 	} else if (!err) {
5931ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5932ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5933ab6734e7SLydia Wang 	}
5934ab6734e7SLydia Wang 
5935ab6734e7SLydia Wang 
5936ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
5937ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
5938ab6734e7SLydia Wang 
5939ab6734e7SLydia Wang 	spec->stream_name_analog = "VT1812 Analog";
5940ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
5941ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
5942ab6734e7SLydia Wang 
5943ab6734e7SLydia Wang 	spec->stream_name_digital = "VT1812 Digital";
5944ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
5945ab6734e7SLydia Wang 
5946ab6734e7SLydia Wang 
5947ab6734e7SLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5948ab6734e7SLydia Wang 		spec->adc_nids = vt1812_adc_nids;
5949ab6734e7SLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
5950ab6734e7SLydia Wang 		get_mux_nids(codec);
5951ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
5952ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
5953ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
5954ab6734e7SLydia Wang 		spec->num_mixers++;
5955ab6734e7SLydia Wang 	}
5956ab6734e7SLydia Wang 
5957ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
5958ab6734e7SLydia Wang 
5959ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
59600f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5961ab6734e7SLydia Wang 
5962ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5963ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
5964ab6734e7SLydia Wang #endif
5965ab6734e7SLydia Wang 
5966ab6734e7SLydia Wang 	return 0;
5967ab6734e7SLydia Wang }
5968ab6734e7SLydia Wang 
5969c577b8a1SJoseph Chan /*
5970c577b8a1SJoseph Chan  * patch entries
5971c577b8a1SJoseph Chan  */
59721289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
59733218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
59743218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
59753218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
59763218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
59773218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
5978f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
59793218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
5980f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
59813218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
5982f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
59833218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
5984f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
59853218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
5986f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
59873218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
5988f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
59893218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
5990f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
59913218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
5992f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
59933218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
5994f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
59953218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
5996f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
59973218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
5998f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
59993218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6000f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
60013218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6002f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60033218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6004f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60053218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6006f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60073218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6008f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60093218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6010d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60113218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6012d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60133218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6014d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60153218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6016d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60173218c178STakashi Iwai 	{ .id = 0x11064397, .name = "VT1708S",
6018d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60193218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6020d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60213218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6022d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60233218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6024d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60253218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6026d949cac1SHarald Welte 	  .patch = patch_vt1702},
60273218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6028d949cac1SHarald Welte 	  .patch = patch_vt1702},
60293218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6030d949cac1SHarald Welte 	  .patch = patch_vt1702},
60313218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6032d949cac1SHarald Welte 	  .patch = patch_vt1702},
60333218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6034d949cac1SHarald Welte 	  .patch = patch_vt1702},
60353218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6036d949cac1SHarald Welte 	  .patch = patch_vt1702},
60373218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6038d949cac1SHarald Welte 	  .patch = patch_vt1702},
60393218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6040d949cac1SHarald Welte 	  .patch = patch_vt1702},
6041eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6042eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6043eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6044eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6045bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6046bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6047bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6048bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6049f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6050f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6051f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6052f3db423dSLydia Wang 	  .patch = patch_vt1716S},
605325eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
605425eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6055ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
605636dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
605736dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
6058c577b8a1SJoseph Chan 	{} /* terminator */
6059c577b8a1SJoseph Chan };
60601289e9e8STakashi Iwai 
60611289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
60621289e9e8STakashi Iwai 
60631289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
60641289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
60651289e9e8STakashi Iwai 	.owner = THIS_MODULE,
60661289e9e8STakashi Iwai };
60671289e9e8STakashi Iwai 
60681289e9e8STakashi Iwai MODULE_LICENSE("GPL");
60691289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
60701289e9e8STakashi Iwai 
60711289e9e8STakashi Iwai static int __init patch_via_init(void)
60721289e9e8STakashi Iwai {
60731289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
60741289e9e8STakashi Iwai }
60751289e9e8STakashi Iwai 
60761289e9e8STakashi Iwai static void __exit patch_via_exit(void)
60771289e9e8STakashi Iwai {
60781289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
60791289e9e8STakashi Iwai }
60801289e9e8STakashi Iwai 
60811289e9e8STakashi Iwai module_init(patch_via_init)
60821289e9e8STakashi Iwai module_exit(patch_via_exit)
6083