xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision bc92df7fe55e49c616a003b0b77e7badf2736429)
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;
1573e95b9abSLydia Wang 
1583e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1593e95b9abSLydia Wang 
1601f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1611f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1621f2e99feSLydia Wang #endif
1631f2e99feSLydia Wang };
1641f2e99feSLydia Wang 
1650341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1665b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1675b0cb1d8SJaroslav Kysela {
1685b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1695b0cb1d8SJaroslav Kysela 
1705b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1715b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1725b0cb1d8SJaroslav Kysela 		return NULL;
1735b0cb1d8SJaroslav Kysela 
1745b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1755b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1760341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1770341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1780341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1790341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1805b0cb1d8SJaroslav Kysela 	return spec;
1815b0cb1d8SJaroslav Kysela }
1825b0cb1d8SJaroslav Kysela 
183744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
184d7426329SHarald Welte {
185744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
186d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
187d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
188d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
189d7426329SHarald Welte 
190d7426329SHarald Welte 	/* get codec type */
191d7426329SHarald Welte 	if (ven_id != 0x1106)
192d7426329SHarald Welte 		codec_type = UNKNOWN;
193d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
194d7426329SHarald Welte 		codec_type = VT1708;
195d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
196d7426329SHarald Welte 		codec_type = VT1709_10CH;
197d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
198d7426329SHarald Welte 		codec_type = VT1709_6CH;
199518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
200d7426329SHarald Welte 		codec_type = VT1708B_8CH;
201518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
202518bf3baSLydia Wang 			codec_type = VT1708BCE;
203518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
204d7426329SHarald Welte 		codec_type = VT1708B_4CH;
205d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
206d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
207d7426329SHarald Welte 		codec_type = VT1708S;
208d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
209d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
210d7426329SHarald Welte 		codec_type = VT1702;
211eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
212eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
213eb7188caSLydia Wang 		codec_type = VT1718S;
214f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
215f3db423dSLydia Wang 		codec_type = VT1716S;
216bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
217bb3c6bfcSLydia Wang 		codec_type = VT1718S;
21825eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
21925eaba2fSLydia Wang 		codec_type = VT2002P;
220ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
221ab6734e7SLydia Wang 		codec_type = VT1812;
22236dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
22336dd5c4aSLydia Wang 		codec_type = VT1708S;
224d7426329SHarald Welte 	else
225d7426329SHarald Welte 		codec_type = UNKNOWN;
226d7426329SHarald Welte 	return codec_type;
227d7426329SHarald Welte };
228d7426329SHarald Welte 
22969e52a80SHarald Welte #define VIA_HP_EVENT		0x01
23069e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
231a34df19aSLydia Wang #define VIA_JACK_EVENT		0x04
232f3db423dSLydia Wang #define VIA_MONO_EVENT		0x08
23325eaba2fSLydia Wang #define VIA_SPEAKER_EVENT	0x10
23425eaba2fSLydia Wang #define VIA_BIND_HP_EVENT	0x20
23569e52a80SHarald Welte 
236c577b8a1SJoseph Chan enum {
237c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
238c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
239f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
24025eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
241c577b8a1SJoseph Chan };
242c577b8a1SJoseph Chan 
243c577b8a1SJoseph Chan enum {
244eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
245c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
246c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
247c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
248c577b8a1SJoseph Chan };
249c577b8a1SJoseph Chan 
250f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2511f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2521f2e99feSLydia Wang 
2531f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2541f2e99feSLydia Wang {
2551f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2561f2e99feSLydia Wang 		return;
2571f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2581f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2591f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2601f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2611f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2621f2e99feSLydia Wang }
2631f2e99feSLydia Wang 
2641f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2651f2e99feSLydia Wang {
2661f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2671f2e99feSLydia Wang 		return;
2681f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2691f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2701f2e99feSLydia Wang 		return;
2711f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
2721f2e99feSLydia Wang 			    !spec->vt1708_jack_detectect);
2735b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2741f2e99feSLydia Wang }
275f5271101SLydia Wang 
2763e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2773e95b9abSLydia Wang {
2783e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2793e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2803e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2813e95b9abSLydia Wang }
28225eaba2fSLydia Wang 
283f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
284f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
285f5271101SLydia Wang {
286f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
287f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
288f5271101SLydia Wang 
2893e95b9abSLydia Wang 	set_widgets_power_state(codec);
290f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
2911f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2921f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2931f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2941f2e99feSLydia Wang 		else
2951f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2961f2e99feSLydia Wang 	}
297f5271101SLydia Wang 	return change;
298f5271101SLydia Wang }
299f5271101SLydia Wang 
300f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
301f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
302f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
303f5271101SLydia Wang 			.name = NULL,					\
304f5271101SLydia Wang 			.index = 0,					\
305f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
306f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
307f5271101SLydia Wang 			.put = analog_input_switch_put,			\
308f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
309f5271101SLydia Wang 
31025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
31125eaba2fSLydia Wang 
31225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
31325eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
31425eaba2fSLydia Wang {
31525eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
31625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
31725eaba2fSLydia Wang 	int i;
31825eaba2fSLydia Wang 	int change = 0;
31925eaba2fSLydia Wang 
32025eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
32125eaba2fSLydia Wang 	int lmute, rmute;
32225eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
32325eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
32425eaba2fSLydia Wang 		return change;
32525eaba2fSLydia Wang 	}
32625eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
32725eaba2fSLydia Wang 					      ucontrol);
32825eaba2fSLydia Wang 	/* Get mute value */
32925eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
33025eaba2fSLydia Wang 	valp++;
33125eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
33225eaba2fSLydia Wang 
33325eaba2fSLydia Wang 	/* Set hp pins */
33425eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
33525eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
33625eaba2fSLydia Wang 			snd_hda_codec_amp_update(
33725eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
33825eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
33925eaba2fSLydia Wang 				lmute);
34025eaba2fSLydia Wang 			snd_hda_codec_amp_update(
34125eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
34225eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
34325eaba2fSLydia Wang 				rmute);
34425eaba2fSLydia Wang 		}
34525eaba2fSLydia Wang 	}
34625eaba2fSLydia Wang 
34725eaba2fSLydia Wang 	if (!lmute && !rmute) {
34825eaba2fSLydia Wang 		/* Line Outs */
34925eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
35025eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
35125eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
35225eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
35325eaba2fSLydia Wang 		/* Speakers */
35425eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
35525eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
35625eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
35725eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
35825eaba2fSLydia Wang 		/* unmute */
35925eaba2fSLydia Wang 		via_hp_bind_automute(codec);
36025eaba2fSLydia Wang 
36125eaba2fSLydia Wang 	} else {
36225eaba2fSLydia Wang 		if (lmute) {
36325eaba2fSLydia Wang 			/* Mute all left channels */
36425eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
36525eaba2fSLydia Wang 				snd_hda_codec_amp_update(
36625eaba2fSLydia Wang 					codec,
36725eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
36825eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36925eaba2fSLydia Wang 					lmute);
37025eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
37125eaba2fSLydia Wang 				snd_hda_codec_amp_update(
37225eaba2fSLydia Wang 					codec,
37325eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
37425eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
37525eaba2fSLydia Wang 					lmute);
37625eaba2fSLydia Wang 		}
37725eaba2fSLydia Wang 		if (rmute) {
37825eaba2fSLydia Wang 			/* mute all right channels */
37925eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38025eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38125eaba2fSLydia Wang 					codec,
38225eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
38325eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
38425eaba2fSLydia Wang 					rmute);
38525eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
38625eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38725eaba2fSLydia Wang 					codec,
38825eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
38925eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39025eaba2fSLydia Wang 					rmute);
39125eaba2fSLydia Wang 		}
39225eaba2fSLydia Wang 	}
39325eaba2fSLydia Wang 	return change;
39425eaba2fSLydia Wang }
39525eaba2fSLydia Wang 
39625eaba2fSLydia Wang #define BIND_PIN_MUTE							\
39725eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
39825eaba2fSLydia Wang 			.name = NULL,					\
39925eaba2fSLydia Wang 			.index = 0,					\
40025eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
40125eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
40225eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
40325eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
40425eaba2fSLydia Wang 
40571eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = {
406c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
407c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
408f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
40925eaba2fSLydia Wang 	BIND_PIN_MUTE,
410c577b8a1SJoseph Chan };
411c577b8a1SJoseph Chan 
412c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = {
413c577b8a1SJoseph Chan 	/* ADC1-2 */
414c577b8a1SJoseph Chan 	0x15, 0x27
415c577b8a1SJoseph Chan };
416c577b8a1SJoseph Chan 
417c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = {
418c577b8a1SJoseph Chan 	/* ADC1-2 */
419c577b8a1SJoseph Chan 	0x14, 0x15, 0x16
420c577b8a1SJoseph Chan };
421c577b8a1SJoseph Chan 
422f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = {
423f7278fd0SJosepch Chan 	/* ADC1-2 */
424f7278fd0SJosepch Chan 	0x13, 0x14
425f7278fd0SJosepch Chan };
426f7278fd0SJosepch Chan 
427d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = {
428d949cac1SHarald Welte 	/* ADC1-2 */
429d949cac1SHarald Welte 	0x13, 0x14
430d949cac1SHarald Welte };
431d949cac1SHarald Welte 
432d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = {
433d949cac1SHarald Welte 	/* ADC1-2 */
434d949cac1SHarald Welte 	0x12, 0x20, 0x1F
435d949cac1SHarald Welte };
436d949cac1SHarald Welte 
437eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = {
438eb7188caSLydia Wang 	/* ADC1-2 */
439eb7188caSLydia Wang 	0x10, 0x11
440eb7188caSLydia Wang };
441eb7188caSLydia Wang 
442f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = {
443f3db423dSLydia Wang 	/* ADC1-2 */
444f3db423dSLydia Wang 	0x13, 0x14
445f3db423dSLydia Wang };
446f3db423dSLydia Wang 
44725eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = {
44825eaba2fSLydia Wang 	/* ADC1-2 */
44925eaba2fSLydia Wang 	0x10, 0x11
45025eaba2fSLydia Wang };
45125eaba2fSLydia Wang 
452ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = {
453ab6734e7SLydia Wang 	/* ADC1-2 */
454ab6734e7SLydia Wang 	0x10, 0x11
455ab6734e7SLydia Wang };
456ab6734e7SLydia Wang 
457ab6734e7SLydia Wang 
458c577b8a1SJoseph Chan /* add dynamic controls */
4597b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
4607b315bb4STakashi Iwai 			     int idx, unsigned long val)
461c577b8a1SJoseph Chan {
462c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
463c577b8a1SJoseph Chan 
464603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
465603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
466c577b8a1SJoseph Chan 	if (!knew)
467c577b8a1SJoseph Chan 		return -ENOMEM;
46871eb7dccSLydia Wang 	*knew = via_control_templates[type];
469c577b8a1SJoseph Chan 	knew->name = kstrdup(name, GFP_KERNEL);
470c577b8a1SJoseph Chan 	if (!knew->name)
471c577b8a1SJoseph Chan 		return -ENOMEM;
4724d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4735e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
474c577b8a1SJoseph Chan 	knew->private_value = val;
475c577b8a1SJoseph Chan 	return 0;
476c577b8a1SJoseph Chan }
477c577b8a1SJoseph Chan 
4787b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4797b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4807b315bb4STakashi Iwai 
4815b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
4825b0cb1d8SJaroslav Kysela 						struct snd_kcontrol_new *tmpl)
4835b0cb1d8SJaroslav Kysela {
4845b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
4855b0cb1d8SJaroslav Kysela 
4865b0cb1d8SJaroslav Kysela 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
4875b0cb1d8SJaroslav Kysela 	knew = snd_array_new(&spec->kctls);
4885b0cb1d8SJaroslav Kysela 	if (!knew)
4895b0cb1d8SJaroslav Kysela 		return NULL;
4905b0cb1d8SJaroslav Kysela 	*knew = *tmpl;
4915b0cb1d8SJaroslav Kysela 	knew->name = kstrdup(tmpl->name, GFP_KERNEL);
4925b0cb1d8SJaroslav Kysela 	if (!knew->name)
4935b0cb1d8SJaroslav Kysela 		return NULL;
494b331439dSTakashi Iwai 	return knew;
4955b0cb1d8SJaroslav Kysela }
4965b0cb1d8SJaroslav Kysela 
497603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
498603c4019STakashi Iwai {
499603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
500603c4019STakashi Iwai 
501603c4019STakashi Iwai 	if (spec->kctls.list) {
502603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
503603c4019STakashi Iwai 		int i;
504603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
505603c4019STakashi Iwai 			kfree(kctl[i].name);
506603c4019STakashi Iwai 	}
507603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
508603c4019STakashi Iwai }
509603c4019STakashi Iwai 
510c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
5119510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
5127b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
513c577b8a1SJoseph Chan {
514c577b8a1SJoseph Chan 	char name[32];
515c577b8a1SJoseph Chan 	int err;
516c577b8a1SJoseph Chan 
517c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
5187b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
519c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
520c577b8a1SJoseph Chan 	if (err < 0)
521c577b8a1SJoseph Chan 		return err;
522c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5237b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
524c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
525c577b8a1SJoseph Chan 	if (err < 0)
526c577b8a1SJoseph Chan 		return err;
527c577b8a1SJoseph Chan 	return 0;
528c577b8a1SJoseph Chan }
529c577b8a1SJoseph Chan 
530c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
531c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
532c577b8a1SJoseph Chan 					   int dac_idx)
533c577b8a1SJoseph Chan {
534c577b8a1SJoseph Chan 	/* set as output */
535c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
536c577b8a1SJoseph Chan 			    pin_type);
537c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
538c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
539d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
540d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
541d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
542c577b8a1SJoseph Chan }
543c577b8a1SJoseph Chan 
544c577b8a1SJoseph Chan 
545c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
546c577b8a1SJoseph Chan {
547c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
548c577b8a1SJoseph Chan 	int i;
549c577b8a1SJoseph Chan 
550c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
551c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
552c577b8a1SJoseph Chan 		if (nid)
553c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
554c577b8a1SJoseph Chan 	}
555c577b8a1SJoseph Chan }
556c577b8a1SJoseph Chan 
557c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
558c577b8a1SJoseph Chan {
559c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
560c577b8a1SJoseph Chan 	hda_nid_t pin;
56125eaba2fSLydia Wang 	int i;
562c577b8a1SJoseph Chan 
56325eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
56425eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
565c577b8a1SJoseph Chan 		if (pin) /* connect to front */
566c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
567c577b8a1SJoseph Chan 	}
56825eaba2fSLydia Wang }
569c577b8a1SJoseph Chan 
57032e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
57132e0191dSClemens Ladisch 
572c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
573c577b8a1SJoseph Chan {
574c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5757b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
57632e0191dSClemens Ladisch 	unsigned int ctl;
577c577b8a1SJoseph Chan 	int i;
578c577b8a1SJoseph Chan 
5797b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5807b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
58132e0191dSClemens Ladisch 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
58232e0191dSClemens Ladisch 			ctl = PIN_OUT;
58330649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
58432e0191dSClemens Ladisch 			ctl = PIN_VREF50;
58532e0191dSClemens Ladisch 		else
58632e0191dSClemens Ladisch 			ctl = PIN_IN;
587c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
58832e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
589c577b8a1SJoseph Chan 	}
590c577b8a1SJoseph Chan }
591f5271101SLydia Wang 
592f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
593f5271101SLydia Wang 				unsigned int *affected_parm)
594f5271101SLydia Wang {
595f5271101SLydia Wang 	unsigned parm;
596f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
597f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
598f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
599f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
600d56757abSTakashi Iwai 	unsigned present = snd_hda_jack_detect(codec, nid);
6011564b287SLydia Wang 	struct via_spec *spec = codec->spec;
6021564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
6031564b287SLydia Wang 	    || ((no_presence || present)
6041564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
605f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
606f5271101SLydia Wang 		parm = AC_PWRST_D0;
607f5271101SLydia Wang 	} else
608f5271101SLydia Wang 		parm = AC_PWRST_D3;
609f5271101SLydia Wang 
610f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
611f5271101SLydia Wang }
612f5271101SLydia Wang 
613c577b8a1SJoseph Chan /*
614c577b8a1SJoseph Chan  * input MUX handling
615c577b8a1SJoseph Chan  */
616c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
617c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
618c577b8a1SJoseph Chan {
619c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
620c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
621c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
622c577b8a1SJoseph Chan }
623c577b8a1SJoseph Chan 
624c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
625c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
626c577b8a1SJoseph Chan {
627c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
628c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
629c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
630c577b8a1SJoseph Chan 
631c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
632c577b8a1SJoseph Chan 	return 0;
633c577b8a1SJoseph Chan }
634c577b8a1SJoseph Chan 
635c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
636c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
637c577b8a1SJoseph Chan {
638c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
639c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
640c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
641bff5fbf5SLydia Wang 	int ret;
642c577b8a1SJoseph Chan 
643337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
644337b9d02STakashi Iwai 		return -EINVAL;
645a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
646a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
647a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
648a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
649a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
650bff5fbf5SLydia Wang 
651bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
652bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
653bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
654a80e6e3cSLydia Wang 	/* update jack power state */
6553e95b9abSLydia Wang 	set_widgets_power_state(codec);
656a80e6e3cSLydia Wang 
657bff5fbf5SLydia Wang 	return ret;
658c577b8a1SJoseph Chan }
659c577b8a1SJoseph Chan 
6600aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6610aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6620aa62aefSHarald Welte {
6630aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6640aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6650aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6660aa62aefSHarald Welte }
6670aa62aefSHarald Welte 
6680aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
6690aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
6700aa62aefSHarald Welte {
6710aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6725b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
673eb7188caSLydia Wang 	unsigned int pinsel;
674eb7188caSLydia Wang 
675eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
676eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
6770aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
6780aa62aefSHarald Welte 				      0x00);
6790aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
6800aa62aefSHarald Welte 
6810aa62aefSHarald Welte 	return 0;
6820aa62aefSHarald Welte }
6830aa62aefSHarald Welte 
6840713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
6850713efebSLydia Wang {
6860713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
6870713efebSLydia Wang 	if (ctl) {
6880713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
6890713efebSLydia Wang 		ctl->vd[0].access |= active
6900713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
6910713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
6920713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
6930713efebSLydia Wang 	}
6940713efebSLydia Wang }
6950713efebSLydia Wang 
6965b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
6975b0cb1d8SJaroslav Kysela {
6985b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
6995b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7005b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7015b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7025b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
7035b0cb1d8SJaroslav Kysela 	default:		return 0;
7045b0cb1d8SJaroslav Kysela 	}
7055b0cb1d8SJaroslav Kysela }
7065b0cb1d8SJaroslav Kysela 
707cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
708cdc1784dSLydia Wang {
709cdc1784dSLydia Wang 	/* mute side channel */
710cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
711cdc1784dSLydia Wang 	unsigned int parm = spec->hp_independent_mode
712cdc1784dSLydia Wang 		? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
7135b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
714cdc1784dSLydia Wang 
715cdc1784dSLydia Wang 	if (sw3)
716cdc1784dSLydia Wang 		snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
717cdc1784dSLydia Wang 				    parm);
718cdc1784dSLydia Wang 	return 0;
719cdc1784dSLydia Wang }
720cdc1784dSLydia Wang 
7210aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7220aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7230aa62aefSHarald Welte {
7240aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7265b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7270aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
728cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
729cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
730cdc1784dSLydia Wang 		? 1 : 0;
731ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
732ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
733ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
734ce0e5a9eSLydia Wang 	else
735ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
736ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7370aa62aefSHarald Welte 
738ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
739ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
740ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
741cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
742cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
743cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7440aa62aefSHarald Welte 					   0, 0, 0);
7450aa62aefSHarald Welte 
746cdc1784dSLydia Wang 	update_side_mute_status(codec);
7470713efebSLydia Wang 	/* update HP volume/swtich active state */
7480713efebSLydia Wang 	if (spec->codec_type == VT1708S
749eb7188caSLydia Wang 	    || spec->codec_type == VT1702
750f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
75125eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
752ab6734e7SLydia Wang 	    || spec->codec_type == VT2002P
753ab6734e7SLydia Wang 	    || spec->codec_type == VT1812) {
7540713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7550713efebSLydia Wang 			     spec->hp_independent_mode);
7560713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
7570713efebSLydia Wang 			     spec->hp_independent_mode);
7580713efebSLydia Wang 	}
759ce0e5a9eSLydia Wang 	/* update jack power state */
7603e95b9abSLydia Wang 	set_widgets_power_state(codec);
7610aa62aefSHarald Welte 	return 0;
7620aa62aefSHarald Welte }
7630aa62aefSHarald Welte 
7645b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = {
7650aa62aefSHarald Welte 	{
7660aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7670aa62aefSHarald Welte 		.name = "Independent HP",
7680aa62aefSHarald Welte 		.info = via_independent_hp_info,
7690aa62aefSHarald Welte 		.get = via_independent_hp_get,
7700aa62aefSHarald Welte 		.put = via_independent_hp_put,
7710aa62aefSHarald Welte 	},
7725b0cb1d8SJaroslav Kysela 	{
7735b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
7745b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
7755b0cb1d8SJaroslav Kysela 	},
7760aa62aefSHarald Welte };
7770aa62aefSHarald Welte 
7783d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
7795b0cb1d8SJaroslav Kysela {
7803d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
7815b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
7825b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
7833d83e577STakashi Iwai 	int nums;
7843d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
7855b0cb1d8SJaroslav Kysela 
7865b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7875b0cb1d8SJaroslav Kysela 	case VT1718S:
7885b0cb1d8SJaroslav Kysela 		nid = 0x34;
7895b0cb1d8SJaroslav Kysela 		break;
7905b0cb1d8SJaroslav Kysela 	case VT2002P:
7915b0cb1d8SJaroslav Kysela 		nid = 0x35;
7925b0cb1d8SJaroslav Kysela 		break;
7935b0cb1d8SJaroslav Kysela 	case VT1812:
7945b0cb1d8SJaroslav Kysela 		nid = 0x3d;
7955b0cb1d8SJaroslav Kysela 		break;
7965b0cb1d8SJaroslav Kysela 	default:
7975b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
7985b0cb1d8SJaroslav Kysela 		break;
7995b0cb1d8SJaroslav Kysela 	}
8005b0cb1d8SJaroslav Kysela 
801ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
802ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
803ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8043d83e577STakashi Iwai 		if (nums <= 1)
8053d83e577STakashi Iwai 			return 0;
806ee3c35c0SLydia Wang 	}
8073d83e577STakashi Iwai 
8083d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8093d83e577STakashi Iwai 	if (knew == NULL)
8103d83e577STakashi Iwai 		return -ENOMEM;
8113d83e577STakashi Iwai 
8125b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8135b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8145b0cb1d8SJaroslav Kysela 
8155b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8165b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8175b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8185b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8195b0cb1d8SJaroslav Kysela 
8205b0cb1d8SJaroslav Kysela 	return 0;
8215b0cb1d8SJaroslav Kysela }
8225b0cb1d8SJaroslav Kysela 
8231564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8241564b287SLydia Wang {
8251564b287SLydia Wang 	int i;
8261564b287SLydia Wang 	struct snd_ctl_elem_id id;
8271564b287SLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line"};
8281564b287SLydia Wang 
8291564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8301564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8311564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8321564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
8331564b287SLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
8341564b287SLydia Wang 			       &id);
8351564b287SLydia Wang 	}
8361564b287SLydia Wang }
8371564b287SLydia Wang 
8381564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8391564b287SLydia Wang {
8401564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8411564b287SLydia Wang 	hda_nid_t  nid_mixer;
8421564b287SLydia Wang 	int start_idx;
8431564b287SLydia Wang 	int end_idx;
8441564b287SLydia Wang 	int i;
8451564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8461564b287SLydia Wang 	switch (spec->codec_type) {
8471564b287SLydia Wang 	case VT1708:
8481564b287SLydia Wang 		nid_mixer = 0x17;
8491564b287SLydia Wang 		start_idx = 2;
8501564b287SLydia Wang 		end_idx = 4;
8511564b287SLydia Wang 		break;
8521564b287SLydia Wang 	case VT1709_10CH:
8531564b287SLydia Wang 	case VT1709_6CH:
8541564b287SLydia Wang 		nid_mixer = 0x18;
8551564b287SLydia Wang 		start_idx = 2;
8561564b287SLydia Wang 		end_idx = 4;
8571564b287SLydia Wang 		break;
8581564b287SLydia Wang 	case VT1708B_8CH:
8591564b287SLydia Wang 	case VT1708B_4CH:
8601564b287SLydia Wang 	case VT1708S:
861f3db423dSLydia Wang 	case VT1716S:
8621564b287SLydia Wang 		nid_mixer = 0x16;
8631564b287SLydia Wang 		start_idx = 2;
8641564b287SLydia Wang 		end_idx = 4;
8651564b287SLydia Wang 		break;
866ab657e0cSLydia Wang 	case VT1718S:
867ab657e0cSLydia Wang 		nid_mixer = 0x21;
868ab657e0cSLydia Wang 		start_idx = 1;
869ab657e0cSLydia Wang 		end_idx = 3;
870ab657e0cSLydia Wang 		break;
8711564b287SLydia Wang 	default:
8721564b287SLydia Wang 		return;
8731564b287SLydia Wang 	}
8741564b287SLydia Wang 	/* check AA path's mute status */
8751564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
8761564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
8771564b287SLydia Wang 		snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i,
8781564b287SLydia Wang 					 HDA_AMP_MUTE, val);
8791564b287SLydia Wang 	}
8801564b287SLydia Wang }
8811564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
8821564b287SLydia Wang {
8837b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
8847b315bb4STakashi Iwai 	int i;
8857b315bb4STakashi Iwai 
8867b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
8877b315bb4STakashi Iwai 		if (pin == cfg->inputs[i].pin)
88886e2959aSTakashi Iwai 			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
8891564b287SLydia Wang 	}
8907b315bb4STakashi Iwai 	return 0;
8911564b287SLydia Wang }
8921564b287SLydia Wang 
8931564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
8941564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
8951564b287SLydia Wang {
8961564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
8971564b287SLydia Wang 	uinfo->count = 1;
8981564b287SLydia Wang 	uinfo->value.integer.min = 0;
8991564b287SLydia Wang 	uinfo->value.integer.max = 1;
9001564b287SLydia Wang 	return 0;
9011564b287SLydia Wang }
9021564b287SLydia Wang 
9031564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9041564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9051564b287SLydia Wang {
9061564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9071564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9087b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9091564b287SLydia Wang 	int on = 1;
9101564b287SLydia Wang 	int i;
9111564b287SLydia Wang 
9127b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9137b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9147b315bb4STakashi Iwai 		int ctl = snd_hda_codec_read(codec, nid, 0,
9157b315bb4STakashi Iwai 					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
91686e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9177b315bb4STakashi Iwai 			continue;
91886e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9197b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9201564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
9217b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9221564b287SLydia Wang 			on = 0;
9231564b287SLydia Wang 	}
9241564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9251564b287SLydia Wang 	return 0;
9261564b287SLydia Wang }
9271564b287SLydia Wang 
9281564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9291564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9301564b287SLydia Wang {
9311564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9321564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9337b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9341564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9351564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9361564b287SLydia Wang 	int i;
9371564b287SLydia Wang 
9387b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9397b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9407b315bb4STakashi Iwai 		unsigned int parm;
9417b315bb4STakashi Iwai 
94286e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9437b315bb4STakashi Iwai 			continue;
94486e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9457b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9461564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
9477b315bb4STakashi Iwai 
9487b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
9491564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9501564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
9511564b287SLydia Wang 		parm |= out_in;
9521564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
9531564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
9541564b287SLydia Wang 				    parm);
9551564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
9561564b287SLydia Wang 			mute_aa_path(codec, 1);
9571564b287SLydia Wang 			notify_aa_path_ctls(codec);
9581564b287SLydia Wang 		}
9597b315bb4STakashi Iwai 		if (spec->codec_type == VT1718S) {
960eb7188caSLydia Wang 			snd_hda_codec_amp_stereo(
961eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
962eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
9631564b287SLydia Wang 		}
96486e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
965f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
966f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
9671564b287SLydia Wang 				/* input = index 1 (AOW3) */
9681564b287SLydia Wang 				snd_hda_codec_write(
9691564b287SLydia Wang 					codec, nid, 0,
9701564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
9711564b287SLydia Wang 				snd_hda_codec_amp_stereo(
9721564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
9731564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
9741564b287SLydia Wang 			}
9751564b287SLydia Wang 		}
9761564b287SLydia Wang 	}
9771564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
9783e95b9abSLydia Wang 	set_widgets_power_state(codec);
9791564b287SLydia Wang 	return 1;
9801564b287SLydia Wang }
9811564b287SLydia Wang 
9825b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = {
9831564b287SLydia Wang 	{
9841564b287SLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9851564b287SLydia Wang 	 .name = "Smart 5.1",
9861564b287SLydia Wang 	 .count = 1,
9871564b287SLydia Wang 	 .info = via_smart51_info,
9881564b287SLydia Wang 	 .get = via_smart51_get,
9891564b287SLydia Wang 	 .put = via_smart51_put,
9901564b287SLydia Wang 	 },
9915b0cb1d8SJaroslav Kysela 	{
9925b0cb1d8SJaroslav Kysela 	 .iface = NID_MAPPING,
9935b0cb1d8SJaroslav Kysela 	 .name = "Smart 5.1",
9945b0cb1d8SJaroslav Kysela 	}
9951564b287SLydia Wang };
9961564b287SLydia Wang 
9975b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
9985b0cb1d8SJaroslav Kysela {
9995b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10007b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10015b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10025b0cb1d8SJaroslav Kysela 	int i;
10035b0cb1d8SJaroslav Kysela 
10045b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_smart51_mixer[0]);
10055b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10065b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10075b0cb1d8SJaroslav Kysela 
10087b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10097b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
101086e2959aSTakashi Iwai 		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
10115b0cb1d8SJaroslav Kysela 			knew = via_clone_control(spec, &via_smart51_mixer[1]);
10125b0cb1d8SJaroslav Kysela 			if (knew == NULL)
10135b0cb1d8SJaroslav Kysela 				return -ENOMEM;
10145b0cb1d8SJaroslav Kysela 			knew->subdevice = nid;
10157b315bb4STakashi Iwai 			break;
10165b0cb1d8SJaroslav Kysela 		}
10175b0cb1d8SJaroslav Kysela 	}
10185b0cb1d8SJaroslav Kysela 
10195b0cb1d8SJaroslav Kysela 	return 0;
10205b0cb1d8SJaroslav Kysela }
10215b0cb1d8SJaroslav Kysela 
1022c577b8a1SJoseph Chan /* capture mixer elements */
1023c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = {
1024c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1025c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1026c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1027c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1028c577b8a1SJoseph Chan 	{
1029c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1030c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1031c577b8a1SJoseph Chan 		 * So call somewhat different..
1032c577b8a1SJoseph Chan 		 */
1033c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1034c577b8a1SJoseph Chan 		.name = "Input Source",
1035c577b8a1SJoseph Chan 		.count = 1,
1036c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1037c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1038c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1039c577b8a1SJoseph Chan 	},
1040c577b8a1SJoseph Chan 	{ } /* end */
1041c577b8a1SJoseph Chan };
1042f5271101SLydia Wang 
1043f5271101SLydia Wang /* check AA path's mute statue */
1044f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1045f5271101SLydia Wang {
1046f5271101SLydia Wang 	int mute = 1;
1047f5271101SLydia Wang 	hda_nid_t  nid_mixer;
1048f5271101SLydia Wang 	int start_idx;
1049f5271101SLydia Wang 	int end_idx;
1050f5271101SLydia Wang 	int i;
1051f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1052f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1053f5271101SLydia Wang 	switch (spec->codec_type) {
1054f5271101SLydia Wang 	case VT1708B_8CH:
1055f5271101SLydia Wang 	case VT1708B_4CH:
1056f5271101SLydia Wang 	case VT1708S:
1057f3db423dSLydia Wang 	case VT1716S:
1058f5271101SLydia Wang 		nid_mixer = 0x16;
1059f5271101SLydia Wang 		start_idx = 2;
1060f5271101SLydia Wang 		end_idx = 4;
1061f5271101SLydia Wang 		break;
1062f5271101SLydia Wang 	case VT1702:
1063f5271101SLydia Wang 		nid_mixer = 0x1a;
1064f5271101SLydia Wang 		start_idx = 1;
1065f5271101SLydia Wang 		end_idx = 3;
1066f5271101SLydia Wang 		break;
1067eb7188caSLydia Wang 	case VT1718S:
1068eb7188caSLydia Wang 		nid_mixer = 0x21;
1069eb7188caSLydia Wang 		start_idx = 1;
1070eb7188caSLydia Wang 		end_idx = 3;
1071eb7188caSLydia Wang 		break;
107225eaba2fSLydia Wang 	case VT2002P:
1073ab6734e7SLydia Wang 	case VT1812:
107425eaba2fSLydia Wang 		nid_mixer = 0x21;
107525eaba2fSLydia Wang 		start_idx = 0;
107625eaba2fSLydia Wang 		end_idx = 2;
107725eaba2fSLydia Wang 		break;
1078f5271101SLydia Wang 	default:
1079f5271101SLydia Wang 		return 0;
1080f5271101SLydia Wang 	}
1081f5271101SLydia Wang 	/* check AA path's mute status */
1082f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1083f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1084f5271101SLydia Wang 			codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1085f5271101SLydia Wang 		int shift = 8 * (i % 4);
1086f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1087f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1088f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1089f5271101SLydia Wang 			/* check mute status while the pin is connected */
1090f5271101SLydia Wang 			int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0,
1091f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1092f5271101SLydia Wang 			int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1,
1093f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1094f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1095f5271101SLydia Wang 				mute = 0;
1096f5271101SLydia Wang 				break;
1097f5271101SLydia Wang 			}
1098f5271101SLydia Wang 		}
1099f5271101SLydia Wang 	}
1100f5271101SLydia Wang 	return mute;
1101f5271101SLydia Wang }
1102f5271101SLydia Wang 
1103f5271101SLydia Wang /* enter/exit analog low-current mode */
1104f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1105f5271101SLydia Wang {
1106f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1107f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1108f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1109f5271101SLydia Wang 	unsigned int verb = 0;
1110f5271101SLydia Wang 	unsigned int parm = 0;
1111f5271101SLydia Wang 
1112f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1113f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1114f5271101SLydia Wang 	else {
1115f5271101SLydia Wang 		enable = enable && stream_idle;
1116f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1117f5271101SLydia Wang 	}
1118f5271101SLydia Wang 
1119f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1120f5271101SLydia Wang 	switch (spec->codec_type) {
1121f5271101SLydia Wang 	case VT1708B_8CH:
1122f5271101SLydia Wang 	case VT1708B_4CH:
1123f5271101SLydia Wang 		verb = 0xf70;
1124f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1125f5271101SLydia Wang 		break;
1126f5271101SLydia Wang 	case VT1708S:
1127eb7188caSLydia Wang 	case VT1718S:
1128f3db423dSLydia Wang 	case VT1716S:
1129f5271101SLydia Wang 		verb = 0xf73;
1130f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1131f5271101SLydia Wang 		break;
1132f5271101SLydia Wang 	case VT1702:
1133f5271101SLydia Wang 		verb = 0xf73;
1134f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1135f5271101SLydia Wang 		break;
113625eaba2fSLydia Wang 	case VT2002P:
1137ab6734e7SLydia Wang 	case VT1812:
113825eaba2fSLydia Wang 		verb = 0xf93;
113925eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
114025eaba2fSLydia Wang 		break;
1141f5271101SLydia Wang 	default:
1142f5271101SLydia Wang 		return;		/* other codecs are not supported */
1143f5271101SLydia Wang 	}
1144f5271101SLydia Wang 	/* send verb */
1145f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1146f5271101SLydia Wang }
1147f5271101SLydia Wang 
1148c577b8a1SJoseph Chan /*
1149c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1150c577b8a1SJoseph Chan  */
1151c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = {
1152c577b8a1SJoseph Chan 	/*
1153c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1154c577b8a1SJoseph Chan 	 */
1155c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1156c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1157c577b8a1SJoseph Chan 
1158c577b8a1SJoseph Chan 
1159f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1160c577b8a1SJoseph Chan 	 * mixer widget
1161c577b8a1SJoseph Chan 	 */
1162c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1163f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1164f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1165f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1166f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1167f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1168c577b8a1SJoseph Chan 
1169c577b8a1SJoseph Chan 	/*
1170c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1171c577b8a1SJoseph Chan 	 */
1172c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1173c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1174c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1175c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1176c577b8a1SJoseph Chan 
1177bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1178bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1179c577b8a1SJoseph Chan 	/* PW9 Output enable */
1180c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1181f7278fd0SJosepch Chan 	{ }
1182c577b8a1SJoseph Chan };
1183c577b8a1SJoseph Chan 
1184c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1185c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1186c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1187c577b8a1SJoseph Chan {
1188c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
118917314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
119017314379SLydia Wang 		&& substream->ref_count == 0;
119117314379SLydia Wang 	analog_low_current_mode(codec, idle);
11929a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
11939a08160bSTakashi Iwai 					     hinfo);
1194c577b8a1SJoseph Chan }
1195c577b8a1SJoseph Chan 
11960aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
11970aa62aefSHarald Welte 				      unsigned int stream_tag,
11980aa62aefSHarald Welte 				      unsigned int format,
11990aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
12000aa62aefSHarald Welte {
12010aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12020aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
12030aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
12040aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12050aa62aefSHarald Welte 	int i;
12060aa62aefSHarald Welte 
12070aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12080aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12090aa62aefSHarald Welte 		if (chs == 2 &&
12100aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12110aa62aefSHarald Welte 						format) &&
12120aa62aefSHarald Welte 		    !(codec->spdif_status & IEC958_AES0_NONAUDIO)) {
12130aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12140aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12150aa62aefSHarald Welte 			 * be updated */
12160aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
12170aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12180aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12190aa62aefSHarald Welte 						    codec->spdif_ctls &
12200aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12210aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12220aa62aefSHarald Welte 						   stream_tag, 0, format);
12230aa62aefSHarald Welte 			/* turn on again (if needed) */
12240aa62aefSHarald Welte 			if (codec->spdif_ctls & AC_DIG1_ENABLE)
12250aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12260aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12270aa62aefSHarald Welte 						    codec->spdif_ctls & 0xff);
12280aa62aefSHarald Welte 		} else {
12290aa62aefSHarald Welte 			mout->dig_out_used = 0;
12300aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12310aa62aefSHarald Welte 						   0, 0, 0);
12320aa62aefSHarald Welte 		}
12330aa62aefSHarald Welte 	}
12340aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12350aa62aefSHarald Welte 
12360aa62aefSHarald Welte 	/* front */
12370aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12380aa62aefSHarald Welte 				   0, format);
12390aa62aefSHarald Welte 
1240eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1241eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12420aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12430aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12440aa62aefSHarald Welte 					   0, format);
12450aa62aefSHarald Welte 
12460aa62aefSHarald Welte 	/* extra outputs copied from front */
12470aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12480aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12490aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12500aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12510aa62aefSHarald Welte 						   stream_tag, 0, format);
12520aa62aefSHarald Welte 
12530aa62aefSHarald Welte 	/* surrounds */
12540aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12550aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12560aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12570aa62aefSHarald Welte 						   i * 2, format);
12580aa62aefSHarald Welte 		else /* copy front */
12590aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12600aa62aefSHarald Welte 						   0, format);
12610aa62aefSHarald Welte 	}
12620aa62aefSHarald Welte }
12630aa62aefSHarald Welte 
12640aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
12650aa62aefSHarald Welte 					  struct hda_codec *codec,
12660aa62aefSHarald Welte 					  unsigned int stream_tag,
12670aa62aefSHarald Welte 					  unsigned int format,
12680aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
12690aa62aefSHarald Welte {
12700aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12710aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
12720aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
12730aa62aefSHarald Welte 
12740aa62aefSHarald Welte 	if (substream->number == 0)
12750aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
12760aa62aefSHarald Welte 					  substream);
12770aa62aefSHarald Welte 	else {
12780aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
12790aa62aefSHarald Welte 		    spec->hp_independent_mode)
12800aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
12810aa62aefSHarald Welte 						   stream_tag, 0, format);
12820aa62aefSHarald Welte 	}
12831f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
12840aa62aefSHarald Welte 	return 0;
12850aa62aefSHarald Welte }
12860aa62aefSHarald Welte 
12870aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
12880aa62aefSHarald Welte 				    struct hda_codec *codec,
12890aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
12900aa62aefSHarald Welte {
12910aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12920aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
12930aa62aefSHarald Welte 	hda_nid_t *nids = mout->dac_nids;
12940aa62aefSHarald Welte 	int i;
12950aa62aefSHarald Welte 
12960aa62aefSHarald Welte 	if (substream->number == 0) {
12970aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
12980aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
12990aa62aefSHarald Welte 
13000aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
13010aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13020aa62aefSHarald Welte 						   0, 0, 0);
13030aa62aefSHarald Welte 
13040aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13050aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
13060aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
13070aa62aefSHarald Welte 							mout->extra_out_nid[i],
13080aa62aefSHarald Welte 							0, 0, 0);
13090aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
13100aa62aefSHarald Welte 		if (mout->dig_out_nid &&
13110aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13120aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13130aa62aefSHarald Welte 						   0, 0, 0);
13140aa62aefSHarald Welte 			mout->dig_out_used = 0;
13150aa62aefSHarald Welte 		}
13160aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
13170aa62aefSHarald Welte 	} else {
13180aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13190aa62aefSHarald Welte 		    spec->hp_independent_mode)
13200aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13210aa62aefSHarald Welte 						   0, 0, 0);
13220aa62aefSHarald Welte 	}
13231f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13240aa62aefSHarald Welte 	return 0;
13250aa62aefSHarald Welte }
13260aa62aefSHarald Welte 
1327c577b8a1SJoseph Chan /*
1328c577b8a1SJoseph Chan  * Digital out
1329c577b8a1SJoseph Chan  */
1330c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1331c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1332c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1333c577b8a1SJoseph Chan {
1334c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1335c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1336c577b8a1SJoseph Chan }
1337c577b8a1SJoseph Chan 
1338c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1339c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1340c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1341c577b8a1SJoseph Chan {
1342c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1343c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1344c577b8a1SJoseph Chan }
1345c577b8a1SJoseph Chan 
13465691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
134798aa34c0SHarald Welte 					struct hda_codec *codec,
134898aa34c0SHarald Welte 					unsigned int stream_tag,
134998aa34c0SHarald Welte 					unsigned int format,
135098aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
135198aa34c0SHarald Welte {
135298aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13539da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13549da29271STakashi Iwai 					     stream_tag, format, substream);
13559da29271STakashi Iwai }
13565691ec7fSHarald Welte 
13579da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
13589da29271STakashi Iwai 					struct hda_codec *codec,
13599da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13609da29271STakashi Iwai {
13619da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
13629da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
136398aa34c0SHarald Welte 	return 0;
136498aa34c0SHarald Welte }
136598aa34c0SHarald Welte 
1366c577b8a1SJoseph Chan /*
1367c577b8a1SJoseph Chan  * Analog capture
1368c577b8a1SJoseph Chan  */
1369c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1370c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1371c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1372c577b8a1SJoseph Chan 				   unsigned int format,
1373c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1374c577b8a1SJoseph Chan {
1375c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1376c577b8a1SJoseph Chan 
1377c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1378c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1379c577b8a1SJoseph Chan 	return 0;
1380c577b8a1SJoseph Chan }
1381c577b8a1SJoseph Chan 
1382c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1383c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1384c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1385c577b8a1SJoseph Chan {
1386c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1387888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1388c577b8a1SJoseph Chan 	return 0;
1389c577b8a1SJoseph Chan }
1390c577b8a1SJoseph Chan 
1391c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = {
13920aa62aefSHarald Welte 	.substreams = 2,
1393c577b8a1SJoseph Chan 	.channels_min = 2,
1394c577b8a1SJoseph Chan 	.channels_max = 8,
1395c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1396c577b8a1SJoseph Chan 	.ops = {
1397c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
13980aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
13990aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1400c577b8a1SJoseph Chan 	},
1401c577b8a1SJoseph Chan };
1402c577b8a1SJoseph Chan 
1403bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1404c873cc25SLydia Wang 	.substreams = 2,
1405bc9b5623STakashi Iwai 	.channels_min = 2,
1406bc9b5623STakashi Iwai 	.channels_max = 8,
1407bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1408bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1409bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1410bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1411bc9b5623STakashi Iwai 	 */
1412bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1413bc9b5623STakashi Iwai 	.ops = {
1414bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1415c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1416c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1417bc9b5623STakashi Iwai 	},
1418bc9b5623STakashi Iwai };
1419bc9b5623STakashi Iwai 
1420c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = {
1421c577b8a1SJoseph Chan 	.substreams = 2,
1422c577b8a1SJoseph Chan 	.channels_min = 2,
1423c577b8a1SJoseph Chan 	.channels_max = 2,
1424c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1425c577b8a1SJoseph Chan 	.ops = {
1426c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1427c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1428c577b8a1SJoseph Chan 	},
1429c577b8a1SJoseph Chan };
1430c577b8a1SJoseph Chan 
1431c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = {
1432c577b8a1SJoseph Chan 	.substreams = 1,
1433c577b8a1SJoseph Chan 	.channels_min = 2,
1434c577b8a1SJoseph Chan 	.channels_max = 2,
1435c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1436c577b8a1SJoseph Chan 	.ops = {
1437c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14386b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14399da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14409da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1441c577b8a1SJoseph Chan 	},
1442c577b8a1SJoseph Chan };
1443c577b8a1SJoseph Chan 
1444c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = {
1445c577b8a1SJoseph Chan 	.substreams = 1,
1446c577b8a1SJoseph Chan 	.channels_min = 2,
1447c577b8a1SJoseph Chan 	.channels_max = 2,
1448c577b8a1SJoseph Chan };
1449c577b8a1SJoseph Chan 
1450c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1451c577b8a1SJoseph Chan {
1452c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14535b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
14545b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
14555b0cb1d8SJaroslav Kysela 	int err, i;
1456c577b8a1SJoseph Chan 
1457c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1458c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1459c577b8a1SJoseph Chan 		if (err < 0)
1460c577b8a1SJoseph Chan 			return err;
1461c577b8a1SJoseph Chan 	}
1462c577b8a1SJoseph Chan 
1463c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1464c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
1465c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1466c577b8a1SJoseph Chan 		if (err < 0)
1467c577b8a1SJoseph Chan 			return err;
14689a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
14699a08160bSTakashi Iwai 						    &spec->multiout);
14709a08160bSTakashi Iwai 		if (err < 0)
14719a08160bSTakashi Iwai 			return err;
14729a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1473c577b8a1SJoseph Chan 	}
1474c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1475c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1476c577b8a1SJoseph Chan 		if (err < 0)
1477c577b8a1SJoseph Chan 			return err;
1478c577b8a1SJoseph Chan 	}
147917314379SLydia Wang 
14805b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
14815b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
14825b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
148321949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
14845b0cb1d8SJaroslav Kysela 		if (err < 0)
14855b0cb1d8SJaroslav Kysela 			return err;
14865b0cb1d8SJaroslav Kysela 	}
14875b0cb1d8SJaroslav Kysela 
14885b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
14895b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
14905b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
14915b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
14925b0cb1d8SJaroslav Kysela 				continue;
14935b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
14945b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
14955b0cb1d8SJaroslav Kysela 				continue;
14965b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
14975b0cb1d8SJaroslav Kysela 					      knew->subdevice);
14985b0cb1d8SJaroslav Kysela 		}
14995b0cb1d8SJaroslav Kysela 	}
15005b0cb1d8SJaroslav Kysela 
150117314379SLydia Wang 	/* init power states */
15023e95b9abSLydia Wang 	set_widgets_power_state(codec);
150317314379SLydia Wang 	analog_low_current_mode(codec, 1);
150417314379SLydia Wang 
1505603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1506c577b8a1SJoseph Chan 	return 0;
1507c577b8a1SJoseph Chan }
1508c577b8a1SJoseph Chan 
1509c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1510c577b8a1SJoseph Chan {
1511c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1512c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1513c577b8a1SJoseph Chan 
1514c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1515c577b8a1SJoseph Chan 	codec->pcm_info = info;
1516c577b8a1SJoseph Chan 
1517c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1518377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1519377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1520377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1521377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1522c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1523c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1524c577b8a1SJoseph Chan 
1525c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1526c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1527c577b8a1SJoseph Chan 
1528c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1529c577b8a1SJoseph Chan 		codec->num_pcms++;
1530c577b8a1SJoseph Chan 		info++;
1531c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
15327ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1533c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1534c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1535c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1536c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1537c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1538c577b8a1SJoseph Chan 		}
1539c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1540c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1541c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1542c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1543c577b8a1SJoseph Chan 				spec->dig_in_nid;
1544c577b8a1SJoseph Chan 		}
1545c577b8a1SJoseph Chan 	}
1546c577b8a1SJoseph Chan 
1547c577b8a1SJoseph Chan 	return 0;
1548c577b8a1SJoseph Chan }
1549c577b8a1SJoseph Chan 
1550c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1551c577b8a1SJoseph Chan {
1552c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1553c577b8a1SJoseph Chan 
1554c577b8a1SJoseph Chan 	if (!spec)
1555c577b8a1SJoseph Chan 		return;
1556c577b8a1SJoseph Chan 
1557603c4019STakashi Iwai 	via_free_kctls(codec);
15581f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1559c577b8a1SJoseph Chan 	kfree(codec->spec);
1560c577b8a1SJoseph Chan }
1561c577b8a1SJoseph Chan 
156269e52a80SHarald Welte /* mute internal speaker if HP is plugged */
156369e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
156469e52a80SHarald Welte {
1565dcf34c8cSLydia Wang 	unsigned int present = 0;
156669e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
156769e52a80SHarald Welte 
1568d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1569dcf34c8cSLydia Wang 
1570dcf34c8cSLydia Wang 	if (!spec->hp_independent_mode) {
1571dcf34c8cSLydia Wang 		struct snd_ctl_elem_id id;
1572dcf34c8cSLydia Wang 		/* auto mute */
1573dcf34c8cSLydia Wang 		snd_hda_codec_amp_stereo(
1574dcf34c8cSLydia Wang 			codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0,
1575dcf34c8cSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
1576dcf34c8cSLydia Wang 		/* notify change */
1577dcf34c8cSLydia Wang 		memset(&id, 0, sizeof(id));
1578dcf34c8cSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
1579dcf34c8cSLydia Wang 		strcpy(id.name, "Front Playback Switch");
1580dcf34c8cSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
1581dcf34c8cSLydia Wang 			       &id);
1582dcf34c8cSLydia Wang 	}
158369e52a80SHarald Welte }
158469e52a80SHarald Welte 
1585f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1586f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1587f3db423dSLydia Wang {
1588f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1589f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1590f3db423dSLydia Wang 
1591f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1592f3db423dSLydia Wang 		return;
1593f3db423dSLydia Wang 
1594d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1595d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1596f3db423dSLydia Wang 
1597f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1598f3db423dSLydia Wang 	if (lineout_present) {
1599f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1600f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE);
1601f3db423dSLydia Wang 		return;
1602f3db423dSLydia Wang 	}
1603f3db423dSLydia Wang 
1604d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1605f3db423dSLydia Wang 
1606f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
1607f3db423dSLydia Wang 		snd_hda_codec_amp_stereo(
1608f3db423dSLydia Wang 			codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1609f3db423dSLydia Wang 			hp_present ? HDA_AMP_MUTE : 0);
1610f3db423dSLydia Wang }
1611f3db423dSLydia Wang 
161269e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
161369e52a80SHarald Welte {
161469e52a80SHarald Welte 	unsigned int gpio_data;
161569e52a80SHarald Welte 	unsigned int vol_counter;
161669e52a80SHarald Welte 	unsigned int vol;
161769e52a80SHarald Welte 	unsigned int master_vol;
161869e52a80SHarald Welte 
161969e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
162069e52a80SHarald Welte 
162169e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
162269e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
162369e52a80SHarald Welte 
162469e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
162569e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
162669e52a80SHarald Welte 
162769e52a80SHarald Welte 	vol = vol_counter & 0x1F;
162869e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
162969e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
163069e52a80SHarald Welte 					AC_AMP_GET_INPUT);
163169e52a80SHarald Welte 
163269e52a80SHarald Welte 	if (gpio_data == 0x02) {
163369e52a80SHarald Welte 		/* unmute line out */
163469e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0],
163569e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
163669e52a80SHarald Welte 
163769e52a80SHarald Welte 		if (vol_counter & 0x20) {
163869e52a80SHarald Welte 			/* decrease volume */
163969e52a80SHarald Welte 			if (vol > master_vol)
164069e52a80SHarald Welte 				vol = master_vol;
164169e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
164269e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
164369e52a80SHarald Welte 						 master_vol-vol);
164469e52a80SHarald Welte 		} else {
164569e52a80SHarald Welte 			/* increase volume */
164669e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
164769e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
164869e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
164969e52a80SHarald Welte 					  (master_vol+vol));
165069e52a80SHarald Welte 		}
165169e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
165269e52a80SHarald Welte 		/* mute line out */
165369e52a80SHarald Welte 		snd_hda_codec_amp_stereo(codec,
165469e52a80SHarald Welte 					 spec->autocfg.line_out_pins[0],
165569e52a80SHarald Welte 					 HDA_OUTPUT, 0, HDA_AMP_MUTE,
165669e52a80SHarald Welte 					 HDA_AMP_MUTE);
165769e52a80SHarald Welte 	}
165869e52a80SHarald Welte }
165969e52a80SHarald Welte 
166025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
166125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
166225eaba2fSLydia Wang {
166325eaba2fSLydia Wang 	unsigned int hp_present;
166425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
166525eaba2fSLydia Wang 
1666ab6734e7SLydia Wang 	if (spec->codec_type != VT2002P && spec->codec_type != VT1812)
166725eaba2fSLydia Wang 		return;
166825eaba2fSLydia Wang 
1669d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
167025eaba2fSLydia Wang 
167125eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
167225eaba2fSLydia Wang 		struct snd_ctl_elem_id id;
167325eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
167425eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0,
167525eaba2fSLydia Wang 			HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
167625eaba2fSLydia Wang 		/* notify change */
167725eaba2fSLydia Wang 		memset(&id, 0, sizeof(id));
167825eaba2fSLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
167925eaba2fSLydia Wang 		strcpy(id.name, "Speaker Playback Switch");
168025eaba2fSLydia Wang 		snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE,
168125eaba2fSLydia Wang 			       &id);
168225eaba2fSLydia Wang 	}
168325eaba2fSLydia Wang }
168425eaba2fSLydia Wang 
168525eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
168625eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
168725eaba2fSLydia Wang {
168801a1796bSakpm@linux-foundation.org 	/* use long instead of int below just to avoid an internal compiler
168901a1796bSakpm@linux-foundation.org 	 * error with gcc 4.0.x
169001a1796bSakpm@linux-foundation.org 	 */
169101a1796bSakpm@linux-foundation.org 	unsigned long hp_present, present = 0;
169225eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
169325eaba2fSLydia Wang 	int i;
169425eaba2fSLydia Wang 
169525eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
169625eaba2fSLydia Wang 		return;
169725eaba2fSLydia Wang 
1698d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
169925eaba2fSLydia Wang 
1700d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]);
170125eaba2fSLydia Wang 
170225eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
170325eaba2fSLydia Wang 		/* Mute Line-Outs */
170425eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
170525eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
170625eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
170725eaba2fSLydia Wang 				HDA_OUTPUT, 0,
170825eaba2fSLydia Wang 				HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0);
170925eaba2fSLydia Wang 		if (hp_present)
171025eaba2fSLydia Wang 			present = hp_present;
171125eaba2fSLydia Wang 	}
171225eaba2fSLydia Wang 	/* Speakers */
171325eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.speaker_outs; i++)
171425eaba2fSLydia Wang 		snd_hda_codec_amp_stereo(
171525eaba2fSLydia Wang 			codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0,
171625eaba2fSLydia Wang 			HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
171725eaba2fSLydia Wang }
171825eaba2fSLydia Wang 
171925eaba2fSLydia Wang 
172069e52a80SHarald Welte /* unsolicited event for jack sensing */
172169e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
172269e52a80SHarald Welte 				  unsigned int res)
172369e52a80SHarald Welte {
172469e52a80SHarald Welte 	res >>= 26;
1725a34df19aSLydia Wang 	if (res & VIA_HP_EVENT)
172669e52a80SHarald Welte 		via_hp_automute(codec);
1727a34df19aSLydia Wang 	if (res & VIA_GPIO_EVENT)
172869e52a80SHarald Welte 		via_gpio_control(codec);
1729a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
17303e95b9abSLydia Wang 		set_widgets_power_state(codec);
1731f3db423dSLydia Wang 	if (res & VIA_MONO_EVENT)
1732f3db423dSLydia Wang 		via_mono_automute(codec);
173325eaba2fSLydia Wang 	if (res & VIA_SPEAKER_EVENT)
173425eaba2fSLydia Wang 		via_speaker_automute(codec);
173525eaba2fSLydia Wang 	if (res & VIA_BIND_HP_EVENT)
173625eaba2fSLydia Wang 		via_hp_bind_automute(codec);
173769e52a80SHarald Welte }
173869e52a80SHarald Welte 
1739c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1740c577b8a1SJoseph Chan {
1741c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
174269e52a80SHarald Welte 	int i;
174369e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
174469e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
174569e52a80SHarald Welte 
1746f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1747f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
174855d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
174955d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1750f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
175112b74c80STakashi Iwai 					    PIN_OUT);
175255d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1753f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1754f7278fd0SJosepch Chan 		}
175512b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
175612b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
175712b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1758f7278fd0SJosepch Chan 
17599da29271STakashi Iwai 	/* assign slave outs */
17609da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
17619da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
17625691ec7fSHarald Welte 
1763c577b8a1SJoseph Chan 	return 0;
1764c577b8a1SJoseph Chan }
1765c577b8a1SJoseph Chan 
17661f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
17671f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
17681f2e99feSLydia Wang {
17691f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
17701f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
17711f2e99feSLydia Wang 	return 0;
17721f2e99feSLydia Wang }
17731f2e99feSLydia Wang #endif
17741f2e99feSLydia Wang 
1775cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1776cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1777cb53c626STakashi Iwai {
1778cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1779cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1780cb53c626STakashi Iwai }
1781cb53c626STakashi Iwai #endif
1782cb53c626STakashi Iwai 
1783c577b8a1SJoseph Chan /*
1784c577b8a1SJoseph Chan  */
1785c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = {
1786c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1787c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1788c577b8a1SJoseph Chan 	.init = via_init,
1789c577b8a1SJoseph Chan 	.free = via_free,
17901f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
17911f2e99feSLydia Wang 	.suspend = via_suspend,
17921f2e99feSLydia Wang #endif
1793cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1794cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1795cb53c626STakashi Iwai #endif
1796c577b8a1SJoseph Chan };
1797c577b8a1SJoseph Chan 
1798c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */
1799c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec,
1800c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
1801c577b8a1SJoseph Chan {
1802c577b8a1SJoseph Chan 	int i;
1803c577b8a1SJoseph Chan 	hda_nid_t nid;
1804c577b8a1SJoseph Chan 
1805c577b8a1SJoseph Chan 	spec->multiout.num_dacs = cfg->line_outs;
1806c577b8a1SJoseph Chan 
1807c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
1808c577b8a1SJoseph Chan 
1809c577b8a1SJoseph Chan 	for (i = 0; i < 4; i++) {
1810c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1811c577b8a1SJoseph Chan 		if (nid) {
1812c577b8a1SJoseph Chan 			/* config dac list */
1813c577b8a1SJoseph Chan 			switch (i) {
1814c577b8a1SJoseph Chan 			case AUTO_SEQ_FRONT:
1815c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x10;
1816c577b8a1SJoseph Chan 				break;
1817c577b8a1SJoseph Chan 			case AUTO_SEQ_CENLFE:
1818c577b8a1SJoseph Chan 				spec->multiout.dac_nids[i] = 0x12;
1819c577b8a1SJoseph Chan 				break;
1820c577b8a1SJoseph Chan 			case AUTO_SEQ_SURROUND:
1821fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
1822c577b8a1SJoseph Chan 				break;
1823c577b8a1SJoseph Chan 			case AUTO_SEQ_SIDE:
1824fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x13;
1825c577b8a1SJoseph Chan 				break;
1826c577b8a1SJoseph Chan 			}
1827c577b8a1SJoseph Chan 		}
1828c577b8a1SJoseph Chan 	}
1829c577b8a1SJoseph Chan 
1830c577b8a1SJoseph Chan 	return 0;
1831c577b8a1SJoseph Chan }
1832c577b8a1SJoseph Chan 
1833c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
1834c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec,
1835c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
1836c577b8a1SJoseph Chan {
1837c577b8a1SJoseph Chan 	char name[32];
1838ea734963STakashi Iwai 	static const char * const chname[4] = {
1839ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
1840ea734963STakashi Iwai 	};
18419645c203SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b};
1842c577b8a1SJoseph Chan 	int i, err;
1843c577b8a1SJoseph Chan 
1844c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
1845c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1846c577b8a1SJoseph Chan 
1847c577b8a1SJoseph Chan 		if (!nid)
1848c577b8a1SJoseph Chan 			continue;
1849c577b8a1SJoseph Chan 
18509645c203SLydia Wang 		nid_vol = nid_vols[i];
1851c577b8a1SJoseph Chan 
1852c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
1853c577b8a1SJoseph Chan 			/* Center/LFE */
1854c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1855c577b8a1SJoseph Chan 					"Center Playback Volume",
1856f7278fd0SJosepch Chan 					HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1857f7278fd0SJosepch Chan 							    HDA_OUTPUT));
1858c577b8a1SJoseph Chan 			if (err < 0)
1859c577b8a1SJoseph Chan 				return err;
1860c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1861c577b8a1SJoseph Chan 					      "LFE Playback Volume",
1862f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1863f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1864c577b8a1SJoseph Chan 			if (err < 0)
1865c577b8a1SJoseph Chan 				return err;
1866c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1867c577b8a1SJoseph Chan 					      "Center Playback Switch",
1868f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
1869f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1870c577b8a1SJoseph Chan 			if (err < 0)
1871c577b8a1SJoseph Chan 				return err;
1872c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1873c577b8a1SJoseph Chan 					      "LFE Playback Switch",
1874f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
1875f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1876c577b8a1SJoseph Chan 			if (err < 0)
1877c577b8a1SJoseph Chan 				return err;
1878c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
1879c577b8a1SJoseph Chan 			/* add control to mixer index 0 */
1880c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1881c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
18829645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1883f7278fd0SJosepch Chan 								  HDA_INPUT));
1884c577b8a1SJoseph Chan 			if (err < 0)
1885c577b8a1SJoseph Chan 				return err;
1886c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1887c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
18889645c203SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1889f7278fd0SJosepch Chan 								  HDA_INPUT));
1890c577b8a1SJoseph Chan 			if (err < 0)
1891c577b8a1SJoseph Chan 				return err;
1892c577b8a1SJoseph Chan 
1893c577b8a1SJoseph Chan 			/* add control to PW3 */
1894c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1895c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1896f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1897f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1898c577b8a1SJoseph Chan 			if (err < 0)
1899c577b8a1SJoseph Chan 				return err;
1900c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1901c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1902f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
1903f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1904c577b8a1SJoseph Chan 			if (err < 0)
1905c577b8a1SJoseph Chan 				return err;
1906c577b8a1SJoseph Chan 		} else {
1907c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
1908c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1909f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1910f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1911c577b8a1SJoseph Chan 			if (err < 0)
1912c577b8a1SJoseph Chan 				return err;
1913c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
1914c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1915f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
1916f7278fd0SJosepch Chan 								  HDA_OUTPUT));
1917c577b8a1SJoseph Chan 			if (err < 0)
1918c577b8a1SJoseph Chan 				return err;
1919c577b8a1SJoseph Chan 		}
1920c577b8a1SJoseph Chan 	}
1921c577b8a1SJoseph Chan 
1922c577b8a1SJoseph Chan 	return 0;
1923c577b8a1SJoseph Chan }
1924c577b8a1SJoseph Chan 
19250aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
19260aa62aefSHarald Welte {
19270aa62aefSHarald Welte 	int i;
19280aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
1929ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
19300aa62aefSHarald Welte 
19310aa62aefSHarald Welte 	/* for hp mode select */
193210a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
193310a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
19340aa62aefSHarald Welte 
19350aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
19360aa62aefSHarald Welte }
19370aa62aefSHarald Welte 
1938c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
1939c577b8a1SJoseph Chan {
1940c577b8a1SJoseph Chan 	int err;
1941c577b8a1SJoseph Chan 
1942c577b8a1SJoseph Chan 	if (!pin)
1943c577b8a1SJoseph Chan 		return 0;
1944c577b8a1SJoseph Chan 
1945c577b8a1SJoseph Chan 	spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */
1946cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
1947c577b8a1SJoseph Chan 
1948c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1949c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
1950c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1951c577b8a1SJoseph Chan 	if (err < 0)
1952c577b8a1SJoseph Chan 		return err;
1953c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1954c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
1955c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
1956c577b8a1SJoseph Chan 	if (err < 0)
1957c577b8a1SJoseph Chan 		return err;
1958c577b8a1SJoseph Chan 
19590aa62aefSHarald Welte 	create_hp_imux(spec);
19600aa62aefSHarald Welte 
1961c577b8a1SJoseph Chan 	return 0;
1962c577b8a1SJoseph Chan }
1963c577b8a1SJoseph Chan 
1964c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
196510a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec,
1966f3268512STakashi Iwai 					    const struct auto_pin_cfg *cfg,
1967f3268512STakashi Iwai 					    hda_nid_t cap_nid,
1968f3268512STakashi Iwai 					    hda_nid_t pin_idxs[], int num_idxs)
1969c577b8a1SJoseph Chan {
197010a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
19710aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
19727b315bb4STakashi Iwai 	int i, err, idx, type, type_idx = 0;
1973c577b8a1SJoseph Chan 
1974c577b8a1SJoseph Chan 	/* for internal loopback recording select */
1975f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
1976f3268512STakashi Iwai 		if (pin_idxs[idx] == 0xff) {
197710a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
1978f3268512STakashi Iwai 			break;
1979f3268512STakashi Iwai 		}
1980f3268512STakashi Iwai 	}
1981c577b8a1SJoseph Chan 
19827b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
198310a20af7STakashi Iwai 		const char *label;
19847b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
1985f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
19867b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
1987c577b8a1SJoseph Chan 				break;
1988f3268512STakashi Iwai 		if (idx >= num_idxs)
1989f3268512STakashi Iwai 			continue;
19907b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
19917b315bb4STakashi Iwai 			type_idx++;
19927b315bb4STakashi Iwai 		else
19937b315bb4STakashi Iwai 			type_idx = 0;
199410a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
199516922281SLydia Wang 		if (spec->codec_type == VT1708S ||
199616922281SLydia Wang 		    spec->codec_type == VT1702 ||
199716922281SLydia Wang 		    spec->codec_type == VT1716S)
199816922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
199916922281SLydia Wang 						   idx+1, cap_nid);
200016922281SLydia Wang 		else
200116922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
200216922281SLydia Wang 						   idx, cap_nid);
2003c577b8a1SJoseph Chan 		if (err < 0)
2004c577b8a1SJoseph Chan 			return err;
200510a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2006c577b8a1SJoseph Chan 	}
2007c577b8a1SJoseph Chan 	return 0;
2008c577b8a1SJoseph Chan }
2009c577b8a1SJoseph Chan 
2010f3268512STakashi Iwai /* create playback/capture controls for input pins */
201110a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec,
2012f3268512STakashi Iwai 						const struct auto_pin_cfg *cfg)
2013f3268512STakashi Iwai {
2014f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 };
201510a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs,
2016f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
2017f3268512STakashi Iwai }
2018f3268512STakashi Iwai 
2019cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2020cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = {
2021cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2022cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2023cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2024cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2025cb53c626STakashi Iwai 	{ } /* end */
2026cb53c626STakashi Iwai };
2027cb53c626STakashi Iwai #endif
2028cb53c626STakashi Iwai 
202976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
203076d9b0ddSHarald Welte {
203176d9b0ddSHarald Welte 	unsigned int def_conf;
203276d9b0ddSHarald Welte 	unsigned char seqassoc;
203376d9b0ddSHarald Welte 
20342f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
203576d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
203676d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
203782ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
203882ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
203976d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
20402f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
204176d9b0ddSHarald Welte 	}
204276d9b0ddSHarald Welte 
204376d9b0ddSHarald Welte 	return;
204476d9b0ddSHarald Welte }
204576d9b0ddSHarald Welte 
20461f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol,
20471f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20481f2e99feSLydia Wang {
20491f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20501f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20511f2e99feSLydia Wang 
20521f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20531f2e99feSLydia Wang 		return 0;
20541f2e99feSLydia Wang 	spec->vt1708_jack_detectect =
20551f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
20561f2e99feSLydia Wang 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect;
20571f2e99feSLydia Wang 	return 0;
20581f2e99feSLydia Wang }
20591f2e99feSLydia Wang 
20601f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol,
20611f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20621f2e99feSLydia Wang {
20631f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20641f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20651f2e99feSLydia Wang 	int change;
20661f2e99feSLydia Wang 
20671f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20681f2e99feSLydia Wang 		return 0;
20691f2e99feSLydia Wang 	spec->vt1708_jack_detectect = ucontrol->value.integer.value[0];
20701f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
20711f2e99feSLydia Wang 		== !spec->vt1708_jack_detectect;
20721f2e99feSLydia Wang 	if (spec->vt1708_jack_detectect) {
20731f2e99feSLydia Wang 		mute_aa_path(codec, 1);
20741f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
20751f2e99feSLydia Wang 	}
20761f2e99feSLydia Wang 	return change;
20771f2e99feSLydia Wang }
20781f2e99feSLydia Wang 
20791f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = {
20801f2e99feSLydia Wang 	{
20811f2e99feSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
20821f2e99feSLydia Wang 		.name = "Jack Detect",
20831f2e99feSLydia Wang 		.count = 1,
20841f2e99feSLydia Wang 		.info = snd_ctl_boolean_mono_info,
20851f2e99feSLydia Wang 		.get = vt1708_jack_detectect_get,
20861f2e99feSLydia Wang 		.put = vt1708_jack_detectect_put,
20871f2e99feSLydia Wang 	},
20881f2e99feSLydia Wang 	{} /* end */
20891f2e99feSLydia Wang };
20901f2e99feSLydia Wang 
2091c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2092c577b8a1SJoseph Chan {
2093c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2094c577b8a1SJoseph Chan 	int err;
2095c577b8a1SJoseph Chan 
209676d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
209776d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
209876d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
209976d9b0ddSHarald Welte 
2100c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2101c577b8a1SJoseph Chan 	if (err < 0)
2102c577b8a1SJoseph Chan 		return err;
2103c577b8a1SJoseph Chan 	err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg);
2104c577b8a1SJoseph Chan 	if (err < 0)
2105c577b8a1SJoseph Chan 		return err;
2106c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2107c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2108c577b8a1SJoseph Chan 
2109c577b8a1SJoseph Chan 	err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg);
2110c577b8a1SJoseph Chan 	if (err < 0)
2111c577b8a1SJoseph Chan 		return err;
2112c577b8a1SJoseph Chan 	err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2113c577b8a1SJoseph Chan 	if (err < 0)
2114c577b8a1SJoseph Chan 		return err;
211510a20af7STakashi Iwai 	err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg);
2116c577b8a1SJoseph Chan 	if (err < 0)
2117c577b8a1SJoseph Chan 		return err;
21181f2e99feSLydia Wang 	/* add jack detect on/off control */
21191f2e99feSLydia Wang 	err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect);
21201f2e99feSLydia Wang 	if (err < 0)
21211f2e99feSLydia Wang 		return err;
2122c577b8a1SJoseph Chan 
2123c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2124c577b8a1SJoseph Chan 
21250852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2126c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
212755d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2128c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2129c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2130c577b8a1SJoseph Chan 
2131603c4019STakashi Iwai 	if (spec->kctls.list)
2132603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2133c577b8a1SJoseph Chan 
213469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2135c577b8a1SJoseph Chan 
21360aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
21370aa62aefSHarald Welte 
2138f8fdd495SHarald Welte 	if (spec->hp_mux)
21393d83e577STakashi Iwai 		via_hp_build(codec);
2140c577b8a1SJoseph Chan 
21415b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2142c577b8a1SJoseph Chan 	return 1;
2143c577b8a1SJoseph Chan }
2144c577b8a1SJoseph Chan 
2145c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2146c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2147c577b8a1SJoseph Chan {
214825eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
214925eaba2fSLydia Wang 
2150c577b8a1SJoseph Chan 	via_init(codec);
2151c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2152c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2153c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
2154ab6734e7SLydia Wang 	if (spec->codec_type == VT2002P || spec->codec_type == VT1812) {
215525eaba2fSLydia Wang 		via_hp_bind_automute(codec);
215625eaba2fSLydia Wang 	} else {
215725eaba2fSLydia Wang 		via_hp_automute(codec);
215825eaba2fSLydia Wang 		via_speaker_automute(codec);
215925eaba2fSLydia Wang 	}
216025eaba2fSLydia Wang 
2161c577b8a1SJoseph Chan 	return 0;
2162c577b8a1SJoseph Chan }
2163c577b8a1SJoseph Chan 
21641f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
21651f2e99feSLydia Wang {
21661f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
21671f2e99feSLydia Wang 					     vt1708_hp_work.work);
21681f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21691f2e99feSLydia Wang 		return;
21701f2e99feSLydia Wang 	/* if jack state toggled */
21711f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2172d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
21731f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
21741f2e99feSLydia Wang 		via_hp_automute(spec->codec);
21751f2e99feSLydia Wang 	}
21761f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
21771f2e99feSLydia Wang }
21781f2e99feSLydia Wang 
2179337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2180337b9d02STakashi Iwai {
2181337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2182337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2183337b9d02STakashi Iwai 	unsigned int type;
2184337b9d02STakashi Iwai 	int i, n;
2185337b9d02STakashi Iwai 
2186337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2187337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2188337b9d02STakashi Iwai 		while (nid) {
2189a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
21901c55d521STakashi Iwai 			if (type == AC_WID_PIN)
21911c55d521STakashi Iwai 				break;
2192337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2193337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2194337b9d02STakashi Iwai 			if (n <= 0)
2195337b9d02STakashi Iwai 				break;
2196337b9d02STakashi Iwai 			if (n > 1) {
2197337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2198337b9d02STakashi Iwai 				break;
2199337b9d02STakashi Iwai 			}
2200337b9d02STakashi Iwai 			nid = conn[0];
2201337b9d02STakashi Iwai 		}
2202337b9d02STakashi Iwai 	}
22031c55d521STakashi Iwai 	return 0;
2204337b9d02STakashi Iwai }
2205337b9d02STakashi Iwai 
2206c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2207c577b8a1SJoseph Chan {
2208c577b8a1SJoseph Chan 	struct via_spec *spec;
2209c577b8a1SJoseph Chan 	int err;
2210c577b8a1SJoseph Chan 
2211c577b8a1SJoseph Chan 	/* create a codec specific record */
22125b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2213c577b8a1SJoseph Chan 	if (spec == NULL)
2214c577b8a1SJoseph Chan 		return -ENOMEM;
2215c577b8a1SJoseph Chan 
2216c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2217c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2218c577b8a1SJoseph Chan 	if (err < 0) {
2219c577b8a1SJoseph Chan 		via_free(codec);
2220c577b8a1SJoseph Chan 		return err;
2221c577b8a1SJoseph Chan 	} else if (!err) {
2222c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2223c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2224c577b8a1SJoseph Chan 	}
2225c577b8a1SJoseph Chan 
2226c577b8a1SJoseph Chan 
2227c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1708 Analog";
2228c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2229bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2230bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2231bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2232c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2233c577b8a1SJoseph Chan 
2234c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1708 Digital";
2235c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2236c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2237c577b8a1SJoseph Chan 
2238c577b8a1SJoseph Chan 
2239c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2240c577b8a1SJoseph Chan 		spec->adc_nids = vt1708_adc_nids;
2241c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids);
22420f67a611STakashi Iwai 		get_mux_nids(codec);
2243c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2244c577b8a1SJoseph Chan 		spec->num_mixers++;
2245c577b8a1SJoseph Chan 	}
2246c577b8a1SJoseph Chan 
2247c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2248c577b8a1SJoseph Chan 
2249c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2250cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2251cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2252cb53c626STakashi Iwai #endif
22531f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2254c577b8a1SJoseph Chan 	return 0;
2255c577b8a1SJoseph Chan }
2256c577b8a1SJoseph Chan 
2257c577b8a1SJoseph Chan /* capture mixer elements */
2258c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = {
2259c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2260c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2261c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2262c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2263c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2264c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2265c577b8a1SJoseph Chan 	{
2266c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2267c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2268c577b8a1SJoseph Chan 		 * So call somewhat different..
2269c577b8a1SJoseph Chan 		 */
2270c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2271c577b8a1SJoseph Chan 		.name = "Input Source",
2272c577b8a1SJoseph Chan 		.count = 1,
2273c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2274c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2275c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2276c577b8a1SJoseph Chan 	},
2277c577b8a1SJoseph Chan 	{ } /* end */
2278c577b8a1SJoseph Chan };
2279c577b8a1SJoseph Chan 
228069e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = {
2281a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2282a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
228369e52a80SHarald Welte 	{ }
228469e52a80SHarald Welte };
228569e52a80SHarald Welte 
2286c577b8a1SJoseph Chan /*
2287c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2288c577b8a1SJoseph Chan  */
2289c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2290c577b8a1SJoseph Chan 	/*
2291c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2292c577b8a1SJoseph Chan 	 */
2293c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2294c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2295c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2296c577b8a1SJoseph Chan 
2297c577b8a1SJoseph Chan 
2298f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2299c577b8a1SJoseph Chan 	 * mixer widget
2300c577b8a1SJoseph Chan 	 */
2301c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2302f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2303f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2304f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2305f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2306f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2307c577b8a1SJoseph Chan 
2308c577b8a1SJoseph Chan 	/*
2309c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2310c577b8a1SJoseph Chan 	 */
2311c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2312c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2313c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2314c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2315c577b8a1SJoseph Chan 
2316c577b8a1SJoseph Chan 	/*
2317c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2318c577b8a1SJoseph Chan 	 */
2319c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2320c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2321c577b8a1SJoseph Chan 
2322bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2323bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2324c577b8a1SJoseph Chan 	/* PW9 Output enable */
2325c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2326c577b8a1SJoseph Chan 	{ }
2327c577b8a1SJoseph Chan };
2328c577b8a1SJoseph Chan 
2329c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2330c577b8a1SJoseph Chan 	.substreams = 1,
2331c577b8a1SJoseph Chan 	.channels_min = 2,
2332c577b8a1SJoseph Chan 	.channels_max = 10,
2333c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2334c577b8a1SJoseph Chan 	.ops = {
2335c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2336c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2337c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2338c577b8a1SJoseph Chan 	},
2339c577b8a1SJoseph Chan };
2340c577b8a1SJoseph Chan 
2341c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2342c577b8a1SJoseph Chan 	.substreams = 1,
2343c577b8a1SJoseph Chan 	.channels_min = 2,
2344c577b8a1SJoseph Chan 	.channels_max = 6,
2345c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2346c577b8a1SJoseph Chan 	.ops = {
2347c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2348c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2349c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2350c577b8a1SJoseph Chan 	},
2351c577b8a1SJoseph Chan };
2352c577b8a1SJoseph Chan 
2353c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = {
2354c577b8a1SJoseph Chan 	.substreams = 2,
2355c577b8a1SJoseph Chan 	.channels_min = 2,
2356c577b8a1SJoseph Chan 	.channels_max = 2,
2357c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2358c577b8a1SJoseph Chan 	.ops = {
2359c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2360c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2361c577b8a1SJoseph Chan 	},
2362c577b8a1SJoseph Chan };
2363c577b8a1SJoseph Chan 
2364c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = {
2365c577b8a1SJoseph Chan 	.substreams = 1,
2366c577b8a1SJoseph Chan 	.channels_min = 2,
2367c577b8a1SJoseph Chan 	.channels_max = 2,
2368c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2369c577b8a1SJoseph Chan 	.ops = {
2370c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2371c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2372c577b8a1SJoseph Chan 	},
2373c577b8a1SJoseph Chan };
2374c577b8a1SJoseph Chan 
2375c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = {
2376c577b8a1SJoseph Chan 	.substreams = 1,
2377c577b8a1SJoseph Chan 	.channels_min = 2,
2378c577b8a1SJoseph Chan 	.channels_max = 2,
2379c577b8a1SJoseph Chan };
2380c577b8a1SJoseph Chan 
2381c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec,
2382c577b8a1SJoseph Chan 				     const struct auto_pin_cfg *cfg)
2383c577b8a1SJoseph Chan {
2384c577b8a1SJoseph Chan 	int i;
2385c577b8a1SJoseph Chan 	hda_nid_t nid;
2386c577b8a1SJoseph Chan 
2387c577b8a1SJoseph Chan 	if (cfg->line_outs == 4)  /* 10 channels */
2388c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */
2389c577b8a1SJoseph Chan 	else if (cfg->line_outs == 3) /* 6 channels */
2390c577b8a1SJoseph Chan 		spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */
2391c577b8a1SJoseph Chan 
2392c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2393c577b8a1SJoseph Chan 
2394c577b8a1SJoseph Chan 	if (cfg->line_outs == 4) { /* 10 channels */
2395c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2396c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2397c577b8a1SJoseph Chan 			if (nid) {
2398c577b8a1SJoseph Chan 				/* config dac list */
2399c577b8a1SJoseph Chan 				switch (i) {
2400c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2401c577b8a1SJoseph Chan 					/* AOW0 */
2402c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2403c577b8a1SJoseph Chan 					break;
2404c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2405c577b8a1SJoseph Chan 					/* AOW2 */
2406c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2407c577b8a1SJoseph Chan 					break;
2408c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2409c577b8a1SJoseph Chan 					/* AOW3 */
2410fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x11;
2411c577b8a1SJoseph Chan 					break;
2412c577b8a1SJoseph Chan 				case AUTO_SEQ_SIDE:
2413c577b8a1SJoseph Chan 					/* AOW1 */
2414fb4cb772SHarald Welte 					spec->multiout.dac_nids[i] = 0x27;
2415c577b8a1SJoseph Chan 					break;
2416c577b8a1SJoseph Chan 				default:
2417c577b8a1SJoseph Chan 					break;
2418c577b8a1SJoseph Chan 				}
2419c577b8a1SJoseph Chan 			}
2420c577b8a1SJoseph Chan 		}
2421c577b8a1SJoseph Chan 		spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */
2422c577b8a1SJoseph Chan 
2423c577b8a1SJoseph Chan 	} else if (cfg->line_outs == 3) { /* 6 channels */
2424c577b8a1SJoseph Chan 		for (i = 0; i < cfg->line_outs; i++) {
2425c577b8a1SJoseph Chan 			nid = cfg->line_out_pins[i];
2426c577b8a1SJoseph Chan 			if (nid) {
2427c577b8a1SJoseph Chan 				/* config dac list */
2428c577b8a1SJoseph Chan 				switch (i) {
2429c577b8a1SJoseph Chan 				case AUTO_SEQ_FRONT:
2430c577b8a1SJoseph Chan 					/* AOW0 */
2431c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x10;
2432c577b8a1SJoseph Chan 					break;
2433c577b8a1SJoseph Chan 				case AUTO_SEQ_CENLFE:
2434c577b8a1SJoseph Chan 					/* AOW2 */
2435c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x12;
2436c577b8a1SJoseph Chan 					break;
2437c577b8a1SJoseph Chan 				case AUTO_SEQ_SURROUND:
2438c577b8a1SJoseph Chan 					/* AOW1 */
2439c577b8a1SJoseph Chan 					spec->multiout.dac_nids[i] = 0x11;
2440c577b8a1SJoseph Chan 					break;
2441c577b8a1SJoseph Chan 				default:
2442c577b8a1SJoseph Chan 					break;
2443c577b8a1SJoseph Chan 				}
2444c577b8a1SJoseph Chan 			}
2445c577b8a1SJoseph Chan 		}
2446c577b8a1SJoseph Chan 	}
2447c577b8a1SJoseph Chan 
2448c577b8a1SJoseph Chan 	return 0;
2449c577b8a1SJoseph Chan }
2450c577b8a1SJoseph Chan 
2451c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */
2452c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec,
2453c577b8a1SJoseph Chan 					     const struct auto_pin_cfg *cfg)
2454c577b8a1SJoseph Chan {
2455c577b8a1SJoseph Chan 	char name[32];
2456ea734963STakashi Iwai 	static const char * const chname[4] = {
2457ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
2458ea734963STakashi Iwai 	};
24594483a2f5SLydia Wang 	hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29};
2460c577b8a1SJoseph Chan 	int i, err;
2461c577b8a1SJoseph Chan 
2462c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
2463c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
2464c577b8a1SJoseph Chan 
2465c577b8a1SJoseph Chan 		if (!nid)
2466c577b8a1SJoseph Chan 			continue;
2467c577b8a1SJoseph Chan 
24684483a2f5SLydia Wang 		nid_vol = nid_vols[i];
24694483a2f5SLydia Wang 
2470c577b8a1SJoseph Chan 		if (i == AUTO_SEQ_CENLFE) {
2471c577b8a1SJoseph Chan 			/* Center/LFE */
2472c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2473c577b8a1SJoseph Chan 					      "Center Playback Volume",
24744483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2475f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2476c577b8a1SJoseph Chan 			if (err < 0)
2477c577b8a1SJoseph Chan 				return err;
2478c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2479c577b8a1SJoseph Chan 					      "LFE Playback Volume",
24804483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2481f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2482c577b8a1SJoseph Chan 			if (err < 0)
2483c577b8a1SJoseph Chan 				return err;
2484c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2485c577b8a1SJoseph Chan 					      "Center Playback Switch",
24864483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
2487f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2488c577b8a1SJoseph Chan 			if (err < 0)
2489c577b8a1SJoseph Chan 				return err;
2490c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2491c577b8a1SJoseph Chan 					      "LFE Playback Switch",
24924483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
2493f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2494c577b8a1SJoseph Chan 			if (err < 0)
2495c577b8a1SJoseph Chan 				return err;
2496c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_FRONT) {
24974483a2f5SLydia Wang 			/* ADD control to mixer index 0 */
2498c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2499c577b8a1SJoseph Chan 					      "Master Front Playback Volume",
25004483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2501f7278fd0SJosepch Chan 								  HDA_INPUT));
2502c577b8a1SJoseph Chan 			if (err < 0)
2503c577b8a1SJoseph Chan 				return err;
2504c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2505c577b8a1SJoseph Chan 					      "Master Front Playback Switch",
25064483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2507f7278fd0SJosepch Chan 								  HDA_INPUT));
2508c577b8a1SJoseph Chan 			if (err < 0)
2509c577b8a1SJoseph Chan 				return err;
2510c577b8a1SJoseph Chan 
2511c577b8a1SJoseph Chan 			/* add control to PW3 */
2512c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2513c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2514f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2515f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2516c577b8a1SJoseph Chan 			if (err < 0)
2517c577b8a1SJoseph Chan 				return err;
2518c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2519c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
2520f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
2521f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2522c577b8a1SJoseph Chan 			if (err < 0)
2523c577b8a1SJoseph Chan 				return err;
2524c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SURROUND) {
2525c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2526c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
25274483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2528f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2529c577b8a1SJoseph Chan 			if (err < 0)
2530c577b8a1SJoseph Chan 				return err;
2531c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2532c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
25334483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2534f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2535c577b8a1SJoseph Chan 			if (err < 0)
2536c577b8a1SJoseph Chan 				return err;
2537c577b8a1SJoseph Chan 		} else if (i == AUTO_SEQ_SIDE) {
2538c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Volume", chname[i]);
2539c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
25404483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2541f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2542c577b8a1SJoseph Chan 			if (err < 0)
2543c577b8a1SJoseph Chan 				return err;
2544c577b8a1SJoseph Chan 			sprintf(name, "%s Playback Switch", chname[i]);
2545c577b8a1SJoseph Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
25464483a2f5SLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
2547f7278fd0SJosepch Chan 								  HDA_OUTPUT));
2548c577b8a1SJoseph Chan 			if (err < 0)
2549c577b8a1SJoseph Chan 				return err;
2550c577b8a1SJoseph Chan 		}
2551c577b8a1SJoseph Chan 	}
2552c577b8a1SJoseph Chan 
2553c577b8a1SJoseph Chan 	return 0;
2554c577b8a1SJoseph Chan }
2555c577b8a1SJoseph Chan 
2556c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
2557c577b8a1SJoseph Chan {
2558c577b8a1SJoseph Chan 	int err;
2559c577b8a1SJoseph Chan 
2560c577b8a1SJoseph Chan 	if (!pin)
2561c577b8a1SJoseph Chan 		return 0;
2562c577b8a1SJoseph Chan 
2563c577b8a1SJoseph Chan 	if (spec->multiout.num_dacs == 5) /* 10 channels */
2564c577b8a1SJoseph Chan 		spec->multiout.hp_nid = VT1709_HP_DAC_NID;
2565c577b8a1SJoseph Chan 	else if (spec->multiout.num_dacs == 3) /* 6 channels */
2566c577b8a1SJoseph Chan 		spec->multiout.hp_nid = 0;
2567cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
2568c577b8a1SJoseph Chan 
2569c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
2570c577b8a1SJoseph Chan 			      "Headphone Playback Volume",
2571c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2572c577b8a1SJoseph Chan 	if (err < 0)
2573c577b8a1SJoseph Chan 		return err;
2574c577b8a1SJoseph Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2575c577b8a1SJoseph Chan 			      "Headphone Playback Switch",
2576c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
2577c577b8a1SJoseph Chan 	if (err < 0)
2578c577b8a1SJoseph Chan 		return err;
2579c577b8a1SJoseph Chan 
2580c577b8a1SJoseph Chan 	return 0;
2581c577b8a1SJoseph Chan }
2582c577b8a1SJoseph Chan 
2583c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
258410a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec,
2585c577b8a1SJoseph Chan 						const struct auto_pin_cfg *cfg)
2586c577b8a1SJoseph Chan {
2587f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 };
258810a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs,
2589f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
2590c577b8a1SJoseph Chan }
2591c577b8a1SJoseph Chan 
2592c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2593c577b8a1SJoseph Chan {
2594c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2595c577b8a1SJoseph Chan 	int err;
2596c577b8a1SJoseph Chan 
2597c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2598c577b8a1SJoseph Chan 	if (err < 0)
2599c577b8a1SJoseph Chan 		return err;
2600c577b8a1SJoseph Chan 	err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg);
2601c577b8a1SJoseph Chan 	if (err < 0)
2602c577b8a1SJoseph Chan 		return err;
2603c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2604c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2605c577b8a1SJoseph Chan 
2606c577b8a1SJoseph Chan 	err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg);
2607c577b8a1SJoseph Chan 	if (err < 0)
2608c577b8a1SJoseph Chan 		return err;
2609c577b8a1SJoseph Chan 	err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
2610c577b8a1SJoseph Chan 	if (err < 0)
2611c577b8a1SJoseph Chan 		return err;
261210a20af7STakashi Iwai 	err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg);
2613c577b8a1SJoseph Chan 	if (err < 0)
2614c577b8a1SJoseph Chan 		return err;
2615c577b8a1SJoseph Chan 
2616c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2617c577b8a1SJoseph Chan 
26180852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2619c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
262055d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2621c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2622c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2623c577b8a1SJoseph Chan 
2624603c4019STakashi Iwai 	if (spec->kctls.list)
2625603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2626c577b8a1SJoseph Chan 
26270aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2628c577b8a1SJoseph Chan 
2629f8fdd495SHarald Welte 	if (spec->hp_mux)
26303d83e577STakashi Iwai 		via_hp_build(codec);
2631f8fdd495SHarald Welte 
26325b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2633c577b8a1SJoseph Chan 	return 1;
2634c577b8a1SJoseph Chan }
2635c577b8a1SJoseph Chan 
2636cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2637cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = {
2638cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2639cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2640cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2641cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2642cb53c626STakashi Iwai 	{ } /* end */
2643cb53c626STakashi Iwai };
2644cb53c626STakashi Iwai #endif
2645cb53c626STakashi Iwai 
2646c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2647c577b8a1SJoseph Chan {
2648c577b8a1SJoseph Chan 	struct via_spec *spec;
2649c577b8a1SJoseph Chan 	int err;
2650c577b8a1SJoseph Chan 
2651c577b8a1SJoseph Chan 	/* create a codec specific record */
26525b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2653c577b8a1SJoseph Chan 	if (spec == NULL)
2654c577b8a1SJoseph Chan 		return -ENOMEM;
2655c577b8a1SJoseph Chan 
2656c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2657c577b8a1SJoseph Chan 	if (err < 0) {
2658c577b8a1SJoseph Chan 		via_free(codec);
2659c577b8a1SJoseph Chan 		return err;
2660c577b8a1SJoseph Chan 	} else if (!err) {
2661c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2662c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2663c577b8a1SJoseph Chan 	}
2664c577b8a1SJoseph Chan 
266569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
266669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2667c577b8a1SJoseph Chan 
2668c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
2669c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2670c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2671c577b8a1SJoseph Chan 
2672c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
2673c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2674c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2675c577b8a1SJoseph Chan 
2676c577b8a1SJoseph Chan 
2677c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2678c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
2679c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
2680337b9d02STakashi Iwai 		get_mux_nids(codec);
2681c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2682c577b8a1SJoseph Chan 		spec->num_mixers++;
2683c577b8a1SJoseph Chan 	}
2684c577b8a1SJoseph Chan 
2685c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2686c577b8a1SJoseph Chan 
2687c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
268869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2689cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2690cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2691cb53c626STakashi Iwai #endif
2692c577b8a1SJoseph Chan 
2693c577b8a1SJoseph Chan 	return 0;
2694c577b8a1SJoseph Chan }
2695c577b8a1SJoseph Chan /*
2696c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2697c577b8a1SJoseph Chan  */
2698c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2699c577b8a1SJoseph Chan 	/*
2700c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2701c577b8a1SJoseph Chan 	 */
2702c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2703c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2704c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2705c577b8a1SJoseph Chan 
2706c577b8a1SJoseph Chan 
2707c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2708c577b8a1SJoseph Chan 	 * mixer widget
2709c577b8a1SJoseph Chan 	 */
2710c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2711c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2712c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2713c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2714c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2715c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2716c577b8a1SJoseph Chan 
2717c577b8a1SJoseph Chan 	/*
2718c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2719c577b8a1SJoseph Chan 	 */
2720c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2721c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2722c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2723c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2724c577b8a1SJoseph Chan 
2725c577b8a1SJoseph Chan 	/*
2726c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2727c577b8a1SJoseph Chan 	 */
2728c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2729c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2730c577b8a1SJoseph Chan 
2731c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2732c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2733c577b8a1SJoseph Chan 	/* PW9 Output enable */
2734c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2735c577b8a1SJoseph Chan 	{ }
2736c577b8a1SJoseph Chan };
2737c577b8a1SJoseph Chan 
2738c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2739c577b8a1SJoseph Chan {
2740c577b8a1SJoseph Chan 	struct via_spec *spec;
2741c577b8a1SJoseph Chan 	int err;
2742c577b8a1SJoseph Chan 
2743c577b8a1SJoseph Chan 	/* create a codec specific record */
27445b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2745c577b8a1SJoseph Chan 	if (spec == NULL)
2746c577b8a1SJoseph Chan 		return -ENOMEM;
2747c577b8a1SJoseph Chan 
2748c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2749c577b8a1SJoseph Chan 	if (err < 0) {
2750c577b8a1SJoseph Chan 		via_free(codec);
2751c577b8a1SJoseph Chan 		return err;
2752c577b8a1SJoseph Chan 	} else if (!err) {
2753c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2754c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2755c577b8a1SJoseph Chan 	}
2756c577b8a1SJoseph Chan 
275769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
275869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2759c577b8a1SJoseph Chan 
2760c577b8a1SJoseph Chan 	spec->stream_name_analog = "VT1709 Analog";
2761c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2762c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2763c577b8a1SJoseph Chan 
2764c577b8a1SJoseph Chan 	spec->stream_name_digital = "VT1709 Digital";
2765c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2766c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2767c577b8a1SJoseph Chan 
2768c577b8a1SJoseph Chan 
2769c577b8a1SJoseph Chan 	if (!spec->adc_nids && spec->input_mux) {
2770c577b8a1SJoseph Chan 		spec->adc_nids = vt1709_adc_nids;
2771c577b8a1SJoseph Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids);
2772337b9d02STakashi Iwai 		get_mux_nids(codec);
2773c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2774c577b8a1SJoseph Chan 		spec->num_mixers++;
2775c577b8a1SJoseph Chan 	}
2776c577b8a1SJoseph Chan 
2777c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2778c577b8a1SJoseph Chan 
2779c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
278069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2781cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2782cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2783cb53c626STakashi Iwai #endif
2784f7278fd0SJosepch Chan 	return 0;
2785f7278fd0SJosepch Chan }
2786f7278fd0SJosepch Chan 
2787f7278fd0SJosepch Chan /* capture mixer elements */
2788f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2789f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2790f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2791f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2792f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2793f7278fd0SJosepch Chan 	{
2794f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2795f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2796f7278fd0SJosepch Chan 		 * So call somewhat different..
2797f7278fd0SJosepch Chan 		 */
2798f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
2799f7278fd0SJosepch Chan 		.name = "Input Source",
2800f7278fd0SJosepch Chan 		.count = 1,
2801f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
2802f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
2803f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
2804f7278fd0SJosepch Chan 	},
2805f7278fd0SJosepch Chan 	{ } /* end */
2806f7278fd0SJosepch Chan };
2807f7278fd0SJosepch Chan /*
2808f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2809f7278fd0SJosepch Chan  */
2810f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2811f7278fd0SJosepch Chan 	/*
2812f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2813f7278fd0SJosepch Chan 	 */
2814f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2815f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2816f7278fd0SJosepch Chan 
2817f7278fd0SJosepch Chan 
2818f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2819f7278fd0SJosepch Chan 	 * mixer widget
2820f7278fd0SJosepch Chan 	 */
2821f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2822f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2823f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2824f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2825f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2826f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2827f7278fd0SJosepch Chan 
2828f7278fd0SJosepch Chan 	/*
2829f7278fd0SJosepch Chan 	 * Set up output mixers
2830f7278fd0SJosepch Chan 	 */
2831f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2832f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2833f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2834f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2835f7278fd0SJosepch Chan 
2836f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2837bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2838f7278fd0SJosepch Chan 	/* PW9 Output enable */
2839f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2840f7278fd0SJosepch Chan 	/* PW10 Input enable */
2841f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2842f7278fd0SJosepch Chan 	{ }
2843f7278fd0SJosepch Chan };
2844f7278fd0SJosepch Chan 
2845f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2846f7278fd0SJosepch Chan 	/*
2847f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2848f7278fd0SJosepch Chan 	 */
2849f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2850f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2851f7278fd0SJosepch Chan 
2852f7278fd0SJosepch Chan 
2853f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2854f7278fd0SJosepch Chan 	 * mixer widget
2855f7278fd0SJosepch Chan 	 */
2856f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2857f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2858f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2859f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2860f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2861f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2862f7278fd0SJosepch Chan 
2863f7278fd0SJosepch Chan 	/*
2864f7278fd0SJosepch Chan 	 * Set up output mixers
2865f7278fd0SJosepch Chan 	 */
2866f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2867f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2868f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2869f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2870f7278fd0SJosepch Chan 
2871f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2872f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2873f7278fd0SJosepch Chan 	/* PW9 Output enable */
2874f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2875f7278fd0SJosepch Chan 	/* PW10 Input enable */
2876f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2877f7278fd0SJosepch Chan 	{ }
2878f7278fd0SJosepch Chan };
2879f7278fd0SJosepch Chan 
288069e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = {
2881a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2882a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2883a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2884a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2885a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2886a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2887a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2888a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2889a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
289069e52a80SHarald Welte 	{ }
289169e52a80SHarald Welte };
289269e52a80SHarald Welte 
289317314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
289417314379SLydia Wang 			      struct hda_codec *codec,
289517314379SLydia Wang 			      struct snd_pcm_substream *substream)
289617314379SLydia Wang {
289717314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
289817314379SLydia Wang 		&& substream->ref_count == 0;
289917314379SLydia Wang 
290017314379SLydia Wang 	analog_low_current_mode(codec, idle);
290117314379SLydia Wang 	return 0;
290217314379SLydia Wang }
290317314379SLydia Wang 
2904f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
29050aa62aefSHarald Welte 	.substreams = 2,
2906f7278fd0SJosepch Chan 	.channels_min = 2,
2907f7278fd0SJosepch Chan 	.channels_max = 8,
2908f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2909f7278fd0SJosepch Chan 	.ops = {
2910f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29110aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
291217314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
291317314379SLydia Wang 		.close = via_pcm_open_close
2914f7278fd0SJosepch Chan 	},
2915f7278fd0SJosepch Chan };
2916f7278fd0SJosepch Chan 
2917f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
29180aa62aefSHarald Welte 	.substreams = 2,
2919f7278fd0SJosepch Chan 	.channels_min = 2,
2920f7278fd0SJosepch Chan 	.channels_max = 4,
2921f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2922f7278fd0SJosepch Chan 	.ops = {
2923f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
29240aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
29250aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
2926f7278fd0SJosepch Chan 	},
2927f7278fd0SJosepch Chan };
2928f7278fd0SJosepch Chan 
2929f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2930f7278fd0SJosepch Chan 	.substreams = 2,
2931f7278fd0SJosepch Chan 	.channels_min = 2,
2932f7278fd0SJosepch Chan 	.channels_max = 2,
2933f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
2934f7278fd0SJosepch Chan 	.ops = {
293517314379SLydia Wang 		.open = via_pcm_open_close,
2936f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
293717314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
293817314379SLydia Wang 		.close = via_pcm_open_close
2939f7278fd0SJosepch Chan 	},
2940f7278fd0SJosepch Chan };
2941f7278fd0SJosepch Chan 
2942f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2943f7278fd0SJosepch Chan 	.substreams = 1,
2944f7278fd0SJosepch Chan 	.channels_min = 2,
2945f7278fd0SJosepch Chan 	.channels_max = 2,
2946f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
2947f7278fd0SJosepch Chan 	.ops = {
2948f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
2949f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
29509da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
29519da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
2952f7278fd0SJosepch Chan 	},
2953f7278fd0SJosepch Chan };
2954f7278fd0SJosepch Chan 
2955f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2956f7278fd0SJosepch Chan 	.substreams = 1,
2957f7278fd0SJosepch Chan 	.channels_min = 2,
2958f7278fd0SJosepch Chan 	.channels_max = 2,
2959f7278fd0SJosepch Chan };
2960f7278fd0SJosepch Chan 
2961f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */
2962f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec,
2963f7278fd0SJosepch Chan 				     const struct auto_pin_cfg *cfg)
2964f7278fd0SJosepch Chan {
2965f7278fd0SJosepch Chan 	int i;
2966f7278fd0SJosepch Chan 	hda_nid_t nid;
2967f7278fd0SJosepch Chan 
2968f7278fd0SJosepch Chan 	spec->multiout.num_dacs = cfg->line_outs;
2969f7278fd0SJosepch Chan 
2970f7278fd0SJosepch Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
2971f7278fd0SJosepch Chan 
2972f7278fd0SJosepch Chan 	for (i = 0; i < 4; i++) {
2973f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
2974f7278fd0SJosepch Chan 		if (nid) {
2975f7278fd0SJosepch Chan 			/* config dac list */
2976f7278fd0SJosepch Chan 			switch (i) {
2977f7278fd0SJosepch Chan 			case AUTO_SEQ_FRONT:
2978f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x10;
2979f7278fd0SJosepch Chan 				break;
2980f7278fd0SJosepch Chan 			case AUTO_SEQ_CENLFE:
2981f7278fd0SJosepch Chan 				spec->multiout.dac_nids[i] = 0x24;
2982f7278fd0SJosepch Chan 				break;
2983f7278fd0SJosepch Chan 			case AUTO_SEQ_SURROUND:
2984fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
2985f7278fd0SJosepch Chan 				break;
2986f7278fd0SJosepch Chan 			case AUTO_SEQ_SIDE:
2987fb4cb772SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
2988f7278fd0SJosepch Chan 				break;
2989f7278fd0SJosepch Chan 			}
2990f7278fd0SJosepch Chan 		}
2991f7278fd0SJosepch Chan 	}
2992f7278fd0SJosepch Chan 
2993f7278fd0SJosepch Chan 	return 0;
2994f7278fd0SJosepch Chan }
2995f7278fd0SJosepch Chan 
2996f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */
2997f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec,
2998f7278fd0SJosepch Chan 					     const struct auto_pin_cfg *cfg)
2999f7278fd0SJosepch Chan {
3000f7278fd0SJosepch Chan 	char name[32];
3001ea734963STakashi Iwai 	static const char * const chname[4] = {
3002ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3003ea734963STakashi Iwai 	};
3004fb4cb772SHarald Welte 	hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27};
3005f7278fd0SJosepch Chan 	hda_nid_t nid, nid_vol = 0;
3006f7278fd0SJosepch Chan 	int i, err;
3007f7278fd0SJosepch Chan 
3008f7278fd0SJosepch Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3009f7278fd0SJosepch Chan 		nid = cfg->line_out_pins[i];
3010f7278fd0SJosepch Chan 
3011f7278fd0SJosepch Chan 		if (!nid)
3012f7278fd0SJosepch Chan 			continue;
3013f7278fd0SJosepch Chan 
3014f7278fd0SJosepch Chan 		nid_vol = nid_vols[i];
3015f7278fd0SJosepch Chan 
3016f7278fd0SJosepch Chan 		if (i == AUTO_SEQ_CENLFE) {
3017f7278fd0SJosepch Chan 			/* Center/LFE */
3018f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3019f7278fd0SJosepch Chan 					      "Center Playback Volume",
3020f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3021f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3022f7278fd0SJosepch Chan 			if (err < 0)
3023f7278fd0SJosepch Chan 				return err;
3024f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3025f7278fd0SJosepch Chan 					      "LFE Playback Volume",
3026f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3027f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3028f7278fd0SJosepch Chan 			if (err < 0)
3029f7278fd0SJosepch Chan 				return err;
3030f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3031f7278fd0SJosepch Chan 					      "Center Playback Switch",
3032f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3033f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3034f7278fd0SJosepch Chan 			if (err < 0)
3035f7278fd0SJosepch Chan 				return err;
3036f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3037f7278fd0SJosepch Chan 					      "LFE Playback Switch",
3038f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3039f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3040f7278fd0SJosepch Chan 			if (err < 0)
3041f7278fd0SJosepch Chan 				return err;
3042f7278fd0SJosepch Chan 		} else if (i == AUTO_SEQ_FRONT) {
3043f7278fd0SJosepch Chan 			/* add control to mixer index 0 */
3044f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3045f7278fd0SJosepch Chan 					      "Master Front Playback Volume",
3046f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3047f7278fd0SJosepch Chan 								  HDA_INPUT));
3048f7278fd0SJosepch Chan 			if (err < 0)
3049f7278fd0SJosepch Chan 				return err;
3050f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3051f7278fd0SJosepch Chan 					      "Master Front Playback Switch",
3052f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3053f7278fd0SJosepch Chan 								  HDA_INPUT));
3054f7278fd0SJosepch Chan 			if (err < 0)
3055f7278fd0SJosepch Chan 				return err;
3056f7278fd0SJosepch Chan 
3057f7278fd0SJosepch Chan 			/* add control to PW3 */
3058f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3059f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3060f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3061f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3062f7278fd0SJosepch Chan 			if (err < 0)
3063f7278fd0SJosepch Chan 				return err;
3064f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3065f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3066f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid, 3, 0,
3067f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3068f7278fd0SJosepch Chan 			if (err < 0)
3069f7278fd0SJosepch Chan 				return err;
3070f7278fd0SJosepch Chan 		} else {
3071f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Volume", chname[i]);
3072f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3073f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3074f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3075f7278fd0SJosepch Chan 			if (err < 0)
3076f7278fd0SJosepch Chan 				return err;
3077f7278fd0SJosepch Chan 			sprintf(name, "%s Playback Switch", chname[i]);
3078f7278fd0SJosepch Chan 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3079f7278fd0SJosepch Chan 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3080f7278fd0SJosepch Chan 								  HDA_OUTPUT));
3081f7278fd0SJosepch Chan 			if (err < 0)
3082f7278fd0SJosepch Chan 				return err;
3083f7278fd0SJosepch Chan 		}
3084f7278fd0SJosepch Chan 	}
3085f7278fd0SJosepch Chan 
3086f7278fd0SJosepch Chan 	return 0;
3087f7278fd0SJosepch Chan }
3088f7278fd0SJosepch Chan 
3089f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3090f7278fd0SJosepch Chan {
3091f7278fd0SJosepch Chan 	int err;
3092f7278fd0SJosepch Chan 
3093f7278fd0SJosepch Chan 	if (!pin)
3094f7278fd0SJosepch Chan 		return 0;
3095f7278fd0SJosepch Chan 
3096f7278fd0SJosepch Chan 	spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */
3097cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3098f7278fd0SJosepch Chan 
3099f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3100f7278fd0SJosepch Chan 			      "Headphone Playback Volume",
3101f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3102f7278fd0SJosepch Chan 	if (err < 0)
3103f7278fd0SJosepch Chan 		return err;
3104f7278fd0SJosepch Chan 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3105f7278fd0SJosepch Chan 			      "Headphone Playback Switch",
3106f7278fd0SJosepch Chan 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3107f7278fd0SJosepch Chan 	if (err < 0)
3108f7278fd0SJosepch Chan 		return err;
3109f7278fd0SJosepch Chan 
31100aa62aefSHarald Welte 	create_hp_imux(spec);
31110aa62aefSHarald Welte 
3112f7278fd0SJosepch Chan 	return 0;
3113f7278fd0SJosepch Chan }
3114f7278fd0SJosepch Chan 
3115f7278fd0SJosepch Chan /* create playback/capture controls for input pins */
311610a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec,
3117f7278fd0SJosepch Chan 						const struct auto_pin_cfg *cfg)
3118f7278fd0SJosepch Chan {
3119f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e };
312010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
3121f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3122f7278fd0SJosepch Chan }
3123f7278fd0SJosepch Chan 
3124f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
3125f7278fd0SJosepch Chan {
3126f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
3127f7278fd0SJosepch Chan 	int err;
3128f7278fd0SJosepch Chan 
3129f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3130f7278fd0SJosepch Chan 	if (err < 0)
3131f7278fd0SJosepch Chan 		return err;
3132f7278fd0SJosepch Chan 	err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg);
3133f7278fd0SJosepch Chan 	if (err < 0)
3134f7278fd0SJosepch Chan 		return err;
3135f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3136f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
3137f7278fd0SJosepch Chan 
3138f7278fd0SJosepch Chan 	err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg);
3139f7278fd0SJosepch Chan 	if (err < 0)
3140f7278fd0SJosepch Chan 		return err;
3141f7278fd0SJosepch Chan 	err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3142f7278fd0SJosepch Chan 	if (err < 0)
3143f7278fd0SJosepch Chan 		return err;
314410a20af7STakashi Iwai 	err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg);
3145f7278fd0SJosepch Chan 	if (err < 0)
3146f7278fd0SJosepch Chan 		return err;
3147f7278fd0SJosepch Chan 
3148f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3149f7278fd0SJosepch Chan 
31500852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
3151f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
315255d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
3153f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
3154f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
3155f7278fd0SJosepch Chan 
3156603c4019STakashi Iwai 	if (spec->kctls.list)
3157603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3158f7278fd0SJosepch Chan 
31590aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
31600aa62aefSHarald Welte 
3161f8fdd495SHarald Welte 	if (spec->hp_mux)
31623d83e577STakashi Iwai 		via_hp_build(codec);
3163f7278fd0SJosepch Chan 
31645b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3165f7278fd0SJosepch Chan 	return 1;
3166f7278fd0SJosepch Chan }
3167f7278fd0SJosepch Chan 
3168f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3169f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = {
3170f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
3171f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
3172f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
3173f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
3174f7278fd0SJosepch Chan 	{ } /* end */
3175f7278fd0SJosepch Chan };
3176f7278fd0SJosepch Chan #endif
31773e95b9abSLydia Wang 
31783e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
31793e95b9abSLydia Wang {
31803e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
31813e95b9abSLydia Wang 	int imux_is_smixer;
31823e95b9abSLydia Wang 	unsigned int parm;
31833e95b9abSLydia Wang 	int is_8ch = 0;
3184*bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
3185*bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
31863e95b9abSLydia Wang 		is_8ch = 1;
31873e95b9abSLydia Wang 
31883e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
31893e95b9abSLydia Wang 	imux_is_smixer =
31903e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
31913e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
31923e95b9abSLydia Wang 	/* inputs */
31933e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
31943e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
31963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
31973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
31983e95b9abSLydia Wang 	if (imux_is_smixer)
31993e95b9abSLydia Wang 		parm = AC_PWRST_D0;
32003e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
32013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
32023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
32033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
32043e95b9abSLydia Wang 
32053e95b9abSLydia Wang 	/* outputs */
32063e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
32073e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
32093e95b9abSLydia Wang 	if (spec->smart51_enabled)
32103e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
32113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
32123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
32133e95b9abSLydia Wang 
32143e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
32153e95b9abSLydia Wang 	if (is_8ch) {
32163e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32173e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
32183e95b9abSLydia Wang 		if (spec->smart51_enabled)
32193e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
32203e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
32213e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32223e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
32233e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3224*bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
3225*bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
3226*bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
3227*bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
3228*bc92df7fSLydia Wang 		if (spec->smart51_enabled)
3229*bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
3230*bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
3231*bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3232*bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3233*bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32343e95b9abSLydia Wang 	}
32353e95b9abSLydia Wang 
32363e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
32373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32383e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
32393e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
32403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
32413e95b9abSLydia Wang 	if (is_8ch)
32423e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
32433e95b9abSLydia Wang 
32443e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
32453e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
32463e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
32473e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
32483e95b9abSLydia Wang 	if (is_8ch) {
32493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
32503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32513e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
32523e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
3253*bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
3254*bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
3255*bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32563e95b9abSLydia Wang }
32573e95b9abSLydia Wang 
3258518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
3259f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
3260f7278fd0SJosepch Chan {
3261f7278fd0SJosepch Chan 	struct via_spec *spec;
3262f7278fd0SJosepch Chan 	int err;
3263f7278fd0SJosepch Chan 
3264518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
3265518bf3baSLydia Wang 		return patch_vt1708S(codec);
3266f7278fd0SJosepch Chan 	/* create a codec specific record */
32675b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3268f7278fd0SJosepch Chan 	if (spec == NULL)
3269f7278fd0SJosepch Chan 		return -ENOMEM;
3270f7278fd0SJosepch Chan 
3271f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3272f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3273f7278fd0SJosepch Chan 	if (err < 0) {
3274f7278fd0SJosepch Chan 		via_free(codec);
3275f7278fd0SJosepch Chan 		return err;
3276f7278fd0SJosepch Chan 	} else if (!err) {
3277f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3278f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3279f7278fd0SJosepch Chan 	}
3280f7278fd0SJosepch Chan 
328169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
328269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3283f7278fd0SJosepch Chan 
3284f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3285f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3286f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3287f7278fd0SJosepch Chan 
3288f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3289f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3290f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3291f7278fd0SJosepch Chan 
3292f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3293f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3294f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3295337b9d02STakashi Iwai 		get_mux_nids(codec);
3296f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3297f7278fd0SJosepch Chan 		spec->num_mixers++;
3298f7278fd0SJosepch Chan 	}
3299f7278fd0SJosepch Chan 
3300f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3301f7278fd0SJosepch Chan 
3302f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
330369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3304f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3305f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3306f7278fd0SJosepch Chan #endif
3307f7278fd0SJosepch Chan 
33083e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
33093e95b9abSLydia Wang 
3310f7278fd0SJosepch Chan 	return 0;
3311f7278fd0SJosepch Chan }
3312f7278fd0SJosepch Chan 
3313f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3314f7278fd0SJosepch Chan {
3315f7278fd0SJosepch Chan 	struct via_spec *spec;
3316f7278fd0SJosepch Chan 	int err;
3317f7278fd0SJosepch Chan 
3318f7278fd0SJosepch Chan 	/* create a codec specific record */
33195b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3320f7278fd0SJosepch Chan 	if (spec == NULL)
3321f7278fd0SJosepch Chan 		return -ENOMEM;
3322f7278fd0SJosepch Chan 
3323f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3324f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3325f7278fd0SJosepch Chan 	if (err < 0) {
3326f7278fd0SJosepch Chan 		via_free(codec);
3327f7278fd0SJosepch Chan 		return err;
3328f7278fd0SJosepch Chan 	} else if (!err) {
3329f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3330f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3331f7278fd0SJosepch Chan 	}
3332f7278fd0SJosepch Chan 
333369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
333469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3335f7278fd0SJosepch Chan 
3336f7278fd0SJosepch Chan 	spec->stream_name_analog = "VT1708B Analog";
3337f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3338f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3339f7278fd0SJosepch Chan 
3340f7278fd0SJosepch Chan 	spec->stream_name_digital = "VT1708B Digital";
3341f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3342f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3343f7278fd0SJosepch Chan 
3344f7278fd0SJosepch Chan 	if (!spec->adc_nids && spec->input_mux) {
3345f7278fd0SJosepch Chan 		spec->adc_nids = vt1708B_adc_nids;
3346f7278fd0SJosepch Chan 		spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids);
3347337b9d02STakashi Iwai 		get_mux_nids(codec);
3348f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3349f7278fd0SJosepch Chan 		spec->num_mixers++;
3350f7278fd0SJosepch Chan 	}
3351f7278fd0SJosepch Chan 
3352f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3353f7278fd0SJosepch Chan 
3354f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
335569e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3356f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3357f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3358f7278fd0SJosepch Chan #endif
3359c577b8a1SJoseph Chan 
33603e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
33613e95b9abSLydia Wang 
3362c577b8a1SJoseph Chan 	return 0;
3363c577b8a1SJoseph Chan }
3364c577b8a1SJoseph Chan 
3365d949cac1SHarald Welte /* Patch for VT1708S */
3366d949cac1SHarald Welte 
3367d949cac1SHarald Welte /* capture mixer elements */
3368d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3369d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3370d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3371d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3372d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
33736369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
33746369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
33756369bcfcSLydia Wang 			 HDA_INPUT),
3376d949cac1SHarald Welte 	{
3377d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3378d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3379d949cac1SHarald Welte 		 * So call somewhat different..
3380d949cac1SHarald Welte 		 */
3381d949cac1SHarald Welte 		/* .name = "Capture Source", */
3382d949cac1SHarald Welte 		.name = "Input Source",
3383d949cac1SHarald Welte 		.count = 1,
3384d949cac1SHarald Welte 		.info = via_mux_enum_info,
3385d949cac1SHarald Welte 		.get = via_mux_enum_get,
3386d949cac1SHarald Welte 		.put = via_mux_enum_put,
3387d949cac1SHarald Welte 	},
3388d949cac1SHarald Welte 	{ } /* end */
3389d949cac1SHarald Welte };
3390d949cac1SHarald Welte 
3391d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = {
3392d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3393d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3394d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3395d949cac1SHarald Welte 
3396d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3397d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3398d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3399d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3400d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3401d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3402d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3403d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3404d949cac1SHarald Welte 
3405d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3406d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
34075691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3408d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
34095691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3410d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3411d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3412bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3413bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3414d949cac1SHarald Welte 	{ }
3415d949cac1SHarald Welte };
3416d949cac1SHarald Welte 
341769e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = {
3418a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3419a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3420a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3421a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3422a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3423a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3424a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3425a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3426a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
342769e52a80SHarald Welte 	{ }
342869e52a80SHarald Welte };
342969e52a80SHarald Welte 
3430*bc92df7fSLydia Wang static struct hda_verb vt1705_uniwill_init_verbs[] = {
3431*bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3432*bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3433*bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3434*bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3435*bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3436*bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3437*bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3438*bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3439*bc92df7fSLydia Wang 	{ }
3440*bc92df7fSLydia Wang };
3441*bc92df7fSLydia Wang 
3442d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3443d949cac1SHarald Welte 	.substreams = 2,
3444d949cac1SHarald Welte 	.channels_min = 2,
3445d949cac1SHarald Welte 	.channels_max = 8,
3446d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3447d949cac1SHarald Welte 	.ops = {
3448d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3449c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3450c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
345117314379SLydia Wang 		.close = via_pcm_open_close
3452d949cac1SHarald Welte 	},
3453d949cac1SHarald Welte };
3454d949cac1SHarald Welte 
3455*bc92df7fSLydia Wang static struct hda_pcm_stream vt1705_pcm_analog_playback = {
3456*bc92df7fSLydia Wang 	.substreams = 2,
3457*bc92df7fSLydia Wang 	.channels_min = 2,
3458*bc92df7fSLydia Wang 	.channels_max = 6,
3459*bc92df7fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
3460*bc92df7fSLydia Wang 	.ops = {
3461*bc92df7fSLydia Wang 		.open = via_playback_pcm_open,
3462*bc92df7fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3463*bc92df7fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
3464*bc92df7fSLydia Wang 		.close = via_pcm_open_close
3465*bc92df7fSLydia Wang 	},
3466*bc92df7fSLydia Wang };
3467*bc92df7fSLydia Wang 
3468d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3469d949cac1SHarald Welte 	.substreams = 2,
3470d949cac1SHarald Welte 	.channels_min = 2,
3471d949cac1SHarald Welte 	.channels_max = 2,
3472d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3473d949cac1SHarald Welte 	.ops = {
347417314379SLydia Wang 		.open = via_pcm_open_close,
3475d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
347617314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
347717314379SLydia Wang 		.close = via_pcm_open_close
3478d949cac1SHarald Welte 	},
3479d949cac1SHarald Welte };
3480d949cac1SHarald Welte 
3481d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = {
34829da29271STakashi Iwai 	.substreams = 1,
3483d949cac1SHarald Welte 	.channels_min = 2,
3484d949cac1SHarald Welte 	.channels_max = 2,
3485d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3486d949cac1SHarald Welte 	.ops = {
3487d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3488d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
34899da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
34909da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3491d949cac1SHarald Welte 	},
3492d949cac1SHarald Welte };
3493d949cac1SHarald Welte 
3494d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3495d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec,
3496d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3497d949cac1SHarald Welte {
3498d949cac1SHarald Welte 	int i;
3499d949cac1SHarald Welte 	hda_nid_t nid;
3500d949cac1SHarald Welte 
3501d949cac1SHarald Welte 	spec->multiout.num_dacs = cfg->line_outs;
3502d949cac1SHarald Welte 
3503d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3504d949cac1SHarald Welte 
3505d949cac1SHarald Welte 	for (i = 0; i < 4; i++) {
3506d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3507d949cac1SHarald Welte 		if (nid) {
3508d949cac1SHarald Welte 			/* config dac list */
3509d949cac1SHarald Welte 			switch (i) {
3510d949cac1SHarald Welte 			case AUTO_SEQ_FRONT:
3511d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x10;
3512d949cac1SHarald Welte 				break;
3513d949cac1SHarald Welte 			case AUTO_SEQ_CENLFE:
3514*bc92df7fSLydia Wang 				if (spec->codec->vendor_id == 0x11064397)
3515*bc92df7fSLydia Wang 					spec->multiout.dac_nids[i] = 0x25;
3516*bc92df7fSLydia Wang 				else
3517d949cac1SHarald Welte 					spec->multiout.dac_nids[i] = 0x24;
3518d949cac1SHarald Welte 				break;
3519d949cac1SHarald Welte 			case AUTO_SEQ_SURROUND:
3520d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x11;
3521d949cac1SHarald Welte 				break;
3522d949cac1SHarald Welte 			case AUTO_SEQ_SIDE:
3523d949cac1SHarald Welte 				spec->multiout.dac_nids[i] = 0x25;
3524d949cac1SHarald Welte 				break;
3525d949cac1SHarald Welte 			}
3526d949cac1SHarald Welte 		}
3527d949cac1SHarald Welte 	}
3528d949cac1SHarald Welte 
352932e0191dSClemens Ladisch 	/* for Smart 5.1, line/mic inputs double as output pins */
353032e0191dSClemens Ladisch 	if (cfg->line_outs == 1) {
353132e0191dSClemens Ladisch 		spec->multiout.num_dacs = 3;
353232e0191dSClemens Ladisch 		spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11;
3533*bc92df7fSLydia Wang 		if (spec->codec->vendor_id == 0x11064397)
3534*bc92df7fSLydia Wang 			spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x25;
3535*bc92df7fSLydia Wang 		else
353632e0191dSClemens Ladisch 			spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24;
353732e0191dSClemens Ladisch 	}
353832e0191dSClemens Ladisch 
3539d949cac1SHarald Welte 	return 0;
3540d949cac1SHarald Welte }
3541d949cac1SHarald Welte 
3542d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3543*bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec,
3544d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3545d949cac1SHarald Welte {
3546*bc92df7fSLydia Wang 	struct via_spec *spec = codec->spec;
3547d949cac1SHarald Welte 	char name[32];
3548ea734963STakashi Iwai 	static const char * const chname[4] = {
3549ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
3550ea734963STakashi Iwai 	};
3551*bc92df7fSLydia Wang 	hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25},
3552*bc92df7fSLydia Wang 				     {0x10, 0x11, 0x25, 0} };
3553*bc92df7fSLydia Wang 	hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27},
3554*bc92df7fSLydia Wang 				      {0x1C, 0x18, 0x27, 0} };
3555d949cac1SHarald Welte 	hda_nid_t nid, nid_vol, nid_mute;
3556d949cac1SHarald Welte 	int i, err;
3557d949cac1SHarald Welte 
3558d949cac1SHarald Welte 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
3559d949cac1SHarald Welte 		nid = cfg->line_out_pins[i];
3560d949cac1SHarald Welte 
356132e0191dSClemens Ladisch 		/* for Smart 5.1, there are always at least six channels */
356232e0191dSClemens Ladisch 		if (!nid && i > AUTO_SEQ_CENLFE)
3563d949cac1SHarald Welte 			continue;
3564d949cac1SHarald Welte 
3565*bc92df7fSLydia Wang 		if (codec->vendor_id == 0x11064397) {
3566*bc92df7fSLydia Wang 			nid_vol = nid_vols[1][i];
3567*bc92df7fSLydia Wang 			nid_mute = nid_mutes[1][i];
3568*bc92df7fSLydia Wang 		} else {
3569*bc92df7fSLydia Wang 			nid_vol = nid_vols[0][i];
3570*bc92df7fSLydia Wang 			nid_mute = nid_mutes[0][i];
3571*bc92df7fSLydia Wang 		}
3572*bc92df7fSLydia Wang 		if (!nid_vol && !nid_mute)
3573*bc92df7fSLydia Wang 			continue;
3574d949cac1SHarald Welte 
3575d949cac1SHarald Welte 		if (i == AUTO_SEQ_CENLFE) {
3576d949cac1SHarald Welte 			/* Center/LFE */
3577d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3578d949cac1SHarald Welte 					      "Center Playback Volume",
3579d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
3580d949cac1SHarald Welte 								  HDA_OUTPUT));
3581d949cac1SHarald Welte 			if (err < 0)
3582d949cac1SHarald Welte 				return err;
3583d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3584d949cac1SHarald Welte 					      "LFE Playback Volume",
3585d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
3586d949cac1SHarald Welte 								  HDA_OUTPUT));
3587d949cac1SHarald Welte 			if (err < 0)
3588d949cac1SHarald Welte 				return err;
3589d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3590d949cac1SHarald Welte 					      "Center Playback Switch",
3591d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3592d949cac1SHarald Welte 								  1, 0,
3593d949cac1SHarald Welte 								  HDA_OUTPUT));
3594d949cac1SHarald Welte 			if (err < 0)
3595d949cac1SHarald Welte 				return err;
3596d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3597d949cac1SHarald Welte 					      "LFE Playback Switch",
3598d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3599d949cac1SHarald Welte 								  2, 0,
3600d949cac1SHarald Welte 								  HDA_OUTPUT));
3601d949cac1SHarald Welte 			if (err < 0)
3602d949cac1SHarald Welte 				return err;
3603d949cac1SHarald Welte 		} else if (i == AUTO_SEQ_FRONT) {
3604d949cac1SHarald Welte 			/* add control to mixer index 0 */
3605d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3606d949cac1SHarald Welte 					      "Master Front Playback Volume",
3607d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3608d949cac1SHarald Welte 								  HDA_INPUT));
3609d949cac1SHarald Welte 			if (err < 0)
3610d949cac1SHarald Welte 				return err;
3611d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3612d949cac1SHarald Welte 					      "Master Front Playback Switch",
3613d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(0x16, 3, 0,
3614d949cac1SHarald Welte 								  HDA_INPUT));
3615d949cac1SHarald Welte 			if (err < 0)
3616d949cac1SHarald Welte 				return err;
3617d949cac1SHarald Welte 
3618d949cac1SHarald Welte 			/* Front */
3619d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3620d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3621d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3622d949cac1SHarald Welte 								  HDA_OUTPUT));
3623d949cac1SHarald Welte 			if (err < 0)
3624d949cac1SHarald Welte 				return err;
3625d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3626d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3627d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3628d949cac1SHarald Welte 								  3, 0,
3629d949cac1SHarald Welte 								  HDA_OUTPUT));
3630d949cac1SHarald Welte 			if (err < 0)
3631d949cac1SHarald Welte 				return err;
3632d949cac1SHarald Welte 		} else {
3633d949cac1SHarald Welte 			sprintf(name, "%s Playback Volume", chname[i]);
3634d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
3635d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0,
3636d949cac1SHarald Welte 								  HDA_OUTPUT));
3637d949cac1SHarald Welte 			if (err < 0)
3638d949cac1SHarald Welte 				return err;
3639d949cac1SHarald Welte 			sprintf(name, "%s Playback Switch", chname[i]);
3640d949cac1SHarald Welte 			err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
3641d949cac1SHarald Welte 					      HDA_COMPOSE_AMP_VAL(nid_mute,
3642d949cac1SHarald Welte 								  3, 0,
3643d949cac1SHarald Welte 								  HDA_OUTPUT));
3644d949cac1SHarald Welte 			if (err < 0)
3645d949cac1SHarald Welte 				return err;
3646d949cac1SHarald Welte 		}
3647d949cac1SHarald Welte 	}
3648d949cac1SHarald Welte 
3649d949cac1SHarald Welte 	return 0;
3650d949cac1SHarald Welte }
3651d949cac1SHarald Welte 
3652d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
3653d949cac1SHarald Welte {
3654d949cac1SHarald Welte 	int err;
3655d949cac1SHarald Welte 
3656d949cac1SHarald Welte 	if (!pin)
3657d949cac1SHarald Welte 		return 0;
3658d949cac1SHarald Welte 
3659d949cac1SHarald Welte 	spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */
3660cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 1;
3661d949cac1SHarald Welte 
3662d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3663d949cac1SHarald Welte 			      "Headphone Playback Volume",
3664d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
3665d949cac1SHarald Welte 	if (err < 0)
3666d949cac1SHarald Welte 		return err;
3667d949cac1SHarald Welte 
3668d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
3669d949cac1SHarald Welte 			      "Headphone Playback Switch",
3670d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
3671d949cac1SHarald Welte 	if (err < 0)
3672d949cac1SHarald Welte 		return err;
3673d949cac1SHarald Welte 
36740aa62aefSHarald Welte 	create_hp_imux(spec);
36750aa62aefSHarald Welte 
3676d949cac1SHarald Welte 	return 0;
3677d949cac1SHarald Welte }
3678d949cac1SHarald Welte 
3679d949cac1SHarald Welte /* create playback/capture controls for input pins */
368010a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec,
3681d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
3682d949cac1SHarald Welte {
3683f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
368410a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
3685f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
3686d949cac1SHarald Welte }
3687d949cac1SHarald Welte 
36889da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
36899da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
36909da29271STakashi Iwai {
36919da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
36929da29271STakashi Iwai 	int i;
36939da29271STakashi Iwai 
36949da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
36959da29271STakashi Iwai 		hda_nid_t nid;
36969da29271STakashi Iwai 		int conn;
36979da29271STakashi Iwai 
36989da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
36999da29271STakashi Iwai 		if (!nid)
37009da29271STakashi Iwai 			continue;
37019da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
37029da29271STakashi Iwai 		if (conn < 1)
37039da29271STakashi Iwai 			continue;
37049da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
37059da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
37069da29271STakashi Iwai 		else {
37079da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
37089da29271STakashi Iwai 			break; /* at most two dig outs */
37099da29271STakashi Iwai 		}
37109da29271STakashi Iwai 	}
37119da29271STakashi Iwai }
37129da29271STakashi Iwai 
3713d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3714d949cac1SHarald Welte {
3715d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3716d949cac1SHarald Welte 	int err;
3717d949cac1SHarald Welte 
37189da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3719d949cac1SHarald Welte 	if (err < 0)
3720d949cac1SHarald Welte 		return err;
3721d949cac1SHarald Welte 	err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg);
3722d949cac1SHarald Welte 	if (err < 0)
3723d949cac1SHarald Welte 		return err;
3724d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3725d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3726d949cac1SHarald Welte 
3727*bc92df7fSLydia Wang 	err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg);
3728d949cac1SHarald Welte 	if (err < 0)
3729d949cac1SHarald Welte 		return err;
3730d949cac1SHarald Welte 	err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
3731d949cac1SHarald Welte 	if (err < 0)
3732d949cac1SHarald Welte 		return err;
373310a20af7STakashi Iwai 	err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg);
3734d949cac1SHarald Welte 	if (err < 0)
3735d949cac1SHarald Welte 		return err;
3736d949cac1SHarald Welte 
3737d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3738d949cac1SHarald Welte 
37399da29271STakashi Iwai 	fill_dig_outs(codec);
374098aa34c0SHarald Welte 
3741603c4019STakashi Iwai 	if (spec->kctls.list)
3742603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3743d949cac1SHarald Welte 
37440aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
37450aa62aefSHarald Welte 
3746f8fdd495SHarald Welte 	if (spec->hp_mux)
37473d83e577STakashi Iwai 		via_hp_build(codec);
3748d949cac1SHarald Welte 
37495b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3750d949cac1SHarald Welte 	return 1;
3751d949cac1SHarald Welte }
3752d949cac1SHarald Welte 
3753d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3754d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = {
3755d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3756d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3757d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3758d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3759d949cac1SHarald Welte 	{ } /* end */
3760d949cac1SHarald Welte };
3761d949cac1SHarald Welte #endif
3762d949cac1SHarald Welte 
37636369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
37646369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
37656369bcfcSLydia Wang {
37666369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
37676369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
37686369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
37696369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
37706369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
37716369bcfcSLydia Wang }
37726369bcfcSLydia Wang 
3773d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3774d949cac1SHarald Welte {
3775d949cac1SHarald Welte 	struct via_spec *spec;
3776d949cac1SHarald Welte 	int err;
3777d949cac1SHarald Welte 
3778d949cac1SHarald Welte 	/* create a codec specific record */
37795b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3780d949cac1SHarald Welte 	if (spec == NULL)
3781d949cac1SHarald Welte 		return -ENOMEM;
3782d949cac1SHarald Welte 
3783d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3784d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3785d949cac1SHarald Welte 	if (err < 0) {
3786d949cac1SHarald Welte 		via_free(codec);
3787d949cac1SHarald Welte 		return err;
3788d949cac1SHarald Welte 	} else if (!err) {
3789d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3790d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3791d949cac1SHarald Welte 	}
3792d949cac1SHarald Welte 
379369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3794*bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3795*bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3796*bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3797*bc92df7fSLydia Wang 	else
3798*bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3799*bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3800d949cac1SHarald Welte 
380136dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
380236dd5c4aSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
3803*bc92df7fSLydia Wang 	else if (codec->vendor_id == 0x11064397)
3804*bc92df7fSLydia Wang 		spec->stream_name_analog = "VT1705 Analog";
380536dd5c4aSLydia Wang 	else
3806d949cac1SHarald Welte 		spec->stream_name_analog = "VT1708S Analog";
3807*bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3808*bc92df7fSLydia Wang 		spec->stream_analog_playback = &vt1705_pcm_analog_playback;
3809*bc92df7fSLydia Wang 	else
3810d949cac1SHarald Welte 		spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3811d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3812d949cac1SHarald Welte 
381336dd5c4aSLydia Wang 	if (codec->vendor_id == 0x11060440)
381436dd5c4aSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
3815*bc92df7fSLydia Wang 	else if (codec->vendor_id == 0x11064397)
3816*bc92df7fSLydia Wang 		spec->stream_name_digital = "VT1705 Digital";
381736dd5c4aSLydia Wang 	else
3818d949cac1SHarald Welte 		spec->stream_name_digital = "VT1708S Digital";
3819d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3820d949cac1SHarald Welte 
3821d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
3822d949cac1SHarald Welte 		spec->adc_nids = vt1708S_adc_nids;
3823d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids);
3824337b9d02STakashi Iwai 		get_mux_nids(codec);
38256369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
38266369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3827d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3828d949cac1SHarald Welte 		spec->num_mixers++;
3829d949cac1SHarald Welte 	}
3830d949cac1SHarald Welte 
3831d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3832d949cac1SHarald Welte 
3833d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
383469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3835d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3836d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3837d949cac1SHarald Welte #endif
3838d949cac1SHarald Welte 
3839518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3840518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3841518bf3baSLydia Wang 		kfree(codec->chip_name);
3842518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3843518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3844518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3845518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3846518bf3baSLydia Wang 		spec->stream_name_analog = "VT1708BCE Analog";
3847518bf3baSLydia Wang 		spec->stream_name_digital = "VT1708BCE Digital";
3848518bf3baSLydia Wang 	}
3849970f630fSLydia Wang 	/* correct names for VT1818S */
3850970f630fSLydia Wang 	if (codec->vendor_id == 0x11060440) {
3851970f630fSLydia Wang 		spec->stream_name_analog = "VT1818S Analog";
3852970f630fSLydia Wang 		spec->stream_name_digital = "VT1818S Digital";
3853970f630fSLydia Wang 	}
3854*bc92df7fSLydia Wang 	/* correct names for VT1705 */
3855*bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3856*bc92df7fSLydia Wang 		kfree(codec->chip_name);
3857*bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3858*bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3859*bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3860*bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3861*bc92df7fSLydia Wang 	}
38623e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3863d949cac1SHarald Welte 	return 0;
3864d949cac1SHarald Welte }
3865d949cac1SHarald Welte 
3866d949cac1SHarald Welte /* Patch for VT1702 */
3867d949cac1SHarald Welte 
3868d949cac1SHarald Welte /* capture mixer elements */
3869d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = {
3870d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3871d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3872d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3873d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3874d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3875d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3876d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3877d949cac1SHarald Welte 			 HDA_INPUT),
3878d949cac1SHarald Welte 	{
3879d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3880d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3881d949cac1SHarald Welte 		 * So call somewhat different..
3882d949cac1SHarald Welte 		 */
3883d949cac1SHarald Welte 		/* .name = "Capture Source", */
3884d949cac1SHarald Welte 		.name = "Input Source",
3885d949cac1SHarald Welte 		.count = 1,
3886d949cac1SHarald Welte 		.info = via_mux_enum_info,
3887d949cac1SHarald Welte 		.get = via_mux_enum_get,
3888d949cac1SHarald Welte 		.put = via_mux_enum_put,
3889d949cac1SHarald Welte 	},
3890d949cac1SHarald Welte 	{ } /* end */
3891d949cac1SHarald Welte };
3892d949cac1SHarald Welte 
3893d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = {
3894d949cac1SHarald Welte 	/*
3895d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3896d949cac1SHarald Welte 	 */
3897d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3898d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3899d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3900d949cac1SHarald Welte 
3901d949cac1SHarald Welte 
3902d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3903d949cac1SHarald Welte 	 * mixer widget
3904d949cac1SHarald Welte 	 */
3905d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3906d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3907d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3908d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3909d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3910d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3911d949cac1SHarald Welte 
3912d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3913d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3914d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3915d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3916d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3917bc7e7e5cSLydia Wang 	/* mixer enable */
3918bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3919bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3920bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3921d949cac1SHarald Welte 	{ }
3922d949cac1SHarald Welte };
3923d949cac1SHarald Welte 
392469e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = {
3925a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3926a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3927a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3928a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3929a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3930a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
393169e52a80SHarald Welte 	{ }
393269e52a80SHarald Welte };
393369e52a80SHarald Welte 
3934d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = {
39350aa62aefSHarald Welte 	.substreams = 2,
3936d949cac1SHarald Welte 	.channels_min = 2,
3937d949cac1SHarald Welte 	.channels_max = 2,
3938d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3939d949cac1SHarald Welte 	.ops = {
3940d949cac1SHarald Welte 		.open = via_playback_pcm_open,
39410aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
394217314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
394317314379SLydia Wang 		.close = via_pcm_open_close
3944d949cac1SHarald Welte 	},
3945d949cac1SHarald Welte };
3946d949cac1SHarald Welte 
3947d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = {
3948d949cac1SHarald Welte 	.substreams = 3,
3949d949cac1SHarald Welte 	.channels_min = 2,
3950d949cac1SHarald Welte 	.channels_max = 2,
3951d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
3952d949cac1SHarald Welte 	.ops = {
395317314379SLydia Wang 		.open = via_pcm_open_close,
3954d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
395517314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
395617314379SLydia Wang 		.close = via_pcm_open_close
3957d949cac1SHarald Welte 	},
3958d949cac1SHarald Welte };
3959d949cac1SHarald Welte 
3960d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = {
39615691ec7fSHarald Welte 	.substreams = 2,
3962d949cac1SHarald Welte 	.channels_min = 2,
3963d949cac1SHarald Welte 	.channels_max = 2,
3964d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3965d949cac1SHarald Welte 	.ops = {
3966d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3967d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
39689da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
39699da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3970d949cac1SHarald Welte 	},
3971d949cac1SHarald Welte };
3972d949cac1SHarald Welte 
3973d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */
3974d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec,
3975d949cac1SHarald Welte 				     const struct auto_pin_cfg *cfg)
3976d949cac1SHarald Welte {
3977d949cac1SHarald Welte 	spec->multiout.num_dacs = 1;
3978d949cac1SHarald Welte 	spec->multiout.dac_nids = spec->private_dac_nids;
3979d949cac1SHarald Welte 
3980d949cac1SHarald Welte 	if (cfg->line_out_pins[0]) {
3981d949cac1SHarald Welte 		/* config dac list */
3982d949cac1SHarald Welte 		spec->multiout.dac_nids[0] = 0x10;
3983d949cac1SHarald Welte 	}
3984d949cac1SHarald Welte 
3985d949cac1SHarald Welte 	return 0;
3986d949cac1SHarald Welte }
3987d949cac1SHarald Welte 
3988d949cac1SHarald Welte /* add playback controls from the parsed DAC table */
3989d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec,
3990d949cac1SHarald Welte 					     const struct auto_pin_cfg *cfg)
3991d949cac1SHarald Welte {
3992d949cac1SHarald Welte 	int err;
3993d949cac1SHarald Welte 
3994d949cac1SHarald Welte 	if (!cfg->line_out_pins[0])
3995d949cac1SHarald Welte 		return -1;
3996d949cac1SHarald Welte 
3997d949cac1SHarald Welte 	/* add control to mixer index 0 */
3998d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
3999d949cac1SHarald Welte 			      "Master Front Playback Volume",
4000d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4001d949cac1SHarald Welte 	if (err < 0)
4002d949cac1SHarald Welte 		return err;
4003d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4004d949cac1SHarald Welte 			      "Master Front Playback Switch",
4005d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT));
4006d949cac1SHarald Welte 	if (err < 0)
4007d949cac1SHarald Welte 		return err;
4008d949cac1SHarald Welte 
4009d949cac1SHarald Welte 	/* Front */
4010d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4011d949cac1SHarald Welte 			      "Front Playback Volume",
4012d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT));
4013d949cac1SHarald Welte 	if (err < 0)
4014d949cac1SHarald Welte 		return err;
4015d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4016d949cac1SHarald Welte 			      "Front Playback Switch",
4017d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT));
4018d949cac1SHarald Welte 	if (err < 0)
4019d949cac1SHarald Welte 		return err;
4020d949cac1SHarald Welte 
4021d949cac1SHarald Welte 	return 0;
4022d949cac1SHarald Welte }
4023d949cac1SHarald Welte 
4024d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4025d949cac1SHarald Welte {
40260713efebSLydia Wang 	int err, i;
40270713efebSLydia Wang 	struct hda_input_mux *imux;
4028ea734963STakashi Iwai 	static const char * const texts[] = { "ON", "OFF", NULL};
4029d949cac1SHarald Welte 	if (!pin)
4030d949cac1SHarald Welte 		return 0;
4031d949cac1SHarald Welte 	spec->multiout.hp_nid = 0x1D;
4032cdc1784dSLydia Wang 	spec->hp_independent_mode_index = 0;
4033d949cac1SHarald Welte 
4034d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4035d949cac1SHarald Welte 			      "Headphone Playback Volume",
4036d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT));
4037d949cac1SHarald Welte 	if (err < 0)
4038d949cac1SHarald Welte 		return err;
4039d949cac1SHarald Welte 
4040d949cac1SHarald Welte 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4041d949cac1SHarald Welte 			      "Headphone Playback Switch",
4042d949cac1SHarald Welte 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4043d949cac1SHarald Welte 	if (err < 0)
4044d949cac1SHarald Welte 		return err;
4045d949cac1SHarald Welte 
40460713efebSLydia Wang 	imux = &spec->private_imux[1];
40470aa62aefSHarald Welte 
40480713efebSLydia Wang 	/* for hp mode select */
404910a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
405010a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
40510713efebSLydia Wang 
40520713efebSLydia Wang 	spec->hp_mux = &spec->private_imux[1];
4053d949cac1SHarald Welte 	return 0;
4054d949cac1SHarald Welte }
4055d949cac1SHarald Welte 
4056d949cac1SHarald Welte /* create playback/capture controls for input pins */
405710a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec,
4058d949cac1SHarald Welte 						const struct auto_pin_cfg *cfg)
4059d949cac1SHarald Welte {
4060f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff };
406110a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs,
4062f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4063d949cac1SHarald Welte }
4064d949cac1SHarald Welte 
4065d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
4066d949cac1SHarald Welte {
4067d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
4068d949cac1SHarald Welte 	int err;
4069d949cac1SHarald Welte 
40709da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4071d949cac1SHarald Welte 	if (err < 0)
4072d949cac1SHarald Welte 		return err;
4073d949cac1SHarald Welte 	err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg);
4074d949cac1SHarald Welte 	if (err < 0)
4075d949cac1SHarald Welte 		return err;
4076d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4077d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
4078d949cac1SHarald Welte 
4079d949cac1SHarald Welte 	err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg);
4080d949cac1SHarald Welte 	if (err < 0)
4081d949cac1SHarald Welte 		return err;
4082d949cac1SHarald Welte 	err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4083d949cac1SHarald Welte 	if (err < 0)
4084d949cac1SHarald Welte 		return err;
4085c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
4086c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
4087c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
4088c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
4089c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
4090c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
409110a20af7STakashi Iwai 	err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg);
4092d949cac1SHarald Welte 	if (err < 0)
4093d949cac1SHarald Welte 		return err;
4094d949cac1SHarald Welte 
4095d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4096d949cac1SHarald Welte 
40979da29271STakashi Iwai 	fill_dig_outs(codec);
409898aa34c0SHarald Welte 
4099603c4019STakashi Iwai 	if (spec->kctls.list)
4100603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4101d949cac1SHarald Welte 
41020aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
41030aa62aefSHarald Welte 
4104f8fdd495SHarald Welte 	if (spec->hp_mux)
41053d83e577STakashi Iwai 		via_hp_build(codec);
4106d949cac1SHarald Welte 
4107d949cac1SHarald Welte 	return 1;
4108d949cac1SHarald Welte }
4109d949cac1SHarald Welte 
4110d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4111d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = {
4112d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
4113d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
4114d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
4115d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
4116d949cac1SHarald Welte 	{ } /* end */
4117d949cac1SHarald Welte };
4118d949cac1SHarald Welte #endif
4119d949cac1SHarald Welte 
41203e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
41213e95b9abSLydia Wang {
41223e95b9abSLydia Wang 	int imux_is_smixer =
41233e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
41243e95b9abSLydia Wang 	unsigned int parm;
41253e95b9abSLydia Wang 	/* inputs */
41263e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
41273e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
41293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
41303e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
41313e95b9abSLydia Wang 	if (imux_is_smixer)
41323e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
41333e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
41343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
41353e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
41363e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
41373e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
41383e95b9abSLydia Wang 
41393e95b9abSLydia Wang 	/* outputs */
41403e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
41413e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41423e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
41433e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
41443e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
41453e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
41463e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
41473e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
41483e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
41493e95b9abSLydia Wang }
41503e95b9abSLydia Wang 
4151d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
4152d949cac1SHarald Welte {
4153d949cac1SHarald Welte 	struct via_spec *spec;
4154d949cac1SHarald Welte 	int err;
4155d949cac1SHarald Welte 
4156d949cac1SHarald Welte 	/* create a codec specific record */
41575b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4158d949cac1SHarald Welte 	if (spec == NULL)
4159d949cac1SHarald Welte 		return -ENOMEM;
4160d949cac1SHarald Welte 
4161d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
4162d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
4163d949cac1SHarald Welte 	if (err < 0) {
4164d949cac1SHarald Welte 		via_free(codec);
4165d949cac1SHarald Welte 		return err;
4166d949cac1SHarald Welte 	} else if (!err) {
4167d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4168d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
4169d949cac1SHarald Welte 	}
4170d949cac1SHarald Welte 
417169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
417269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
4173d949cac1SHarald Welte 
4174d949cac1SHarald Welte 	spec->stream_name_analog = "VT1702 Analog";
4175d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
4176d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
4177d949cac1SHarald Welte 
4178d949cac1SHarald Welte 	spec->stream_name_digital = "VT1702 Digital";
4179d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
4180d949cac1SHarald Welte 
4181d949cac1SHarald Welte 	if (!spec->adc_nids && spec->input_mux) {
4182d949cac1SHarald Welte 		spec->adc_nids = vt1702_adc_nids;
4183d949cac1SHarald Welte 		spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids);
4184337b9d02STakashi Iwai 		get_mux_nids(codec);
4185d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
4186d949cac1SHarald Welte 		spec->num_mixers++;
4187d949cac1SHarald Welte 	}
4188d949cac1SHarald Welte 
4189d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
4190d949cac1SHarald Welte 
4191d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
419269e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
4193d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
4194d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
4195d949cac1SHarald Welte #endif
4196d949cac1SHarald Welte 
41973e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
4198d949cac1SHarald Welte 	return 0;
4199d949cac1SHarald Welte }
4200d949cac1SHarald Welte 
4201eb7188caSLydia Wang /* Patch for VT1718S */
4202eb7188caSLydia Wang 
4203eb7188caSLydia Wang /* capture mixer elements */
4204eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = {
4205eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4206eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4207eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4208eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4209eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4210eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
4211eb7188caSLydia Wang 			 HDA_INPUT),
4212eb7188caSLydia Wang 	{
4213eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4214eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4215eb7188caSLydia Wang 		 * So call somewhat different..
4216eb7188caSLydia Wang 		 */
4217eb7188caSLydia Wang 		.name = "Input Source",
4218eb7188caSLydia Wang 		.count = 2,
4219eb7188caSLydia Wang 		.info = via_mux_enum_info,
4220eb7188caSLydia Wang 		.get = via_mux_enum_get,
4221eb7188caSLydia Wang 		.put = via_mux_enum_put,
4222eb7188caSLydia Wang 	},
4223eb7188caSLydia Wang 	{ } /* end */
4224eb7188caSLydia Wang };
4225eb7188caSLydia Wang 
4226eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = {
4227eb7188caSLydia Wang 	/*
4228eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4229eb7188caSLydia Wang 	 */
4230eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4231eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4232eb7188caSLydia Wang 
4233eb7188caSLydia Wang 
4234eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4235eb7188caSLydia Wang 	 * mixer widget
4236eb7188caSLydia Wang 	 */
4237eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4238eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4239eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4240eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4241eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4242eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
4243eb7188caSLydia Wang 
4244eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
4245eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4246eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
4247eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4248eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4249eb7188caSLydia Wang 	/* PW11 Input enable */
4250eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
4251eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
4252eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
4253eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
4254eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4255eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4256eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4257eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4258eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4259eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4260eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4261eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4262eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4263eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4264eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
4265eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
4266eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
4267eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
4268eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4269eb7188caSLydia Wang 	{ }
4270eb7188caSLydia Wang };
4271eb7188caSLydia Wang 
4272eb7188caSLydia Wang 
4273eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = {
4274eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4275eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4276eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4277eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4278eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4279eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4280eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4281eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4282eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4283eb7188caSLydia Wang 	{ }
4284eb7188caSLydia Wang };
4285eb7188caSLydia Wang 
4286eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = {
4287eb7188caSLydia Wang 	.substreams = 2,
4288eb7188caSLydia Wang 	.channels_min = 2,
4289eb7188caSLydia Wang 	.channels_max = 10,
4290eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4291eb7188caSLydia Wang 	.ops = {
4292eb7188caSLydia Wang 		.open = via_playback_pcm_open,
4293eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4294eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4295eb7188caSLydia Wang 		.close = via_pcm_open_close,
4296eb7188caSLydia Wang 	},
4297eb7188caSLydia Wang };
4298eb7188caSLydia Wang 
4299eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = {
4300eb7188caSLydia Wang 	.substreams = 2,
4301eb7188caSLydia Wang 	.channels_min = 2,
4302eb7188caSLydia Wang 	.channels_max = 2,
4303eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4304eb7188caSLydia Wang 	.ops = {
4305eb7188caSLydia Wang 		.open = via_pcm_open_close,
4306eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
4307eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4308eb7188caSLydia Wang 		.close = via_pcm_open_close,
4309eb7188caSLydia Wang 	},
4310eb7188caSLydia Wang };
4311eb7188caSLydia Wang 
4312eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = {
4313eb7188caSLydia Wang 	.substreams = 2,
4314eb7188caSLydia Wang 	.channels_min = 2,
4315eb7188caSLydia Wang 	.channels_max = 2,
4316eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
4317eb7188caSLydia Wang 	.ops = {
4318eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
4319eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
4320eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4321eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4322eb7188caSLydia Wang 	},
4323eb7188caSLydia Wang };
4324eb7188caSLydia Wang 
4325eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = {
4326eb7188caSLydia Wang 	.substreams = 1,
4327eb7188caSLydia Wang 	.channels_min = 2,
4328eb7188caSLydia Wang 	.channels_max = 2,
4329eb7188caSLydia Wang };
4330eb7188caSLydia Wang 
4331eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4332eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec,
4333eb7188caSLydia Wang 				     const struct auto_pin_cfg *cfg)
4334eb7188caSLydia Wang {
4335eb7188caSLydia Wang 	int i;
4336eb7188caSLydia Wang 	hda_nid_t nid;
4337eb7188caSLydia Wang 
4338eb7188caSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4339eb7188caSLydia Wang 
4340eb7188caSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4341eb7188caSLydia Wang 
4342eb7188caSLydia Wang 	for (i = 0; i < 4; i++) {
4343eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4344eb7188caSLydia Wang 		if (nid) {
4345eb7188caSLydia Wang 			/* config dac list */
4346eb7188caSLydia Wang 			switch (i) {
4347eb7188caSLydia Wang 			case AUTO_SEQ_FRONT:
4348eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x8;
4349eb7188caSLydia Wang 				break;
4350eb7188caSLydia Wang 			case AUTO_SEQ_CENLFE:
4351eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xa;
4352eb7188caSLydia Wang 				break;
4353eb7188caSLydia Wang 			case AUTO_SEQ_SURROUND:
4354eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0x9;
4355eb7188caSLydia Wang 				break;
4356eb7188caSLydia Wang 			case AUTO_SEQ_SIDE:
4357eb7188caSLydia Wang 				spec->multiout.dac_nids[i] = 0xb;
4358eb7188caSLydia Wang 				break;
4359eb7188caSLydia Wang 			}
4360eb7188caSLydia Wang 		}
4361eb7188caSLydia Wang 	}
4362eb7188caSLydia Wang 
4363eb7188caSLydia Wang 	return 0;
4364eb7188caSLydia Wang }
4365eb7188caSLydia Wang 
4366eb7188caSLydia Wang /* add playback controls from the parsed DAC table */
4367eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec,
4368eb7188caSLydia Wang 					     const struct auto_pin_cfg *cfg)
4369eb7188caSLydia Wang {
4370eb7188caSLydia Wang 	char name[32];
4371ea734963STakashi Iwai 	static const char * const chname[4] = {
4372ea734963STakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
4373ea734963STakashi Iwai 	};
4374eb7188caSLydia Wang 	hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb};
4375eb7188caSLydia Wang 	hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27};
4376eb7188caSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute = 0;
4377eb7188caSLydia Wang 	int i, err;
4378eb7188caSLydia Wang 
4379eb7188caSLydia Wang 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
4380eb7188caSLydia Wang 		nid = cfg->line_out_pins[i];
4381eb7188caSLydia Wang 
4382eb7188caSLydia Wang 		if (!nid)
4383eb7188caSLydia Wang 			continue;
4384eb7188caSLydia Wang 		nid_vol = nid_vols[i];
4385eb7188caSLydia Wang 		nid_mute = nid_mutes[i];
4386eb7188caSLydia Wang 
4387eb7188caSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4388eb7188caSLydia Wang 			/* Center/LFE */
4389eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4390eb7188caSLydia Wang 					      "Center Playback Volume",
4391eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0,
4392eb7188caSLydia Wang 								  HDA_OUTPUT));
4393eb7188caSLydia Wang 			if (err < 0)
4394eb7188caSLydia Wang 				return err;
4395eb7188caSLydia Wang 			err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4396eb7188caSLydia Wang 					      "LFE Playback Volume",
4397eb7188caSLydia Wang 					      HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0,
4398eb7188caSLydia Wang 								  HDA_OUTPUT));
4399eb7188caSLydia Wang 			if (err < 0)
4400eb7188caSLydia Wang 				return err;
4401eb7188caSLydia Wang 			err = via_add_control(
4402eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4403eb7188caSLydia Wang 				"Center Playback Switch",
4404eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4405eb7188caSLydia Wang 						    HDA_OUTPUT));
4406eb7188caSLydia Wang 			if (err < 0)
4407eb7188caSLydia Wang 				return err;
4408eb7188caSLydia Wang 			err = via_add_control(
4409eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4410eb7188caSLydia Wang 				"LFE Playback Switch",
4411eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4412eb7188caSLydia Wang 						    HDA_OUTPUT));
4413eb7188caSLydia Wang 			if (err < 0)
4414eb7188caSLydia Wang 				return err;
4415eb7188caSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4416eb7188caSLydia Wang 			/* Front */
4417eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4418eb7188caSLydia Wang 			err = via_add_control(
4419eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4420eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4421eb7188caSLydia Wang 			if (err < 0)
4422eb7188caSLydia Wang 				return err;
4423eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4424eb7188caSLydia Wang 			err = via_add_control(
4425eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4426eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4427eb7188caSLydia Wang 						    HDA_OUTPUT));
4428eb7188caSLydia Wang 			if (err < 0)
4429eb7188caSLydia Wang 				return err;
4430eb7188caSLydia Wang 		} else {
4431eb7188caSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4432eb7188caSLydia Wang 			err = via_add_control(
4433eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4434eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4435eb7188caSLydia Wang 			if (err < 0)
4436eb7188caSLydia Wang 				return err;
4437eb7188caSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4438eb7188caSLydia Wang 			err = via_add_control(
4439eb7188caSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4440eb7188caSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4441eb7188caSLydia Wang 						    HDA_OUTPUT));
4442eb7188caSLydia Wang 			if (err < 0)
4443eb7188caSLydia Wang 				return err;
4444eb7188caSLydia Wang 		}
4445eb7188caSLydia Wang 	}
4446eb7188caSLydia Wang 	return 0;
4447eb7188caSLydia Wang }
4448eb7188caSLydia Wang 
4449eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4450eb7188caSLydia Wang {
4451eb7188caSLydia Wang 	int err;
4452eb7188caSLydia Wang 
4453eb7188caSLydia Wang 	if (!pin)
4454eb7188caSLydia Wang 		return 0;
4455eb7188caSLydia Wang 
4456eb7188caSLydia Wang 	spec->multiout.hp_nid = 0xc; /* AOW4 */
4457eb7188caSLydia Wang 	spec->hp_independent_mode_index = 1;
4458eb7188caSLydia Wang 
4459eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4460eb7188caSLydia Wang 			      "Headphone Playback Volume",
4461eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT));
4462eb7188caSLydia Wang 	if (err < 0)
4463eb7188caSLydia Wang 		return err;
4464eb7188caSLydia Wang 
4465eb7188caSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4466eb7188caSLydia Wang 			      "Headphone Playback Switch",
4467eb7188caSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4468eb7188caSLydia Wang 	if (err < 0)
4469eb7188caSLydia Wang 		return err;
4470eb7188caSLydia Wang 
4471eb7188caSLydia Wang 	create_hp_imux(spec);
4472eb7188caSLydia Wang 	return 0;
4473eb7188caSLydia Wang }
4474eb7188caSLydia Wang 
4475eb7188caSLydia Wang /* create playback/capture controls for input pins */
447610a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec,
4477eb7188caSLydia Wang 						const struct auto_pin_cfg *cfg)
4478eb7188caSLydia Wang {
4479f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff };
448010a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
4481f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
4482eb7188caSLydia Wang }
4483eb7188caSLydia Wang 
4484eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
4485eb7188caSLydia Wang {
4486eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
4487eb7188caSLydia Wang 	int err;
4488eb7188caSLydia Wang 
4489eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4490eb7188caSLydia Wang 
4491eb7188caSLydia Wang 	if (err < 0)
4492eb7188caSLydia Wang 		return err;
4493eb7188caSLydia Wang 	err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg);
4494eb7188caSLydia Wang 	if (err < 0)
4495eb7188caSLydia Wang 		return err;
4496eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4497eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4498eb7188caSLydia Wang 
4499eb7188caSLydia Wang 	err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg);
4500eb7188caSLydia Wang 	if (err < 0)
4501eb7188caSLydia Wang 		return err;
4502eb7188caSLydia Wang 	err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
4503eb7188caSLydia Wang 	if (err < 0)
4504eb7188caSLydia Wang 		return err;
450510a20af7STakashi Iwai 	err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg);
4506eb7188caSLydia Wang 	if (err < 0)
4507eb7188caSLydia Wang 		return err;
4508eb7188caSLydia Wang 
4509eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4510eb7188caSLydia Wang 
4511eb7188caSLydia Wang 	fill_dig_outs(codec);
4512eb7188caSLydia Wang 
4513eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
4514eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
4515eb7188caSLydia Wang 
4516eb7188caSLydia Wang 	if (spec->kctls.list)
4517eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4518eb7188caSLydia Wang 
4519eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4520eb7188caSLydia Wang 
4521eb7188caSLydia Wang 	if (spec->hp_mux)
45223d83e577STakashi Iwai 		via_hp_build(codec);
4523eb7188caSLydia Wang 
45245b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4525eb7188caSLydia Wang 
4526eb7188caSLydia Wang 	return 1;
4527eb7188caSLydia Wang }
4528eb7188caSLydia Wang 
4529eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4530eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = {
4531eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4532eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4533eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
4534eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
4535eb7188caSLydia Wang 	{ } /* end */
4536eb7188caSLydia Wang };
4537eb7188caSLydia Wang #endif
4538eb7188caSLydia Wang 
45393e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
45403e95b9abSLydia Wang {
45413e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
45423e95b9abSLydia Wang 	int imux_is_smixer;
45433e95b9abSLydia Wang 	unsigned int parm;
45443e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
45453e95b9abSLydia Wang 	imux_is_smixer =
45463e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
45473e95b9abSLydia Wang 	/* inputs */
45483e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
45493e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
45513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
45523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
45533e95b9abSLydia Wang 	if (imux_is_smixer)
45543e95b9abSLydia Wang 		parm = AC_PWRST_D0;
45553e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
45563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
45573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
45583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
45593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
45603e95b9abSLydia Wang 
45613e95b9abSLydia Wang 	/* outputs */
45623e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
45633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
45653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
45663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
45673e95b9abSLydia Wang 
45683e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
45693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
45713e95b9abSLydia Wang 	if (spec->smart51_enabled)
45723e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
45733e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
45743e95b9abSLydia Wang 
45753e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
45763e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45773e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
45783e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
45793e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
45803e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
45813e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
45823e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
45833e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
45843e95b9abSLydia Wang 
45853e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
45863e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
45883e95b9abSLydia Wang 	if (spec->smart51_enabled)
45893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
45903e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
45913e95b9abSLydia Wang 
45923e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
45933e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
45943e95b9abSLydia Wang 		parm = AC_PWRST_D3;
45953e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
45963e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
45973e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
45983e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
45993e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46003e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
46013e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46023e95b9abSLydia Wang 	}
46033e95b9abSLydia Wang }
46043e95b9abSLydia Wang 
4605eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
4606eb7188caSLydia Wang {
4607eb7188caSLydia Wang 	struct via_spec *spec;
4608eb7188caSLydia Wang 	int err;
4609eb7188caSLydia Wang 
4610eb7188caSLydia Wang 	/* create a codec specific record */
46115b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4612eb7188caSLydia Wang 	if (spec == NULL)
4613eb7188caSLydia Wang 		return -ENOMEM;
4614eb7188caSLydia Wang 
4615eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
4616eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
4617eb7188caSLydia Wang 	if (err < 0) {
4618eb7188caSLydia Wang 		via_free(codec);
4619eb7188caSLydia Wang 		return err;
4620eb7188caSLydia Wang 	} else if (!err) {
4621eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4622eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4623eb7188caSLydia Wang 	}
4624eb7188caSLydia Wang 
4625eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
4626eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
4627eb7188caSLydia Wang 
4628bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4629bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT2020 Analog";
4630bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4631bb3c6bfcSLydia Wang 		spec->stream_name_analog = "VT1828S Analog";
4632bb3c6bfcSLydia Wang 	else
4633eb7188caSLydia Wang 		spec->stream_name_analog = "VT1718S Analog";
4634eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
4635eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
4636eb7188caSLydia Wang 
4637bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060441)
4638bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT2020 Digital";
4639bb3c6bfcSLydia Wang 	else if (codec->vendor_id == 0x11064441)
4640bb3c6bfcSLydia Wang 		spec->stream_name_digital = "VT1828S Digital";
4641bb3c6bfcSLydia Wang 	else
4642eb7188caSLydia Wang 		spec->stream_name_digital = "VT1718S Digital";
4643eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
4644bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
4645eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
4646eb7188caSLydia Wang 
4647eb7188caSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
4648eb7188caSLydia Wang 		spec->adc_nids = vt1718S_adc_nids;
4649eb7188caSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids);
4650eb7188caSLydia Wang 		get_mux_nids(codec);
4651bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4652bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4653eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
4654eb7188caSLydia Wang 		spec->num_mixers++;
4655eb7188caSLydia Wang 	}
4656eb7188caSLydia Wang 
4657eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
4658eb7188caSLydia Wang 
4659eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
46600f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4661eb7188caSLydia Wang 
4662eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4663eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
4664eb7188caSLydia Wang #endif
4665eb7188caSLydia Wang 
46663e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
46673e95b9abSLydia Wang 
4668eb7188caSLydia Wang 	return 0;
4669eb7188caSLydia Wang }
4670f3db423dSLydia Wang 
4671f3db423dSLydia Wang /* Patch for VT1716S */
4672f3db423dSLydia Wang 
4673f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
4674f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
4675f3db423dSLydia Wang {
4676f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
4677f3db423dSLydia Wang 	uinfo->count = 1;
4678f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
4679f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
4680f3db423dSLydia Wang 	return 0;
4681f3db423dSLydia Wang }
4682f3db423dSLydia Wang 
4683f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
4684f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4685f3db423dSLydia Wang {
4686f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4687f3db423dSLydia Wang 	int index = 0;
4688f3db423dSLydia Wang 
4689f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
4690f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
4691f3db423dSLydia Wang 	if (index != -1)
4692f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
4693f3db423dSLydia Wang 
4694f3db423dSLydia Wang 	return 0;
4695f3db423dSLydia Wang }
4696f3db423dSLydia Wang 
4697f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
4698f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
4699f3db423dSLydia Wang {
4700f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4701f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4702f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
4703f3db423dSLydia Wang 
4704f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
4705f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
4706f3db423dSLydia Wang 	spec->dmic_enabled = index;
47073e95b9abSLydia Wang 	set_widgets_power_state(codec);
4708f3db423dSLydia Wang 	return 1;
4709f3db423dSLydia Wang }
4710f3db423dSLydia Wang 
4711f3db423dSLydia Wang /* capture mixer elements */
4712f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = {
4713f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
4714f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
4715f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
4716f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
4717f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
4718f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
4719f3db423dSLydia Wang 			 HDA_INPUT),
4720f3db423dSLydia Wang 	{
4721f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4722f3db423dSLydia Wang 		.name = "Input Source",
4723f3db423dSLydia Wang 		.count = 1,
4724f3db423dSLydia Wang 		.info = via_mux_enum_info,
4725f3db423dSLydia Wang 		.get = via_mux_enum_get,
4726f3db423dSLydia Wang 		.put = via_mux_enum_put,
4727f3db423dSLydia Wang 	},
4728f3db423dSLydia Wang 	{ } /* end */
4729f3db423dSLydia Wang };
4730f3db423dSLydia Wang 
4731f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
4732f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
4733f3db423dSLydia Wang 	{
4734f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4735f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
47365b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
4737f3db423dSLydia Wang 	 .count = 1,
4738f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
4739f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
4740f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
4741f3db423dSLydia Wang 	 },
4742f3db423dSLydia Wang 	{}			/* end */
4743f3db423dSLydia Wang };
4744f3db423dSLydia Wang 
4745f3db423dSLydia Wang 
4746f3db423dSLydia Wang /* mono-out mixer elements */
4747f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
4748f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
4749f3db423dSLydia Wang 	{ } /* end */
4750f3db423dSLydia Wang };
4751f3db423dSLydia Wang 
4752f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = {
4753f3db423dSLydia Wang 	/*
4754f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4755f3db423dSLydia Wang 	 */
4756f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4757f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4758f3db423dSLydia Wang 
4759f3db423dSLydia Wang 
4760f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4761f3db423dSLydia Wang 	 * mixer widget
4762f3db423dSLydia Wang 	 */
4763f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4764f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4765f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4766f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4767f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4768f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4769f3db423dSLydia Wang 
4770f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4771f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4772f3db423dSLydia Wang 
4773f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4774f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4775f3db423dSLydia Wang 
4776f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4777f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4778f3db423dSLydia Wang 
4779f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4780f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4781f3db423dSLydia Wang 
4782f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4783f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4784f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4785f3db423dSLydia Wang 
4786f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4787f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4788f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4789f3db423dSLydia Wang 	/* PW12 Output enable */
4790f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4791f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4792f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4793f3db423dSLydia Wang 	/* don't bybass mixer */
4794f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4795f3db423dSLydia Wang 	/* Enable mono output */
4796f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4797f3db423dSLydia Wang 	{ }
4798f3db423dSLydia Wang };
4799f3db423dSLydia Wang 
4800f3db423dSLydia Wang 
4801f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = {
4802f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4803f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4804f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4805f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4806f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4807f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4808f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4809f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4810f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4811f3db423dSLydia Wang 	{ }
4812f3db423dSLydia Wang };
4813f3db423dSLydia Wang 
4814f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4815f3db423dSLydia Wang 	.substreams = 2,
4816f3db423dSLydia Wang 	.channels_min = 2,
4817f3db423dSLydia Wang 	.channels_max = 6,
4818f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4819f3db423dSLydia Wang 	.ops = {
4820f3db423dSLydia Wang 		.open = via_playback_pcm_open,
4821f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4822f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4823f3db423dSLydia Wang 		.close = via_pcm_open_close,
4824f3db423dSLydia Wang 	},
4825f3db423dSLydia Wang };
4826f3db423dSLydia Wang 
4827f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4828f3db423dSLydia Wang 	.substreams = 2,
4829f3db423dSLydia Wang 	.channels_min = 2,
4830f3db423dSLydia Wang 	.channels_max = 2,
4831f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
4832f3db423dSLydia Wang 	.ops = {
4833f3db423dSLydia Wang 		.open = via_pcm_open_close,
4834f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
4835f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4836f3db423dSLydia Wang 		.close = via_pcm_open_close,
4837f3db423dSLydia Wang 	},
4838f3db423dSLydia Wang };
4839f3db423dSLydia Wang 
4840f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4841f3db423dSLydia Wang 	.substreams = 2,
4842f3db423dSLydia Wang 	.channels_min = 2,
4843f3db423dSLydia Wang 	.channels_max = 2,
4844f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
4845f3db423dSLydia Wang 	.ops = {
4846f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
4847f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
4848f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4849f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4850f3db423dSLydia Wang 	},
4851f3db423dSLydia Wang };
4852f3db423dSLydia Wang 
4853f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
4854f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec,
4855f3db423dSLydia Wang 				      const struct auto_pin_cfg *cfg)
4856f3db423dSLydia Wang {	int i;
4857f3db423dSLydia Wang 	hda_nid_t nid;
4858f3db423dSLydia Wang 
4859f3db423dSLydia Wang 	spec->multiout.num_dacs = cfg->line_outs;
4860f3db423dSLydia Wang 
4861f3db423dSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
4862f3db423dSLydia Wang 
4863f3db423dSLydia Wang 	for (i = 0; i < 3; i++) {
4864f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4865f3db423dSLydia Wang 		if (nid) {
4866f3db423dSLydia Wang 			/* config dac list */
4867f3db423dSLydia Wang 			switch (i) {
4868f3db423dSLydia Wang 			case AUTO_SEQ_FRONT:
4869f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x10;
4870f3db423dSLydia Wang 				break;
4871f3db423dSLydia Wang 			case AUTO_SEQ_CENLFE:
4872f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x25;
4873f3db423dSLydia Wang 				break;
4874f3db423dSLydia Wang 			case AUTO_SEQ_SURROUND:
4875f3db423dSLydia Wang 				spec->multiout.dac_nids[i] = 0x11;
4876f3db423dSLydia Wang 				break;
4877f3db423dSLydia Wang 			}
4878f3db423dSLydia Wang 		}
4879f3db423dSLydia Wang 	}
4880f3db423dSLydia Wang 
4881f3db423dSLydia Wang 	return 0;
4882f3db423dSLydia Wang }
4883f3db423dSLydia Wang 
4884f3db423dSLydia Wang /* add playback controls from the parsed DAC table */
4885f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec,
4886f3db423dSLydia Wang 					      const struct auto_pin_cfg *cfg)
4887f3db423dSLydia Wang {
4888f3db423dSLydia Wang 	char name[32];
4889ea734963STakashi Iwai 	static const char * const chname[3] = {
4890ea734963STakashi Iwai 		"Front", "Surround", "C/LFE"
4891ea734963STakashi Iwai 	};
4892f3db423dSLydia Wang 	hda_nid_t nid_vols[] = {0x10, 0x11, 0x25};
4893f3db423dSLydia Wang 	hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27};
4894f3db423dSLydia Wang 	hda_nid_t nid, nid_vol, nid_mute;
4895f3db423dSLydia Wang 	int i, err;
4896f3db423dSLydia Wang 
4897f3db423dSLydia Wang 	for (i = 0; i <= AUTO_SEQ_CENLFE; i++) {
4898f3db423dSLydia Wang 		nid = cfg->line_out_pins[i];
4899f3db423dSLydia Wang 
4900f3db423dSLydia Wang 		if (!nid)
4901f3db423dSLydia Wang 			continue;
4902f3db423dSLydia Wang 
4903f3db423dSLydia Wang 		nid_vol = nid_vols[i];
4904f3db423dSLydia Wang 		nid_mute = nid_mutes[i];
4905f3db423dSLydia Wang 
4906f3db423dSLydia Wang 		if (i == AUTO_SEQ_CENLFE) {
4907f3db423dSLydia Wang 			err = via_add_control(
4908f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4909f3db423dSLydia Wang 				"Center Playback Volume",
4910f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT));
4911f3db423dSLydia Wang 			if (err < 0)
4912f3db423dSLydia Wang 				return err;
4913f3db423dSLydia Wang 			err = via_add_control(
4914f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4915f3db423dSLydia Wang 				"LFE Playback Volume",
4916f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT));
4917f3db423dSLydia Wang 			if (err < 0)
4918f3db423dSLydia Wang 				return err;
4919f3db423dSLydia Wang 			err = via_add_control(
4920f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4921f3db423dSLydia Wang 				"Center Playback Switch",
4922f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0,
4923f3db423dSLydia Wang 						    HDA_OUTPUT));
4924f3db423dSLydia Wang 			if (err < 0)
4925f3db423dSLydia Wang 				return err;
4926f3db423dSLydia Wang 			err = via_add_control(
4927f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4928f3db423dSLydia Wang 				"LFE Playback Switch",
4929f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0,
4930f3db423dSLydia Wang 						    HDA_OUTPUT));
4931f3db423dSLydia Wang 			if (err < 0)
4932f3db423dSLydia Wang 				return err;
4933f3db423dSLydia Wang 		} else if (i == AUTO_SEQ_FRONT) {
4934f3db423dSLydia Wang 
4935f3db423dSLydia Wang 			err = via_add_control(
4936f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL,
4937f3db423dSLydia Wang 				"Master Front Playback Volume",
4938f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4939f3db423dSLydia Wang 			if (err < 0)
4940f3db423dSLydia Wang 				return err;
4941f3db423dSLydia Wang 			err = via_add_control(
4942f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE,
4943f3db423dSLydia Wang 				"Master Front Playback Switch",
4944f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT));
4945f3db423dSLydia Wang 			if (err < 0)
4946f3db423dSLydia Wang 				return err;
4947f3db423dSLydia Wang 
4948f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4949f3db423dSLydia Wang 			err = via_add_control(
4950f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4951f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4952f3db423dSLydia Wang 			if (err < 0)
4953f3db423dSLydia Wang 				return err;
4954f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4955f3db423dSLydia Wang 			err = via_add_control(
4956f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4957f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4958f3db423dSLydia Wang 						    HDA_OUTPUT));
4959f3db423dSLydia Wang 			if (err < 0)
4960f3db423dSLydia Wang 				return err;
4961f3db423dSLydia Wang 		} else {
4962f3db423dSLydia Wang 			sprintf(name, "%s Playback Volume", chname[i]);
4963f3db423dSLydia Wang 			err = via_add_control(
4964f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_VOL, name,
4965f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT));
4966f3db423dSLydia Wang 			if (err < 0)
4967f3db423dSLydia Wang 				return err;
4968f3db423dSLydia Wang 			sprintf(name, "%s Playback Switch", chname[i]);
4969f3db423dSLydia Wang 			err = via_add_control(
4970f3db423dSLydia Wang 				spec, VIA_CTL_WIDGET_MUTE, name,
4971f3db423dSLydia Wang 				HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0,
4972f3db423dSLydia Wang 						    HDA_OUTPUT));
4973f3db423dSLydia Wang 			if (err < 0)
4974f3db423dSLydia Wang 				return err;
4975f3db423dSLydia Wang 		}
4976f3db423dSLydia Wang 	}
4977f3db423dSLydia Wang 	return 0;
4978f3db423dSLydia Wang }
4979f3db423dSLydia Wang 
4980f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
4981f3db423dSLydia Wang {
4982f3db423dSLydia Wang 	int err;
4983f3db423dSLydia Wang 
4984f3db423dSLydia Wang 	if (!pin)
4985f3db423dSLydia Wang 		return 0;
4986f3db423dSLydia Wang 
4987f3db423dSLydia Wang 	spec->multiout.hp_nid = 0x25; /* AOW3 */
4988f3db423dSLydia Wang 	spec->hp_independent_mode_index = 1;
4989f3db423dSLydia Wang 
4990f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
4991f3db423dSLydia Wang 			      "Headphone Playback Volume",
4992f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
4993f3db423dSLydia Wang 	if (err < 0)
4994f3db423dSLydia Wang 		return err;
4995f3db423dSLydia Wang 
4996f3db423dSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
4997f3db423dSLydia Wang 			      "Headphone Playback Switch",
4998f3db423dSLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
4999f3db423dSLydia Wang 	if (err < 0)
5000f3db423dSLydia Wang 		return err;
5001f3db423dSLydia Wang 
5002f3db423dSLydia Wang 	create_hp_imux(spec);
5003f3db423dSLydia Wang 	return 0;
5004f3db423dSLydia Wang }
5005f3db423dSLydia Wang 
5006f3db423dSLydia Wang /* create playback/capture controls for input pins */
500710a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec,
5008f3db423dSLydia Wang 						const struct auto_pin_cfg *cfg)
5009f3db423dSLydia Wang {
5010f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff };
501110a20af7STakashi Iwai 	return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs,
5012f3268512STakashi Iwai 						ARRAY_SIZE(pin_idxs));
5013f3db423dSLydia Wang }
5014f3db423dSLydia Wang 
5015f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
5016f3db423dSLydia Wang {
5017f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
5018f3db423dSLydia Wang 	int err;
5019f3db423dSLydia Wang 
5020f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5021f3db423dSLydia Wang 	if (err < 0)
5022f3db423dSLydia Wang 		return err;
5023f3db423dSLydia Wang 	err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg);
5024f3db423dSLydia Wang 	if (err < 0)
5025f3db423dSLydia Wang 		return err;
5026f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
5027f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
5028f3db423dSLydia Wang 
5029f3db423dSLydia Wang 	err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg);
5030f3db423dSLydia Wang 	if (err < 0)
5031f3db423dSLydia Wang 		return err;
5032f3db423dSLydia Wang 	err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5033f3db423dSLydia Wang 	if (err < 0)
5034f3db423dSLydia Wang 		return err;
503510a20af7STakashi Iwai 	err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg);
5036f3db423dSLydia Wang 	if (err < 0)
5037f3db423dSLydia Wang 		return err;
5038f3db423dSLydia Wang 
5039f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5040f3db423dSLydia Wang 
5041f3db423dSLydia Wang 	fill_dig_outs(codec);
5042f3db423dSLydia Wang 
5043f3db423dSLydia Wang 	if (spec->kctls.list)
5044f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5045f3db423dSLydia Wang 
5046f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
5047f3db423dSLydia Wang 
5048f3db423dSLydia Wang 	if (spec->hp_mux)
50493d83e577STakashi Iwai 		via_hp_build(codec);
5050f3db423dSLydia Wang 
50515b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
5052f3db423dSLydia Wang 
5053f3db423dSLydia Wang 	return 1;
5054f3db423dSLydia Wang }
5055f3db423dSLydia Wang 
5056f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5057f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = {
5058f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
5059f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
5060f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
5061f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
5062f3db423dSLydia Wang 	{ } /* end */
5063f3db423dSLydia Wang };
5064f3db423dSLydia Wang #endif
5065f3db423dSLydia Wang 
50663e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
50673e95b9abSLydia Wang {
50683e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
50693e95b9abSLydia Wang 	int imux_is_smixer;
50703e95b9abSLydia Wang 	unsigned int parm;
50713e95b9abSLydia Wang 	unsigned int mono_out, present;
50723e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
50733e95b9abSLydia Wang 	imux_is_smixer =
50743e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
50753e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
50763e95b9abSLydia Wang 	/* inputs */
50773e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
50783e95b9abSLydia Wang 	parm = AC_PWRST_D3;
50793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
50803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
50813e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
50823e95b9abSLydia Wang 	if (imux_is_smixer)
50833e95b9abSLydia Wang 		parm = AC_PWRST_D0;
50843e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
50853e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
50863e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
50873e95b9abSLydia Wang 
50883e95b9abSLydia Wang 	parm = AC_PWRST_D3;
50893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
50903e95b9abSLydia Wang 	/* PW11 (22h) */
50913e95b9abSLydia Wang 	if (spec->dmic_enabled)
50923e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
50933e95b9abSLydia Wang 	else
50943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
50953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
50963e95b9abSLydia Wang 
50973e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
50983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
50993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
51003e95b9abSLydia Wang 
51013e95b9abSLydia Wang 	/* outputs */
51023e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
51033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
51053e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
51063e95b9abSLydia Wang 	if (spec->smart51_enabled)
51073e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
51083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
51093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
51103e95b9abSLydia Wang 
51113e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
51123e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51133e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
51143e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
51153e95b9abSLydia Wang 	if (spec->smart51_enabled)
51163e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
51173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
51183e95b9abSLydia Wang 
51193e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
51203e95b9abSLydia Wang 	if (spec->smart51_enabled)
51213e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
51223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
51233e95b9abSLydia Wang 
51243e95b9abSLydia Wang 	/* Mono out */
51253e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
51263e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
51273e95b9abSLydia Wang 
51283e95b9abSLydia Wang 	if (present)
51293e95b9abSLydia Wang 		mono_out = 0;
51303e95b9abSLydia Wang 	else {
51313e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
51323e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
51333e95b9abSLydia Wang 			mono_out = 0;
51343e95b9abSLydia Wang 		else
51353e95b9abSLydia Wang 			mono_out = 1;
51363e95b9abSLydia Wang 	}
51373e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
51383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
51393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
51403e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
51413e95b9abSLydia Wang 
51423e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
51433e95b9abSLydia Wang 	parm = AC_PWRST_D3;
51443e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
51453e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
51463e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
51473e95b9abSLydia Wang 	if (spec->hp_independent_mode)
51483e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
51493e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
51503e95b9abSLydia Wang 
51513e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
51523e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
51533e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
51543e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
51553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
51563e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
51573e95b9abSLydia Wang }
51583e95b9abSLydia Wang 
5159f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
5160f3db423dSLydia Wang {
5161f3db423dSLydia Wang 	struct via_spec *spec;
5162f3db423dSLydia Wang 	int err;
5163f3db423dSLydia Wang 
5164f3db423dSLydia Wang 	/* create a codec specific record */
51655b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5166f3db423dSLydia Wang 	if (spec == NULL)
5167f3db423dSLydia Wang 		return -ENOMEM;
5168f3db423dSLydia Wang 
5169f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
5170f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
5171f3db423dSLydia Wang 	if (err < 0) {
5172f3db423dSLydia Wang 		via_free(codec);
5173f3db423dSLydia Wang 		return err;
5174f3db423dSLydia Wang 	} else if (!err) {
5175f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5176f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5177f3db423dSLydia Wang 	}
5178f3db423dSLydia Wang 
5179f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
5180f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
5181f3db423dSLydia Wang 
5182f3db423dSLydia Wang 	spec->stream_name_analog = "VT1716S Analog";
5183f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
5184f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
5185f3db423dSLydia Wang 
5186f3db423dSLydia Wang 	spec->stream_name_digital = "VT1716S Digital";
5187f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
5188f3db423dSLydia Wang 
5189f3db423dSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
5190f3db423dSLydia Wang 		spec->adc_nids = vt1716S_adc_nids;
5191f3db423dSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids);
5192f3db423dSLydia Wang 		get_mux_nids(codec);
5193f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
5194f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
5195f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
5196f3db423dSLydia Wang 		spec->num_mixers++;
5197f3db423dSLydia Wang 	}
5198f3db423dSLydia Wang 
5199f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
5200f3db423dSLydia Wang 	spec->num_mixers++;
5201f3db423dSLydia Wang 
5202f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
5203f3db423dSLydia Wang 
5204f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
5205f3db423dSLydia Wang 
5206f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
52070f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5208f3db423dSLydia Wang 
5209f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5210f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
5211f3db423dSLydia Wang #endif
5212f3db423dSLydia Wang 
52133e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
5214f3db423dSLydia Wang 	return 0;
5215f3db423dSLydia Wang }
521625eaba2fSLydia Wang 
521725eaba2fSLydia Wang /* for vt2002P */
521825eaba2fSLydia Wang 
521925eaba2fSLydia Wang /* capture mixer elements */
522025eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = {
522125eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
522225eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
522325eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
522425eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
522525eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
522625eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
522725eaba2fSLydia Wang 			 HDA_INPUT),
522825eaba2fSLydia Wang 	{
522925eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
523025eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
523125eaba2fSLydia Wang 		 * So call somewhat different..
523225eaba2fSLydia Wang 		 */
523325eaba2fSLydia Wang 		/* .name = "Capture Source", */
523425eaba2fSLydia Wang 		.name = "Input Source",
523525eaba2fSLydia Wang 		.count = 2,
523625eaba2fSLydia Wang 		.info = via_mux_enum_info,
523725eaba2fSLydia Wang 		.get = via_mux_enum_get,
523825eaba2fSLydia Wang 		.put = via_mux_enum_put,
523925eaba2fSLydia Wang 	},
524025eaba2fSLydia Wang 	{ } /* end */
524125eaba2fSLydia Wang };
524225eaba2fSLydia Wang 
524325eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = {
524425eaba2fSLydia Wang 	/*
524525eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
524625eaba2fSLydia Wang 	 */
524725eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
524825eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
524925eaba2fSLydia Wang 
525025eaba2fSLydia Wang 
525125eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
525225eaba2fSLydia Wang 	 * mixer widget
525325eaba2fSLydia Wang 	 */
525425eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
525525eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
525625eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
525725eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
525825eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
525925eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
526025eaba2fSLydia Wang 
526125eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
526225eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
526325eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
526425eaba2fSLydia Wang 
526525eaba2fSLydia Wang 	/* PW9 Output enable */
526625eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
526725eaba2fSLydia Wang 
526825eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
526925eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
527025eaba2fSLydia Wang 
527125eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
527225eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
527325eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
527425eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
527525eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
527625eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
527725eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
527825eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
527925eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
528025eaba2fSLydia Wang 
528125eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
528225eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
528325eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
528425eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
528525eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
528625eaba2fSLydia Wang 
528725eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
528825eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
528925eaba2fSLydia Wang 
529025eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
529125eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
529225eaba2fSLydia Wang 	{ }
529325eaba2fSLydia Wang };
529425eaba2fSLydia Wang 
529525eaba2fSLydia Wang 
529625eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = {
529725eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
529825eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
529925eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
530025eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
530125eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
530225eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
530325eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
530425eaba2fSLydia Wang 	{ }
530525eaba2fSLydia Wang };
530625eaba2fSLydia Wang 
530725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = {
530825eaba2fSLydia Wang 	.substreams = 2,
530925eaba2fSLydia Wang 	.channels_min = 2,
531025eaba2fSLydia Wang 	.channels_max = 2,
531125eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
531225eaba2fSLydia Wang 	.ops = {
531325eaba2fSLydia Wang 		.open = via_playback_pcm_open,
531425eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
531525eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
531625eaba2fSLydia Wang 		.close = via_pcm_open_close,
531725eaba2fSLydia Wang 	},
531825eaba2fSLydia Wang };
531925eaba2fSLydia Wang 
532025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = {
532125eaba2fSLydia Wang 	.substreams = 2,
532225eaba2fSLydia Wang 	.channels_min = 2,
532325eaba2fSLydia Wang 	.channels_max = 2,
532425eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
532525eaba2fSLydia Wang 	.ops = {
532625eaba2fSLydia Wang 		.open = via_pcm_open_close,
532725eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
532825eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
532925eaba2fSLydia Wang 		.close = via_pcm_open_close,
533025eaba2fSLydia Wang 	},
533125eaba2fSLydia Wang };
533225eaba2fSLydia Wang 
533325eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = {
533425eaba2fSLydia Wang 	.substreams = 1,
533525eaba2fSLydia Wang 	.channels_min = 2,
533625eaba2fSLydia Wang 	.channels_max = 2,
533725eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
533825eaba2fSLydia Wang 	.ops = {
533925eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
534025eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
534125eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
534225eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
534325eaba2fSLydia Wang 	},
534425eaba2fSLydia Wang };
534525eaba2fSLydia Wang 
534625eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
534725eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec,
534825eaba2fSLydia Wang 				      const struct auto_pin_cfg *cfg)
534925eaba2fSLydia Wang {
535025eaba2fSLydia Wang 	spec->multiout.num_dacs = 1;
535125eaba2fSLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
535225eaba2fSLydia Wang 	if (cfg->line_out_pins[0])
535325eaba2fSLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
535425eaba2fSLydia Wang 	return 0;
535525eaba2fSLydia Wang }
535625eaba2fSLydia Wang 
535725eaba2fSLydia Wang /* add playback controls from the parsed DAC table */
535825eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec,
535925eaba2fSLydia Wang 					     const struct auto_pin_cfg *cfg)
536025eaba2fSLydia Wang {
536125eaba2fSLydia Wang 	int err;
536225eaba2fSLydia Wang 
536325eaba2fSLydia Wang 	if (!cfg->line_out_pins[0])
536425eaba2fSLydia Wang 		return -1;
536525eaba2fSLydia Wang 
536625eaba2fSLydia Wang 
536725eaba2fSLydia Wang 	/* Line-Out: PortE */
536825eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
536925eaba2fSLydia Wang 			      "Master Front Playback Volume",
537025eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
537125eaba2fSLydia Wang 	if (err < 0)
537225eaba2fSLydia Wang 		return err;
537325eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
537425eaba2fSLydia Wang 			      "Master Front Playback Switch",
537525eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT));
537625eaba2fSLydia Wang 	if (err < 0)
537725eaba2fSLydia Wang 		return err;
537825eaba2fSLydia Wang 
537925eaba2fSLydia Wang 	return 0;
538025eaba2fSLydia Wang }
538125eaba2fSLydia Wang 
538225eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
538325eaba2fSLydia Wang {
538425eaba2fSLydia Wang 	int err;
538525eaba2fSLydia Wang 
538625eaba2fSLydia Wang 	if (!pin)
538725eaba2fSLydia Wang 		return 0;
538825eaba2fSLydia Wang 
538925eaba2fSLydia Wang 	spec->multiout.hp_nid = 0x9;
539025eaba2fSLydia Wang 	spec->hp_independent_mode_index = 1;
539125eaba2fSLydia Wang 
539225eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
539325eaba2fSLydia Wang 			      "Headphone Playback Volume",
539425eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(
539525eaba2fSLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
539625eaba2fSLydia Wang 	if (err < 0)
539725eaba2fSLydia Wang 		return err;
539825eaba2fSLydia Wang 
539925eaba2fSLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
540025eaba2fSLydia Wang 			      "Headphone Playback Switch",
540125eaba2fSLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT));
540225eaba2fSLydia Wang 	if (err < 0)
540325eaba2fSLydia Wang 		return err;
540425eaba2fSLydia Wang 
540525eaba2fSLydia Wang 	create_hp_imux(spec);
540625eaba2fSLydia Wang 	return 0;
540725eaba2fSLydia Wang }
540825eaba2fSLydia Wang 
540925eaba2fSLydia Wang /* create playback/capture controls for input pins */
541010a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec,
541125eaba2fSLydia Wang 						const struct auto_pin_cfg *cfg)
541225eaba2fSLydia Wang {
541310a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
541425eaba2fSLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5415f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff };
5416f3268512STakashi Iwai 	int err;
541725eaba2fSLydia Wang 
541810a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5419f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
542025eaba2fSLydia Wang 	if (err < 0)
542125eaba2fSLydia Wang 		return err;
542225eaba2fSLydia Wang 	/* build volume/mute control of loopback */
54237b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21);
542425eaba2fSLydia Wang 	if (err < 0)
542525eaba2fSLydia Wang 		return err;
542625eaba2fSLydia Wang 
542725eaba2fSLydia Wang 	/* for digital mic select */
542810a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL);
542925eaba2fSLydia Wang 
543025eaba2fSLydia Wang 	return 0;
543125eaba2fSLydia Wang }
543225eaba2fSLydia Wang 
543325eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
543425eaba2fSLydia Wang {
543525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
543625eaba2fSLydia Wang 	int err;
543725eaba2fSLydia Wang 
543825eaba2fSLydia Wang 
543925eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
544025eaba2fSLydia Wang 	if (err < 0)
544125eaba2fSLydia Wang 		return err;
544225eaba2fSLydia Wang 
544325eaba2fSLydia Wang 	err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg);
544425eaba2fSLydia Wang 	if (err < 0)
544525eaba2fSLydia Wang 		return err;
544625eaba2fSLydia Wang 
544725eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
544825eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
544925eaba2fSLydia Wang 
545025eaba2fSLydia Wang 	err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg);
545125eaba2fSLydia Wang 	if (err < 0)
545225eaba2fSLydia Wang 		return err;
545325eaba2fSLydia Wang 	err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
545425eaba2fSLydia Wang 	if (err < 0)
545525eaba2fSLydia Wang 		return err;
545610a20af7STakashi Iwai 	err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg);
545725eaba2fSLydia Wang 	if (err < 0)
545825eaba2fSLydia Wang 		return err;
545925eaba2fSLydia Wang 
546025eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
546125eaba2fSLydia Wang 
546225eaba2fSLydia Wang 	fill_dig_outs(codec);
546325eaba2fSLydia Wang 
546425eaba2fSLydia Wang 	if (spec->kctls.list)
546525eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
546625eaba2fSLydia Wang 
546725eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
546825eaba2fSLydia Wang 
546925eaba2fSLydia Wang 	if (spec->hp_mux)
54703d83e577STakashi Iwai 		via_hp_build(codec);
547125eaba2fSLydia Wang 
547225eaba2fSLydia Wang 	return 1;
547325eaba2fSLydia Wang }
547425eaba2fSLydia Wang 
547525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
547625eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = {
547725eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
547825eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
547925eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
548025eaba2fSLydia Wang 	{ } /* end */
548125eaba2fSLydia Wang };
548225eaba2fSLydia Wang #endif
548325eaba2fSLydia Wang 
54843e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
54853e95b9abSLydia Wang {
54863e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
54873e95b9abSLydia Wang 	int imux_is_smixer;
54883e95b9abSLydia Wang 	unsigned int parm;
54893e95b9abSLydia Wang 	unsigned int present;
54903e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
54913e95b9abSLydia Wang 	imux_is_smixer =
54923e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
54933e95b9abSLydia Wang 	/* inputs */
54943e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
54953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
54963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
54973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
54983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
54993e95b9abSLydia Wang 	parm = AC_PWRST_D0;
55003e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
55013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
55023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
55033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
55043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
55053e95b9abSLydia Wang 
55063e95b9abSLydia Wang 	/* outputs */
55073e95b9abSLydia Wang 	/* AOW0 (8h)*/
55083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
55093e95b9abSLydia Wang 
55103e95b9abSLydia Wang 	/* PW4 (26h), MW4 (1ch), MUX4(37h) */
55113e95b9abSLydia Wang 	parm = AC_PWRST_D3;
55123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
55133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1c, 0,
55143e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55153e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x37, 0,
55163e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55173e95b9abSLydia Wang 
55183e95b9abSLydia Wang 	/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
55193e95b9abSLydia Wang 	parm = AC_PWRST_D3;
55203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
55213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x19, 0,
55223e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0,
55243e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55253e95b9abSLydia Wang 
55263e95b9abSLydia Wang 	if (spec->hp_independent_mode)
55273e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
55283e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
55293e95b9abSLydia Wang 
55303e95b9abSLydia Wang 	/* Class-D */
55313e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
55323e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
55333e95b9abSLydia Wang 
55343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
55353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
55363e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
55373e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0,
55383e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
55403e95b9abSLydia Wang 
55413e95b9abSLydia Wang 	/* Mono Out */
55423e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
55433e95b9abSLydia Wang 
55443e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
55453e95b9abSLydia Wang 	/* PW15 (31h), MW8(17h), MUX8(3bh) */
55463e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x31, 0,
55473e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55483e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0,
55493e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55503e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3b, 0,
55513e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, parm);
55523e95b9abSLydia Wang 
55533e95b9abSLydia Wang 	/* MW9 (21h) */
55543e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
55553e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
55563e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
55573e95b9abSLydia Wang 	else
55583e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
55593e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
55603e95b9abSLydia Wang }
556125eaba2fSLydia Wang 
556225eaba2fSLydia Wang /* patch for vt2002P */
556325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
556425eaba2fSLydia Wang {
556525eaba2fSLydia Wang 	struct via_spec *spec;
556625eaba2fSLydia Wang 	int err;
556725eaba2fSLydia Wang 
556825eaba2fSLydia Wang 	/* create a codec specific record */
55695b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
557025eaba2fSLydia Wang 	if (spec == NULL)
557125eaba2fSLydia Wang 		return -ENOMEM;
557225eaba2fSLydia Wang 
557325eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
557425eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
557525eaba2fSLydia Wang 	if (err < 0) {
557625eaba2fSLydia Wang 		via_free(codec);
557725eaba2fSLydia Wang 		return err;
557825eaba2fSLydia Wang 	} else if (!err) {
557925eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
558025eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
558125eaba2fSLydia Wang 	}
558225eaba2fSLydia Wang 
558325eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt2002P_volume_init_verbs;
558425eaba2fSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs;
558525eaba2fSLydia Wang 
558625eaba2fSLydia Wang 	spec->stream_name_analog = "VT2002P Analog";
558725eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
558825eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
558925eaba2fSLydia Wang 
559025eaba2fSLydia Wang 	spec->stream_name_digital = "VT2002P Digital";
559125eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
559225eaba2fSLydia Wang 
559325eaba2fSLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
559425eaba2fSLydia Wang 		spec->adc_nids = vt2002P_adc_nids;
559525eaba2fSLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids);
559625eaba2fSLydia Wang 		get_mux_nids(codec);
559725eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
559825eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
559925eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
560025eaba2fSLydia Wang 		spec->num_mixers++;
560125eaba2fSLydia Wang 	}
560225eaba2fSLydia Wang 
560325eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
560425eaba2fSLydia Wang 
560525eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
56060f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
560725eaba2fSLydia Wang 
560825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
560925eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
561025eaba2fSLydia Wang #endif
561125eaba2fSLydia Wang 
56123e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
561325eaba2fSLydia Wang 	return 0;
561425eaba2fSLydia Wang }
5615ab6734e7SLydia Wang 
5616ab6734e7SLydia Wang /* for vt1812 */
5617ab6734e7SLydia Wang 
5618ab6734e7SLydia Wang /* capture mixer elements */
5619ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = {
5620ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
5621ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
5622ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
5623ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
5624ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
5625ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
5626ab6734e7SLydia Wang 		       HDA_INPUT),
5627ab6734e7SLydia Wang 	{
5628ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5629ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
5630ab6734e7SLydia Wang 		 * So call somewhat different..
5631ab6734e7SLydia Wang 		 */
5632ab6734e7SLydia Wang 		.name = "Input Source",
5633ab6734e7SLydia Wang 		.count = 2,
5634ab6734e7SLydia Wang 		.info = via_mux_enum_info,
5635ab6734e7SLydia Wang 		.get = via_mux_enum_get,
5636ab6734e7SLydia Wang 		.put = via_mux_enum_put,
5637ab6734e7SLydia Wang 	},
5638ab6734e7SLydia Wang 	{ } /* end */
5639ab6734e7SLydia Wang };
5640ab6734e7SLydia Wang 
5641ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = {
5642ab6734e7SLydia Wang 	/*
5643ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
5644ab6734e7SLydia Wang 	 */
5645ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5646ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5647ab6734e7SLydia Wang 
5648ab6734e7SLydia Wang 
5649ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
5650ab6734e7SLydia Wang 	 * mixer widget
5651ab6734e7SLydia Wang 	 */
5652ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
5653ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
5654ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
5655ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
5656ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
5657ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
5658ab6734e7SLydia Wang 
5659ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
5660ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
5661ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
5662ab6734e7SLydia Wang 
5663ab6734e7SLydia Wang 	/* PW9 Output enable */
5664ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
5665ab6734e7SLydia Wang 
5666ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
5667ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
5668ab6734e7SLydia Wang 
5669ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
5670ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5671ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5672ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5673ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5674ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
5675ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5676ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5677ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5678ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5679ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
5680ab6734e7SLydia Wang 
5681ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
5682ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
5683ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
5684ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
5685ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
5686ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
5687ab6734e7SLydia Wang 
5688ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
5689ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
5690ab6734e7SLydia Wang 	{ }
5691ab6734e7SLydia Wang };
5692ab6734e7SLydia Wang 
5693ab6734e7SLydia Wang 
5694ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = {
5695ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
5696ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5697ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
5698ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
5699ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
5700ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5701ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5702ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
5703ab6734e7SLydia Wang 	{ }
5704ab6734e7SLydia Wang };
5705ab6734e7SLydia Wang 
5706ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = {
5707ab6734e7SLydia Wang 	.substreams = 2,
5708ab6734e7SLydia Wang 	.channels_min = 2,
5709ab6734e7SLydia Wang 	.channels_max = 2,
5710ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
5711ab6734e7SLydia Wang 	.ops = {
5712ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
5713ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
5714ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
5715ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5716ab6734e7SLydia Wang 	},
5717ab6734e7SLydia Wang };
5718ab6734e7SLydia Wang 
5719ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = {
5720ab6734e7SLydia Wang 	.substreams = 2,
5721ab6734e7SLydia Wang 	.channels_min = 2,
5722ab6734e7SLydia Wang 	.channels_max = 2,
5723ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
5724ab6734e7SLydia Wang 	.ops = {
5725ab6734e7SLydia Wang 		.open = via_pcm_open_close,
5726ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
5727ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
5728ab6734e7SLydia Wang 		.close = via_pcm_open_close,
5729ab6734e7SLydia Wang 	},
5730ab6734e7SLydia Wang };
5731ab6734e7SLydia Wang 
5732ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = {
5733ab6734e7SLydia Wang 	.substreams = 1,
5734ab6734e7SLydia Wang 	.channels_min = 2,
5735ab6734e7SLydia Wang 	.channels_max = 2,
5736ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
5737ab6734e7SLydia Wang 	.ops = {
5738ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
5739ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
5740ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
5741ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
5742ab6734e7SLydia Wang 	},
5743ab6734e7SLydia Wang };
5744ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */
5745ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec,
5746ab6734e7SLydia Wang 				     const struct auto_pin_cfg *cfg)
5747ab6734e7SLydia Wang {
5748ab6734e7SLydia Wang 	spec->multiout.num_dacs = 1;
5749ab6734e7SLydia Wang 	spec->multiout.dac_nids = spec->private_dac_nids;
5750ab6734e7SLydia Wang 	if (cfg->line_out_pins[0])
5751ab6734e7SLydia Wang 		spec->multiout.dac_nids[0] = 0x8;
5752ab6734e7SLydia Wang 	return 0;
5753ab6734e7SLydia Wang }
5754ab6734e7SLydia Wang 
5755ab6734e7SLydia Wang 
5756ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */
5757ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec,
5758ab6734e7SLydia Wang 					     const struct auto_pin_cfg *cfg)
5759ab6734e7SLydia Wang {
5760ab6734e7SLydia Wang 	int err;
5761ab6734e7SLydia Wang 
5762ab6734e7SLydia Wang 	if (!cfg->line_out_pins[0])
5763ab6734e7SLydia Wang 		return -1;
5764ab6734e7SLydia Wang 
5765ab6734e7SLydia Wang 	/* Line-Out: PortE */
5766ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
57673d83e577STakashi Iwai 			      "Front Playback Volume",
5768ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT));
5769ab6734e7SLydia Wang 	if (err < 0)
5770ab6734e7SLydia Wang 		return err;
5771ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE,
57723d83e577STakashi Iwai 			      "Front Playback Switch",
5773ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT));
5774ab6734e7SLydia Wang 	if (err < 0)
5775ab6734e7SLydia Wang 		return err;
5776ab6734e7SLydia Wang 
5777ab6734e7SLydia Wang 	return 0;
5778ab6734e7SLydia Wang }
5779ab6734e7SLydia Wang 
5780ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin)
5781ab6734e7SLydia Wang {
5782ab6734e7SLydia Wang 	int err;
5783ab6734e7SLydia Wang 
5784ab6734e7SLydia Wang 	if (!pin)
5785ab6734e7SLydia Wang 		return 0;
5786ab6734e7SLydia Wang 
5787ab6734e7SLydia Wang 	spec->multiout.hp_nid = 0x9;
5788ab6734e7SLydia Wang 	spec->hp_independent_mode_index = 1;
5789ab6734e7SLydia Wang 
5790ab6734e7SLydia Wang 
5791ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
5792ab6734e7SLydia Wang 			      "Headphone Playback Volume",
5793ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(
5794ab6734e7SLydia Wang 				      spec->multiout.hp_nid, 3, 0, HDA_OUTPUT));
5795ab6734e7SLydia Wang 	if (err < 0)
5796ab6734e7SLydia Wang 		return err;
5797ab6734e7SLydia Wang 
5798ab6734e7SLydia Wang 	err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
5799ab6734e7SLydia Wang 			      "Headphone Playback Switch",
5800ab6734e7SLydia Wang 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT));
5801ab6734e7SLydia Wang 	if (err < 0)
5802ab6734e7SLydia Wang 		return err;
5803ab6734e7SLydia Wang 
5804ab6734e7SLydia Wang 	create_hp_imux(spec);
5805ab6734e7SLydia Wang 	return 0;
5806ab6734e7SLydia Wang }
5807ab6734e7SLydia Wang 
5808ab6734e7SLydia Wang /* create playback/capture controls for input pins */
580910a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec,
5810ab6734e7SLydia Wang 						const struct auto_pin_cfg *cfg)
5811ab6734e7SLydia Wang {
581210a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
5813ab6734e7SLydia Wang 	struct hda_input_mux *imux = &spec->private_imux[0];
5814f3268512STakashi Iwai 	static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff };
5815f3268512STakashi Iwai 	int err;
5816ab6734e7SLydia Wang 
581710a20af7STakashi Iwai 	err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs,
5818f3268512STakashi Iwai 					       ARRAY_SIZE(pin_idxs));
5819ab6734e7SLydia Wang 	if (err < 0)
5820ab6734e7SLydia Wang 		return err;
5821f3268512STakashi Iwai 
5822ab6734e7SLydia Wang 	/* build volume/mute control of loopback */
58237b315bb4STakashi Iwai 	err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21);
5824ab6734e7SLydia Wang 	if (err < 0)
5825ab6734e7SLydia Wang 		return err;
5826ab6734e7SLydia Wang 
5827ab6734e7SLydia Wang 	/* for digital mic select */
582810a20af7STakashi Iwai 	snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL);
5829ab6734e7SLydia Wang 
5830ab6734e7SLydia Wang 	return 0;
5831ab6734e7SLydia Wang }
5832ab6734e7SLydia Wang 
5833ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
5834ab6734e7SLydia Wang {
5835ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
5836ab6734e7SLydia Wang 	int err;
5837ab6734e7SLydia Wang 
5838ab6734e7SLydia Wang 
5839ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
5840ab6734e7SLydia Wang 	if (err < 0)
5841ab6734e7SLydia Wang 		return err;
5842ab6734e7SLydia Wang 	fill_dig_outs(codec);
5843ab6734e7SLydia Wang 	err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg);
5844ab6734e7SLydia Wang 	if (err < 0)
5845ab6734e7SLydia Wang 		return err;
5846ab6734e7SLydia Wang 
5847ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
5848ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
5849ab6734e7SLydia Wang 
5850ab6734e7SLydia Wang 	err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg);
5851ab6734e7SLydia Wang 	if (err < 0)
5852ab6734e7SLydia Wang 		return err;
5853ab6734e7SLydia Wang 	err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]);
5854ab6734e7SLydia Wang 	if (err < 0)
5855ab6734e7SLydia Wang 		return err;
585610a20af7STakashi Iwai 	err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg);
5857ab6734e7SLydia Wang 	if (err < 0)
5858ab6734e7SLydia Wang 		return err;
5859ab6734e7SLydia Wang 
5860ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
5861ab6734e7SLydia Wang 
5862ab6734e7SLydia Wang 	fill_dig_outs(codec);
5863ab6734e7SLydia Wang 
5864ab6734e7SLydia Wang 	if (spec->kctls.list)
5865ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
5866ab6734e7SLydia Wang 
5867ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
5868ab6734e7SLydia Wang 
5869ab6734e7SLydia Wang 	if (spec->hp_mux)
58703d83e577STakashi Iwai 		via_hp_build(codec);
5871ab6734e7SLydia Wang 
5872ab6734e7SLydia Wang 	return 1;
5873ab6734e7SLydia Wang }
5874ab6734e7SLydia Wang 
5875ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5876ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = {
5877ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
5878ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
5879ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
5880ab6734e7SLydia Wang 	{ } /* end */
5881ab6734e7SLydia Wang };
5882ab6734e7SLydia Wang #endif
5883ab6734e7SLydia Wang 
58843e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
58853e95b9abSLydia Wang {
58863e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
58873e95b9abSLydia Wang 	int imux_is_smixer =
58883e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
58893e95b9abSLydia Wang 	unsigned int parm;
58903e95b9abSLydia Wang 	unsigned int present;
58913e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
58923e95b9abSLydia Wang 	imux_is_smixer =
58933e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
58943e95b9abSLydia Wang 	/* inputs */
58953e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
58963e95b9abSLydia Wang 	parm = AC_PWRST_D3;
58973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
58983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
58993e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
59003e95b9abSLydia Wang 	parm = AC_PWRST_D0;
59013e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
59023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
59033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
59043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
59053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
59063e95b9abSLydia Wang 
59073e95b9abSLydia Wang 	/* outputs */
59083e95b9abSLydia Wang 	/* AOW0 (8h)*/
59093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
59103e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59113e95b9abSLydia Wang 
59123e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
59133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
59153e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
59163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
59173e95b9abSLydia Wang 
59183e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
59193e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
59213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
59223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
59233e95b9abSLydia Wang 	if (spec->hp_independent_mode)
59243e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
59253e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59263e95b9abSLydia Wang 
59273e95b9abSLydia Wang 	/* Internal Speaker */
59283e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
59293e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
59303e95b9abSLydia Wang 
59313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
59333e95b9abSLydia Wang 	if (present) {
59343e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
59353e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
59363e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
59373e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
59383e95b9abSLydia Wang 	} else {
59393e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
59403e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
59423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59433e95b9abSLydia Wang 	}
59443e95b9abSLydia Wang 
59453e95b9abSLydia Wang 
59463e95b9abSLydia Wang 	/* Mono Out */
59473e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
59483e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
59493e95b9abSLydia Wang 
59503e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
59523e95b9abSLydia Wang 	if (present) {
59533e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
59543e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
59553e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
59563e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
59573e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
59583e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
59593e95b9abSLydia Wang 	} else {
59603e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
59613e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59623e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
59633e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59643e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
59653e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
59663e95b9abSLydia Wang 	}
59673e95b9abSLydia Wang 
59683e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
59693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
59703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
59713e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
59723e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
59733e95b9abSLydia Wang 
59743e95b9abSLydia Wang }
5975ab6734e7SLydia Wang 
5976ab6734e7SLydia Wang /* patch for vt1812 */
5977ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
5978ab6734e7SLydia Wang {
5979ab6734e7SLydia Wang 	struct via_spec *spec;
5980ab6734e7SLydia Wang 	int err;
5981ab6734e7SLydia Wang 
5982ab6734e7SLydia Wang 	/* create a codec specific record */
59835b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
5984ab6734e7SLydia Wang 	if (spec == NULL)
5985ab6734e7SLydia Wang 		return -ENOMEM;
5986ab6734e7SLydia Wang 
5987ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
5988ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
5989ab6734e7SLydia Wang 	if (err < 0) {
5990ab6734e7SLydia Wang 		via_free(codec);
5991ab6734e7SLydia Wang 		return err;
5992ab6734e7SLydia Wang 	} else if (!err) {
5993ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5994ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5995ab6734e7SLydia Wang 	}
5996ab6734e7SLydia Wang 
5997ab6734e7SLydia Wang 
5998ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
5999ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
6000ab6734e7SLydia Wang 
6001ab6734e7SLydia Wang 	spec->stream_name_analog = "VT1812 Analog";
6002ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
6003ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
6004ab6734e7SLydia Wang 
6005ab6734e7SLydia Wang 	spec->stream_name_digital = "VT1812 Digital";
6006ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
6007ab6734e7SLydia Wang 
6008ab6734e7SLydia Wang 
6009ab6734e7SLydia Wang 	if (!spec->adc_nids && spec->input_mux) {
6010ab6734e7SLydia Wang 		spec->adc_nids = vt1812_adc_nids;
6011ab6734e7SLydia Wang 		spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids);
6012ab6734e7SLydia Wang 		get_mux_nids(codec);
6013ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
6014ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
6015ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
6016ab6734e7SLydia Wang 		spec->num_mixers++;
6017ab6734e7SLydia Wang 	}
6018ab6734e7SLydia Wang 
6019ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
6020ab6734e7SLydia Wang 
6021ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
60220f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
6023ab6734e7SLydia Wang 
6024ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
6025ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
6026ab6734e7SLydia Wang #endif
6027ab6734e7SLydia Wang 
60283e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
6029ab6734e7SLydia Wang 	return 0;
6030ab6734e7SLydia Wang }
6031ab6734e7SLydia Wang 
6032c577b8a1SJoseph Chan /*
6033c577b8a1SJoseph Chan  * patch entries
6034c577b8a1SJoseph Chan  */
60351289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = {
60363218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
60373218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
60383218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
60393218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
60403218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
6041f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
60423218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
6043f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
60443218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
6045f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
60463218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
6047f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
60483218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
6049f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
60503218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
6051f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
60523218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
6053f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
60543218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
6055f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
60563218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
6057f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
60583218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
6059f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
60603218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
6061f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
60623218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
6063f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
60643218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
6065f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60663218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
6067f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60683218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
6069f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60703218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
6071f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
60723218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
6073d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60743218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
6075d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60763218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
6077d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60783218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
6079d949cac1SHarald Welte 	  .patch = patch_vt1708S},
6080*bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
6081d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60823218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
6083d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60843218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
6085d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60863218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
6087d949cac1SHarald Welte 	  .patch = patch_vt1708S},
60883218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
6089d949cac1SHarald Welte 	  .patch = patch_vt1702},
60903218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
6091d949cac1SHarald Welte 	  .patch = patch_vt1702},
60923218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
6093d949cac1SHarald Welte 	  .patch = patch_vt1702},
60943218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
6095d949cac1SHarald Welte 	  .patch = patch_vt1702},
60963218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
6097d949cac1SHarald Welte 	  .patch = patch_vt1702},
60983218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
6099d949cac1SHarald Welte 	  .patch = patch_vt1702},
61003218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
6101d949cac1SHarald Welte 	  .patch = patch_vt1702},
61023218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
6103d949cac1SHarald Welte 	  .patch = patch_vt1702},
6104eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
6105eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6106eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
6107eb7188caSLydia Wang 	  .patch = patch_vt1718S},
6108bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
6109bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6110bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
6111bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
6112f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
6113f3db423dSLydia Wang 	  .patch = patch_vt1716S},
6114f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
6115f3db423dSLydia Wang 	  .patch = patch_vt1716S},
611625eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
611725eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
6118ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
611936dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
612036dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
6121c577b8a1SJoseph Chan 	{} /* terminator */
6122c577b8a1SJoseph Chan };
61231289e9e8STakashi Iwai 
61241289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
61251289e9e8STakashi Iwai 
61261289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
61271289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
61281289e9e8STakashi Iwai 	.owner = THIS_MODULE,
61291289e9e8STakashi Iwai };
61301289e9e8STakashi Iwai 
61311289e9e8STakashi Iwai MODULE_LICENSE("GPL");
61321289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
61331289e9e8STakashi Iwai 
61341289e9e8STakashi Iwai static int __init patch_via_init(void)
61351289e9e8STakashi Iwai {
61361289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
61371289e9e8STakashi Iwai }
61381289e9e8STakashi Iwai 
61391289e9e8STakashi Iwai static void __exit patch_via_exit(void)
61401289e9e8STakashi Iwai {
61411289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
61421289e9e8STakashi Iwai }
61431289e9e8STakashi Iwai 
61441289e9e8STakashi Iwai module_init(patch_via_init)
61451289e9e8STakashi Iwai module_exit(patch_via_exit)
6146