xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 9af7421091fd37a2f8c35ca8b3a5f78a6f20fa89)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
575b0cb1d8SJaroslav Kysela #define NID_MAPPING		(-1)
585b0cb1d8SJaroslav Kysela 
59c577b8a1SJoseph Chan /* amp values */
60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan /* Pin Widget NID */
64c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
70c577b8a1SJoseph Chan 
71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
75f7278fd0SJosepch Chan 
76f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
80c577b8a1SJoseph Chan 
81d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
83d949cac1SHarald Welte 
84d949cac1SHarald Welte #define VT1702_HP_NID		0x17
85d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
86d949cac1SHarald Welte 
87d7426329SHarald Welte enum VIA_HDA_CODEC {
88d7426329SHarald Welte 	UNKNOWN = -1,
89d7426329SHarald Welte 	VT1708,
90d7426329SHarald Welte 	VT1709_10CH,
91d7426329SHarald Welte 	VT1709_6CH,
92d7426329SHarald Welte 	VT1708B_8CH,
93d7426329SHarald Welte 	VT1708B_4CH,
94d7426329SHarald Welte 	VT1708S,
95518bf3baSLydia Wang 	VT1708BCE,
96d7426329SHarald Welte 	VT1702,
97eb7188caSLydia Wang 	VT1718S,
98f3db423dSLydia Wang 	VT1716S,
9925eaba2fSLydia Wang 	VT2002P,
100ab6734e7SLydia Wang 	VT1812,
10111890956SLydia Wang 	VT1802,
102d7426329SHarald Welte 	CODEC_TYPES,
103d7426329SHarald Welte };
104d7426329SHarald Welte 
10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
10611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
10711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
10811890956SLydia Wang 	 (spec)->codec_type == VT1802)
10911890956SLydia Wang 
1104a79616dSTakashi Iwai struct nid_path {
1114a79616dSTakashi Iwai 	int depth;
1124a79616dSTakashi Iwai 	hda_nid_t path[5];
1134a79616dSTakashi Iwai 	short idx[5];
1144a79616dSTakashi Iwai };
1154a79616dSTakashi Iwai 
1161f2e99feSLydia Wang struct via_spec {
1171f2e99feSLydia Wang 	/* codec parameterization */
11890dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1191f2e99feSLydia Wang 	unsigned int num_mixers;
1201f2e99feSLydia Wang 
12190dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1221f2e99feSLydia Wang 	unsigned int num_iverbs;
1231f2e99feSLydia Wang 
12482673bc8STakashi Iwai 	char stream_name_analog[32];
12590dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12690dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1271f2e99feSLydia Wang 
12882673bc8STakashi Iwai 	char stream_name_digital[32];
12990dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
13090dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1311f2e99feSLydia Wang 
1321f2e99feSLydia Wang 	/* playback */
1331f2e99feSLydia Wang 	struct hda_multi_out multiout;
1341f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1351f2e99feSLydia Wang 
1364a79616dSTakashi Iwai 	struct nid_path out_path[4];
1374a79616dSTakashi Iwai 	struct nid_path hp_path;
1384a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
1394a79616dSTakashi Iwai 
1401f2e99feSLydia Wang 	/* capture */
1411f2e99feSLydia Wang 	unsigned int num_adc_nids;
142a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1431f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
144620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1451f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1461f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1471f2e99feSLydia Wang 
1481f2e99feSLydia Wang 	/* capture source */
1491f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1501f2e99feSLydia Wang 	unsigned int cur_mux[3];
1511f2e99feSLydia Wang 
1521f2e99feSLydia Wang 	/* PCM information */
1531f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1541f2e99feSLydia Wang 
1551f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1561f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1571f2e99feSLydia Wang 	struct snd_array kctls;
1581f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1591f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1601f2e99feSLydia Wang 
1611f2e99feSLydia Wang 	/* HP mode source */
1621f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1631f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1641f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
165f4a7828bSTakashi Iwai 	unsigned int can_smart51;
1661f2e99feSLydia Wang 	unsigned int smart51_enabled;
167f3db423dSLydia Wang 	unsigned int dmic_enabled;
16824088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1691f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1701f2e99feSLydia Wang 
1711f2e99feSLydia Wang 	/* work to check hp jack state */
1721f2e99feSLydia Wang 	struct hda_codec *codec;
1731f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
174e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1751f2e99feSLydia Wang 	int vt1708_hp_present;
1763e95b9abSLydia Wang 
1773e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1783e95b9abSLydia Wang 
1791f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1801f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1811f2e99feSLydia Wang #endif
1821f2e99feSLydia Wang };
1831f2e99feSLydia Wang 
1840341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1855b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1865b0cb1d8SJaroslav Kysela {
1875b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1885b0cb1d8SJaroslav Kysela 
1895b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1905b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1915b0cb1d8SJaroslav Kysela 		return NULL;
1925b0cb1d8SJaroslav Kysela 
1935b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1945b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1950341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1960341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1970341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1980341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1995b0cb1d8SJaroslav Kysela 	return spec;
2005b0cb1d8SJaroslav Kysela }
2015b0cb1d8SJaroslav Kysela 
202744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
203d7426329SHarald Welte {
204744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
205d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
206d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
207d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
208d7426329SHarald Welte 
209d7426329SHarald Welte 	/* get codec type */
210d7426329SHarald Welte 	if (ven_id != 0x1106)
211d7426329SHarald Welte 		codec_type = UNKNOWN;
212d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
213d7426329SHarald Welte 		codec_type = VT1708;
214d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
215d7426329SHarald Welte 		codec_type = VT1709_10CH;
216d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
217d7426329SHarald Welte 		codec_type = VT1709_6CH;
218518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
219d7426329SHarald Welte 		codec_type = VT1708B_8CH;
220518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
221518bf3baSLydia Wang 			codec_type = VT1708BCE;
222518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
223d7426329SHarald Welte 		codec_type = VT1708B_4CH;
224d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
225d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
226d7426329SHarald Welte 		codec_type = VT1708S;
227d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
228d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
229d7426329SHarald Welte 		codec_type = VT1702;
230eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
231eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
232eb7188caSLydia Wang 		codec_type = VT1718S;
233f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
234f3db423dSLydia Wang 		codec_type = VT1716S;
235bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
236bb3c6bfcSLydia Wang 		codec_type = VT1718S;
23725eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
23825eaba2fSLydia Wang 		codec_type = VT2002P;
239ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
240ab6734e7SLydia Wang 		codec_type = VT1812;
24136dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
24236dd5c4aSLydia Wang 		codec_type = VT1708S;
24311890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
24411890956SLydia Wang 		codec_type = VT1802;
245d7426329SHarald Welte 	else
246d7426329SHarald Welte 		codec_type = UNKNOWN;
247d7426329SHarald Welte 	return codec_type;
248d7426329SHarald Welte };
249d7426329SHarald Welte 
250ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
25169e52a80SHarald Welte #define VIA_HP_EVENT		0x01
25269e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
253ec7e7e42SLydia Wang #define VIA_MONO_EVENT		0x03
254ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT	0x04
255ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT	0x05
25669e52a80SHarald Welte 
257c577b8a1SJoseph Chan enum {
258c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
259c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
260f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
26125eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
262c577b8a1SJoseph Chan };
263c577b8a1SJoseph Chan 
264c577b8a1SJoseph Chan enum {
265eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
266c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
267c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
268c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
269c577b8a1SJoseph Chan };
270c577b8a1SJoseph Chan 
271f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2721f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2731f2e99feSLydia Wang 
2741f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2751f2e99feSLydia Wang {
2761f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2771f2e99feSLydia Wang 		return;
2781f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
279e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2801f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2811f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2821f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2831f2e99feSLydia Wang }
2841f2e99feSLydia Wang 
2851f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2861f2e99feSLydia Wang {
2871f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2881f2e99feSLydia Wang 		return;
2891f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2901f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2911f2e99feSLydia Wang 		return;
2921f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
293e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2945b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2951f2e99feSLydia Wang }
296f5271101SLydia Wang 
2973e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2983e95b9abSLydia Wang {
2993e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
3003e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3013e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3023e95b9abSLydia Wang }
30325eaba2fSLydia Wang 
304f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
305f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
306f5271101SLydia Wang {
307f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
308f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
309f5271101SLydia Wang 
3103e95b9abSLydia Wang 	set_widgets_power_state(codec);
311f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
3121f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3131f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3141f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3151f2e99feSLydia Wang 		else
3161f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3171f2e99feSLydia Wang 	}
318f5271101SLydia Wang 	return change;
319f5271101SLydia Wang }
320f5271101SLydia Wang 
321f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
322f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
323f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
324f5271101SLydia Wang 			.name = NULL,					\
325f5271101SLydia Wang 			.index = 0,					\
326f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
327f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
328f5271101SLydia Wang 			.put = analog_input_switch_put,			\
329f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
330f5271101SLydia Wang 
33125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
33225eaba2fSLydia Wang 
33325eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
33425eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
33525eaba2fSLydia Wang {
33625eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33725eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
33825eaba2fSLydia Wang 	int i;
33925eaba2fSLydia Wang 	int change = 0;
34025eaba2fSLydia Wang 
34125eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
34225eaba2fSLydia Wang 	int lmute, rmute;
34325eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
34425eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
34525eaba2fSLydia Wang 		return change;
34625eaba2fSLydia Wang 	}
34725eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
34825eaba2fSLydia Wang 					      ucontrol);
34925eaba2fSLydia Wang 	/* Get mute value */
35025eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
35125eaba2fSLydia Wang 	valp++;
35225eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
35325eaba2fSLydia Wang 
35425eaba2fSLydia Wang 	/* Set hp pins */
35525eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
35625eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
35725eaba2fSLydia Wang 			snd_hda_codec_amp_update(
35825eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
35925eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36025eaba2fSLydia Wang 				lmute);
36125eaba2fSLydia Wang 			snd_hda_codec_amp_update(
36225eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
36325eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36425eaba2fSLydia Wang 				rmute);
36525eaba2fSLydia Wang 		}
36625eaba2fSLydia Wang 	}
36725eaba2fSLydia Wang 
36825eaba2fSLydia Wang 	if (!lmute && !rmute) {
36925eaba2fSLydia Wang 		/* Line Outs */
37025eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
37125eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37225eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
37325eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
37425eaba2fSLydia Wang 		/* Speakers */
37525eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
37625eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37725eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
37825eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
37925eaba2fSLydia Wang 		/* unmute */
38025eaba2fSLydia Wang 		via_hp_bind_automute(codec);
38125eaba2fSLydia Wang 
38225eaba2fSLydia Wang 	} else {
38325eaba2fSLydia Wang 		if (lmute) {
38425eaba2fSLydia Wang 			/* Mute all left channels */
38525eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38625eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38725eaba2fSLydia Wang 					codec,
38825eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
38925eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39025eaba2fSLydia Wang 					lmute);
39125eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
39225eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39325eaba2fSLydia Wang 					codec,
39425eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
39525eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39625eaba2fSLydia Wang 					lmute);
39725eaba2fSLydia Wang 		}
39825eaba2fSLydia Wang 		if (rmute) {
39925eaba2fSLydia Wang 			/* mute all right channels */
40025eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
40125eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40225eaba2fSLydia Wang 					codec,
40325eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
40425eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
40525eaba2fSLydia Wang 					rmute);
40625eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
40725eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40825eaba2fSLydia Wang 					codec,
40925eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
41025eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
41125eaba2fSLydia Wang 					rmute);
41225eaba2fSLydia Wang 		}
41325eaba2fSLydia Wang 	}
41425eaba2fSLydia Wang 	return change;
41525eaba2fSLydia Wang }
41625eaba2fSLydia Wang 
41725eaba2fSLydia Wang #define BIND_PIN_MUTE							\
41825eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
41925eaba2fSLydia Wang 			.name = NULL,					\
42025eaba2fSLydia Wang 			.index = 0,					\
42125eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
42225eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
42325eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
42425eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
42525eaba2fSLydia Wang 
42690dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
427c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
428c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
429f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
43025eaba2fSLydia Wang 	BIND_PIN_MUTE,
431c577b8a1SJoseph Chan };
432c577b8a1SJoseph Chan 
433ab6734e7SLydia Wang 
434c577b8a1SJoseph Chan /* add dynamic controls */
435291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
436291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
437291c9e33STakashi Iwai 				const char *name)
438c577b8a1SJoseph Chan {
439c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
440c577b8a1SJoseph Chan 
441603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
442603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
443c577b8a1SJoseph Chan 	if (!knew)
444291c9e33STakashi Iwai 		return NULL;
445291c9e33STakashi Iwai 	*knew = *tmpl;
446291c9e33STakashi Iwai 	if (!name)
447291c9e33STakashi Iwai 		name = tmpl->name;
448291c9e33STakashi Iwai 	if (name) {
449c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
450c577b8a1SJoseph Chan 		if (!knew->name)
451291c9e33STakashi Iwai 			return NULL;
452291c9e33STakashi Iwai 	}
453291c9e33STakashi Iwai 	return knew;
454291c9e33STakashi Iwai }
455291c9e33STakashi Iwai 
456291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
457291c9e33STakashi Iwai 			     int idx, unsigned long val)
458291c9e33STakashi Iwai {
459291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
460291c9e33STakashi Iwai 
461291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
462291c9e33STakashi Iwai 	if (!knew)
463c577b8a1SJoseph Chan 		return -ENOMEM;
4644d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4655e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
466c577b8a1SJoseph Chan 	knew->private_value = val;
467c577b8a1SJoseph Chan 	return 0;
468c577b8a1SJoseph Chan }
469c577b8a1SJoseph Chan 
4707b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4717b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4727b315bb4STakashi Iwai 
473291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
4745b0cb1d8SJaroslav Kysela 
475603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
476603c4019STakashi Iwai {
477603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
478603c4019STakashi Iwai 
479603c4019STakashi Iwai 	if (spec->kctls.list) {
480603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
481603c4019STakashi Iwai 		int i;
482603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
483603c4019STakashi Iwai 			kfree(kctl[i].name);
484603c4019STakashi Iwai 	}
485603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
486603c4019STakashi Iwai }
487603c4019STakashi Iwai 
488c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4899510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4907b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
491c577b8a1SJoseph Chan {
492c577b8a1SJoseph Chan 	char name[32];
493c577b8a1SJoseph Chan 	int err;
494c577b8a1SJoseph Chan 
495c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4967b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
497c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
498c577b8a1SJoseph Chan 	if (err < 0)
499c577b8a1SJoseph Chan 		return err;
500c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5017b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
502c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
503c577b8a1SJoseph Chan 	if (err < 0)
504c577b8a1SJoseph Chan 		return err;
505c577b8a1SJoseph Chan 	return 0;
506c577b8a1SJoseph Chan }
507c577b8a1SJoseph Chan 
508c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
509c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
510c577b8a1SJoseph Chan 					   int dac_idx)
511c577b8a1SJoseph Chan {
512c577b8a1SJoseph Chan 	/* set as output */
513c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
514c577b8a1SJoseph Chan 			    pin_type);
515c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
516c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
517d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
518d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
519d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
520c577b8a1SJoseph Chan }
521c577b8a1SJoseph Chan 
522c577b8a1SJoseph Chan 
523c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
524c577b8a1SJoseph Chan {
525c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
526c577b8a1SJoseph Chan 	int i;
527c577b8a1SJoseph Chan 
528c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
529c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
530c577b8a1SJoseph Chan 		if (nid)
531c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
532c577b8a1SJoseph Chan 	}
533c577b8a1SJoseph Chan }
534c577b8a1SJoseph Chan 
535c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
536c577b8a1SJoseph Chan {
537c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
538c577b8a1SJoseph Chan 	hda_nid_t pin;
53925eaba2fSLydia Wang 	int i;
540c577b8a1SJoseph Chan 
54125eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
54225eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
543c577b8a1SJoseph Chan 		if (pin) /* connect to front */
544c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
545c577b8a1SJoseph Chan 	}
54625eaba2fSLydia Wang }
547c577b8a1SJoseph Chan 
548f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
54932e0191dSClemens Ladisch 
550c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
551c577b8a1SJoseph Chan {
552c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5537b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
55432e0191dSClemens Ladisch 	unsigned int ctl;
555c577b8a1SJoseph Chan 	int i;
556c577b8a1SJoseph Chan 
5577b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5587b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
559f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
56032e0191dSClemens Ladisch 			ctl = PIN_OUT;
56130649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
56232e0191dSClemens Ladisch 			ctl = PIN_VREF50;
56332e0191dSClemens Ladisch 		else
56432e0191dSClemens Ladisch 			ctl = PIN_IN;
565c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
56632e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
567c577b8a1SJoseph Chan 	}
568c577b8a1SJoseph Chan }
569f5271101SLydia Wang 
570f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
571f5271101SLydia Wang 				unsigned int *affected_parm)
572f5271101SLydia Wang {
573f5271101SLydia Wang 	unsigned parm;
574f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
575f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
576f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
577f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
5781564b287SLydia Wang 	struct via_spec *spec = codec->spec;
57924088a58STakashi Iwai 	unsigned present = 0;
58024088a58STakashi Iwai 
58124088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
58224088a58STakashi Iwai 	if (!no_presence)
58324088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
584f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
5851564b287SLydia Wang 	    || ((no_presence || present)
5861564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
587f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
588f5271101SLydia Wang 		parm = AC_PWRST_D0;
589f5271101SLydia Wang 	} else
590f5271101SLydia Wang 		parm = AC_PWRST_D3;
591f5271101SLydia Wang 
592f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
593f5271101SLydia Wang }
594f5271101SLydia Wang 
59524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
59624088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
59724088a58STakashi Iwai {
59824088a58STakashi Iwai 	static const char * const texts[] = {
59924088a58STakashi Iwai 		"Disabled", "Enabled"
60024088a58STakashi Iwai 	};
60124088a58STakashi Iwai 
60224088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
60324088a58STakashi Iwai 	uinfo->count = 1;
60424088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
60524088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
60624088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
60724088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
60824088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
60924088a58STakashi Iwai 	return 0;
61024088a58STakashi Iwai }
61124088a58STakashi Iwai 
61224088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
61324088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
61424088a58STakashi Iwai {
61524088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
61624088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
61724088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
61824088a58STakashi Iwai 	return 0;
61924088a58STakashi Iwai }
62024088a58STakashi Iwai 
62124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
62224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
62324088a58STakashi Iwai {
62424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
62524088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
62624088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
62724088a58STakashi Iwai 
62824088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
62924088a58STakashi Iwai 		return 0;
63024088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
63124088a58STakashi Iwai 	set_widgets_power_state(codec);
63224088a58STakashi Iwai 	return 1;
63324088a58STakashi Iwai }
63424088a58STakashi Iwai 
63524088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
63624088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
63724088a58STakashi Iwai 	.name = "Dynamic Power-Control",
63824088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
63924088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
64024088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
64124088a58STakashi Iwai };
64224088a58STakashi Iwai 
64324088a58STakashi Iwai 
644c577b8a1SJoseph Chan /*
645c577b8a1SJoseph Chan  * input MUX handling
646c577b8a1SJoseph Chan  */
647c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
648c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
649c577b8a1SJoseph Chan {
650c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
651c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
652c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
653c577b8a1SJoseph Chan }
654c577b8a1SJoseph Chan 
655c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
656c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
657c577b8a1SJoseph Chan {
658c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
659c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
660c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
661c577b8a1SJoseph Chan 
662c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
663c577b8a1SJoseph Chan 	return 0;
664c577b8a1SJoseph Chan }
665c577b8a1SJoseph Chan 
666c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
667c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
668c577b8a1SJoseph Chan {
669c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
670c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
671c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
672bff5fbf5SLydia Wang 	int ret;
673c577b8a1SJoseph Chan 
674337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
675337b9d02STakashi Iwai 		return -EINVAL;
676a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
677a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
678a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
679a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
680a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
681bff5fbf5SLydia Wang 
682bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
683bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
684bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
685a80e6e3cSLydia Wang 	/* update jack power state */
6863e95b9abSLydia Wang 	set_widgets_power_state(codec);
687a80e6e3cSLydia Wang 
688bff5fbf5SLydia Wang 	return ret;
689c577b8a1SJoseph Chan }
690c577b8a1SJoseph Chan 
6910aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6920aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6930aa62aefSHarald Welte {
6940aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6950aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6960aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6970aa62aefSHarald Welte }
6980aa62aefSHarald Welte 
6990aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7000aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7010aa62aefSHarald Welte {
7020aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7035b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
704eb7188caSLydia Wang 	unsigned int pinsel;
705eb7188caSLydia Wang 
706eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
707eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
7080aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
7090aa62aefSHarald Welte 				      0x00);
7100aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
7110aa62aefSHarald Welte 
7120aa62aefSHarald Welte 	return 0;
7130aa62aefSHarald Welte }
7140aa62aefSHarald Welte 
7150713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
7160713efebSLydia Wang {
7170713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
7180713efebSLydia Wang 	if (ctl) {
7190713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7200713efebSLydia Wang 		ctl->vd[0].access |= active
7210713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7220713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
7230713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
7240713efebSLydia Wang 	}
7250713efebSLydia Wang }
7260713efebSLydia Wang 
7275b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
7285b0cb1d8SJaroslav Kysela {
7295b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7305b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7315b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7325b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7335b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
734e87885feSLydia Wang 	case VT2002P:		return 0x19;
735e87885feSLydia Wang 	case VT1802:		return 0x15;
736e87885feSLydia Wang 	case VT1812:		return 0x15;
7375b0cb1d8SJaroslav Kysela 	default:		return 0;
7385b0cb1d8SJaroslav Kysela 	}
7395b0cb1d8SJaroslav Kysela }
7405b0cb1d8SJaroslav Kysela 
741cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
742cdc1784dSLydia Wang {
743cdc1784dSLydia Wang 	/* mute side channel */
744cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
745e87885feSLydia Wang 	unsigned int parm;
7465b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
747cdc1784dSLydia Wang 
748e87885feSLydia Wang 	if (sw3) {
749e87885feSLydia Wang 		if (VT2002P_COMPATIBLE(spec))
750e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
751e87885feSLydia Wang 			       AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
752e87885feSLydia Wang 		else
753e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
754e87885feSLydia Wang 			       AMP_OUT_MUTE : AMP_OUT_UNMUTE;
755e87885feSLydia Wang 		snd_hda_codec_write(codec, sw3, 0,
756e87885feSLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, parm);
757e87885feSLydia Wang 		if (spec->codec_type == VT1812)
758e87885feSLydia Wang 			snd_hda_codec_write(codec, 0x1d, 0,
759e87885feSLydia Wang 					    AC_VERB_SET_AMP_GAIN_MUTE, parm);
760e87885feSLydia Wang 	}
761cdc1784dSLydia Wang 	return 0;
762cdc1784dSLydia Wang }
763cdc1784dSLydia Wang 
7640aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7650aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7660aa62aefSHarald Welte {
7670aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7680aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7695b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7700aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
771cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
772cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
773cdc1784dSLydia Wang 		? 1 : 0;
774ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
775ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
776ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
777ce0e5a9eSLydia Wang 	else
778ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
779ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7800aa62aefSHarald Welte 
781ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
782ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
783ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
784cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
785cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
786cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7870aa62aefSHarald Welte 					   0, 0, 0);
7880aa62aefSHarald Welte 
789cdc1784dSLydia Wang 	update_side_mute_status(codec);
7900713efebSLydia Wang 	/* update HP volume/swtich active state */
7910713efebSLydia Wang 	if (spec->codec_type == VT1708S
792eb7188caSLydia Wang 	    || spec->codec_type == VT1702
793f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
79425eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
79511890956SLydia Wang 	    || VT2002P_COMPATIBLE(spec)) {
7960713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7970713efebSLydia Wang 			     spec->hp_independent_mode);
7980713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
7990713efebSLydia Wang 			     spec->hp_independent_mode);
8000713efebSLydia Wang 	}
801ce0e5a9eSLydia Wang 	/* update jack power state */
8023e95b9abSLydia Wang 	set_widgets_power_state(codec);
8030aa62aefSHarald Welte 	return 0;
8040aa62aefSHarald Welte }
8050aa62aefSHarald Welte 
80690dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = {
8070aa62aefSHarald Welte 	{
8080aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8090aa62aefSHarald Welte 		.name = "Independent HP",
8100aa62aefSHarald Welte 		.info = via_independent_hp_info,
8110aa62aefSHarald Welte 		.get = via_independent_hp_get,
8120aa62aefSHarald Welte 		.put = via_independent_hp_put,
8130aa62aefSHarald Welte 	},
8145b0cb1d8SJaroslav Kysela 	{
8155b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
8165b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
8175b0cb1d8SJaroslav Kysela 	},
8180aa62aefSHarald Welte };
8190aa62aefSHarald Welte 
8203d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
8215b0cb1d8SJaroslav Kysela {
8223d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8235b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8245b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8253d83e577STakashi Iwai 	int nums;
8263d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
8275b0cb1d8SJaroslav Kysela 
8285b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
8295b0cb1d8SJaroslav Kysela 	case VT1718S:
8305b0cb1d8SJaroslav Kysela 		nid = 0x34;
8315b0cb1d8SJaroslav Kysela 		break;
8325b0cb1d8SJaroslav Kysela 	case VT2002P:
83311890956SLydia Wang 	case VT1802:
8345b0cb1d8SJaroslav Kysela 		nid = 0x35;
8355b0cb1d8SJaroslav Kysela 		break;
8365b0cb1d8SJaroslav Kysela 	case VT1812:
8375b0cb1d8SJaroslav Kysela 		nid = 0x3d;
8385b0cb1d8SJaroslav Kysela 		break;
8395b0cb1d8SJaroslav Kysela 	default:
8405b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
8415b0cb1d8SJaroslav Kysela 		break;
8425b0cb1d8SJaroslav Kysela 	}
8435b0cb1d8SJaroslav Kysela 
844ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
845ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
846ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8473d83e577STakashi Iwai 		if (nums <= 1)
8483d83e577STakashi Iwai 			return 0;
849ee3c35c0SLydia Wang 	}
8503d83e577STakashi Iwai 
8513d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8523d83e577STakashi Iwai 	if (knew == NULL)
8533d83e577STakashi Iwai 		return -ENOMEM;
8543d83e577STakashi Iwai 
8555b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8565b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8575b0cb1d8SJaroslav Kysela 
8585b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8595b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8605b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8615b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8625b0cb1d8SJaroslav Kysela 
8635b0cb1d8SJaroslav Kysela 	return 0;
8645b0cb1d8SJaroslav Kysela }
8655b0cb1d8SJaroslav Kysela 
8661564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8671564b287SLydia Wang {
8681564b287SLydia Wang 	int i;
8691564b287SLydia Wang 	struct snd_ctl_elem_id id;
870525566cbSLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
871525566cbSLydia Wang 	struct snd_kcontrol *ctl;
8721564b287SLydia Wang 
8731564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8741564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8751564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8761564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
877525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
878525566cbSLydia Wang 		if (ctl)
879525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
880525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
881525566cbSLydia Wang 					&ctl->id);
8821564b287SLydia Wang 	}
8831564b287SLydia Wang }
8841564b287SLydia Wang 
8851564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8861564b287SLydia Wang {
8871564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8881564b287SLydia Wang 	int start_idx;
8891564b287SLydia Wang 	int end_idx;
8901564b287SLydia Wang 	int i;
8911564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8921564b287SLydia Wang 	switch (spec->codec_type) {
8931564b287SLydia Wang 	case VT1708:
8941564b287SLydia Wang 		start_idx = 2;
8951564b287SLydia Wang 		end_idx = 4;
8961564b287SLydia Wang 		break;
8971564b287SLydia Wang 	case VT1709_10CH:
8981564b287SLydia Wang 	case VT1709_6CH:
8991564b287SLydia Wang 		start_idx = 2;
9001564b287SLydia Wang 		end_idx = 4;
9011564b287SLydia Wang 		break;
9021564b287SLydia Wang 	case VT1708B_8CH:
9031564b287SLydia Wang 	case VT1708B_4CH:
9041564b287SLydia Wang 	case VT1708S:
905f3db423dSLydia Wang 	case VT1716S:
9061564b287SLydia Wang 		start_idx = 2;
9071564b287SLydia Wang 		end_idx = 4;
9081564b287SLydia Wang 		break;
909ab657e0cSLydia Wang 	case VT1718S:
910ab657e0cSLydia Wang 		start_idx = 1;
911ab657e0cSLydia Wang 		end_idx = 3;
912ab657e0cSLydia Wang 		break;
9131564b287SLydia Wang 	default:
9141564b287SLydia Wang 		return;
9151564b287SLydia Wang 	}
9161564b287SLydia Wang 	/* check AA path's mute status */
9171564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
9181564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
919620e2b28STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i,
9201564b287SLydia Wang 					 HDA_AMP_MUTE, val);
9211564b287SLydia Wang 	}
9221564b287SLydia Wang }
923f4a7828bSTakashi Iwai 
924f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
9251564b287SLydia Wang {
926f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
9277b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9287b315bb4STakashi Iwai 	int i;
9297b315bb4STakashi Iwai 
9307b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
931f4a7828bSTakashi Iwai 		unsigned int defcfg;
932f4a7828bSTakashi Iwai 		if (pin != cfg->inputs[i].pin)
933f4a7828bSTakashi Iwai 			continue;
934f4a7828bSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
935f4a7828bSTakashi Iwai 			return false;
936f4a7828bSTakashi Iwai 		defcfg = snd_hda_codec_get_pincfg(codec, pin);
937f4a7828bSTakashi Iwai 		if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
938f4a7828bSTakashi Iwai 			return false;
939f4a7828bSTakashi Iwai 		return true;
9401564b287SLydia Wang 	}
941f4a7828bSTakashi Iwai 	return false;
9421564b287SLydia Wang }
9431564b287SLydia Wang 
9441564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
9451564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
9461564b287SLydia Wang {
9471564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9481564b287SLydia Wang 	uinfo->count = 1;
9491564b287SLydia Wang 	uinfo->value.integer.min = 0;
9501564b287SLydia Wang 	uinfo->value.integer.max = 1;
9511564b287SLydia Wang 	return 0;
9521564b287SLydia Wang }
9531564b287SLydia Wang 
9541564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9551564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9561564b287SLydia Wang {
9571564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9581564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9597b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9601564b287SLydia Wang 	int on = 1;
9611564b287SLydia Wang 	int i;
9621564b287SLydia Wang 
9637b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9647b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
965f4a7828bSTakashi Iwai 		unsigned int ctl;
96686e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9677b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9681564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
969f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, nid))
970f4a7828bSTakashi Iwai 			continue;
971f4a7828bSTakashi Iwai 		ctl = snd_hda_codec_read(codec, nid, 0,
972f4a7828bSTakashi Iwai 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9737b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9741564b287SLydia Wang 			on = 0;
9751564b287SLydia Wang 	}
9761564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9771564b287SLydia Wang 	return 0;
9781564b287SLydia Wang }
9791564b287SLydia Wang 
9801564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9811564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9821564b287SLydia Wang {
9831564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9841564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9857b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9861564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9871564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9881564b287SLydia Wang 	int i;
9891564b287SLydia Wang 
9907b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9917b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9927b315bb4STakashi Iwai 		unsigned int parm;
9937b315bb4STakashi Iwai 
99486e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9957b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9961564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
997f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, nid))
998f4a7828bSTakashi Iwai 			continue;
9997b315bb4STakashi Iwai 
10007b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
10011564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
10021564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
10031564b287SLydia Wang 		parm |= out_in;
10041564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
10051564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
10061564b287SLydia Wang 				    parm);
10071564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
10081564b287SLydia Wang 			mute_aa_path(codec, 1);
10091564b287SLydia Wang 			notify_aa_path_ctls(codec);
10101564b287SLydia Wang 		}
10111564b287SLydia Wang 	}
10121564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
10133e95b9abSLydia Wang 	set_widgets_power_state(codec);
10141564b287SLydia Wang 	return 1;
10151564b287SLydia Wang }
10161564b287SLydia Wang 
10175f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
10181564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10191564b287SLydia Wang 	.name = "Smart 5.1",
10201564b287SLydia Wang 	.count = 1,
10211564b287SLydia Wang 	.info = via_smart51_info,
10221564b287SLydia Wang 	.get = via_smart51_get,
10231564b287SLydia Wang 	.put = via_smart51_put,
10241564b287SLydia Wang };
10251564b287SLydia Wang 
1026f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
10275b0cb1d8SJaroslav Kysela {
1028f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
10295b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10307b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10315b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10325b0cb1d8SJaroslav Kysela 	int i;
10335b0cb1d8SJaroslav Kysela 
1034f4a7828bSTakashi Iwai 	if (!spec->can_smart51)
1035cb34c207SLydia Wang 		return 0;
1036cb34c207SLydia Wang 
10375f4b36d6STakashi Iwai 	knew = via_clone_control(spec, &via_smart51_mixer);
10385b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10395b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10405b0cb1d8SJaroslav Kysela 
10417b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10427b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
1043f4a7828bSTakashi Iwai 		if (is_smart51_pins(codec, nid)) {
10445f4b36d6STakashi Iwai 			knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
10457b315bb4STakashi Iwai 			break;
10465b0cb1d8SJaroslav Kysela 		}
10475b0cb1d8SJaroslav Kysela 	}
10485b0cb1d8SJaroslav Kysela 
10495b0cb1d8SJaroslav Kysela 	return 0;
10505b0cb1d8SJaroslav Kysela }
10515b0cb1d8SJaroslav Kysela 
1052c577b8a1SJoseph Chan /* capture mixer elements */
105390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = {
1054c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1055c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1056c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1057c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1058c577b8a1SJoseph Chan 	{
1059c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1060c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1061c577b8a1SJoseph Chan 		 * So call somewhat different..
1062c577b8a1SJoseph Chan 		 */
1063c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1064c577b8a1SJoseph Chan 		.name = "Input Source",
1065c577b8a1SJoseph Chan 		.count = 1,
1066c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1067c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1068c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1069c577b8a1SJoseph Chan 	},
1070c577b8a1SJoseph Chan 	{ } /* end */
1071c577b8a1SJoseph Chan };
1072f5271101SLydia Wang 
1073f5271101SLydia Wang /* check AA path's mute statue */
1074f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1075f5271101SLydia Wang {
1076f5271101SLydia Wang 	int mute = 1;
1077f5271101SLydia Wang 	int start_idx;
1078f5271101SLydia Wang 	int end_idx;
1079f5271101SLydia Wang 	int i;
1080f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1081f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1082f5271101SLydia Wang 	switch (spec->codec_type) {
1083f5271101SLydia Wang 	case VT1708B_8CH:
1084f5271101SLydia Wang 	case VT1708B_4CH:
1085f5271101SLydia Wang 	case VT1708S:
1086f3db423dSLydia Wang 	case VT1716S:
1087f5271101SLydia Wang 		start_idx = 2;
1088f5271101SLydia Wang 		end_idx = 4;
1089f5271101SLydia Wang 		break;
1090f5271101SLydia Wang 	case VT1702:
1091f5271101SLydia Wang 		start_idx = 1;
1092f5271101SLydia Wang 		end_idx = 3;
1093f5271101SLydia Wang 		break;
1094eb7188caSLydia Wang 	case VT1718S:
1095eb7188caSLydia Wang 		start_idx = 1;
1096eb7188caSLydia Wang 		end_idx = 3;
1097eb7188caSLydia Wang 		break;
109825eaba2fSLydia Wang 	case VT2002P:
1099ab6734e7SLydia Wang 	case VT1812:
110011890956SLydia Wang 	case VT1802:
110125eaba2fSLydia Wang 		start_idx = 0;
110225eaba2fSLydia Wang 		end_idx = 2;
110325eaba2fSLydia Wang 		break;
1104f5271101SLydia Wang 	default:
1105f5271101SLydia Wang 		return 0;
1106f5271101SLydia Wang 	}
1107f5271101SLydia Wang 	/* check AA path's mute status */
1108f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1109f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1110620e2b28STakashi Iwai 			codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1111f5271101SLydia Wang 		int shift = 8 * (i % 4);
1112f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1113f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1114f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1115f5271101SLydia Wang 			/* check mute status while the pin is connected */
1116620e2b28STakashi Iwai 			int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
1117f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1118620e2b28STakashi Iwai 			int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
1119f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1120f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1121f5271101SLydia Wang 				mute = 0;
1122f5271101SLydia Wang 				break;
1123f5271101SLydia Wang 			}
1124f5271101SLydia Wang 		}
1125f5271101SLydia Wang 	}
1126f5271101SLydia Wang 	return mute;
1127f5271101SLydia Wang }
1128f5271101SLydia Wang 
1129f5271101SLydia Wang /* enter/exit analog low-current mode */
1130f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1131f5271101SLydia Wang {
1132f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1133f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1134f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1135f5271101SLydia Wang 	unsigned int verb = 0;
1136f5271101SLydia Wang 	unsigned int parm = 0;
1137f5271101SLydia Wang 
1138f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1139f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1140f5271101SLydia Wang 	else {
1141f5271101SLydia Wang 		enable = enable && stream_idle;
1142f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1143f5271101SLydia Wang 	}
1144f5271101SLydia Wang 
1145f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1146f5271101SLydia Wang 	switch (spec->codec_type) {
1147f5271101SLydia Wang 	case VT1708B_8CH:
1148f5271101SLydia Wang 	case VT1708B_4CH:
1149f5271101SLydia Wang 		verb = 0xf70;
1150f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1151f5271101SLydia Wang 		break;
1152f5271101SLydia Wang 	case VT1708S:
1153eb7188caSLydia Wang 	case VT1718S:
1154f3db423dSLydia Wang 	case VT1716S:
1155f5271101SLydia Wang 		verb = 0xf73;
1156f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1157f5271101SLydia Wang 		break;
1158f5271101SLydia Wang 	case VT1702:
1159f5271101SLydia Wang 		verb = 0xf73;
1160f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1161f5271101SLydia Wang 		break;
116225eaba2fSLydia Wang 	case VT2002P:
1163ab6734e7SLydia Wang 	case VT1812:
116411890956SLydia Wang 	case VT1802:
116525eaba2fSLydia Wang 		verb = 0xf93;
116625eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
116725eaba2fSLydia Wang 		break;
1168f5271101SLydia Wang 	default:
1169f5271101SLydia Wang 		return;		/* other codecs are not supported */
1170f5271101SLydia Wang 	}
1171f5271101SLydia Wang 	/* send verb */
1172f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1173f5271101SLydia Wang }
1174f5271101SLydia Wang 
1175c577b8a1SJoseph Chan /*
1176c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1177c577b8a1SJoseph Chan  */
117890dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = {
1179c577b8a1SJoseph Chan 	/*
1180c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1181c577b8a1SJoseph Chan 	 */
1182c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1183c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1184c577b8a1SJoseph Chan 
1185c577b8a1SJoseph Chan 
1186f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1187c577b8a1SJoseph Chan 	 * mixer widget
1188c577b8a1SJoseph Chan 	 */
1189c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1190f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1191f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1192f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1193f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1194f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1195c577b8a1SJoseph Chan 
1196c577b8a1SJoseph Chan 	/*
1197c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1198c577b8a1SJoseph Chan 	 */
1199c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1200c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1201c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1202c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1203c577b8a1SJoseph Chan 
1204bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1205bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1206c577b8a1SJoseph Chan 	/* PW9 Output enable */
1207c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1208aa266fccSLydia Wang 	/* power down jack detect function */
1209aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1210f7278fd0SJosepch Chan 	{ }
1211c577b8a1SJoseph Chan };
1212c577b8a1SJoseph Chan 
1213c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1214c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1215c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1216c577b8a1SJoseph Chan {
1217c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
121817314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
121917314379SLydia Wang 		&& substream->ref_count == 0;
122017314379SLydia Wang 	analog_low_current_mode(codec, idle);
12219a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
12229a08160bSTakashi Iwai 					     hinfo);
1223c577b8a1SJoseph Chan }
1224c577b8a1SJoseph Chan 
1225*9af74210STakashi Iwai static int via_playback_pcm_close(struct hda_pcm_stream *hinfo,
1226*9af74210STakashi Iwai 				  struct hda_codec *codec,
1227*9af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
1228*9af74210STakashi Iwai {
1229*9af74210STakashi Iwai 	int idle = substream->pstr->substream_opened == 1
1230*9af74210STakashi Iwai 		&& substream->ref_count == 0;
1231*9af74210STakashi Iwai 
1232*9af74210STakashi Iwai 	analog_low_current_mode(codec, idle);
1233*9af74210STakashi Iwai 	return 0;
1234*9af74210STakashi Iwai }
1235*9af74210STakashi Iwai 
12360aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
12370aa62aefSHarald Welte 				      unsigned int stream_tag,
12380aa62aefSHarald Welte 				      unsigned int format,
12390aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
12400aa62aefSHarald Welte {
12410aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12420aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1243dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
12440aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12450aa62aefSHarald Welte 	int i;
12467c935976SStephen Warren 	struct hda_spdif_out *spdif =
12477c935976SStephen Warren 		snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid);
12480aa62aefSHarald Welte 
12490aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12500aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12510aa62aefSHarald Welte 		if (chs == 2 &&
12520aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12530aa62aefSHarald Welte 						format) &&
12547c935976SStephen Warren 		    !(spdif->status & IEC958_AES0_NONAUDIO)) {
12550aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12560aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12570aa62aefSHarald Welte 			 * be updated */
12587c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12590aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12600aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12617c935976SStephen Warren 						    spdif->ctls &
12620aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12630aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12640aa62aefSHarald Welte 						   stream_tag, 0, format);
12650aa62aefSHarald Welte 			/* turn on again (if needed) */
12667c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12670aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12680aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12697c935976SStephen Warren 						    spdif->ctls & 0xff);
12700aa62aefSHarald Welte 		} else {
12710aa62aefSHarald Welte 			mout->dig_out_used = 0;
12720aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12730aa62aefSHarald Welte 						   0, 0, 0);
12740aa62aefSHarald Welte 		}
12750aa62aefSHarald Welte 	}
12760aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12770aa62aefSHarald Welte 
12780aa62aefSHarald Welte 	/* front */
12790aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12800aa62aefSHarald Welte 				   0, format);
12810aa62aefSHarald Welte 
1282eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1283eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12840aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12850aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12860aa62aefSHarald Welte 					   0, format);
12870aa62aefSHarald Welte 
12880aa62aefSHarald Welte 	/* extra outputs copied from front */
12890aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12900aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12910aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12920aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12930aa62aefSHarald Welte 						   stream_tag, 0, format);
12940aa62aefSHarald Welte 
12950aa62aefSHarald Welte 	/* surrounds */
12960aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12970aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12980aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12990aa62aefSHarald Welte 						   i * 2, format);
13000aa62aefSHarald Welte 		else /* copy front */
13010aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
13020aa62aefSHarald Welte 						   0, format);
13030aa62aefSHarald Welte 	}
13040aa62aefSHarald Welte }
13050aa62aefSHarald Welte 
13060aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
13070aa62aefSHarald Welte 					  struct hda_codec *codec,
13080aa62aefSHarald Welte 					  unsigned int stream_tag,
13090aa62aefSHarald Welte 					  unsigned int format,
13100aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
13110aa62aefSHarald Welte {
13120aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13130aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1314dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13150aa62aefSHarald Welte 
13160aa62aefSHarald Welte 	if (substream->number == 0)
13170aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
13180aa62aefSHarald Welte 					  substream);
13190aa62aefSHarald Welte 	else {
13200aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13210aa62aefSHarald Welte 		    spec->hp_independent_mode)
13220aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13230aa62aefSHarald Welte 						   stream_tag, 0, format);
13240aa62aefSHarald Welte 	}
13251f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
13260aa62aefSHarald Welte 	return 0;
13270aa62aefSHarald Welte }
13280aa62aefSHarald Welte 
13290aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
13300aa62aefSHarald Welte 				    struct hda_codec *codec,
13310aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
13320aa62aefSHarald Welte {
13330aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13340aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1335dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13360aa62aefSHarald Welte 	int i;
13370aa62aefSHarald Welte 
13380aa62aefSHarald Welte 	if (substream->number == 0) {
13390aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
13400aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
13410aa62aefSHarald Welte 
13420aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
13430aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13440aa62aefSHarald Welte 						   0, 0, 0);
13450aa62aefSHarald Welte 
13460aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13470aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
13480aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
13490aa62aefSHarald Welte 							mout->extra_out_nid[i],
13500aa62aefSHarald Welte 							0, 0, 0);
13510aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
13520aa62aefSHarald Welte 		if (mout->dig_out_nid &&
13530aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13540aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13550aa62aefSHarald Welte 						   0, 0, 0);
13560aa62aefSHarald Welte 			mout->dig_out_used = 0;
13570aa62aefSHarald Welte 		}
13580aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
13590aa62aefSHarald Welte 	} else {
13600aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13610aa62aefSHarald Welte 		    spec->hp_independent_mode)
13620aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13630aa62aefSHarald Welte 						   0, 0, 0);
13640aa62aefSHarald Welte 	}
13651f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13660aa62aefSHarald Welte 	return 0;
13670aa62aefSHarald Welte }
13680aa62aefSHarald Welte 
1369c577b8a1SJoseph Chan /*
1370c577b8a1SJoseph Chan  * Digital out
1371c577b8a1SJoseph Chan  */
1372c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1373c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1374c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1375c577b8a1SJoseph Chan {
1376c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1377c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1378c577b8a1SJoseph Chan }
1379c577b8a1SJoseph Chan 
1380c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1381c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1382c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1383c577b8a1SJoseph Chan {
1384c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1385c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1386c577b8a1SJoseph Chan }
1387c577b8a1SJoseph Chan 
13885691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
138998aa34c0SHarald Welte 					struct hda_codec *codec,
139098aa34c0SHarald Welte 					unsigned int stream_tag,
139198aa34c0SHarald Welte 					unsigned int format,
139298aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
139398aa34c0SHarald Welte {
139498aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13959da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13969da29271STakashi Iwai 					     stream_tag, format, substream);
13979da29271STakashi Iwai }
13985691ec7fSHarald Welte 
13999da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
14009da29271STakashi Iwai 					struct hda_codec *codec,
14019da29271STakashi Iwai 					struct snd_pcm_substream *substream)
14029da29271STakashi Iwai {
14039da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
14049da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
140598aa34c0SHarald Welte 	return 0;
140698aa34c0SHarald Welte }
140798aa34c0SHarald Welte 
1408c577b8a1SJoseph Chan /*
1409c577b8a1SJoseph Chan  * Analog capture
1410c577b8a1SJoseph Chan  */
1411c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1412c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1413c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1414c577b8a1SJoseph Chan 				   unsigned int format,
1415c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1416c577b8a1SJoseph Chan {
1417c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1418c577b8a1SJoseph Chan 
1419c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1420c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1421c577b8a1SJoseph Chan 	return 0;
1422c577b8a1SJoseph Chan }
1423c577b8a1SJoseph Chan 
1424c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1425c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1426c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1427c577b8a1SJoseph Chan {
1428c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1429888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1430c577b8a1SJoseph Chan 	return 0;
1431c577b8a1SJoseph Chan }
1432c577b8a1SJoseph Chan 
1433*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
1434*9af74210STakashi Iwai 	.substreams = 2, /* will be changed in via_build_pcms() */
1435c577b8a1SJoseph Chan 	.channels_min = 2,
1436c577b8a1SJoseph Chan 	.channels_max = 8,
1437*9af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1438c577b8a1SJoseph Chan 	.ops = {
1439c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
1440*9af74210STakashi Iwai 		.close = via_playback_pcm_close,
14410aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
14420aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1443c577b8a1SJoseph Chan 	},
1444c577b8a1SJoseph Chan };
1445c577b8a1SJoseph Chan 
144690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1447*9af74210STakashi Iwai 	.substreams = 2, /* will be changed in via_build_pcms() */
1448bc9b5623STakashi Iwai 	.channels_min = 2,
1449bc9b5623STakashi Iwai 	.channels_max = 8,
1450*9af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1451bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1452bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1453bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1454bc9b5623STakashi Iwai 	 */
1455bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1456bc9b5623STakashi Iwai 	.ops = {
1457bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1458*9af74210STakashi Iwai 		.close = via_playback_pcm_close,
1459c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1460c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1461bc9b5623STakashi Iwai 	},
1462bc9b5623STakashi Iwai };
1463bc9b5623STakashi Iwai 
1464*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
1465*9af74210STakashi Iwai 	.substreams = 2, /* will be changed in via_build_pcms() */
1466c577b8a1SJoseph Chan 	.channels_min = 2,
1467c577b8a1SJoseph Chan 	.channels_max = 2,
1468*9af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1469c577b8a1SJoseph Chan 	.ops = {
1470*9af74210STakashi Iwai 		.open = via_playback_pcm_open,
1471*9af74210STakashi Iwai 		.close = via_playback_pcm_close,
1472c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1473c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1474c577b8a1SJoseph Chan 	},
1475c577b8a1SJoseph Chan };
1476c577b8a1SJoseph Chan 
1477*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1478c577b8a1SJoseph Chan 	.substreams = 1,
1479c577b8a1SJoseph Chan 	.channels_min = 2,
1480c577b8a1SJoseph Chan 	.channels_max = 2,
1481c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1482c577b8a1SJoseph Chan 	.ops = {
1483c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14846b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14859da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14869da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1487c577b8a1SJoseph Chan 	},
1488c577b8a1SJoseph Chan };
1489c577b8a1SJoseph Chan 
1490*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1491c577b8a1SJoseph Chan 	.substreams = 1,
1492c577b8a1SJoseph Chan 	.channels_min = 2,
1493c577b8a1SJoseph Chan 	.channels_max = 2,
1494c577b8a1SJoseph Chan };
1495c577b8a1SJoseph Chan 
1496c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1497c577b8a1SJoseph Chan {
1498c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14995b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
150090dd48a1STakashi Iwai 	const struct snd_kcontrol_new *knew;
15015b0cb1d8SJaroslav Kysela 	int err, i;
1502c577b8a1SJoseph Chan 
150324088a58STakashi Iwai 	if (spec->set_widgets_power_state)
150424088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
150524088a58STakashi Iwai 			return -ENOMEM;
150624088a58STakashi Iwai 
1507c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1508c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1509c577b8a1SJoseph Chan 		if (err < 0)
1510c577b8a1SJoseph Chan 			return err;
1511c577b8a1SJoseph Chan 	}
1512c577b8a1SJoseph Chan 
1513c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1514c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
151574b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1516c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1517c577b8a1SJoseph Chan 		if (err < 0)
1518c577b8a1SJoseph Chan 			return err;
15199a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
15209a08160bSTakashi Iwai 						    &spec->multiout);
15219a08160bSTakashi Iwai 		if (err < 0)
15229a08160bSTakashi Iwai 			return err;
15239a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1524c577b8a1SJoseph Chan 	}
1525c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1526c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1527c577b8a1SJoseph Chan 		if (err < 0)
1528c577b8a1SJoseph Chan 			return err;
1529c577b8a1SJoseph Chan 	}
153017314379SLydia Wang 
15315b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15325b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15335b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
153421949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15355b0cb1d8SJaroslav Kysela 		if (err < 0)
15365b0cb1d8SJaroslav Kysela 			return err;
15375b0cb1d8SJaroslav Kysela 	}
15385b0cb1d8SJaroslav Kysela 
15395b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
15405b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
15415b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
15425b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
15435b0cb1d8SJaroslav Kysela 				continue;
15445b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
15455b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
15465b0cb1d8SJaroslav Kysela 				continue;
15475b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
15485b0cb1d8SJaroslav Kysela 					      knew->subdevice);
15495b0cb1d8SJaroslav Kysela 		}
15505b0cb1d8SJaroslav Kysela 	}
15515b0cb1d8SJaroslav Kysela 
155217314379SLydia Wang 	/* init power states */
15533e95b9abSLydia Wang 	set_widgets_power_state(codec);
155417314379SLydia Wang 	analog_low_current_mode(codec, 1);
155517314379SLydia Wang 
1556603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1557c577b8a1SJoseph Chan 	return 0;
1558c577b8a1SJoseph Chan }
1559c577b8a1SJoseph Chan 
1560c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1561c577b8a1SJoseph Chan {
1562c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1563c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1564c577b8a1SJoseph Chan 
1565c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1566c577b8a1SJoseph Chan 	codec->pcm_info = info;
1567c577b8a1SJoseph Chan 
156882673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
156982673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1570c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1571*9af74210STakashi Iwai 
1572*9af74210STakashi Iwai 	if (!spec->stream_analog_playback)
1573*9af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1574377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1575*9af74210STakashi Iwai 		*spec->stream_analog_playback;
1576377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1577377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1578c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1579c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1580*9af74210STakashi Iwai 	if (!spec->multiout.hp_nid)
1581*9af74210STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams = 1;
1582*9af74210STakashi Iwai 
1583*9af74210STakashi Iwai 	if (!spec->stream_analog_capture)
1584*9af74210STakashi Iwai 		spec->stream_analog_capture = &via_pcm_analog_capture;
1585*9af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1586*9af74210STakashi Iwai 		*spec->stream_analog_capture;
1587*9af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1588*9af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1589*9af74210STakashi Iwai 		spec->num_adc_nids;
1590c577b8a1SJoseph Chan 
1591c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1592c577b8a1SJoseph Chan 		codec->num_pcms++;
1593c577b8a1SJoseph Chan 		info++;
159482673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
159582673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
159682673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1597c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
15987ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1599c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1600*9af74210STakashi Iwai 			if (!spec->stream_digital_playback)
1601*9af74210STakashi Iwai 				spec->stream_digital_playback =
1602*9af74210STakashi Iwai 					&via_pcm_digital_playback;
1603c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1604*9af74210STakashi Iwai 				*spec->stream_digital_playback;
1605c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1606c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1607c577b8a1SJoseph Chan 		}
1608c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1609*9af74210STakashi Iwai 			if (!spec->stream_digital_capture)
1610*9af74210STakashi Iwai 				spec->stream_digital_capture =
1611*9af74210STakashi Iwai 					&via_pcm_digital_capture;
1612c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1613*9af74210STakashi Iwai 				*spec->stream_digital_capture;
1614c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1615c577b8a1SJoseph Chan 				spec->dig_in_nid;
1616c577b8a1SJoseph Chan 		}
1617c577b8a1SJoseph Chan 	}
1618c577b8a1SJoseph Chan 
1619c577b8a1SJoseph Chan 	return 0;
1620c577b8a1SJoseph Chan }
1621c577b8a1SJoseph Chan 
1622c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1623c577b8a1SJoseph Chan {
1624c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1625c577b8a1SJoseph Chan 
1626c577b8a1SJoseph Chan 	if (!spec)
1627c577b8a1SJoseph Chan 		return;
1628c577b8a1SJoseph Chan 
1629603c4019STakashi Iwai 	via_free_kctls(codec);
16301f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1631c577b8a1SJoseph Chan 	kfree(codec->spec);
1632c577b8a1SJoseph Chan }
1633c577b8a1SJoseph Chan 
163464be285bSTakashi Iwai /* mute/unmute outputs */
163564be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
163664be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
163764be285bSTakashi Iwai {
163864be285bSTakashi Iwai 	int i;
163964be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
164064be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
164164be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
164264be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
164364be285bSTakashi Iwai }
164464be285bSTakashi Iwai 
164569e52a80SHarald Welte /* mute internal speaker if HP is plugged */
164669e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
164769e52a80SHarald Welte {
1648dcf34c8cSLydia Wang 	unsigned int present = 0;
164969e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
165069e52a80SHarald Welte 
1651d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1652dcf34c8cSLydia Wang 
165364be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
165464be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
165564be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
165664be285bSTakashi Iwai 				    present);
165769e52a80SHarald Welte }
165869e52a80SHarald Welte 
1659f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1660f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1661f3db423dSLydia Wang {
1662f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1663f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1664f3db423dSLydia Wang 
1665f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1666f3db423dSLydia Wang 		return;
1667f3db423dSLydia Wang 
1668d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1669d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1670f3db423dSLydia Wang 
1671f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1672f3db423dSLydia Wang 	if (lineout_present) {
16733e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
16743e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16753e0693e2STakashi Iwai 				    lineout_present ? 0 : PIN_OUT);
1676f3db423dSLydia Wang 		return;
1677f3db423dSLydia Wang 	}
1678f3db423dSLydia Wang 
1679d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1680f3db423dSLydia Wang 
1681f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
16823e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
16833e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16843e0693e2STakashi Iwai 				    hp_present ? 0 : PIN_OUT);
1685f3db423dSLydia Wang }
1686f3db423dSLydia Wang 
168769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
168869e52a80SHarald Welte {
168969e52a80SHarald Welte 	unsigned int gpio_data;
169069e52a80SHarald Welte 	unsigned int vol_counter;
169169e52a80SHarald Welte 	unsigned int vol;
169269e52a80SHarald Welte 	unsigned int master_vol;
169369e52a80SHarald Welte 
169469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
169569e52a80SHarald Welte 
169669e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
169769e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
169869e52a80SHarald Welte 
169969e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
170069e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
170169e52a80SHarald Welte 
170269e52a80SHarald Welte 	vol = vol_counter & 0x1F;
170369e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
170469e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
170569e52a80SHarald Welte 					AC_AMP_GET_INPUT);
170669e52a80SHarald Welte 
170769e52a80SHarald Welte 	if (gpio_data == 0x02) {
170869e52a80SHarald Welte 		/* unmute line out */
17093e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
17103e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17113e0693e2STakashi Iwai 				    PIN_OUT);
171269e52a80SHarald Welte 		if (vol_counter & 0x20) {
171369e52a80SHarald Welte 			/* decrease volume */
171469e52a80SHarald Welte 			if (vol > master_vol)
171569e52a80SHarald Welte 				vol = master_vol;
171669e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
171769e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
171869e52a80SHarald Welte 						 master_vol-vol);
171969e52a80SHarald Welte 		} else {
172069e52a80SHarald Welte 			/* increase volume */
172169e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
172269e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
172369e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
172469e52a80SHarald Welte 					  (master_vol+vol));
172569e52a80SHarald Welte 		}
172669e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
172769e52a80SHarald Welte 		/* mute line out */
17283e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
17293e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17303e0693e2STakashi Iwai 				    0);
173169e52a80SHarald Welte 	}
173269e52a80SHarald Welte }
173369e52a80SHarald Welte 
173425eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
173525eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
173625eaba2fSLydia Wang {
173725eaba2fSLydia Wang 	unsigned int hp_present;
173825eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
173925eaba2fSLydia Wang 
174027439ce7SLydia Wang 	if (!VT2002P_COMPATIBLE(spec))
174125eaba2fSLydia Wang 		return;
174225eaba2fSLydia Wang 
1743d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
174425eaba2fSLydia Wang 
174564be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
174664be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.speaker_outs,
174764be285bSTakashi Iwai 				    spec->autocfg.speaker_pins,
174864be285bSTakashi Iwai 				    hp_present);
174925eaba2fSLydia Wang }
175025eaba2fSLydia Wang 
175125eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
175225eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
175325eaba2fSLydia Wang {
175464be285bSTakashi Iwai 	int present;
175525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
175625eaba2fSLydia Wang 
175725eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
175825eaba2fSLydia Wang 		return;
175925eaba2fSLydia Wang 
176064be285bSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
176164be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
176264be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
176364be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
176464be285bSTakashi Iwai 				    present);
176525eaba2fSLydia Wang 
176664be285bSTakashi Iwai 	if (!present)
176764be285bSTakashi Iwai 		present = snd_hda_jack_detect(codec,
176864be285bSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
176925eaba2fSLydia Wang 
177025eaba2fSLydia Wang 	/* Speakers */
177164be285bSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
177264be285bSTakashi Iwai 			    spec->autocfg.speaker_pins,
177364be285bSTakashi Iwai 			    present);
177425eaba2fSLydia Wang }
177525eaba2fSLydia Wang 
177625eaba2fSLydia Wang 
177769e52a80SHarald Welte /* unsolicited event for jack sensing */
177869e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
177969e52a80SHarald Welte 				  unsigned int res)
178069e52a80SHarald Welte {
178169e52a80SHarald Welte 	res >>= 26;
1782ec7e7e42SLydia Wang 
1783a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
17843e95b9abSLydia Wang 		set_widgets_power_state(codec);
1785ec7e7e42SLydia Wang 
1786ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1787ec7e7e42SLydia Wang 
1788ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1789ec7e7e42SLydia Wang 		via_hp_automute(codec);
1790ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1791ec7e7e42SLydia Wang 		via_gpio_control(codec);
1792ec7e7e42SLydia Wang 	else if (res == VIA_MONO_EVENT)
1793f3db423dSLydia Wang 		via_mono_automute(codec);
1794ec7e7e42SLydia Wang 	else if (res == VIA_SPEAKER_EVENT)
179525eaba2fSLydia Wang 		via_speaker_automute(codec);
1796ec7e7e42SLydia Wang 	else if (res == VIA_BIND_HP_EVENT)
179725eaba2fSLydia Wang 		via_hp_bind_automute(codec);
179869e52a80SHarald Welte }
179969e52a80SHarald Welte 
1800c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1801c577b8a1SJoseph Chan {
1802c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
180369e52a80SHarald Welte 	int i;
180469e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
180569e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
180669e52a80SHarald Welte 
1807f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1808f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
180955d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
181055d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1811f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
181212b74c80STakashi Iwai 					    PIN_OUT);
181355d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1814f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1815f7278fd0SJosepch Chan 		}
181612b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
181712b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
181812b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1819f7278fd0SJosepch Chan 
18209da29271STakashi Iwai 	/* assign slave outs */
18219da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
18229da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
18235691ec7fSHarald Welte 
1824c577b8a1SJoseph Chan 	return 0;
1825c577b8a1SJoseph Chan }
1826c577b8a1SJoseph Chan 
18271f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18281f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
18291f2e99feSLydia Wang {
18301f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
18311f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
18321f2e99feSLydia Wang 	return 0;
18331f2e99feSLydia Wang }
18341f2e99feSLydia Wang #endif
18351f2e99feSLydia Wang 
1836cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1837cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1838cb53c626STakashi Iwai {
1839cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1840cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1841cb53c626STakashi Iwai }
1842cb53c626STakashi Iwai #endif
1843cb53c626STakashi Iwai 
1844c577b8a1SJoseph Chan /*
1845c577b8a1SJoseph Chan  */
184690dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1847c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1848c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1849c577b8a1SJoseph Chan 	.init = via_init,
1850c577b8a1SJoseph Chan 	.free = via_free,
18511f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18521f2e99feSLydia Wang 	.suspend = via_suspend,
18531f2e99feSLydia Wang #endif
1854cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1855cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1856cb53c626STakashi Iwai #endif
1857c577b8a1SJoseph Chan };
1858c577b8a1SJoseph Chan 
18594a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1860c577b8a1SJoseph Chan {
18614a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
18624a79616dSTakashi Iwai 	int i;
18634a79616dSTakashi Iwai 
18644a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
18654a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
18664a79616dSTakashi Iwai 			return false;
18674a79616dSTakashi Iwai 	}
18684a79616dSTakashi Iwai 	if (spec->multiout.hp_nid == dac)
18694a79616dSTakashi Iwai 		return false;
18704a79616dSTakashi Iwai 	return true;
18714a79616dSTakashi Iwai }
18724a79616dSTakashi Iwai 
18734a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
18744a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
18754a79616dSTakashi Iwai 			      int depth, int wid_type)
18764a79616dSTakashi Iwai {
18774a79616dSTakashi Iwai 	hda_nid_t conn[8];
18784a79616dSTakashi Iwai 	int i, nums;
18794a79616dSTakashi Iwai 
18804a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
18814a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
18824a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
18834a79616dSTakashi Iwai 			continue;
18844a79616dSTakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
18854a79616dSTakashi Iwai 			path->path[depth] = conn[i];
18864a79616dSTakashi Iwai 			path->idx[depth] = i;
18874a79616dSTakashi Iwai 			path->depth = ++depth;
18884a79616dSTakashi Iwai 			return true;
18894a79616dSTakashi Iwai 		}
18904a79616dSTakashi Iwai 	}
18914a79616dSTakashi Iwai 	if (depth > 4)
18924a79616dSTakashi Iwai 		return false;
18934a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
18944a79616dSTakashi Iwai 		unsigned int type;
18954a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
18964a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
18974a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
18984a79616dSTakashi Iwai 			continue;
18994a79616dSTakashi Iwai 		if (parse_output_path(codec, conn[i], target_dac,
19004a79616dSTakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL)) {
19014a79616dSTakashi Iwai 			path->path[depth] = conn[i];
19024a79616dSTakashi Iwai 			path->idx[depth] = i;
19034a79616dSTakashi Iwai 			return true;
19044a79616dSTakashi Iwai 		}
19054a79616dSTakashi Iwai 	}
19064a79616dSTakashi Iwai 	return false;
19074a79616dSTakashi Iwai }
19084a79616dSTakashi Iwai 
19094a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
19104a79616dSTakashi Iwai {
19114a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
19124a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1913c577b8a1SJoseph Chan 	int i;
1914c577b8a1SJoseph Chan 	hda_nid_t nid;
1915c577b8a1SJoseph Chan 
1916c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
19174a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
19184a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1919c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
19204a79616dSTakashi Iwai 		if (!nid)
19214a79616dSTakashi Iwai 			continue;
19224a79616dSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
19234a79616dSTakashi Iwai 			spec->private_dac_nids[i] =
19244a79616dSTakashi Iwai 				spec->out_path[i].path[spec->out_path[i].depth - 1];
1925c577b8a1SJoseph Chan 	}
1926c577b8a1SJoseph Chan 	return 0;
1927c577b8a1SJoseph Chan }
1928c577b8a1SJoseph Chan 
19294a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
19304a79616dSTakashi Iwai 			  hda_nid_t pin, hda_nid_t dac, int chs)
1931c577b8a1SJoseph Chan {
19324a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1933c577b8a1SJoseph Chan 	char name[32];
19344a79616dSTakashi Iwai 	hda_nid_t nid;
19354a79616dSTakashi Iwai 	int err;
1936c577b8a1SJoseph Chan 
19374a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
19384a79616dSTakashi Iwai 		nid = dac;
19394a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
19404a79616dSTakashi Iwai 		nid = pin;
19414a79616dSTakashi Iwai 	else
19424a79616dSTakashi Iwai 		nid = 0;
19434a79616dSTakashi Iwai 	if (nid) {
19444a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1945c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
19464a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1947c577b8a1SJoseph Chan 		if (err < 0)
1948c577b8a1SJoseph Chan 			return err;
1949c577b8a1SJoseph Chan 	}
19504a79616dSTakashi Iwai 
19514a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
19524a79616dSTakashi Iwai 		nid = dac;
19534a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
19544a79616dSTakashi Iwai 		nid = pin;
19554a79616dSTakashi Iwai 	else
19564a79616dSTakashi Iwai 		nid = 0;
19574a79616dSTakashi Iwai 	if (nid) {
19584a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
19594a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
19604a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
19614a79616dSTakashi Iwai 		if (err < 0)
19624a79616dSTakashi Iwai 			return err;
19634a79616dSTakashi Iwai 	}
19644a79616dSTakashi Iwai 	return 0;
19654a79616dSTakashi Iwai }
19664a79616dSTakashi Iwai 
19674a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
19684a79616dSTakashi Iwai 				hda_nid_t nid);
19694a79616dSTakashi Iwai 
1970f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1971f4a7828bSTakashi Iwai {
1972f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1973f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
1974f4a7828bSTakashi Iwai 	int i;
1975f4a7828bSTakashi Iwai 
1976f4a7828bSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1977f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, cfg->inputs[i].pin))
1978f4a7828bSTakashi Iwai 			continue;
1979f4a7828bSTakashi Iwai 		spec->can_smart51 = 1;
1980f4a7828bSTakashi Iwai 		cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1981f4a7828bSTakashi Iwai 		if (cfg->line_outs == 3)
1982f4a7828bSTakashi Iwai 			break;
1983f4a7828bSTakashi Iwai 	}
1984f4a7828bSTakashi Iwai }
1985f4a7828bSTakashi Iwai 
19864a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
19874a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
19884a79616dSTakashi Iwai {
19894a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1990f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
19914a79616dSTakashi Iwai 	static const char * const chname[4] = {
19924a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
19934a79616dSTakashi Iwai 	};
19944a79616dSTakashi Iwai 	int i, idx, err;
1995f4a7828bSTakashi Iwai 	int old_line_outs;
1996f4a7828bSTakashi Iwai 
1997f4a7828bSTakashi Iwai 	/* check smart51 */
1998f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
1999f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
2000f4a7828bSTakashi Iwai 		mangle_smart51(codec);
20014a79616dSTakashi Iwai 
20024a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
20034a79616dSTakashi Iwai 		hda_nid_t pin, dac;
20044a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
20054a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
20064a79616dSTakashi Iwai 		if (!pin || !dac)
20074a79616dSTakashi Iwai 			continue;
20084a79616dSTakashi Iwai 		if (i == AUTO_SEQ_CENLFE) {
20094a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "Center", pin, dac, 1);
20104a79616dSTakashi Iwai 			if (err < 0)
20114a79616dSTakashi Iwai 				return err;
20124a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "LFE", pin, dac, 2);
20134a79616dSTakashi Iwai 			if (err < 0)
20144a79616dSTakashi Iwai 				return err;
20154a79616dSTakashi Iwai 		} else {
20164a79616dSTakashi Iwai 			err = create_ch_ctls(codec, chname[i], pin, dac, 3);
20174a79616dSTakashi Iwai 			if (err < 0)
20184a79616dSTakashi Iwai 				return err;
20194a79616dSTakashi Iwai 		}
20204a79616dSTakashi Iwai 	}
20214a79616dSTakashi Iwai 
20224a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
20234a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
20244a79616dSTakashi Iwai 	if (idx >= 0) {
20254a79616dSTakashi Iwai 		/* add control to mixer */
20264a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
20274a79616dSTakashi Iwai 				      "PCM Playback Volume",
20284a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20294a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20304a79616dSTakashi Iwai 		if (err < 0)
20314a79616dSTakashi Iwai 			return err;
20324a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
20334a79616dSTakashi Iwai 				      "PCM Playback Switch",
20344a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20354a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20364a79616dSTakashi Iwai 		if (err < 0)
20374a79616dSTakashi Iwai 			return err;
2038c577b8a1SJoseph Chan 	}
2039c577b8a1SJoseph Chan 
2040f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
2041f4a7828bSTakashi Iwai 
2042c577b8a1SJoseph Chan 	return 0;
2043c577b8a1SJoseph Chan }
2044c577b8a1SJoseph Chan 
20450aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
20460aa62aefSHarald Welte {
20470aa62aefSHarald Welte 	int i;
20480aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
2049ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
20500aa62aefSHarald Welte 
20510aa62aefSHarald Welte 	/* for hp mode select */
205210a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
205310a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
20540aa62aefSHarald Welte 
20550aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
20560aa62aefSHarald Welte }
20570aa62aefSHarald Welte 
20584a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
2059c577b8a1SJoseph Chan {
20604a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
20614a79616dSTakashi Iwai 	hda_nid_t dac = 0;
2062c577b8a1SJoseph Chan 	int err;
2063c577b8a1SJoseph Chan 
2064c577b8a1SJoseph Chan 	if (!pin)
2065c577b8a1SJoseph Chan 		return 0;
2066c577b8a1SJoseph Chan 
20674a79616dSTakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
20684a79616dSTakashi Iwai 			       &spec->hp_dep_path, 0, -1))
20694a79616dSTakashi Iwai 		return 0;
20704a79616dSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
20714a79616dSTakashi Iwai 		dac = spec->hp_path.path[spec->hp_path.depth - 1];
20724a79616dSTakashi Iwai 		spec->multiout.hp_nid = dac;
20734a79616dSTakashi Iwai 		spec->hp_independent_mode_index =
20744a79616dSTakashi Iwai 			spec->hp_path.idx[spec->hp_path.depth - 1];
20750aa62aefSHarald Welte 		create_hp_imux(spec);
20764a79616dSTakashi Iwai 	}
20774a79616dSTakashi Iwai 
20784a79616dSTakashi Iwai 	err = create_ch_ctls(codec, "Headphone", pin, dac, 3);
20794a79616dSTakashi Iwai 	if (err < 0)
20804a79616dSTakashi Iwai 		return err;
20810aa62aefSHarald Welte 
2082c577b8a1SJoseph Chan 	return 0;
2083c577b8a1SJoseph Chan }
2084c577b8a1SJoseph Chan 
2085a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
2086a766d0d7STakashi Iwai 				hda_nid_t nid)
2087a766d0d7STakashi Iwai {
2088a766d0d7STakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
2089a766d0d7STakashi Iwai 	int i, nums;
2090a766d0d7STakashi Iwai 
2091a766d0d7STakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
2092a766d0d7STakashi Iwai 	for (i = 0; i < nums; i++)
2093a766d0d7STakashi Iwai 		if (conn[i] == nid)
2094a766d0d7STakashi Iwai 			return i;
2095a766d0d7STakashi Iwai 	return -1;
2096a766d0d7STakashi Iwai }
2097a766d0d7STakashi Iwai 
2098a766d0d7STakashi Iwai /* look for ADCs */
2099a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2100a766d0d7STakashi Iwai {
2101a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2102a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2103a766d0d7STakashi Iwai 	int i;
2104a766d0d7STakashi Iwai 
2105a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2106a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2107a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2108a766d0d7STakashi Iwai 			continue;
2109a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2110a766d0d7STakashi Iwai 			continue;
2111a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2112a766d0d7STakashi Iwai 			continue;
2113a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2114a766d0d7STakashi Iwai 			return -ENOMEM;
2115a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2116a766d0d7STakashi Iwai 	}
2117a766d0d7STakashi Iwai 	return 0;
2118a766d0d7STakashi Iwai }
2119a766d0d7STakashi Iwai 
2120a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2121a766d0d7STakashi Iwai 
2122c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2123620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
2124620e2b28STakashi Iwai 					     const struct auto_pin_cfg *cfg)
2125c577b8a1SJoseph Chan {
212610a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
21270aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2128a766d0d7STakashi Iwai 	int i, err, idx, idx2, type, type_idx = 0;
2129a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
2130a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
2131a766d0d7STakashi Iwai 	int num_idxs;
2132a766d0d7STakashi Iwai 
2133a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2134a766d0d7STakashi Iwai 	if (err < 0)
2135a766d0d7STakashi Iwai 		return err;
2136a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2137a766d0d7STakashi Iwai 	if (err < 0)
2138a766d0d7STakashi Iwai 		return err;
2139a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
2140a766d0d7STakashi Iwai 
2141a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
2142a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
2143a766d0d7STakashi Iwai 	if (num_idxs <= 0)
2144a766d0d7STakashi Iwai 		return 0;
2145c577b8a1SJoseph Chan 
2146c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2147f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2148620e2b28STakashi Iwai 		if (pin_idxs[idx] == spec->aa_mix_nid) {
214910a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2150f3268512STakashi Iwai 			break;
2151f3268512STakashi Iwai 		}
2152f3268512STakashi Iwai 	}
2153c577b8a1SJoseph Chan 
21547b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
215510a20af7STakashi Iwai 		const char *label;
21567b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2157f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
21587b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2159c577b8a1SJoseph Chan 				break;
2160f3268512STakashi Iwai 		if (idx >= num_idxs)
2161f3268512STakashi Iwai 			continue;
21627b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
21637b315bb4STakashi Iwai 			type_idx++;
21647b315bb4STakashi Iwai 		else
21657b315bb4STakashi Iwai 			type_idx = 0;
216610a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2167620e2b28STakashi Iwai 		idx2 = get_connection_index(codec, spec->aa_mix_nid,
2168620e2b28STakashi Iwai 					    pin_idxs[idx]);
2169a766d0d7STakashi Iwai 		if (idx2 >= 0)
217016922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2171620e2b28STakashi Iwai 						   idx2, spec->aa_mix_nid);
2172c577b8a1SJoseph Chan 		if (err < 0)
2173c577b8a1SJoseph Chan 			return err;
217410a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2175c577b8a1SJoseph Chan 	}
2176c577b8a1SJoseph Chan 	return 0;
2177c577b8a1SJoseph Chan }
2178c577b8a1SJoseph Chan 
2179cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
218090dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = {
2181cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2182cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2183cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2184cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2185cb53c626STakashi Iwai 	{ } /* end */
2186cb53c626STakashi Iwai };
2187cb53c626STakashi Iwai #endif
2188cb53c626STakashi Iwai 
218976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
219076d9b0ddSHarald Welte {
219176d9b0ddSHarald Welte 	unsigned int def_conf;
219276d9b0ddSHarald Welte 	unsigned char seqassoc;
219376d9b0ddSHarald Welte 
21942f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
219576d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
219676d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
219782ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
219882ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
219976d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
22002f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
220176d9b0ddSHarald Welte 	}
220276d9b0ddSHarald Welte 
220376d9b0ddSHarald Welte 	return;
220476d9b0ddSHarald Welte }
220576d9b0ddSHarald Welte 
2206e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
22071f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22081f2e99feSLydia Wang {
22091f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22101f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22111f2e99feSLydia Wang 
22121f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22131f2e99feSLydia Wang 		return 0;
2214e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
22151f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2216e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
22171f2e99feSLydia Wang 	return 0;
22181f2e99feSLydia Wang }
22191f2e99feSLydia Wang 
2220e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
22211f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22221f2e99feSLydia Wang {
22231f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22241f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22251f2e99feSLydia Wang 	int change;
22261f2e99feSLydia Wang 
22271f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22281f2e99feSLydia Wang 		return 0;
2229e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
22301f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2231e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2232e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
22331f2e99feSLydia Wang 		mute_aa_path(codec, 1);
22341f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
22351f2e99feSLydia Wang 	}
22361f2e99feSLydia Wang 	return change;
22371f2e99feSLydia Wang }
22381f2e99feSLydia Wang 
2239e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
22401f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
22411f2e99feSLydia Wang 	.name = "Jack Detect",
22421f2e99feSLydia Wang 	.count = 1,
22431f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2244e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2245e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
22461f2e99feSLydia Wang };
22471f2e99feSLydia Wang 
2248c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2249c577b8a1SJoseph Chan {
2250c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2251c577b8a1SJoseph Chan 	int err;
2252c577b8a1SJoseph Chan 
225376d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
225476d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
225576d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
225676d9b0ddSHarald Welte 
2257c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2258c577b8a1SJoseph Chan 	if (err < 0)
2259c577b8a1SJoseph Chan 		return err;
22604a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2261c577b8a1SJoseph Chan 	if (err < 0)
2262c577b8a1SJoseph Chan 		return err;
2263c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2264c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2265c577b8a1SJoseph Chan 
22664a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2267c577b8a1SJoseph Chan 	if (err < 0)
2268c577b8a1SJoseph Chan 		return err;
22694a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2270c577b8a1SJoseph Chan 	if (err < 0)
2271c577b8a1SJoseph Chan 		return err;
2272620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2273c577b8a1SJoseph Chan 	if (err < 0)
2274c577b8a1SJoseph Chan 		return err;
22751f2e99feSLydia Wang 	/* add jack detect on/off control */
2276e06e5a29STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2277e06e5a29STakashi Iwai 		return -ENOMEM;
2278c577b8a1SJoseph Chan 
2279c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2280c577b8a1SJoseph Chan 
22810852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2282c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
228355d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2284c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2285c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2286c577b8a1SJoseph Chan 
2287603c4019STakashi Iwai 	if (spec->kctls.list)
2288603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2289c577b8a1SJoseph Chan 
229069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2291c577b8a1SJoseph Chan 
22920aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
22930aa62aefSHarald Welte 
2294f8fdd495SHarald Welte 	if (spec->hp_mux)
22953d83e577STakashi Iwai 		via_hp_build(codec);
2296c577b8a1SJoseph Chan 
2297f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2298f4a7828bSTakashi Iwai 	if (err < 0)
2299f4a7828bSTakashi Iwai 		return err;
2300f4a7828bSTakashi Iwai 
2301c577b8a1SJoseph Chan 	return 1;
2302c577b8a1SJoseph Chan }
2303c577b8a1SJoseph Chan 
2304c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2305c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2306c577b8a1SJoseph Chan {
230725eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
230825eaba2fSLydia Wang 
2309c577b8a1SJoseph Chan 	via_init(codec);
2310c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2311c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2312c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
231311890956SLydia Wang 
231411890956SLydia Wang 	if (VT2002P_COMPATIBLE(spec)) {
231525eaba2fSLydia Wang 		via_hp_bind_automute(codec);
231625eaba2fSLydia Wang 	} else {
231725eaba2fSLydia Wang 		via_hp_automute(codec);
231825eaba2fSLydia Wang 		via_speaker_automute(codec);
231925eaba2fSLydia Wang 	}
232025eaba2fSLydia Wang 
2321c577b8a1SJoseph Chan 	return 0;
2322c577b8a1SJoseph Chan }
2323c577b8a1SJoseph Chan 
23241f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
23251f2e99feSLydia Wang {
23261f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
23271f2e99feSLydia Wang 					     vt1708_hp_work.work);
23281f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
23291f2e99feSLydia Wang 		return;
23301f2e99feSLydia Wang 	/* if jack state toggled */
23311f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2332d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
23331f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
23341f2e99feSLydia Wang 		via_hp_automute(spec->codec);
23351f2e99feSLydia Wang 	}
23361f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
23371f2e99feSLydia Wang }
23381f2e99feSLydia Wang 
2339337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2340337b9d02STakashi Iwai {
2341337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2342337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2343337b9d02STakashi Iwai 	unsigned int type;
2344337b9d02STakashi Iwai 	int i, n;
2345337b9d02STakashi Iwai 
2346337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2347337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2348337b9d02STakashi Iwai 		while (nid) {
2349a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
23501c55d521STakashi Iwai 			if (type == AC_WID_PIN)
23511c55d521STakashi Iwai 				break;
2352337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2353337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2354337b9d02STakashi Iwai 			if (n <= 0)
2355337b9d02STakashi Iwai 				break;
2356337b9d02STakashi Iwai 			if (n > 1) {
2357337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2358337b9d02STakashi Iwai 				break;
2359337b9d02STakashi Iwai 			}
2360337b9d02STakashi Iwai 			nid = conn[0];
2361337b9d02STakashi Iwai 		}
2362337b9d02STakashi Iwai 	}
23631c55d521STakashi Iwai 	return 0;
2364337b9d02STakashi Iwai }
2365337b9d02STakashi Iwai 
2366c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2367c577b8a1SJoseph Chan {
2368c577b8a1SJoseph Chan 	struct via_spec *spec;
2369c577b8a1SJoseph Chan 	int err;
2370c577b8a1SJoseph Chan 
2371c577b8a1SJoseph Chan 	/* create a codec specific record */
23725b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2373c577b8a1SJoseph Chan 	if (spec == NULL)
2374c577b8a1SJoseph Chan 		return -ENOMEM;
2375c577b8a1SJoseph Chan 
2376620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2377620e2b28STakashi Iwai 
2378c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2379c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2380c577b8a1SJoseph Chan 	if (err < 0) {
2381c577b8a1SJoseph Chan 		via_free(codec);
2382c577b8a1SJoseph Chan 		return err;
2383c577b8a1SJoseph Chan 	} else if (!err) {
2384c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2385c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2386c577b8a1SJoseph Chan 	}
2387c577b8a1SJoseph Chan 
2388c577b8a1SJoseph Chan 
2389bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2390bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2391bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2392c577b8a1SJoseph Chan 
2393a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2394c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2395c577b8a1SJoseph Chan 		spec->num_mixers++;
2396c577b8a1SJoseph Chan 	}
2397c577b8a1SJoseph Chan 
2398c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2399c577b8a1SJoseph Chan 
2400c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2401cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2402cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2403cb53c626STakashi Iwai #endif
24041f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2405c577b8a1SJoseph Chan 	return 0;
2406c577b8a1SJoseph Chan }
2407c577b8a1SJoseph Chan 
2408c577b8a1SJoseph Chan /* capture mixer elements */
240990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = {
2410c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2411c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2412c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2413c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2414c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2415c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2416c577b8a1SJoseph Chan 	{
2417c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2418c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2419c577b8a1SJoseph Chan 		 * So call somewhat different..
2420c577b8a1SJoseph Chan 		 */
2421c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2422c577b8a1SJoseph Chan 		.name = "Input Source",
2423c577b8a1SJoseph Chan 		.count = 1,
2424c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2425c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2426c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2427c577b8a1SJoseph Chan 	},
2428c577b8a1SJoseph Chan 	{ } /* end */
2429c577b8a1SJoseph Chan };
2430c577b8a1SJoseph Chan 
243190dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = {
2432a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2433a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
243469e52a80SHarald Welte 	{ }
243569e52a80SHarald Welte };
243669e52a80SHarald Welte 
2437c577b8a1SJoseph Chan /*
2438c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2439c577b8a1SJoseph Chan  */
244090dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2441c577b8a1SJoseph Chan 	/*
2442c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2443c577b8a1SJoseph Chan 	 */
2444c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2445c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2446c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2447c577b8a1SJoseph Chan 
2448c577b8a1SJoseph Chan 
2449f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2450c577b8a1SJoseph Chan 	 * mixer widget
2451c577b8a1SJoseph Chan 	 */
2452c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2453f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2454f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2455f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2456f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2457f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2458c577b8a1SJoseph Chan 
2459c577b8a1SJoseph Chan 	/*
2460c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2461c577b8a1SJoseph Chan 	 */
2462c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2463c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2464c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2465c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2466c577b8a1SJoseph Chan 
2467c577b8a1SJoseph Chan 	/*
2468c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2469c577b8a1SJoseph Chan 	 */
2470c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2471c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2472c577b8a1SJoseph Chan 
2473bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2474bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2475c577b8a1SJoseph Chan 	/* PW9 Output enable */
2476c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2477c577b8a1SJoseph Chan 	{ }
2478c577b8a1SJoseph Chan };
2479c577b8a1SJoseph Chan 
2480c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2481c577b8a1SJoseph Chan {
2482c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2483c577b8a1SJoseph Chan 	int err;
2484c577b8a1SJoseph Chan 
2485c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2486c577b8a1SJoseph Chan 	if (err < 0)
2487c577b8a1SJoseph Chan 		return err;
24884a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2489c577b8a1SJoseph Chan 	if (err < 0)
2490c577b8a1SJoseph Chan 		return err;
2491c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2492c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2493c577b8a1SJoseph Chan 
24944a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2495c577b8a1SJoseph Chan 	if (err < 0)
2496c577b8a1SJoseph Chan 		return err;
24974a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2498c577b8a1SJoseph Chan 	if (err < 0)
2499c577b8a1SJoseph Chan 		return err;
2500620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2501c577b8a1SJoseph Chan 	if (err < 0)
2502c577b8a1SJoseph Chan 		return err;
2503c577b8a1SJoseph Chan 
2504c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2505c577b8a1SJoseph Chan 
25060852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2507c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
250855d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2509c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2510c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2511c577b8a1SJoseph Chan 
2512603c4019STakashi Iwai 	if (spec->kctls.list)
2513603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2514c577b8a1SJoseph Chan 
25150aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2516c577b8a1SJoseph Chan 
2517f8fdd495SHarald Welte 	if (spec->hp_mux)
25183d83e577STakashi Iwai 		via_hp_build(codec);
2519f8fdd495SHarald Welte 
2520f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2521f4a7828bSTakashi Iwai 	if (err < 0)
2522f4a7828bSTakashi Iwai 		return err;
2523f4a7828bSTakashi Iwai 
2524c577b8a1SJoseph Chan 	return 1;
2525c577b8a1SJoseph Chan }
2526c577b8a1SJoseph Chan 
2527cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
252890dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = {
2529cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2530cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2531cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2532cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2533cb53c626STakashi Iwai 	{ } /* end */
2534cb53c626STakashi Iwai };
2535cb53c626STakashi Iwai #endif
2536cb53c626STakashi Iwai 
2537c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2538c577b8a1SJoseph Chan {
2539c577b8a1SJoseph Chan 	struct via_spec *spec;
2540c577b8a1SJoseph Chan 	int err;
2541c577b8a1SJoseph Chan 
2542c577b8a1SJoseph Chan 	/* create a codec specific record */
25435b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2544c577b8a1SJoseph Chan 	if (spec == NULL)
2545c577b8a1SJoseph Chan 		return -ENOMEM;
2546c577b8a1SJoseph Chan 
2547620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2548620e2b28STakashi Iwai 
2549c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2550c577b8a1SJoseph Chan 	if (err < 0) {
2551c577b8a1SJoseph Chan 		via_free(codec);
2552c577b8a1SJoseph Chan 		return err;
2553c577b8a1SJoseph Chan 	} else if (!err) {
2554c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2555c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2556c577b8a1SJoseph Chan 	}
2557c577b8a1SJoseph Chan 
255869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
255969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2560c577b8a1SJoseph Chan 
2561a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2562c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2563c577b8a1SJoseph Chan 		spec->num_mixers++;
2564c577b8a1SJoseph Chan 	}
2565c577b8a1SJoseph Chan 
2566c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2567c577b8a1SJoseph Chan 
2568c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
256969e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2570cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2571cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2572cb53c626STakashi Iwai #endif
2573c577b8a1SJoseph Chan 
2574c577b8a1SJoseph Chan 	return 0;
2575c577b8a1SJoseph Chan }
2576c577b8a1SJoseph Chan /*
2577c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2578c577b8a1SJoseph Chan  */
257990dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2580c577b8a1SJoseph Chan 	/*
2581c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2582c577b8a1SJoseph Chan 	 */
2583c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2584c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2585c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2586c577b8a1SJoseph Chan 
2587c577b8a1SJoseph Chan 
2588c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2589c577b8a1SJoseph Chan 	 * mixer widget
2590c577b8a1SJoseph Chan 	 */
2591c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2592c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2593c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2594c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2595c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2596c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2597c577b8a1SJoseph Chan 
2598c577b8a1SJoseph Chan 	/*
2599c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2600c577b8a1SJoseph Chan 	 */
2601c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2602c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2603c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2604c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2605c577b8a1SJoseph Chan 
2606c577b8a1SJoseph Chan 	/*
2607c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2608c577b8a1SJoseph Chan 	 */
2609c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2610c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2611c577b8a1SJoseph Chan 
2612c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2613c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2614c577b8a1SJoseph Chan 	/* PW9 Output enable */
2615c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2616c577b8a1SJoseph Chan 	{ }
2617c577b8a1SJoseph Chan };
2618c577b8a1SJoseph Chan 
2619c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2620c577b8a1SJoseph Chan {
2621c577b8a1SJoseph Chan 	struct via_spec *spec;
2622c577b8a1SJoseph Chan 	int err;
2623c577b8a1SJoseph Chan 
2624c577b8a1SJoseph Chan 	/* create a codec specific record */
26255b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2626c577b8a1SJoseph Chan 	if (spec == NULL)
2627c577b8a1SJoseph Chan 		return -ENOMEM;
2628c577b8a1SJoseph Chan 
2629620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2630620e2b28STakashi Iwai 
2631c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2632c577b8a1SJoseph Chan 	if (err < 0) {
2633c577b8a1SJoseph Chan 		via_free(codec);
2634c577b8a1SJoseph Chan 		return err;
2635c577b8a1SJoseph Chan 	} else if (!err) {
2636c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2637c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2638c577b8a1SJoseph Chan 	}
2639c577b8a1SJoseph Chan 
264069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
264169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2642c577b8a1SJoseph Chan 
2643a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2644c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2645c577b8a1SJoseph Chan 		spec->num_mixers++;
2646c577b8a1SJoseph Chan 	}
2647c577b8a1SJoseph Chan 
2648c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2649c577b8a1SJoseph Chan 
2650c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
265169e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2652cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2653cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2654cb53c626STakashi Iwai #endif
2655f7278fd0SJosepch Chan 	return 0;
2656f7278fd0SJosepch Chan }
2657f7278fd0SJosepch Chan 
2658f7278fd0SJosepch Chan /* capture mixer elements */
265990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2660f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2661f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2662f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2663f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2664f7278fd0SJosepch Chan 	{
2665f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2666f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2667f7278fd0SJosepch Chan 		 * So call somewhat different..
2668f7278fd0SJosepch Chan 		 */
2669f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
2670f7278fd0SJosepch Chan 		.name = "Input Source",
2671f7278fd0SJosepch Chan 		.count = 1,
2672f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
2673f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
2674f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
2675f7278fd0SJosepch Chan 	},
2676f7278fd0SJosepch Chan 	{ } /* end */
2677f7278fd0SJosepch Chan };
2678f7278fd0SJosepch Chan /*
2679f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2680f7278fd0SJosepch Chan  */
268190dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2682f7278fd0SJosepch Chan 	/*
2683f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2684f7278fd0SJosepch Chan 	 */
2685f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2686f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2687f7278fd0SJosepch Chan 
2688f7278fd0SJosepch Chan 
2689f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2690f7278fd0SJosepch Chan 	 * mixer widget
2691f7278fd0SJosepch Chan 	 */
2692f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2693f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2694f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2695f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2696f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2697f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2698f7278fd0SJosepch Chan 
2699f7278fd0SJosepch Chan 	/*
2700f7278fd0SJosepch Chan 	 * Set up output mixers
2701f7278fd0SJosepch Chan 	 */
2702f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2703f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2704f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2705f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2706f7278fd0SJosepch Chan 
2707f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2708bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2709f7278fd0SJosepch Chan 	/* PW9 Output enable */
2710f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2711f7278fd0SJosepch Chan 	/* PW10 Input enable */
2712f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2713f7278fd0SJosepch Chan 	{ }
2714f7278fd0SJosepch Chan };
2715f7278fd0SJosepch Chan 
271690dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2717f7278fd0SJosepch Chan 	/*
2718f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2719f7278fd0SJosepch Chan 	 */
2720f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2721f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2722f7278fd0SJosepch Chan 
2723f7278fd0SJosepch Chan 
2724f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2725f7278fd0SJosepch Chan 	 * mixer widget
2726f7278fd0SJosepch Chan 	 */
2727f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2728f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2729f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2730f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2731f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2732f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2733f7278fd0SJosepch Chan 
2734f7278fd0SJosepch Chan 	/*
2735f7278fd0SJosepch Chan 	 * Set up output mixers
2736f7278fd0SJosepch Chan 	 */
2737f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2738f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2739f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2740f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2741f7278fd0SJosepch Chan 
2742f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2743f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2744f7278fd0SJosepch Chan 	/* PW9 Output enable */
2745f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2746f7278fd0SJosepch Chan 	/* PW10 Input enable */
2747f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2748f7278fd0SJosepch Chan 	{ }
2749f7278fd0SJosepch Chan };
2750f7278fd0SJosepch Chan 
275190dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = {
2752a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2753a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2754a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2755a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2756a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2757a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2758a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2759a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2760a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
276169e52a80SHarald Welte 	{ }
276269e52a80SHarald Welte };
276369e52a80SHarald Welte 
2764f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
2765f7278fd0SJosepch Chan {
2766f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
2767f7278fd0SJosepch Chan 	int err;
2768f7278fd0SJosepch Chan 
2769f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2770f7278fd0SJosepch Chan 	if (err < 0)
2771f7278fd0SJosepch Chan 		return err;
27724a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2773f7278fd0SJosepch Chan 	if (err < 0)
2774f7278fd0SJosepch Chan 		return err;
2775f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2776f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
2777f7278fd0SJosepch Chan 
27784a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2779f7278fd0SJosepch Chan 	if (err < 0)
2780f7278fd0SJosepch Chan 		return err;
27814a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2782f7278fd0SJosepch Chan 	if (err < 0)
2783f7278fd0SJosepch Chan 		return err;
2784620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2785f7278fd0SJosepch Chan 	if (err < 0)
2786f7278fd0SJosepch Chan 		return err;
2787f7278fd0SJosepch Chan 
2788f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2789f7278fd0SJosepch Chan 
27900852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2791f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
279255d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
2793f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
2794f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
2795f7278fd0SJosepch Chan 
2796603c4019STakashi Iwai 	if (spec->kctls.list)
2797603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2798f7278fd0SJosepch Chan 
27990aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
28000aa62aefSHarald Welte 
2801f8fdd495SHarald Welte 	if (spec->hp_mux)
28023d83e577STakashi Iwai 		via_hp_build(codec);
2803f7278fd0SJosepch Chan 
2804f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2805f4a7828bSTakashi Iwai 	if (err < 0)
2806f4a7828bSTakashi Iwai 		return err;
2807f4a7828bSTakashi Iwai 
2808f7278fd0SJosepch Chan 	return 1;
2809f7278fd0SJosepch Chan }
2810f7278fd0SJosepch Chan 
2811f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
281290dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = {
2813f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
2814f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
2815f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
2816f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
2817f7278fd0SJosepch Chan 	{ } /* end */
2818f7278fd0SJosepch Chan };
2819f7278fd0SJosepch Chan #endif
28203e95b9abSLydia Wang 
28213e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
28223e95b9abSLydia Wang {
28233e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
28243e95b9abSLydia Wang 	int imux_is_smixer;
28253e95b9abSLydia Wang 	unsigned int parm;
28263e95b9abSLydia Wang 	int is_8ch = 0;
2827bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2828bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
28293e95b9abSLydia Wang 		is_8ch = 1;
28303e95b9abSLydia Wang 
28313e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
28323e95b9abSLydia Wang 	imux_is_smixer =
28333e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
28343e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
28353e95b9abSLydia Wang 	/* inputs */
28363e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
28373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
28393e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
28403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
28413e95b9abSLydia Wang 	if (imux_is_smixer)
28423e95b9abSLydia Wang 		parm = AC_PWRST_D0;
28433e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
28443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
28453e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
28463e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
28473e95b9abSLydia Wang 
28483e95b9abSLydia Wang 	/* outputs */
28493e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
28503e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
28523e95b9abSLydia Wang 	if (spec->smart51_enabled)
28533e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
28543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
28553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
28563e95b9abSLydia Wang 
28573e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
28583e95b9abSLydia Wang 	if (is_8ch) {
28593e95b9abSLydia Wang 		parm = AC_PWRST_D3;
28603e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
28613e95b9abSLydia Wang 		if (spec->smart51_enabled)
28623e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
28633e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
28643e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28653e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
28663e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2867bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2868bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2869bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2870bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2871bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2872bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2873bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2874bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2875bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2876bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28773e95b9abSLydia Wang 	}
28783e95b9abSLydia Wang 
28793e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
28803e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28813e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
28823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
28833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
28843e95b9abSLydia Wang 	if (is_8ch)
28853e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
28863e95b9abSLydia Wang 
28873e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
28883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
28893e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
28903e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
28913e95b9abSLydia Wang 	if (is_8ch) {
28923e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
28933e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
28953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2896bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2897bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2898bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28993e95b9abSLydia Wang }
29003e95b9abSLydia Wang 
2901518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2902f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
2903f7278fd0SJosepch Chan {
2904f7278fd0SJosepch Chan 	struct via_spec *spec;
2905f7278fd0SJosepch Chan 	int err;
2906f7278fd0SJosepch Chan 
2907518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2908518bf3baSLydia Wang 		return patch_vt1708S(codec);
2909f7278fd0SJosepch Chan 	/* create a codec specific record */
29105b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2911f7278fd0SJosepch Chan 	if (spec == NULL)
2912f7278fd0SJosepch Chan 		return -ENOMEM;
2913f7278fd0SJosepch Chan 
2914620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2915620e2b28STakashi Iwai 
2916f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
2917f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
2918f7278fd0SJosepch Chan 	if (err < 0) {
2919f7278fd0SJosepch Chan 		via_free(codec);
2920f7278fd0SJosepch Chan 		return err;
2921f7278fd0SJosepch Chan 	} else if (!err) {
2922f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2923f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
2924f7278fd0SJosepch Chan 	}
2925f7278fd0SJosepch Chan 
292669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
292769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
2928f7278fd0SJosepch Chan 
2929a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2930f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
2931f7278fd0SJosepch Chan 		spec->num_mixers++;
2932f7278fd0SJosepch Chan 	}
2933f7278fd0SJosepch Chan 
2934f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2935f7278fd0SJosepch Chan 
2936f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
293769e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2938f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2939f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2940f7278fd0SJosepch Chan #endif
2941f7278fd0SJosepch Chan 
29423e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
29433e95b9abSLydia Wang 
2944f7278fd0SJosepch Chan 	return 0;
2945f7278fd0SJosepch Chan }
2946f7278fd0SJosepch Chan 
2947f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
2948f7278fd0SJosepch Chan {
2949f7278fd0SJosepch Chan 	struct via_spec *spec;
2950f7278fd0SJosepch Chan 	int err;
2951f7278fd0SJosepch Chan 
2952f7278fd0SJosepch Chan 	/* create a codec specific record */
29535b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2954f7278fd0SJosepch Chan 	if (spec == NULL)
2955f7278fd0SJosepch Chan 		return -ENOMEM;
2956f7278fd0SJosepch Chan 
2957f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
2958f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
2959f7278fd0SJosepch Chan 	if (err < 0) {
2960f7278fd0SJosepch Chan 		via_free(codec);
2961f7278fd0SJosepch Chan 		return err;
2962f7278fd0SJosepch Chan 	} else if (!err) {
2963f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2964f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
2965f7278fd0SJosepch Chan 	}
2966f7278fd0SJosepch Chan 
296769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
296869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
2969f7278fd0SJosepch Chan 
2970a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2971f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
2972f7278fd0SJosepch Chan 		spec->num_mixers++;
2973f7278fd0SJosepch Chan 	}
2974f7278fd0SJosepch Chan 
2975f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2976f7278fd0SJosepch Chan 
2977f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
297869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2979f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2980f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2981f7278fd0SJosepch Chan #endif
2982c577b8a1SJoseph Chan 
29833e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
29843e95b9abSLydia Wang 
2985c577b8a1SJoseph Chan 	return 0;
2986c577b8a1SJoseph Chan }
2987c577b8a1SJoseph Chan 
2988d949cac1SHarald Welte /* Patch for VT1708S */
2989d949cac1SHarald Welte 
2990d949cac1SHarald Welte /* capture mixer elements */
299190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = {
2992d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2993d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2994d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2995d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
29966369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
29976369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
29986369bcfcSLydia Wang 			 HDA_INPUT),
2999d949cac1SHarald Welte 	{
3000d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3001d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3002d949cac1SHarald Welte 		 * So call somewhat different..
3003d949cac1SHarald Welte 		 */
3004d949cac1SHarald Welte 		/* .name = "Capture Source", */
3005d949cac1SHarald Welte 		.name = "Input Source",
3006d949cac1SHarald Welte 		.count = 1,
3007d949cac1SHarald Welte 		.info = via_mux_enum_info,
3008d949cac1SHarald Welte 		.get = via_mux_enum_get,
3009d949cac1SHarald Welte 		.put = via_mux_enum_put,
3010d949cac1SHarald Welte 	},
3011d949cac1SHarald Welte 	{ } /* end */
3012d949cac1SHarald Welte };
3013d949cac1SHarald Welte 
301490dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = {
3015d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3016d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3017d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3018d949cac1SHarald Welte 
3019d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3020d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3021d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3022d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3023d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3024d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3025d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3026d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3027d949cac1SHarald Welte 
3028d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3029d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
30305691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3031d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
30325691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3033d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3034d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3035bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3036bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3037d949cac1SHarald Welte 	{ }
3038d949cac1SHarald Welte };
3039d949cac1SHarald Welte 
304090dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = {
3041a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3042a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3043a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3044a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3045a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3046a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3047a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3048a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3049a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
305069e52a80SHarald Welte 	{ }
305169e52a80SHarald Welte };
305269e52a80SHarald Welte 
305390dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = {
3054bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3055bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3056bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3057bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3058bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3059bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3060bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3061bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3062bc92df7fSLydia Wang 	{ }
3063bc92df7fSLydia Wang };
3064bc92df7fSLydia Wang 
30659da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
30669da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
30679da29271STakashi Iwai {
30689da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
30699da29271STakashi Iwai 	int i;
30709da29271STakashi Iwai 
30719da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
30729da29271STakashi Iwai 		hda_nid_t nid;
30739da29271STakashi Iwai 		int conn;
30749da29271STakashi Iwai 
30759da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
30769da29271STakashi Iwai 		if (!nid)
30779da29271STakashi Iwai 			continue;
30789da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
30799da29271STakashi Iwai 		if (conn < 1)
30809da29271STakashi Iwai 			continue;
30819da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
30829da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
30839da29271STakashi Iwai 		else {
30849da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
30859da29271STakashi Iwai 			break; /* at most two dig outs */
30869da29271STakashi Iwai 		}
30879da29271STakashi Iwai 	}
30889da29271STakashi Iwai }
30899da29271STakashi Iwai 
3090d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3091d949cac1SHarald Welte {
3092d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3093d949cac1SHarald Welte 	int err;
3094d949cac1SHarald Welte 
30959da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3096d949cac1SHarald Welte 	if (err < 0)
3097d949cac1SHarald Welte 		return err;
30984a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3099d949cac1SHarald Welte 	if (err < 0)
3100d949cac1SHarald Welte 		return err;
3101d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3102d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3103d949cac1SHarald Welte 
31044a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3105d949cac1SHarald Welte 	if (err < 0)
3106d949cac1SHarald Welte 		return err;
31074a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3108d949cac1SHarald Welte 	if (err < 0)
3109d949cac1SHarald Welte 		return err;
3110620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3111d949cac1SHarald Welte 	if (err < 0)
3112d949cac1SHarald Welte 		return err;
3113d949cac1SHarald Welte 
3114d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3115d949cac1SHarald Welte 
31169da29271STakashi Iwai 	fill_dig_outs(codec);
311798aa34c0SHarald Welte 
3118603c4019STakashi Iwai 	if (spec->kctls.list)
3119603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3120d949cac1SHarald Welte 
31210aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
31220aa62aefSHarald Welte 
3123f8fdd495SHarald Welte 	if (spec->hp_mux)
31243d83e577STakashi Iwai 		via_hp_build(codec);
3125d949cac1SHarald Welte 
3126f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3127f4a7828bSTakashi Iwai 	if (err < 0)
3128f4a7828bSTakashi Iwai 		return err;
3129f4a7828bSTakashi Iwai 
3130d949cac1SHarald Welte 	return 1;
3131d949cac1SHarald Welte }
3132d949cac1SHarald Welte 
3133d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
313490dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = {
3135d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3136d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3137d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3138d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3139d949cac1SHarald Welte 	{ } /* end */
3140d949cac1SHarald Welte };
3141d949cac1SHarald Welte #endif
3142d949cac1SHarald Welte 
31436369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
31446369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
31456369bcfcSLydia Wang {
31466369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
31476369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
31486369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
31496369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
31506369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
31516369bcfcSLydia Wang }
31526369bcfcSLydia Wang 
3153d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3154d949cac1SHarald Welte {
3155d949cac1SHarald Welte 	struct via_spec *spec;
3156d949cac1SHarald Welte 	int err;
3157d949cac1SHarald Welte 
3158d949cac1SHarald Welte 	/* create a codec specific record */
31595b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3160d949cac1SHarald Welte 	if (spec == NULL)
3161d949cac1SHarald Welte 		return -ENOMEM;
3162d949cac1SHarald Welte 
3163620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3164620e2b28STakashi Iwai 
3165d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3166d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3167d949cac1SHarald Welte 	if (err < 0) {
3168d949cac1SHarald Welte 		via_free(codec);
3169d949cac1SHarald Welte 		return err;
3170d949cac1SHarald Welte 	} else if (!err) {
3171d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3172d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3173d949cac1SHarald Welte 	}
3174d949cac1SHarald Welte 
317569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3176bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3177bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3178bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3179bc92df7fSLydia Wang 	else
3180bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3181bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3182d949cac1SHarald Welte 
3183a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
31846369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
31856369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3186d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3187d949cac1SHarald Welte 		spec->num_mixers++;
3188d949cac1SHarald Welte 	}
3189d949cac1SHarald Welte 
3190d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3191d949cac1SHarald Welte 
3192d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
319369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3194d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3195d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3196d949cac1SHarald Welte #endif
3197d949cac1SHarald Welte 
3198518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3199518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3200518bf3baSLydia Wang 		kfree(codec->chip_name);
3201518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3202518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3203518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3204518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3205970f630fSLydia Wang 	}
3206bc92df7fSLydia Wang 	/* correct names for VT1705 */
3207bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3208bc92df7fSLydia Wang 		kfree(codec->chip_name);
3209bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3210bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3211bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3212bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3213bc92df7fSLydia Wang 	}
32143e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3215d949cac1SHarald Welte 	return 0;
3216d949cac1SHarald Welte }
3217d949cac1SHarald Welte 
3218d949cac1SHarald Welte /* Patch for VT1702 */
3219d949cac1SHarald Welte 
3220d949cac1SHarald Welte /* capture mixer elements */
322190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = {
3222d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3223d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3224d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3225d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3226d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3227d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3228d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3229d949cac1SHarald Welte 			 HDA_INPUT),
3230d949cac1SHarald Welte 	{
3231d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3232d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3233d949cac1SHarald Welte 		 * So call somewhat different..
3234d949cac1SHarald Welte 		 */
3235d949cac1SHarald Welte 		/* .name = "Capture Source", */
3236d949cac1SHarald Welte 		.name = "Input Source",
3237d949cac1SHarald Welte 		.count = 1,
3238d949cac1SHarald Welte 		.info = via_mux_enum_info,
3239d949cac1SHarald Welte 		.get = via_mux_enum_get,
3240d949cac1SHarald Welte 		.put = via_mux_enum_put,
3241d949cac1SHarald Welte 	},
3242d949cac1SHarald Welte 	{ } /* end */
3243d949cac1SHarald Welte };
3244d949cac1SHarald Welte 
324590dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = {
3246d949cac1SHarald Welte 	/*
3247d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3248d949cac1SHarald Welte 	 */
3249d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3250d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3251d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3252d949cac1SHarald Welte 
3253d949cac1SHarald Welte 
3254d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3255d949cac1SHarald Welte 	 * mixer widget
3256d949cac1SHarald Welte 	 */
3257d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3258d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3259d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3260d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3261d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3262d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3263d949cac1SHarald Welte 
3264d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3265d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3266d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3267d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3268d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3269bc7e7e5cSLydia Wang 	/* mixer enable */
3270bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3271bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3272bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3273d949cac1SHarald Welte 	{ }
3274d949cac1SHarald Welte };
3275d949cac1SHarald Welte 
327690dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = {
3277a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3278a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3279a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3280a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3281a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3282a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
328369e52a80SHarald Welte 	{ }
328469e52a80SHarald Welte };
328569e52a80SHarald Welte 
3286d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
3287d949cac1SHarald Welte {
3288d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3289d949cac1SHarald Welte 	int err;
3290d949cac1SHarald Welte 
32919da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3292d949cac1SHarald Welte 	if (err < 0)
3293d949cac1SHarald Welte 		return err;
32944a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3295d949cac1SHarald Welte 	if (err < 0)
3296d949cac1SHarald Welte 		return err;
3297d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3298d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3299d949cac1SHarald Welte 
33004a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3301d949cac1SHarald Welte 	if (err < 0)
3302d949cac1SHarald Welte 		return err;
33034a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3304d949cac1SHarald Welte 	if (err < 0)
3305d949cac1SHarald Welte 		return err;
3306c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
3307c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3308c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3309c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3310c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3311c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
3312620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3313d949cac1SHarald Welte 	if (err < 0)
3314d949cac1SHarald Welte 		return err;
3315d949cac1SHarald Welte 
3316d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3317d949cac1SHarald Welte 
33189da29271STakashi Iwai 	fill_dig_outs(codec);
331998aa34c0SHarald Welte 
3320603c4019STakashi Iwai 	if (spec->kctls.list)
3321603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3322d949cac1SHarald Welte 
33230aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
33240aa62aefSHarald Welte 
3325f8fdd495SHarald Welte 	if (spec->hp_mux)
33263d83e577STakashi Iwai 		via_hp_build(codec);
3327d949cac1SHarald Welte 
3328d949cac1SHarald Welte 	return 1;
3329d949cac1SHarald Welte }
3330d949cac1SHarald Welte 
3331d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
333290dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = {
3333d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
3334d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
3335d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
3336d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
3337d949cac1SHarald Welte 	{ } /* end */
3338d949cac1SHarald Welte };
3339d949cac1SHarald Welte #endif
3340d949cac1SHarald Welte 
33413e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
33423e95b9abSLydia Wang {
33433e95b9abSLydia Wang 	int imux_is_smixer =
33443e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
33453e95b9abSLydia Wang 	unsigned int parm;
33463e95b9abSLydia Wang 	/* inputs */
33473e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
33483e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
33503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
33513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
33523e95b9abSLydia Wang 	if (imux_is_smixer)
33533e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
33543e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
33553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
33563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
33573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
33583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
33593e95b9abSLydia Wang 
33603e95b9abSLydia Wang 	/* outputs */
33613e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
33623e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33633e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
33643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
33653e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
33663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
33673e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
33683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
33693e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
33703e95b9abSLydia Wang }
33713e95b9abSLydia Wang 
3372d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
3373d949cac1SHarald Welte {
3374d949cac1SHarald Welte 	struct via_spec *spec;
3375d949cac1SHarald Welte 	int err;
3376d949cac1SHarald Welte 
3377d949cac1SHarald Welte 	/* create a codec specific record */
33785b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3379d949cac1SHarald Welte 	if (spec == NULL)
3380d949cac1SHarald Welte 		return -ENOMEM;
3381d949cac1SHarald Welte 
3382620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
3383620e2b28STakashi Iwai 
3384d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3385d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
3386d949cac1SHarald Welte 	if (err < 0) {
3387d949cac1SHarald Welte 		via_free(codec);
3388d949cac1SHarald Welte 		return err;
3389d949cac1SHarald Welte 	} else if (!err) {
3390d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3391d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3392d949cac1SHarald Welte 	}
3393d949cac1SHarald Welte 
339469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
339569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
3396d949cac1SHarald Welte 
3397a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3398d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
3399d949cac1SHarald Welte 		spec->num_mixers++;
3400d949cac1SHarald Welte 	}
3401d949cac1SHarald Welte 
3402d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3403d949cac1SHarald Welte 
3404d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
340569e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3406d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3407d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
3408d949cac1SHarald Welte #endif
3409d949cac1SHarald Welte 
34103e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
3411d949cac1SHarald Welte 	return 0;
3412d949cac1SHarald Welte }
3413d949cac1SHarald Welte 
3414eb7188caSLydia Wang /* Patch for VT1718S */
3415eb7188caSLydia Wang 
3416eb7188caSLydia Wang /* capture mixer elements */
341790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = {
3418eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
3419eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
3420eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
3421eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
3422eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
3423eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
3424eb7188caSLydia Wang 			 HDA_INPUT),
3425eb7188caSLydia Wang 	{
3426eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3427eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
3428eb7188caSLydia Wang 		 * So call somewhat different..
3429eb7188caSLydia Wang 		 */
3430eb7188caSLydia Wang 		.name = "Input Source",
3431eb7188caSLydia Wang 		.count = 2,
3432eb7188caSLydia Wang 		.info = via_mux_enum_info,
3433eb7188caSLydia Wang 		.get = via_mux_enum_get,
3434eb7188caSLydia Wang 		.put = via_mux_enum_put,
3435eb7188caSLydia Wang 	},
3436eb7188caSLydia Wang 	{ } /* end */
3437eb7188caSLydia Wang };
3438eb7188caSLydia Wang 
343990dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = {
3440eb7188caSLydia Wang 	/*
3441eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3442eb7188caSLydia Wang 	 */
3443eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3444eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3445eb7188caSLydia Wang 
34464ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
34474ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
3448eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3449eb7188caSLydia Wang 	 * mixer widget
3450eb7188caSLydia Wang 	 */
3451eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3452eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3453eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3454eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3455eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
34564ab2d53aSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
3457eb7188caSLydia Wang 
3458eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
3459eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3460eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
3461eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3462eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3463eb7188caSLydia Wang 	/* PW11 Input enable */
3464eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
3465eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
3466eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
3467eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
3468eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3469eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3470eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3471eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3472eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3473eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3474eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3475eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3476eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3477eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3478eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
3479eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
3480eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
3481eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
3482eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3483eb7188caSLydia Wang 	{ }
3484eb7188caSLydia Wang };
3485eb7188caSLydia Wang 
3486eb7188caSLydia Wang 
348790dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = {
3488eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
3489eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3490eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3491eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3492eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3493eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3494eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3495eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3496eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3497eb7188caSLydia Wang 	{ }
3498eb7188caSLydia Wang };
3499eb7188caSLydia Wang 
3500eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
3501eb7188caSLydia Wang {
3502eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
3503eb7188caSLydia Wang 	int err;
3504eb7188caSLydia Wang 
3505eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3506eb7188caSLydia Wang 
3507eb7188caSLydia Wang 	if (err < 0)
3508eb7188caSLydia Wang 		return err;
35094a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3510eb7188caSLydia Wang 	if (err < 0)
3511eb7188caSLydia Wang 		return err;
3512eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3513eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
3514eb7188caSLydia Wang 
35154a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3516eb7188caSLydia Wang 	if (err < 0)
3517eb7188caSLydia Wang 		return err;
35184a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3519eb7188caSLydia Wang 	if (err < 0)
3520eb7188caSLydia Wang 		return err;
3521620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3522eb7188caSLydia Wang 	if (err < 0)
3523eb7188caSLydia Wang 		return err;
3524eb7188caSLydia Wang 
3525eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3526eb7188caSLydia Wang 
3527eb7188caSLydia Wang 	fill_dig_outs(codec);
3528eb7188caSLydia Wang 
3529eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
3530eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
3531eb7188caSLydia Wang 
3532eb7188caSLydia Wang 	if (spec->kctls.list)
3533eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3534eb7188caSLydia Wang 
3535eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
3536eb7188caSLydia Wang 
3537eb7188caSLydia Wang 	if (spec->hp_mux)
35383d83e577STakashi Iwai 		via_hp_build(codec);
3539eb7188caSLydia Wang 
3540f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3541f4a7828bSTakashi Iwai 	if (err < 0)
3542f4a7828bSTakashi Iwai 		return err;
3543eb7188caSLydia Wang 
3544eb7188caSLydia Wang 	return 1;
3545eb7188caSLydia Wang }
3546eb7188caSLydia Wang 
3547eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
354890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = {
3549eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
3550eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
3551eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
3552eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
3553eb7188caSLydia Wang 	{ } /* end */
3554eb7188caSLydia Wang };
3555eb7188caSLydia Wang #endif
3556eb7188caSLydia Wang 
35573e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
35583e95b9abSLydia Wang {
35593e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
35603e95b9abSLydia Wang 	int imux_is_smixer;
35613e95b9abSLydia Wang 	unsigned int parm;
35623e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
35633e95b9abSLydia Wang 	imux_is_smixer =
35643e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
35653e95b9abSLydia Wang 	/* inputs */
35663e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
35673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
35693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
35703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
35713e95b9abSLydia Wang 	if (imux_is_smixer)
35723e95b9abSLydia Wang 		parm = AC_PWRST_D0;
35733e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
35743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
35753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
35763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
35773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
35783e95b9abSLydia Wang 
35793e95b9abSLydia Wang 	/* outputs */
35803e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
35813e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
35833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
35843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
35853e95b9abSLydia Wang 
35863e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
35873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
35893e95b9abSLydia Wang 	if (spec->smart51_enabled)
35903e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
35913e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
35923e95b9abSLydia Wang 
35933e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
35943e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
35963e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
35973e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
35983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
35993e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
36003e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
36013e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
36023e95b9abSLydia Wang 
36033e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
36043e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
36063e95b9abSLydia Wang 	if (spec->smart51_enabled)
36073e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
36083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
36093e95b9abSLydia Wang 
36103e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
36113e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
36123e95b9abSLydia Wang 		parm = AC_PWRST_D3;
36133e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
36143e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
36153e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
36163e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
36173e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
36183e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
36193e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
36203e95b9abSLydia Wang 	}
36213e95b9abSLydia Wang }
36223e95b9abSLydia Wang 
3623eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
3624eb7188caSLydia Wang {
3625eb7188caSLydia Wang 	struct via_spec *spec;
3626eb7188caSLydia Wang 	int err;
3627eb7188caSLydia Wang 
3628eb7188caSLydia Wang 	/* create a codec specific record */
36295b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3630eb7188caSLydia Wang 	if (spec == NULL)
3631eb7188caSLydia Wang 		return -ENOMEM;
3632eb7188caSLydia Wang 
3633620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3634620e2b28STakashi Iwai 
3635eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
3636eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
3637eb7188caSLydia Wang 	if (err < 0) {
3638eb7188caSLydia Wang 		via_free(codec);
3639eb7188caSLydia Wang 		return err;
3640eb7188caSLydia Wang 	} else if (!err) {
3641eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3642eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
3643eb7188caSLydia Wang 	}
3644eb7188caSLydia Wang 
3645eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
3646eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
3647eb7188caSLydia Wang 
3648a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3649bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
3650bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
3651eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
3652eb7188caSLydia Wang 		spec->num_mixers++;
3653eb7188caSLydia Wang 	}
3654eb7188caSLydia Wang 
3655eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
3656eb7188caSLydia Wang 
3657eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
36580f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
3659eb7188caSLydia Wang 
3660eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3661eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
3662eb7188caSLydia Wang #endif
3663eb7188caSLydia Wang 
36643e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
36653e95b9abSLydia Wang 
3666eb7188caSLydia Wang 	return 0;
3667eb7188caSLydia Wang }
3668f3db423dSLydia Wang 
3669f3db423dSLydia Wang /* Patch for VT1716S */
3670f3db423dSLydia Wang 
3671f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3672f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
3673f3db423dSLydia Wang {
3674f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3675f3db423dSLydia Wang 	uinfo->count = 1;
3676f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
3677f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
3678f3db423dSLydia Wang 	return 0;
3679f3db423dSLydia Wang }
3680f3db423dSLydia Wang 
3681f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3682f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3683f3db423dSLydia Wang {
3684f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3685f3db423dSLydia Wang 	int index = 0;
3686f3db423dSLydia Wang 
3687f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
3688f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
3689f3db423dSLydia Wang 	if (index != -1)
3690f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
3691f3db423dSLydia Wang 
3692f3db423dSLydia Wang 	return 0;
3693f3db423dSLydia Wang }
3694f3db423dSLydia Wang 
3695f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3696f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3697f3db423dSLydia Wang {
3698f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3699f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3700f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
3701f3db423dSLydia Wang 
3702f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
3703f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
3704f3db423dSLydia Wang 	spec->dmic_enabled = index;
37053e95b9abSLydia Wang 	set_widgets_power_state(codec);
3706f3db423dSLydia Wang 	return 1;
3707f3db423dSLydia Wang }
3708f3db423dSLydia Wang 
3709f3db423dSLydia Wang /* capture mixer elements */
371090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = {
3711f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3712f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3713f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3714f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3715f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3716f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3717f3db423dSLydia Wang 			 HDA_INPUT),
3718f3db423dSLydia Wang 	{
3719f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3720f3db423dSLydia Wang 		.name = "Input Source",
3721f3db423dSLydia Wang 		.count = 1,
3722f3db423dSLydia Wang 		.info = via_mux_enum_info,
3723f3db423dSLydia Wang 		.get = via_mux_enum_get,
3724f3db423dSLydia Wang 		.put = via_mux_enum_put,
3725f3db423dSLydia Wang 	},
3726f3db423dSLydia Wang 	{ } /* end */
3727f3db423dSLydia Wang };
3728f3db423dSLydia Wang 
372990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
3730f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3731f3db423dSLydia Wang 	{
3732f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3733f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
37345b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
3735f3db423dSLydia Wang 	 .count = 1,
3736f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
3737f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
3738f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
3739f3db423dSLydia Wang 	 },
3740f3db423dSLydia Wang 	{}			/* end */
3741f3db423dSLydia Wang };
3742f3db423dSLydia Wang 
3743f3db423dSLydia Wang 
3744f3db423dSLydia Wang /* mono-out mixer elements */
374590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3746f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3747f3db423dSLydia Wang 	{ } /* end */
3748f3db423dSLydia Wang };
3749f3db423dSLydia Wang 
375090dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = {
3751f3db423dSLydia Wang 	/*
3752f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3753f3db423dSLydia Wang 	 */
3754f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3755f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3756f3db423dSLydia Wang 
3757f3db423dSLydia Wang 
3758f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3759f3db423dSLydia Wang 	 * mixer widget
3760f3db423dSLydia Wang 	 */
3761f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3762f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3763f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3764f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3765f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3766f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3767f3db423dSLydia Wang 
3768f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
3769f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
3770f3db423dSLydia Wang 
3771f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
3772f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3773f3db423dSLydia Wang 
3774f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
3775f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
3776f3db423dSLydia Wang 
3777f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
3778f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3779f3db423dSLydia Wang 
3780f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
3781f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3782f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3783f3db423dSLydia Wang 
3784f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
3785f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3786f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3787f3db423dSLydia Wang 	/* PW12 Output enable */
3788f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3789f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
3790f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
3791f3db423dSLydia Wang 	/* don't bybass mixer */
3792f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
3793f3db423dSLydia Wang 	/* Enable mono output */
3794f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
3795f3db423dSLydia Wang 	{ }
3796f3db423dSLydia Wang };
3797f3db423dSLydia Wang 
3798f3db423dSLydia Wang 
379990dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = {
3800f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3801f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3802f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3803f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3804f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3805f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
3806f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
3807f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3808f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3809f3db423dSLydia Wang 	{ }
3810f3db423dSLydia Wang };
3811f3db423dSLydia Wang 
3812f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
3813f3db423dSLydia Wang {
3814f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3815f3db423dSLydia Wang 	int err;
3816f3db423dSLydia Wang 
3817f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3818f3db423dSLydia Wang 	if (err < 0)
3819f3db423dSLydia Wang 		return err;
38204a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3821f3db423dSLydia Wang 	if (err < 0)
3822f3db423dSLydia Wang 		return err;
3823f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3824f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
3825f3db423dSLydia Wang 
38264a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3827f3db423dSLydia Wang 	if (err < 0)
3828f3db423dSLydia Wang 		return err;
38294a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3830f3db423dSLydia Wang 	if (err < 0)
3831f3db423dSLydia Wang 		return err;
3832620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3833f3db423dSLydia Wang 	if (err < 0)
3834f3db423dSLydia Wang 		return err;
3835f3db423dSLydia Wang 
3836f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3837f3db423dSLydia Wang 
3838f3db423dSLydia Wang 	fill_dig_outs(codec);
3839f3db423dSLydia Wang 
3840f3db423dSLydia Wang 	if (spec->kctls.list)
3841f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3842f3db423dSLydia Wang 
3843f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
3844f3db423dSLydia Wang 
3845f3db423dSLydia Wang 	if (spec->hp_mux)
38463d83e577STakashi Iwai 		via_hp_build(codec);
3847f3db423dSLydia Wang 
3848f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3849f4a7828bSTakashi Iwai 	if (err < 0)
3850f4a7828bSTakashi Iwai 		return err;
3851f3db423dSLydia Wang 
3852f3db423dSLydia Wang 	return 1;
3853f3db423dSLydia Wang }
3854f3db423dSLydia Wang 
3855f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
385690dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = {
3857f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
3858f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
3859f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
3860f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
3861f3db423dSLydia Wang 	{ } /* end */
3862f3db423dSLydia Wang };
3863f3db423dSLydia Wang #endif
3864f3db423dSLydia Wang 
38653e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
38663e95b9abSLydia Wang {
38673e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
38683e95b9abSLydia Wang 	int imux_is_smixer;
38693e95b9abSLydia Wang 	unsigned int parm;
38703e95b9abSLydia Wang 	unsigned int mono_out, present;
38713e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
38723e95b9abSLydia Wang 	imux_is_smixer =
38733e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
38743e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
38753e95b9abSLydia Wang 	/* inputs */
38763e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
38773e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38783e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
38793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
38803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
38813e95b9abSLydia Wang 	if (imux_is_smixer)
38823e95b9abSLydia Wang 		parm = AC_PWRST_D0;
38833e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
38843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
38853e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
38863e95b9abSLydia Wang 
38873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
38893e95b9abSLydia Wang 	/* PW11 (22h) */
38903e95b9abSLydia Wang 	if (spec->dmic_enabled)
38913e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
38923e95b9abSLydia Wang 	else
38933e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
38943e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
38953e95b9abSLydia Wang 
38963e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
38973e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
38983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
38993e95b9abSLydia Wang 
39003e95b9abSLydia Wang 	/* outputs */
39013e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
39023e95b9abSLydia Wang 	parm = AC_PWRST_D3;
39033e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
39043e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
39053e95b9abSLydia Wang 	if (spec->smart51_enabled)
39063e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
39073e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
39083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
39093e95b9abSLydia Wang 
39103e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
39113e95b9abSLydia Wang 	parm = AC_PWRST_D3;
39123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
39133e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
39143e95b9abSLydia Wang 	if (spec->smart51_enabled)
39153e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
39163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
39173e95b9abSLydia Wang 
39183e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
39193e95b9abSLydia Wang 	if (spec->smart51_enabled)
39203e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
39213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
39223e95b9abSLydia Wang 
39233e95b9abSLydia Wang 	/* Mono out */
39243e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
39253e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
39263e95b9abSLydia Wang 
39273e95b9abSLydia Wang 	if (present)
39283e95b9abSLydia Wang 		mono_out = 0;
39293e95b9abSLydia Wang 	else {
39303e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
39313e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
39323e95b9abSLydia Wang 			mono_out = 0;
39333e95b9abSLydia Wang 		else
39343e95b9abSLydia Wang 			mono_out = 1;
39353e95b9abSLydia Wang 	}
39363e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
39373e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
39383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
39393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
39403e95b9abSLydia Wang 
39413e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
39423e95b9abSLydia Wang 	parm = AC_PWRST_D3;
39433e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
39443e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
39453e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
39463e95b9abSLydia Wang 	if (spec->hp_independent_mode)
39473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
39483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
39493e95b9abSLydia Wang 
39503e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
39513e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
39523e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
39533e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
39543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
39553e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
39563e95b9abSLydia Wang }
39573e95b9abSLydia Wang 
3958f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3959f3db423dSLydia Wang {
3960f3db423dSLydia Wang 	struct via_spec *spec;
3961f3db423dSLydia Wang 	int err;
3962f3db423dSLydia Wang 
3963f3db423dSLydia Wang 	/* create a codec specific record */
39645b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3965f3db423dSLydia Wang 	if (spec == NULL)
3966f3db423dSLydia Wang 		return -ENOMEM;
3967f3db423dSLydia Wang 
3968620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3969620e2b28STakashi Iwai 
3970f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
3971f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
3972f3db423dSLydia Wang 	if (err < 0) {
3973f3db423dSLydia Wang 		via_free(codec);
3974f3db423dSLydia Wang 		return err;
3975f3db423dSLydia Wang 	} else if (!err) {
3976f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3977f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
3978f3db423dSLydia Wang 	}
3979f3db423dSLydia Wang 
3980f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
3981f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
3982f3db423dSLydia Wang 
3983a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3984f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
3985f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3986f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
3987f3db423dSLydia Wang 		spec->num_mixers++;
3988f3db423dSLydia Wang 	}
3989f3db423dSLydia Wang 
3990f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3991f3db423dSLydia Wang 	spec->num_mixers++;
3992f3db423dSLydia Wang 
3993f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3994f3db423dSLydia Wang 
3995f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3996f3db423dSLydia Wang 
3997f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
39980f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
3999f3db423dSLydia Wang 
4000f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4001f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
4002f3db423dSLydia Wang #endif
4003f3db423dSLydia Wang 
40043e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
4005f3db423dSLydia Wang 	return 0;
4006f3db423dSLydia Wang }
400725eaba2fSLydia Wang 
400825eaba2fSLydia Wang /* for vt2002P */
400925eaba2fSLydia Wang 
401025eaba2fSLydia Wang /* capture mixer elements */
401190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = {
401225eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
401325eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
401425eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
401525eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
401625eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
401725eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
401825eaba2fSLydia Wang 			 HDA_INPUT),
401925eaba2fSLydia Wang 	{
402025eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
402125eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
402225eaba2fSLydia Wang 		 * So call somewhat different..
402325eaba2fSLydia Wang 		 */
402425eaba2fSLydia Wang 		/* .name = "Capture Source", */
402525eaba2fSLydia Wang 		.name = "Input Source",
402625eaba2fSLydia Wang 		.count = 2,
402725eaba2fSLydia Wang 		.info = via_mux_enum_info,
402825eaba2fSLydia Wang 		.get = via_mux_enum_get,
402925eaba2fSLydia Wang 		.put = via_mux_enum_put,
403025eaba2fSLydia Wang 	},
403125eaba2fSLydia Wang 	{ } /* end */
403225eaba2fSLydia Wang };
403325eaba2fSLydia Wang 
403490dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = {
4035eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
4036eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
4037eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
4038eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
403925eaba2fSLydia Wang 	/*
404025eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
404125eaba2fSLydia Wang 	 */
404225eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
404325eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
404425eaba2fSLydia Wang 
404525eaba2fSLydia Wang 
404625eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
404725eaba2fSLydia Wang 	 * mixer widget
404825eaba2fSLydia Wang 	 */
404925eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
405025eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
405125eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
405225eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
405325eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
405425eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
405525eaba2fSLydia Wang 
405625eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
405725eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
405825eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
405925eaba2fSLydia Wang 
406025eaba2fSLydia Wang 	/* PW9 Output enable */
406125eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
406225eaba2fSLydia Wang 
406325eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
406425eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
406525eaba2fSLydia Wang 
406625eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
406725eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
406825eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
406925eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
407025eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
407125eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
407225eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
407325eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
407425eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
407525eaba2fSLydia Wang 
407625eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
407725eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
407825eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
407925eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
408025eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
408125eaba2fSLydia Wang 
408225eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
408325eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
408425eaba2fSLydia Wang 
408525eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
408625eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
408725eaba2fSLydia Wang 	{ }
408825eaba2fSLydia Wang };
408990dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = {
409011890956SLydia Wang 	/*
409111890956SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
409211890956SLydia Wang 	 */
409311890956SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
409411890956SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
409511890956SLydia Wang 
409611890956SLydia Wang 
409711890956SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
409811890956SLydia Wang 	 * mixer widget
409911890956SLydia Wang 	 */
410011890956SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
410111890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
410211890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
410311890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
410411890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
410511890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
410611890956SLydia Wang 
410711890956SLydia Wang 	/* MUX Indices: Mic = 0 */
410811890956SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
410911890956SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
411011890956SLydia Wang 
411111890956SLydia Wang 	/* PW9 Output enable */
411211890956SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
411311890956SLydia Wang 
411411890956SLydia Wang 	/* Enable Boost Volume backdoor */
411511890956SLydia Wang 	{0x1, 0xfb9, 0x24},
411611890956SLydia Wang 
411711890956SLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
411811890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
411911890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
412011890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
412111890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
412211890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
412311890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
412411890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
412511890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
412611890956SLydia Wang 
412711890956SLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
412811890956SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
412911890956SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
413011890956SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
413111890956SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
413211890956SLydia Wang 
413311890956SLydia Wang 	/* set PW0 index=0 (MW0) */
413411890956SLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
413511890956SLydia Wang 
413611890956SLydia Wang 	/* Enable AOW0 to MW9 */
413711890956SLydia Wang 	{0x1, 0xfb8, 0x88},
413811890956SLydia Wang 	{ }
413911890956SLydia Wang };
414025eaba2fSLydia Wang 
414125eaba2fSLydia Wang 
414290dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = {
414325eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
414425eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
414525eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
414625eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
414725eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
414825eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
414925eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
415025eaba2fSLydia Wang 	{ }
415125eaba2fSLydia Wang };
415290dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = {
415311890956SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
415411890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
415511890956SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
415611890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
415711890956SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
415811890956SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
415911890956SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
416011890956SLydia Wang 	{ }
416111890956SLydia Wang };
416225eaba2fSLydia Wang 
416325eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
416425eaba2fSLydia Wang {
416525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
416625eaba2fSLydia Wang 	int err;
416725eaba2fSLydia Wang 
416825eaba2fSLydia Wang 
416925eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
417025eaba2fSLydia Wang 	if (err < 0)
417125eaba2fSLydia Wang 		return err;
417225eaba2fSLydia Wang 
41734a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
417425eaba2fSLydia Wang 	if (err < 0)
417525eaba2fSLydia Wang 		return err;
417625eaba2fSLydia Wang 
417725eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
417825eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
417925eaba2fSLydia Wang 
41804a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
418125eaba2fSLydia Wang 	if (err < 0)
418225eaba2fSLydia Wang 		return err;
41834a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
418425eaba2fSLydia Wang 	if (err < 0)
418525eaba2fSLydia Wang 		return err;
4186620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
418725eaba2fSLydia Wang 	if (err < 0)
418825eaba2fSLydia Wang 		return err;
418925eaba2fSLydia Wang 
419025eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
419125eaba2fSLydia Wang 
419225eaba2fSLydia Wang 	fill_dig_outs(codec);
419325eaba2fSLydia Wang 
419425eaba2fSLydia Wang 	if (spec->kctls.list)
419525eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
419625eaba2fSLydia Wang 
419725eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
419825eaba2fSLydia Wang 
419925eaba2fSLydia Wang 	if (spec->hp_mux)
42003d83e577STakashi Iwai 		via_hp_build(codec);
420125eaba2fSLydia Wang 
420225eaba2fSLydia Wang 	return 1;
420325eaba2fSLydia Wang }
420425eaba2fSLydia Wang 
420525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
420690dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = {
420725eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
420825eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
420925eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
421025eaba2fSLydia Wang 	{ } /* end */
421125eaba2fSLydia Wang };
421225eaba2fSLydia Wang #endif
421325eaba2fSLydia Wang 
42143e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
42153e95b9abSLydia Wang {
42163e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
42173e95b9abSLydia Wang 	int imux_is_smixer;
42183e95b9abSLydia Wang 	unsigned int parm;
42193e95b9abSLydia Wang 	unsigned int present;
42203e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
42213e95b9abSLydia Wang 	imux_is_smixer =
42223e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
42233e95b9abSLydia Wang 	/* inputs */
42243e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
42253e95b9abSLydia Wang 	parm = AC_PWRST_D3;
42263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
42273e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
42283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
42293e95b9abSLydia Wang 	parm = AC_PWRST_D0;
42303e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
42313e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
42323e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
42333e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
42343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
42353e95b9abSLydia Wang 
42363e95b9abSLydia Wang 	/* outputs */
42373e95b9abSLydia Wang 	/* AOW0 (8h)*/
42383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
42393e95b9abSLydia Wang 
424011890956SLydia Wang 	if (spec->codec_type == VT1802) {
424111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
424211890956SLydia Wang 		parm = AC_PWRST_D3;
424311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
424411890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
424511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
424611890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
424711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
424811890956SLydia Wang 	} else {
42493e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
42503e95b9abSLydia Wang 		parm = AC_PWRST_D3;
42513e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
42523e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
42533e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
42543e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
42553e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
425611890956SLydia Wang 	}
42573e95b9abSLydia Wang 
425811890956SLydia Wang 	if (spec->codec_type == VT1802) {
425911890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
426011890956SLydia Wang 		parm = AC_PWRST_D3;
426111890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
426211890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
426311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
426411890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
426511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
426611890956SLydia Wang 	} else {
42673e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
42683e95b9abSLydia Wang 		parm = AC_PWRST_D3;
42693e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
42703e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
42713e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
42723e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
42733e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
427411890956SLydia Wang 	}
42753e95b9abSLydia Wang 
42763e95b9abSLydia Wang 	if (spec->hp_independent_mode)
42773e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
42783e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
42793e95b9abSLydia Wang 
42803e95b9abSLydia Wang 	/* Class-D */
42813e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
42823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
42833e95b9abSLydia Wang 
42843e95b9abSLydia Wang 	parm = AC_PWRST_D3;
42853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
42863e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
428711890956SLydia Wang 	if (spec->codec_type == VT1802)
428811890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
428911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
429011890956SLydia Wang 	else
42913e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
42923e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
42933e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
42943e95b9abSLydia Wang 
42953e95b9abSLydia Wang 	/* Mono Out */
42963e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
42973e95b9abSLydia Wang 
42983e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
429911890956SLydia Wang 	if (spec->codec_type == VT1802) {
430011890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
430111890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
430211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
430311890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
430411890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
430511890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
430611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
430711890956SLydia Wang 	} else {
43083e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
43093e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
43103e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
43113e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
43123e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
43133e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
43143e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
431511890956SLydia Wang 	}
43163e95b9abSLydia Wang 	/* MW9 (21h) */
43173e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
43183e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
43193e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
43203e95b9abSLydia Wang 	else
43213e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
43223e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
43233e95b9abSLydia Wang }
432425eaba2fSLydia Wang 
432525eaba2fSLydia Wang /* patch for vt2002P */
432625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
432725eaba2fSLydia Wang {
432825eaba2fSLydia Wang 	struct via_spec *spec;
432925eaba2fSLydia Wang 	int err;
433025eaba2fSLydia Wang 
433125eaba2fSLydia Wang 	/* create a codec specific record */
43325b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
433325eaba2fSLydia Wang 	if (spec == NULL)
433425eaba2fSLydia Wang 		return -ENOMEM;
433525eaba2fSLydia Wang 
4336620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4337620e2b28STakashi Iwai 
433825eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
433925eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
434025eaba2fSLydia Wang 	if (err < 0) {
434125eaba2fSLydia Wang 		via_free(codec);
434225eaba2fSLydia Wang 		return err;
434325eaba2fSLydia Wang 	} else if (!err) {
434425eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
434525eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
434625eaba2fSLydia Wang 	}
434725eaba2fSLydia Wang 
434811890956SLydia Wang 	if (spec->codec_type == VT1802)
434911890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
435011890956SLydia Wang 			vt1802_volume_init_verbs;
435111890956SLydia Wang 	else
435211890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
435311890956SLydia Wang 			vt2002P_volume_init_verbs;
435425eaba2fSLydia Wang 
435511890956SLydia Wang 	if (spec->codec_type == VT1802)
435611890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
435711890956SLydia Wang 			vt1802_uniwill_init_verbs;
435811890956SLydia Wang 	else
435911890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
436011890956SLydia Wang 			vt2002P_uniwill_init_verbs;
436111890956SLydia Wang 
4362a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
436325eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
436425eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
436525eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
436625eaba2fSLydia Wang 		spec->num_mixers++;
436725eaba2fSLydia Wang 	}
436825eaba2fSLydia Wang 
436925eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
437025eaba2fSLydia Wang 
437125eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
43720f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
437325eaba2fSLydia Wang 
437425eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
437525eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
437625eaba2fSLydia Wang #endif
437725eaba2fSLydia Wang 
43783e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
437925eaba2fSLydia Wang 	return 0;
438025eaba2fSLydia Wang }
4381ab6734e7SLydia Wang 
4382ab6734e7SLydia Wang /* for vt1812 */
4383ab6734e7SLydia Wang 
4384ab6734e7SLydia Wang /* capture mixer elements */
438590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = {
4386ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4387ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4388ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4389ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4390ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4391ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
4392ab6734e7SLydia Wang 		       HDA_INPUT),
4393ab6734e7SLydia Wang 	{
4394ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4395ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4396ab6734e7SLydia Wang 		 * So call somewhat different..
4397ab6734e7SLydia Wang 		 */
4398ab6734e7SLydia Wang 		.name = "Input Source",
4399ab6734e7SLydia Wang 		.count = 2,
4400ab6734e7SLydia Wang 		.info = via_mux_enum_info,
4401ab6734e7SLydia Wang 		.get = via_mux_enum_get,
4402ab6734e7SLydia Wang 		.put = via_mux_enum_put,
4403ab6734e7SLydia Wang 	},
4404ab6734e7SLydia Wang 	{ } /* end */
4405ab6734e7SLydia Wang };
4406ab6734e7SLydia Wang 
440790dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = {
4408ab6734e7SLydia Wang 	/*
4409ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4410ab6734e7SLydia Wang 	 */
4411ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4412ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4413ab6734e7SLydia Wang 
4414ab6734e7SLydia Wang 
4415ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4416ab6734e7SLydia Wang 	 * mixer widget
4417ab6734e7SLydia Wang 	 */
4418ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4419ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4420ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4421ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4422ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4423ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4424ab6734e7SLydia Wang 
4425ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
4426ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
4427ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
4428ab6734e7SLydia Wang 
4429ab6734e7SLydia Wang 	/* PW9 Output enable */
4430ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4431ab6734e7SLydia Wang 
4432ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
4433ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
4434ab6734e7SLydia Wang 
4435ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
4436ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4437ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4438ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4439ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4440ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4441ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4442ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4443ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4444ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4445ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4446ab6734e7SLydia Wang 
4447ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
4448ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
4449ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
4450ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
4451ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
4452ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
4453ab6734e7SLydia Wang 
4454ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
4455ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
4456ab6734e7SLydia Wang 	{ }
4457ab6734e7SLydia Wang };
4458ab6734e7SLydia Wang 
4459ab6734e7SLydia Wang 
446090dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = {
4461ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
4462ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4463ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
4464ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4465ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4466ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4467ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4468ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4469ab6734e7SLydia Wang 	{ }
4470ab6734e7SLydia Wang };
4471ab6734e7SLydia Wang 
4472ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
4473ab6734e7SLydia Wang {
4474ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
4475ab6734e7SLydia Wang 	int err;
4476ab6734e7SLydia Wang 
4477ab6734e7SLydia Wang 
4478ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4479ab6734e7SLydia Wang 	if (err < 0)
4480ab6734e7SLydia Wang 		return err;
4481ab6734e7SLydia Wang 	fill_dig_outs(codec);
44824a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
4483ab6734e7SLydia Wang 	if (err < 0)
4484ab6734e7SLydia Wang 		return err;
4485ab6734e7SLydia Wang 
4486ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
4487ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
4488ab6734e7SLydia Wang 
44894a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
4490ab6734e7SLydia Wang 	if (err < 0)
4491ab6734e7SLydia Wang 		return err;
44924a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
4493ab6734e7SLydia Wang 	if (err < 0)
4494ab6734e7SLydia Wang 		return err;
4495620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
4496ab6734e7SLydia Wang 	if (err < 0)
4497ab6734e7SLydia Wang 		return err;
4498ab6734e7SLydia Wang 
4499ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4500ab6734e7SLydia Wang 
4501ab6734e7SLydia Wang 	fill_dig_outs(codec);
4502ab6734e7SLydia Wang 
4503ab6734e7SLydia Wang 	if (spec->kctls.list)
4504ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4505ab6734e7SLydia Wang 
4506ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
4507ab6734e7SLydia Wang 
4508ab6734e7SLydia Wang 	if (spec->hp_mux)
45093d83e577STakashi Iwai 		via_hp_build(codec);
4510ab6734e7SLydia Wang 
4511ab6734e7SLydia Wang 	return 1;
4512ab6734e7SLydia Wang }
4513ab6734e7SLydia Wang 
4514ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
451590dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = {
4516ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
4517ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4518ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4519ab6734e7SLydia Wang 	{ } /* end */
4520ab6734e7SLydia Wang };
4521ab6734e7SLydia Wang #endif
4522ab6734e7SLydia Wang 
45233e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
45243e95b9abSLydia Wang {
45253e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
45263e95b9abSLydia Wang 	int imux_is_smixer =
45273e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
45283e95b9abSLydia Wang 	unsigned int parm;
45293e95b9abSLydia Wang 	unsigned int present;
45303e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
45313e95b9abSLydia Wang 	imux_is_smixer =
45323e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
45333e95b9abSLydia Wang 	/* inputs */
45343e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
45353e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45363e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
45373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
45383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
45393e95b9abSLydia Wang 	parm = AC_PWRST_D0;
45403e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
45413e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
45423e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
45433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
45443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
45453e95b9abSLydia Wang 
45463e95b9abSLydia Wang 	/* outputs */
45473e95b9abSLydia Wang 	/* AOW0 (8h)*/
45483e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
45493e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
45503e95b9abSLydia Wang 
45513e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
45523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
45543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
45553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
45563e95b9abSLydia Wang 
45573e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
45583e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
45603e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
45613e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
45623e95b9abSLydia Wang 	if (spec->hp_independent_mode)
45633e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
45643e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
45653e95b9abSLydia Wang 
45663e95b9abSLydia Wang 	/* Internal Speaker */
45673e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
45683e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
45693e95b9abSLydia Wang 
45703e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45713e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
45723e95b9abSLydia Wang 	if (present) {
45733e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
45743e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
45753e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
45763e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
45773e95b9abSLydia Wang 	} else {
45783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
45793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
45803e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
45813e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
45823e95b9abSLydia Wang 	}
45833e95b9abSLydia Wang 
45843e95b9abSLydia Wang 
45853e95b9abSLydia Wang 	/* Mono Out */
45863e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
45873e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
45883e95b9abSLydia Wang 
45893e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
45913e95b9abSLydia Wang 	if (present) {
45923e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
45933e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
45943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
45953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
45963e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
45973e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
45983e95b9abSLydia Wang 	} else {
45993e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
46003e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
46013e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
46023e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
46033e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
46043e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
46053e95b9abSLydia Wang 	}
46063e95b9abSLydia Wang 
46073e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
46083e95b9abSLydia Wang 	parm = AC_PWRST_D3;
46093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
46103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
46113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
46123e95b9abSLydia Wang 
46133e95b9abSLydia Wang }
4614ab6734e7SLydia Wang 
4615ab6734e7SLydia Wang /* patch for vt1812 */
4616ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
4617ab6734e7SLydia Wang {
4618ab6734e7SLydia Wang 	struct via_spec *spec;
4619ab6734e7SLydia Wang 	int err;
4620ab6734e7SLydia Wang 
4621ab6734e7SLydia Wang 	/* create a codec specific record */
46225b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4623ab6734e7SLydia Wang 	if (spec == NULL)
4624ab6734e7SLydia Wang 		return -ENOMEM;
4625ab6734e7SLydia Wang 
4626620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4627620e2b28STakashi Iwai 
4628ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
4629ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
4630ab6734e7SLydia Wang 	if (err < 0) {
4631ab6734e7SLydia Wang 		via_free(codec);
4632ab6734e7SLydia Wang 		return err;
4633ab6734e7SLydia Wang 	} else if (!err) {
4634ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4635ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4636ab6734e7SLydia Wang 	}
4637ab6734e7SLydia Wang 
4638ab6734e7SLydia Wang 
4639ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
4640ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
4641ab6734e7SLydia Wang 
4642a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
4643ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
4644ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
4645ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
4646ab6734e7SLydia Wang 		spec->num_mixers++;
4647ab6734e7SLydia Wang 	}
4648ab6734e7SLydia Wang 
4649ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
4650ab6734e7SLydia Wang 
4651ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
46520f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4653ab6734e7SLydia Wang 
4654ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4655ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
4656ab6734e7SLydia Wang #endif
4657ab6734e7SLydia Wang 
46583e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
4659ab6734e7SLydia Wang 	return 0;
4660ab6734e7SLydia Wang }
4661ab6734e7SLydia Wang 
4662c577b8a1SJoseph Chan /*
4663c577b8a1SJoseph Chan  * patch entries
4664c577b8a1SJoseph Chan  */
466590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
46663218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
46673218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
46683218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
46693218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
46703218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
4671f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
46723218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
4673f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
46743218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
4675f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
46763218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
4677f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
46783218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
4679f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
46803218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
4681f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
46823218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
4683f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
46843218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
4685f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
46863218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
4687f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
46883218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
4689f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
46903218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
4691f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
46923218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
4693f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
46943218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
4695f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
46963218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
4697f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
46983218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
4699f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
47003218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
4701f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
47023218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
4703d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47043218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
4705d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47063218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
4707d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47083218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
4709d949cac1SHarald Welte 	  .patch = patch_vt1708S},
4710bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
4711d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47123218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
4713d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47143218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
4715d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47163218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
4717d949cac1SHarald Welte 	  .patch = patch_vt1708S},
47183218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
4719d949cac1SHarald Welte 	  .patch = patch_vt1702},
47203218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
4721d949cac1SHarald Welte 	  .patch = patch_vt1702},
47223218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
4723d949cac1SHarald Welte 	  .patch = patch_vt1702},
47243218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
4725d949cac1SHarald Welte 	  .patch = patch_vt1702},
47263218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
4727d949cac1SHarald Welte 	  .patch = patch_vt1702},
47283218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
4729d949cac1SHarald Welte 	  .patch = patch_vt1702},
47303218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
4731d949cac1SHarald Welte 	  .patch = patch_vt1702},
47323218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
4733d949cac1SHarald Welte 	  .patch = patch_vt1702},
4734eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
4735eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4736eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
4737eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4738bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
4739bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4740bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
4741bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4742f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
4743f3db423dSLydia Wang 	  .patch = patch_vt1716S},
4744f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
4745f3db423dSLydia Wang 	  .patch = patch_vt1716S},
474625eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
474725eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
4748ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
474936dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
475036dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
475111890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
475211890956SLydia Wang 		.patch = patch_vt2002P},
475311890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
475411890956SLydia Wang 		.patch = patch_vt2002P},
4755c577b8a1SJoseph Chan 	{} /* terminator */
4756c577b8a1SJoseph Chan };
47571289e9e8STakashi Iwai 
47581289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
47591289e9e8STakashi Iwai 
47601289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
47611289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
47621289e9e8STakashi Iwai 	.owner = THIS_MODULE,
47631289e9e8STakashi Iwai };
47641289e9e8STakashi Iwai 
47651289e9e8STakashi Iwai MODULE_LICENSE("GPL");
47661289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
47671289e9e8STakashi Iwai 
47681289e9e8STakashi Iwai static int __init patch_via_init(void)
47691289e9e8STakashi Iwai {
47701289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
47711289e9e8STakashi Iwai }
47721289e9e8STakashi Iwai 
47731289e9e8STakashi Iwai static void __exit patch_via_exit(void)
47741289e9e8STakashi Iwai {
47751289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
47761289e9e8STakashi Iwai }
47771289e9e8STakashi Iwai 
47781289e9e8STakashi Iwai module_init(patch_via_init)
47791289e9e8STakashi Iwai module_exit(patch_via_exit)
4780