xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 4a79616d079f833714c9ef63a9b825caacafe675)
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 
110*4a79616dSTakashi Iwai struct nid_path {
111*4a79616dSTakashi Iwai 	int depth;
112*4a79616dSTakashi Iwai 	hda_nid_t path[5];
113*4a79616dSTakashi Iwai 	short idx[5];
114*4a79616dSTakashi Iwai };
115*4a79616dSTakashi 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 
136*4a79616dSTakashi Iwai 	struct nid_path out_path[4];
137*4a79616dSTakashi Iwai 	struct nid_path hp_path;
138*4a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
139*4a79616dSTakashi 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;
1651f2e99feSLydia Wang 	unsigned int smart51_enabled;
166f3db423dSLydia Wang 	unsigned int dmic_enabled;
16724088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1681f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1691f2e99feSLydia Wang 
1701f2e99feSLydia Wang 	/* work to check hp jack state */
1711f2e99feSLydia Wang 	struct hda_codec *codec;
1721f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
173e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1741f2e99feSLydia Wang 	int vt1708_hp_present;
1753e95b9abSLydia Wang 
1763e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1773e95b9abSLydia Wang 
1781f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1791f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1801f2e99feSLydia Wang #endif
1811f2e99feSLydia Wang };
1821f2e99feSLydia Wang 
1830341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1845b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1855b0cb1d8SJaroslav Kysela {
1865b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1875b0cb1d8SJaroslav Kysela 
1885b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1895b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1905b0cb1d8SJaroslav Kysela 		return NULL;
1915b0cb1d8SJaroslav Kysela 
1925b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1935b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1940341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1950341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1960341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1970341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1985b0cb1d8SJaroslav Kysela 	return spec;
1995b0cb1d8SJaroslav Kysela }
2005b0cb1d8SJaroslav Kysela 
201744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
202d7426329SHarald Welte {
203744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
204d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
205d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
206d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
207d7426329SHarald Welte 
208d7426329SHarald Welte 	/* get codec type */
209d7426329SHarald Welte 	if (ven_id != 0x1106)
210d7426329SHarald Welte 		codec_type = UNKNOWN;
211d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
212d7426329SHarald Welte 		codec_type = VT1708;
213d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
214d7426329SHarald Welte 		codec_type = VT1709_10CH;
215d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
216d7426329SHarald Welte 		codec_type = VT1709_6CH;
217518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
218d7426329SHarald Welte 		codec_type = VT1708B_8CH;
219518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
220518bf3baSLydia Wang 			codec_type = VT1708BCE;
221518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
222d7426329SHarald Welte 		codec_type = VT1708B_4CH;
223d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
224d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
225d7426329SHarald Welte 		codec_type = VT1708S;
226d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
227d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
228d7426329SHarald Welte 		codec_type = VT1702;
229eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
230eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
231eb7188caSLydia Wang 		codec_type = VT1718S;
232f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
233f3db423dSLydia Wang 		codec_type = VT1716S;
234bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
235bb3c6bfcSLydia Wang 		codec_type = VT1718S;
23625eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
23725eaba2fSLydia Wang 		codec_type = VT2002P;
238ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
239ab6734e7SLydia Wang 		codec_type = VT1812;
24036dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
24136dd5c4aSLydia Wang 		codec_type = VT1708S;
24211890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
24311890956SLydia Wang 		codec_type = VT1802;
244d7426329SHarald Welte 	else
245d7426329SHarald Welte 		codec_type = UNKNOWN;
246d7426329SHarald Welte 	return codec_type;
247d7426329SHarald Welte };
248d7426329SHarald Welte 
249ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
25069e52a80SHarald Welte #define VIA_HP_EVENT		0x01
25169e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
252ec7e7e42SLydia Wang #define VIA_MONO_EVENT		0x03
253ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT	0x04
254ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT	0x05
25569e52a80SHarald Welte 
256c577b8a1SJoseph Chan enum {
257c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
258c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
259f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
26025eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
261c577b8a1SJoseph Chan };
262c577b8a1SJoseph Chan 
263c577b8a1SJoseph Chan enum {
264eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
265c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
266c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
267c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
268c577b8a1SJoseph Chan };
269c577b8a1SJoseph Chan 
270f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2711f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2721f2e99feSLydia Wang 
2731f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2741f2e99feSLydia Wang {
2751f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2761f2e99feSLydia Wang 		return;
2771f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
278e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2791f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2801f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2811f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2821f2e99feSLydia Wang }
2831f2e99feSLydia Wang 
2841f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2851f2e99feSLydia Wang {
2861f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2871f2e99feSLydia Wang 		return;
2881f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2891f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2901f2e99feSLydia Wang 		return;
2911f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
292e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2935b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2941f2e99feSLydia Wang }
295f5271101SLydia Wang 
2963e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2973e95b9abSLydia Wang {
2983e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2993e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3003e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3013e95b9abSLydia Wang }
30225eaba2fSLydia Wang 
303f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
304f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
305f5271101SLydia Wang {
306f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
307f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
308f5271101SLydia Wang 
3093e95b9abSLydia Wang 	set_widgets_power_state(codec);
310f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
3111f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3121f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3131f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3141f2e99feSLydia Wang 		else
3151f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3161f2e99feSLydia Wang 	}
317f5271101SLydia Wang 	return change;
318f5271101SLydia Wang }
319f5271101SLydia Wang 
320f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
321f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
322f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
323f5271101SLydia Wang 			.name = NULL,					\
324f5271101SLydia Wang 			.index = 0,					\
325f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
326f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
327f5271101SLydia Wang 			.put = analog_input_switch_put,			\
328f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
329f5271101SLydia Wang 
33025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
33125eaba2fSLydia Wang 
33225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
33325eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
33425eaba2fSLydia Wang {
33525eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
33725eaba2fSLydia Wang 	int i;
33825eaba2fSLydia Wang 	int change = 0;
33925eaba2fSLydia Wang 
34025eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
34125eaba2fSLydia Wang 	int lmute, rmute;
34225eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
34325eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
34425eaba2fSLydia Wang 		return change;
34525eaba2fSLydia Wang 	}
34625eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
34725eaba2fSLydia Wang 					      ucontrol);
34825eaba2fSLydia Wang 	/* Get mute value */
34925eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
35025eaba2fSLydia Wang 	valp++;
35125eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
35225eaba2fSLydia Wang 
35325eaba2fSLydia Wang 	/* Set hp pins */
35425eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
35525eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
35625eaba2fSLydia Wang 			snd_hda_codec_amp_update(
35725eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
35825eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
35925eaba2fSLydia Wang 				lmute);
36025eaba2fSLydia Wang 			snd_hda_codec_amp_update(
36125eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
36225eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36325eaba2fSLydia Wang 				rmute);
36425eaba2fSLydia Wang 		}
36525eaba2fSLydia Wang 	}
36625eaba2fSLydia Wang 
36725eaba2fSLydia Wang 	if (!lmute && !rmute) {
36825eaba2fSLydia Wang 		/* Line Outs */
36925eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
37025eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37125eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
37225eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
37325eaba2fSLydia Wang 		/* Speakers */
37425eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
37525eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37625eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
37725eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
37825eaba2fSLydia Wang 		/* unmute */
37925eaba2fSLydia Wang 		via_hp_bind_automute(codec);
38025eaba2fSLydia Wang 
38125eaba2fSLydia Wang 	} else {
38225eaba2fSLydia Wang 		if (lmute) {
38325eaba2fSLydia Wang 			/* Mute all left channels */
38425eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38525eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38625eaba2fSLydia Wang 					codec,
38725eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
38825eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
38925eaba2fSLydia Wang 					lmute);
39025eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
39125eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39225eaba2fSLydia Wang 					codec,
39325eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
39425eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39525eaba2fSLydia Wang 					lmute);
39625eaba2fSLydia Wang 		}
39725eaba2fSLydia Wang 		if (rmute) {
39825eaba2fSLydia Wang 			/* mute all right channels */
39925eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
40025eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40125eaba2fSLydia Wang 					codec,
40225eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
40325eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
40425eaba2fSLydia Wang 					rmute);
40525eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
40625eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40725eaba2fSLydia Wang 					codec,
40825eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
40925eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
41025eaba2fSLydia Wang 					rmute);
41125eaba2fSLydia Wang 		}
41225eaba2fSLydia Wang 	}
41325eaba2fSLydia Wang 	return change;
41425eaba2fSLydia Wang }
41525eaba2fSLydia Wang 
41625eaba2fSLydia Wang #define BIND_PIN_MUTE							\
41725eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
41825eaba2fSLydia Wang 			.name = NULL,					\
41925eaba2fSLydia Wang 			.index = 0,					\
42025eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
42125eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
42225eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
42325eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
42425eaba2fSLydia Wang 
42590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
426c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
427c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
428f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
42925eaba2fSLydia Wang 	BIND_PIN_MUTE,
430c577b8a1SJoseph Chan };
431c577b8a1SJoseph Chan 
432ab6734e7SLydia Wang 
433c577b8a1SJoseph Chan /* add dynamic controls */
434291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
435291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
436291c9e33STakashi Iwai 				const char *name)
437c577b8a1SJoseph Chan {
438c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
439c577b8a1SJoseph Chan 
440603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
441603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
442c577b8a1SJoseph Chan 	if (!knew)
443291c9e33STakashi Iwai 		return NULL;
444291c9e33STakashi Iwai 	*knew = *tmpl;
445291c9e33STakashi Iwai 	if (!name)
446291c9e33STakashi Iwai 		name = tmpl->name;
447291c9e33STakashi Iwai 	if (name) {
448c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
449c577b8a1SJoseph Chan 		if (!knew->name)
450291c9e33STakashi Iwai 			return NULL;
451291c9e33STakashi Iwai 	}
452291c9e33STakashi Iwai 	return knew;
453291c9e33STakashi Iwai }
454291c9e33STakashi Iwai 
455291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
456291c9e33STakashi Iwai 			     int idx, unsigned long val)
457291c9e33STakashi Iwai {
458291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
459291c9e33STakashi Iwai 
460291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
461291c9e33STakashi Iwai 	if (!knew)
462c577b8a1SJoseph Chan 		return -ENOMEM;
4634d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4645e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
465c577b8a1SJoseph Chan 	knew->private_value = val;
466c577b8a1SJoseph Chan 	return 0;
467c577b8a1SJoseph Chan }
468c577b8a1SJoseph Chan 
4697b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4707b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4717b315bb4STakashi Iwai 
472291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
4735b0cb1d8SJaroslav Kysela 
474603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
475603c4019STakashi Iwai {
476603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
477603c4019STakashi Iwai 
478603c4019STakashi Iwai 	if (spec->kctls.list) {
479603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
480603c4019STakashi Iwai 		int i;
481603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
482603c4019STakashi Iwai 			kfree(kctl[i].name);
483603c4019STakashi Iwai 	}
484603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
485603c4019STakashi Iwai }
486603c4019STakashi Iwai 
487c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4889510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4897b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
490c577b8a1SJoseph Chan {
491c577b8a1SJoseph Chan 	char name[32];
492c577b8a1SJoseph Chan 	int err;
493c577b8a1SJoseph Chan 
494c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4957b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
496c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
497c577b8a1SJoseph Chan 	if (err < 0)
498c577b8a1SJoseph Chan 		return err;
499c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5007b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
501c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
502c577b8a1SJoseph Chan 	if (err < 0)
503c577b8a1SJoseph Chan 		return err;
504c577b8a1SJoseph Chan 	return 0;
505c577b8a1SJoseph Chan }
506c577b8a1SJoseph Chan 
507c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
508c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
509c577b8a1SJoseph Chan 					   int dac_idx)
510c577b8a1SJoseph Chan {
511c577b8a1SJoseph Chan 	/* set as output */
512c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
513c577b8a1SJoseph Chan 			    pin_type);
514c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
515c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
516d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
517d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
518d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
519c577b8a1SJoseph Chan }
520c577b8a1SJoseph Chan 
521c577b8a1SJoseph Chan 
522c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
523c577b8a1SJoseph Chan {
524c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
525c577b8a1SJoseph Chan 	int i;
526c577b8a1SJoseph Chan 
527c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
528c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
529c577b8a1SJoseph Chan 		if (nid)
530c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
531c577b8a1SJoseph Chan 	}
532c577b8a1SJoseph Chan }
533c577b8a1SJoseph Chan 
534c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
535c577b8a1SJoseph Chan {
536c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
537c577b8a1SJoseph Chan 	hda_nid_t pin;
53825eaba2fSLydia Wang 	int i;
539c577b8a1SJoseph Chan 
54025eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
54125eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
542c577b8a1SJoseph Chan 		if (pin) /* connect to front */
543c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
544c577b8a1SJoseph Chan 	}
54525eaba2fSLydia Wang }
546c577b8a1SJoseph Chan 
54732e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin);
54832e0191dSClemens Ladisch 
549c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
550c577b8a1SJoseph Chan {
551c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5527b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
55332e0191dSClemens Ladisch 	unsigned int ctl;
554c577b8a1SJoseph Chan 	int i;
555c577b8a1SJoseph Chan 
5567b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5577b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
55832e0191dSClemens Ladisch 		if (spec->smart51_enabled && is_smart51_pins(spec, nid))
55932e0191dSClemens Ladisch 			ctl = PIN_OUT;
56030649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
56132e0191dSClemens Ladisch 			ctl = PIN_VREF50;
56232e0191dSClemens Ladisch 		else
56332e0191dSClemens Ladisch 			ctl = PIN_IN;
564c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
56532e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
566c577b8a1SJoseph Chan 	}
567c577b8a1SJoseph Chan }
568f5271101SLydia Wang 
569f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
570f5271101SLydia Wang 				unsigned int *affected_parm)
571f5271101SLydia Wang {
572f5271101SLydia Wang 	unsigned parm;
573f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
574f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
575f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
576f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
5771564b287SLydia Wang 	struct via_spec *spec = codec->spec;
57824088a58STakashi Iwai 	unsigned present = 0;
57924088a58STakashi Iwai 
58024088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
58124088a58STakashi Iwai 	if (!no_presence)
58224088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
5831564b287SLydia Wang 	if ((spec->smart51_enabled && is_smart51_pins(spec, nid))
5841564b287SLydia Wang 	    || ((no_presence || present)
5851564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
586f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
587f5271101SLydia Wang 		parm = AC_PWRST_D0;
588f5271101SLydia Wang 	} else
589f5271101SLydia Wang 		parm = AC_PWRST_D3;
590f5271101SLydia Wang 
591f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
592f5271101SLydia Wang }
593f5271101SLydia Wang 
59424088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
59524088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
59624088a58STakashi Iwai {
59724088a58STakashi Iwai 	static const char * const texts[] = {
59824088a58STakashi Iwai 		"Disabled", "Enabled"
59924088a58STakashi Iwai 	};
60024088a58STakashi Iwai 
60124088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
60224088a58STakashi Iwai 	uinfo->count = 1;
60324088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
60424088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
60524088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
60624088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
60724088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
60824088a58STakashi Iwai 	return 0;
60924088a58STakashi Iwai }
61024088a58STakashi Iwai 
61124088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
61224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
61324088a58STakashi Iwai {
61424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
61524088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
61624088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
61724088a58STakashi Iwai 	return 0;
61824088a58STakashi Iwai }
61924088a58STakashi Iwai 
62024088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
62124088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
62224088a58STakashi Iwai {
62324088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
62424088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
62524088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
62624088a58STakashi Iwai 
62724088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
62824088a58STakashi Iwai 		return 0;
62924088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
63024088a58STakashi Iwai 	set_widgets_power_state(codec);
63124088a58STakashi Iwai 	return 1;
63224088a58STakashi Iwai }
63324088a58STakashi Iwai 
63424088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
63524088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
63624088a58STakashi Iwai 	.name = "Dynamic Power-Control",
63724088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
63824088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
63924088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
64024088a58STakashi Iwai };
64124088a58STakashi Iwai 
64224088a58STakashi Iwai 
643c577b8a1SJoseph Chan /*
644c577b8a1SJoseph Chan  * input MUX handling
645c577b8a1SJoseph Chan  */
646c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
647c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
648c577b8a1SJoseph Chan {
649c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
650c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
651c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
652c577b8a1SJoseph Chan }
653c577b8a1SJoseph Chan 
654c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
655c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
656c577b8a1SJoseph Chan {
657c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
658c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
659c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
660c577b8a1SJoseph Chan 
661c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
662c577b8a1SJoseph Chan 	return 0;
663c577b8a1SJoseph Chan }
664c577b8a1SJoseph Chan 
665c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
666c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
667c577b8a1SJoseph Chan {
668c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
669c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
670c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
671bff5fbf5SLydia Wang 	int ret;
672c577b8a1SJoseph Chan 
673337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
674337b9d02STakashi Iwai 		return -EINVAL;
675a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
676a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
677a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
678a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
679a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
680bff5fbf5SLydia Wang 
681bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
682bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
683bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
684a80e6e3cSLydia Wang 	/* update jack power state */
6853e95b9abSLydia Wang 	set_widgets_power_state(codec);
686a80e6e3cSLydia Wang 
687bff5fbf5SLydia Wang 	return ret;
688c577b8a1SJoseph Chan }
689c577b8a1SJoseph Chan 
6900aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6910aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6920aa62aefSHarald Welte {
6930aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6940aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6950aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6960aa62aefSHarald Welte }
6970aa62aefSHarald Welte 
6980aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
6990aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7000aa62aefSHarald Welte {
7010aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7025b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
703eb7188caSLydia Wang 	unsigned int pinsel;
704eb7188caSLydia Wang 
705eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
706eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
7070aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
7080aa62aefSHarald Welte 				      0x00);
7090aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
7100aa62aefSHarald Welte 
7110aa62aefSHarald Welte 	return 0;
7120aa62aefSHarald Welte }
7130aa62aefSHarald Welte 
7140713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
7150713efebSLydia Wang {
7160713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
7170713efebSLydia Wang 	if (ctl) {
7180713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7190713efebSLydia Wang 		ctl->vd[0].access |= active
7200713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7210713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
7220713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
7230713efebSLydia Wang 	}
7240713efebSLydia Wang }
7250713efebSLydia Wang 
7265b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
7275b0cb1d8SJaroslav Kysela {
7285b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7295b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7305b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7315b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7325b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
733e87885feSLydia Wang 	case VT2002P:		return 0x19;
734e87885feSLydia Wang 	case VT1802:		return 0x15;
735e87885feSLydia Wang 	case VT1812:		return 0x15;
7365b0cb1d8SJaroslav Kysela 	default:		return 0;
7375b0cb1d8SJaroslav Kysela 	}
7385b0cb1d8SJaroslav Kysela }
7395b0cb1d8SJaroslav Kysela 
740cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
741cdc1784dSLydia Wang {
742cdc1784dSLydia Wang 	/* mute side channel */
743cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
744e87885feSLydia Wang 	unsigned int parm;
7455b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
746cdc1784dSLydia Wang 
747e87885feSLydia Wang 	if (sw3) {
748e87885feSLydia Wang 		if (VT2002P_COMPATIBLE(spec))
749e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
750e87885feSLydia Wang 			       AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
751e87885feSLydia Wang 		else
752e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
753e87885feSLydia Wang 			       AMP_OUT_MUTE : AMP_OUT_UNMUTE;
754e87885feSLydia Wang 		snd_hda_codec_write(codec, sw3, 0,
755e87885feSLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, parm);
756e87885feSLydia Wang 		if (spec->codec_type == VT1812)
757e87885feSLydia Wang 			snd_hda_codec_write(codec, 0x1d, 0,
758e87885feSLydia Wang 					    AC_VERB_SET_AMP_GAIN_MUTE, parm);
759e87885feSLydia Wang 	}
760cdc1784dSLydia Wang 	return 0;
761cdc1784dSLydia Wang }
762cdc1784dSLydia Wang 
7630aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7640aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7650aa62aefSHarald Welte {
7660aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7670aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7685b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7690aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
770cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
771cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
772cdc1784dSLydia Wang 		? 1 : 0;
773ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
774ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
775ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
776ce0e5a9eSLydia Wang 	else
777ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
778ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7790aa62aefSHarald Welte 
780ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
781ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
782ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
783cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
784cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
785cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7860aa62aefSHarald Welte 					   0, 0, 0);
7870aa62aefSHarald Welte 
788cdc1784dSLydia Wang 	update_side_mute_status(codec);
7890713efebSLydia Wang 	/* update HP volume/swtich active state */
7900713efebSLydia Wang 	if (spec->codec_type == VT1708S
791eb7188caSLydia Wang 	    || spec->codec_type == VT1702
792f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
79325eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
79411890956SLydia Wang 	    || VT2002P_COMPATIBLE(spec)) {
7950713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7960713efebSLydia Wang 			     spec->hp_independent_mode);
7970713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
7980713efebSLydia Wang 			     spec->hp_independent_mode);
7990713efebSLydia Wang 	}
800ce0e5a9eSLydia Wang 	/* update jack power state */
8013e95b9abSLydia Wang 	set_widgets_power_state(codec);
8020aa62aefSHarald Welte 	return 0;
8030aa62aefSHarald Welte }
8040aa62aefSHarald Welte 
80590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = {
8060aa62aefSHarald Welte 	{
8070aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8080aa62aefSHarald Welte 		.name = "Independent HP",
8090aa62aefSHarald Welte 		.info = via_independent_hp_info,
8100aa62aefSHarald Welte 		.get = via_independent_hp_get,
8110aa62aefSHarald Welte 		.put = via_independent_hp_put,
8120aa62aefSHarald Welte 	},
8135b0cb1d8SJaroslav Kysela 	{
8145b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
8155b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
8165b0cb1d8SJaroslav Kysela 	},
8170aa62aefSHarald Welte };
8180aa62aefSHarald Welte 
8193d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
8205b0cb1d8SJaroslav Kysela {
8213d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8225b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8235b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8243d83e577STakashi Iwai 	int nums;
8253d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
8265b0cb1d8SJaroslav Kysela 
8275b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
8285b0cb1d8SJaroslav Kysela 	case VT1718S:
8295b0cb1d8SJaroslav Kysela 		nid = 0x34;
8305b0cb1d8SJaroslav Kysela 		break;
8315b0cb1d8SJaroslav Kysela 	case VT2002P:
83211890956SLydia Wang 	case VT1802:
8335b0cb1d8SJaroslav Kysela 		nid = 0x35;
8345b0cb1d8SJaroslav Kysela 		break;
8355b0cb1d8SJaroslav Kysela 	case VT1812:
8365b0cb1d8SJaroslav Kysela 		nid = 0x3d;
8375b0cb1d8SJaroslav Kysela 		break;
8385b0cb1d8SJaroslav Kysela 	default:
8395b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
8405b0cb1d8SJaroslav Kysela 		break;
8415b0cb1d8SJaroslav Kysela 	}
8425b0cb1d8SJaroslav Kysela 
843ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
844ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
845ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8463d83e577STakashi Iwai 		if (nums <= 1)
8473d83e577STakashi Iwai 			return 0;
848ee3c35c0SLydia Wang 	}
8493d83e577STakashi Iwai 
8503d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8513d83e577STakashi Iwai 	if (knew == NULL)
8523d83e577STakashi Iwai 		return -ENOMEM;
8533d83e577STakashi Iwai 
8545b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8555b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8565b0cb1d8SJaroslav Kysela 
8575b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8585b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8595b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8605b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8615b0cb1d8SJaroslav Kysela 
8625b0cb1d8SJaroslav Kysela 	return 0;
8635b0cb1d8SJaroslav Kysela }
8645b0cb1d8SJaroslav Kysela 
8651564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8661564b287SLydia Wang {
8671564b287SLydia Wang 	int i;
8681564b287SLydia Wang 	struct snd_ctl_elem_id id;
869525566cbSLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
870525566cbSLydia Wang 	struct snd_kcontrol *ctl;
8711564b287SLydia Wang 
8721564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8731564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8741564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8751564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
876525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
877525566cbSLydia Wang 		if (ctl)
878525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
879525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
880525566cbSLydia Wang 					&ctl->id);
8811564b287SLydia Wang 	}
8821564b287SLydia Wang }
8831564b287SLydia Wang 
8841564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8851564b287SLydia Wang {
8861564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8871564b287SLydia Wang 	int start_idx;
8881564b287SLydia Wang 	int end_idx;
8891564b287SLydia Wang 	int i;
8901564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8911564b287SLydia Wang 	switch (spec->codec_type) {
8921564b287SLydia Wang 	case VT1708:
8931564b287SLydia Wang 		start_idx = 2;
8941564b287SLydia Wang 		end_idx = 4;
8951564b287SLydia Wang 		break;
8961564b287SLydia Wang 	case VT1709_10CH:
8971564b287SLydia Wang 	case VT1709_6CH:
8981564b287SLydia Wang 		start_idx = 2;
8991564b287SLydia Wang 		end_idx = 4;
9001564b287SLydia Wang 		break;
9011564b287SLydia Wang 	case VT1708B_8CH:
9021564b287SLydia Wang 	case VT1708B_4CH:
9031564b287SLydia Wang 	case VT1708S:
904f3db423dSLydia Wang 	case VT1716S:
9051564b287SLydia Wang 		start_idx = 2;
9061564b287SLydia Wang 		end_idx = 4;
9071564b287SLydia Wang 		break;
908ab657e0cSLydia Wang 	case VT1718S:
909ab657e0cSLydia Wang 		start_idx = 1;
910ab657e0cSLydia Wang 		end_idx = 3;
911ab657e0cSLydia Wang 		break;
9121564b287SLydia Wang 	default:
9131564b287SLydia Wang 		return;
9141564b287SLydia Wang 	}
9151564b287SLydia Wang 	/* check AA path's mute status */
9161564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
9171564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
918620e2b28STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i,
9191564b287SLydia Wang 					 HDA_AMP_MUTE, val);
9201564b287SLydia Wang 	}
9211564b287SLydia Wang }
9221564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin)
9231564b287SLydia Wang {
9247b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9257b315bb4STakashi Iwai 	int i;
9267b315bb4STakashi Iwai 
9277b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9287b315bb4STakashi Iwai 		if (pin == cfg->inputs[i].pin)
92986e2959aSTakashi Iwai 			return cfg->inputs[i].type <= AUTO_PIN_LINE_IN;
9301564b287SLydia Wang 	}
9317b315bb4STakashi Iwai 	return 0;
9321564b287SLydia Wang }
9331564b287SLydia Wang 
9341564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
9351564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
9361564b287SLydia Wang {
9371564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9381564b287SLydia Wang 	uinfo->count = 1;
9391564b287SLydia Wang 	uinfo->value.integer.min = 0;
9401564b287SLydia Wang 	uinfo->value.integer.max = 1;
9411564b287SLydia Wang 	return 0;
9421564b287SLydia Wang }
9431564b287SLydia Wang 
9441564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9451564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9461564b287SLydia Wang {
9471564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9481564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9497b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9501564b287SLydia Wang 	int on = 1;
9511564b287SLydia Wang 	int i;
9521564b287SLydia Wang 
9537b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9547b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9557b315bb4STakashi Iwai 		int ctl = snd_hda_codec_read(codec, nid, 0,
9567b315bb4STakashi Iwai 					     AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
95786e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9587b315bb4STakashi Iwai 			continue;
95986e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9607b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9611564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
9627b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9631564b287SLydia Wang 			on = 0;
9641564b287SLydia Wang 	}
9651564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9661564b287SLydia Wang 	return 0;
9671564b287SLydia Wang }
9681564b287SLydia Wang 
9691564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9701564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9711564b287SLydia Wang {
9721564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9731564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9747b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9751564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9761564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9771564b287SLydia Wang 	int i;
9781564b287SLydia Wang 
9797b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9807b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9817b315bb4STakashi Iwai 		unsigned int parm;
9827b315bb4STakashi Iwai 
98386e2959aSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
9847b315bb4STakashi Iwai 			continue;
98586e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9867b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9871564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
9887b315bb4STakashi Iwai 
9897b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
9901564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9911564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
9921564b287SLydia Wang 		parm |= out_in;
9931564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
9941564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
9951564b287SLydia Wang 				    parm);
9961564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
9971564b287SLydia Wang 			mute_aa_path(codec, 1);
9981564b287SLydia Wang 			notify_aa_path_ctls(codec);
9991564b287SLydia Wang 		}
10007b315bb4STakashi Iwai 		if (spec->codec_type == VT1718S) {
1001eb7188caSLydia Wang 			snd_hda_codec_amp_stereo(
1002eb7188caSLydia Wang 					codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE,
1003eb7188caSLydia Wang 					HDA_AMP_UNMUTE);
10041564b287SLydia Wang 		}
100586e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC) {
1006f3db423dSLydia Wang 			if (spec->codec_type == VT1708S
1007f3db423dSLydia Wang 			    || spec->codec_type == VT1716S) {
10081564b287SLydia Wang 				/* input = index 1 (AOW3) */
10091564b287SLydia Wang 				snd_hda_codec_write(
10101564b287SLydia Wang 					codec, nid, 0,
10111564b287SLydia Wang 					AC_VERB_SET_CONNECT_SEL, 1);
10121564b287SLydia Wang 				snd_hda_codec_amp_stereo(
10131564b287SLydia Wang 					codec, nid, HDA_OUTPUT,
10141564b287SLydia Wang 					0, HDA_AMP_MUTE, HDA_AMP_UNMUTE);
10151564b287SLydia Wang 			}
10161564b287SLydia Wang 		}
10171564b287SLydia Wang 	}
10181564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
10193e95b9abSLydia Wang 	set_widgets_power_state(codec);
10201564b287SLydia Wang 	return 1;
10211564b287SLydia Wang }
10221564b287SLydia Wang 
10235f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
10241564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10251564b287SLydia Wang 	.name = "Smart 5.1",
10261564b287SLydia Wang 	.count = 1,
10271564b287SLydia Wang 	.info = via_smart51_info,
10281564b287SLydia Wang 	.get = via_smart51_get,
10291564b287SLydia Wang 	.put = via_smart51_put,
10301564b287SLydia Wang };
10311564b287SLydia Wang 
10325b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec)
10335b0cb1d8SJaroslav Kysela {
10345b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10357b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10365b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10375b0cb1d8SJaroslav Kysela 	int i;
10385b0cb1d8SJaroslav Kysela 
1039cb34c207SLydia Wang 	if (!cfg)
1040cb34c207SLydia Wang 		return 0;
1041cb34c207SLydia Wang 	if (cfg->line_outs > 2)
1042cb34c207SLydia Wang 		return 0;
1043cb34c207SLydia Wang 
10445f4b36d6STakashi Iwai 	knew = via_clone_control(spec, &via_smart51_mixer);
10455b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10465b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10475b0cb1d8SJaroslav Kysela 
10487b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10497b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
105086e2959aSTakashi Iwai 		if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) {
10515f4b36d6STakashi Iwai 			knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
10527b315bb4STakashi Iwai 			break;
10535b0cb1d8SJaroslav Kysela 		}
10545b0cb1d8SJaroslav Kysela 	}
10555b0cb1d8SJaroslav Kysela 
10565b0cb1d8SJaroslav Kysela 	return 0;
10575b0cb1d8SJaroslav Kysela }
10585b0cb1d8SJaroslav Kysela 
1059c577b8a1SJoseph Chan /* capture mixer elements */
106090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = {
1061c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
1062c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT),
1063c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT),
1064c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT),
1065c577b8a1SJoseph Chan 	{
1066c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1067c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
1068c577b8a1SJoseph Chan 		 * So call somewhat different..
1069c577b8a1SJoseph Chan 		 */
1070c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
1071c577b8a1SJoseph Chan 		.name = "Input Source",
1072c577b8a1SJoseph Chan 		.count = 1,
1073c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
1074c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
1075c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
1076c577b8a1SJoseph Chan 	},
1077c577b8a1SJoseph Chan 	{ } /* end */
1078c577b8a1SJoseph Chan };
1079f5271101SLydia Wang 
1080f5271101SLydia Wang /* check AA path's mute statue */
1081f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1082f5271101SLydia Wang {
1083f5271101SLydia Wang 	int mute = 1;
1084f5271101SLydia Wang 	int start_idx;
1085f5271101SLydia Wang 	int end_idx;
1086f5271101SLydia Wang 	int i;
1087f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1088f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1089f5271101SLydia Wang 	switch (spec->codec_type) {
1090f5271101SLydia Wang 	case VT1708B_8CH:
1091f5271101SLydia Wang 	case VT1708B_4CH:
1092f5271101SLydia Wang 	case VT1708S:
1093f3db423dSLydia Wang 	case VT1716S:
1094f5271101SLydia Wang 		start_idx = 2;
1095f5271101SLydia Wang 		end_idx = 4;
1096f5271101SLydia Wang 		break;
1097f5271101SLydia Wang 	case VT1702:
1098f5271101SLydia Wang 		start_idx = 1;
1099f5271101SLydia Wang 		end_idx = 3;
1100f5271101SLydia Wang 		break;
1101eb7188caSLydia Wang 	case VT1718S:
1102eb7188caSLydia Wang 		start_idx = 1;
1103eb7188caSLydia Wang 		end_idx = 3;
1104eb7188caSLydia Wang 		break;
110525eaba2fSLydia Wang 	case VT2002P:
1106ab6734e7SLydia Wang 	case VT1812:
110711890956SLydia Wang 	case VT1802:
110825eaba2fSLydia Wang 		start_idx = 0;
110925eaba2fSLydia Wang 		end_idx = 2;
111025eaba2fSLydia Wang 		break;
1111f5271101SLydia Wang 	default:
1112f5271101SLydia Wang 		return 0;
1113f5271101SLydia Wang 	}
1114f5271101SLydia Wang 	/* check AA path's mute status */
1115f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1116f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1117620e2b28STakashi Iwai 			codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1118f5271101SLydia Wang 		int shift = 8 * (i % 4);
1119f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1120f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1121f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1122f5271101SLydia Wang 			/* check mute status while the pin is connected */
1123620e2b28STakashi Iwai 			int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
1124f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1125620e2b28STakashi Iwai 			int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
1126f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1127f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1128f5271101SLydia Wang 				mute = 0;
1129f5271101SLydia Wang 				break;
1130f5271101SLydia Wang 			}
1131f5271101SLydia Wang 		}
1132f5271101SLydia Wang 	}
1133f5271101SLydia Wang 	return mute;
1134f5271101SLydia Wang }
1135f5271101SLydia Wang 
1136f5271101SLydia Wang /* enter/exit analog low-current mode */
1137f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1138f5271101SLydia Wang {
1139f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1140f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1141f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1142f5271101SLydia Wang 	unsigned int verb = 0;
1143f5271101SLydia Wang 	unsigned int parm = 0;
1144f5271101SLydia Wang 
1145f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1146f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1147f5271101SLydia Wang 	else {
1148f5271101SLydia Wang 		enable = enable && stream_idle;
1149f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1150f5271101SLydia Wang 	}
1151f5271101SLydia Wang 
1152f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1153f5271101SLydia Wang 	switch (spec->codec_type) {
1154f5271101SLydia Wang 	case VT1708B_8CH:
1155f5271101SLydia Wang 	case VT1708B_4CH:
1156f5271101SLydia Wang 		verb = 0xf70;
1157f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1158f5271101SLydia Wang 		break;
1159f5271101SLydia Wang 	case VT1708S:
1160eb7188caSLydia Wang 	case VT1718S:
1161f3db423dSLydia Wang 	case VT1716S:
1162f5271101SLydia Wang 		verb = 0xf73;
1163f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1164f5271101SLydia Wang 		break;
1165f5271101SLydia Wang 	case VT1702:
1166f5271101SLydia Wang 		verb = 0xf73;
1167f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1168f5271101SLydia Wang 		break;
116925eaba2fSLydia Wang 	case VT2002P:
1170ab6734e7SLydia Wang 	case VT1812:
117111890956SLydia Wang 	case VT1802:
117225eaba2fSLydia Wang 		verb = 0xf93;
117325eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
117425eaba2fSLydia Wang 		break;
1175f5271101SLydia Wang 	default:
1176f5271101SLydia Wang 		return;		/* other codecs are not supported */
1177f5271101SLydia Wang 	}
1178f5271101SLydia Wang 	/* send verb */
1179f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1180f5271101SLydia Wang }
1181f5271101SLydia Wang 
1182c577b8a1SJoseph Chan /*
1183c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1184c577b8a1SJoseph Chan  */
118590dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = {
1186c577b8a1SJoseph Chan 	/*
1187c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1188c577b8a1SJoseph Chan 	 */
1189c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1190c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1191c577b8a1SJoseph Chan 
1192c577b8a1SJoseph Chan 
1193f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1194c577b8a1SJoseph Chan 	 * mixer widget
1195c577b8a1SJoseph Chan 	 */
1196c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1197f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1198f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1199f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1200f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1201f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1202c577b8a1SJoseph Chan 
1203c577b8a1SJoseph Chan 	/*
1204c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1205c577b8a1SJoseph Chan 	 */
1206c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1207c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1208c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1209c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1210c577b8a1SJoseph Chan 
1211bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1212bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1213c577b8a1SJoseph Chan 	/* PW9 Output enable */
1214c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1215aa266fccSLydia Wang 	/* power down jack detect function */
1216aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1217f7278fd0SJosepch Chan 	{ }
1218c577b8a1SJoseph Chan };
1219c577b8a1SJoseph Chan 
1220c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1221c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1222c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1223c577b8a1SJoseph Chan {
1224c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
122517314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
122617314379SLydia Wang 		&& substream->ref_count == 0;
122717314379SLydia Wang 	analog_low_current_mode(codec, idle);
12289a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
12299a08160bSTakashi Iwai 					     hinfo);
1230c577b8a1SJoseph Chan }
1231c577b8a1SJoseph Chan 
12320aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec,
12330aa62aefSHarald Welte 				      unsigned int stream_tag,
12340aa62aefSHarald Welte 				      unsigned int format,
12350aa62aefSHarald Welte 				      struct snd_pcm_substream *substream)
12360aa62aefSHarald Welte {
12370aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12380aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1239dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
12400aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12410aa62aefSHarald Welte 	int i;
12427c935976SStephen Warren 	struct hda_spdif_out *spdif =
12437c935976SStephen Warren 		snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid);
12440aa62aefSHarald Welte 
12450aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12460aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12470aa62aefSHarald Welte 		if (chs == 2 &&
12480aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12490aa62aefSHarald Welte 						format) &&
12507c935976SStephen Warren 		    !(spdif->status & IEC958_AES0_NONAUDIO)) {
12510aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12520aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12530aa62aefSHarald Welte 			 * be updated */
12547c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12550aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12560aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12577c935976SStephen Warren 						    spdif->ctls &
12580aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12590aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12600aa62aefSHarald Welte 						   stream_tag, 0, format);
12610aa62aefSHarald Welte 			/* turn on again (if needed) */
12627c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12630aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12640aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12657c935976SStephen Warren 						    spdif->ctls & 0xff);
12660aa62aefSHarald Welte 		} else {
12670aa62aefSHarald Welte 			mout->dig_out_used = 0;
12680aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12690aa62aefSHarald Welte 						   0, 0, 0);
12700aa62aefSHarald Welte 		}
12710aa62aefSHarald Welte 	}
12720aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12730aa62aefSHarald Welte 
12740aa62aefSHarald Welte 	/* front */
12750aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12760aa62aefSHarald Welte 				   0, format);
12770aa62aefSHarald Welte 
1278eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1279eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12800aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12810aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12820aa62aefSHarald Welte 					   0, format);
12830aa62aefSHarald Welte 
12840aa62aefSHarald Welte 	/* extra outputs copied from front */
12850aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12860aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12870aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12880aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12890aa62aefSHarald Welte 						   stream_tag, 0, format);
12900aa62aefSHarald Welte 
12910aa62aefSHarald Welte 	/* surrounds */
12920aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12930aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12940aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12950aa62aefSHarald Welte 						   i * 2, format);
12960aa62aefSHarald Welte 		else /* copy front */
12970aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12980aa62aefSHarald Welte 						   0, format);
12990aa62aefSHarald Welte 	}
13000aa62aefSHarald Welte }
13010aa62aefSHarald Welte 
13020aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
13030aa62aefSHarald Welte 					  struct hda_codec *codec,
13040aa62aefSHarald Welte 					  unsigned int stream_tag,
13050aa62aefSHarald Welte 					  unsigned int format,
13060aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
13070aa62aefSHarald Welte {
13080aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13090aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1310dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13110aa62aefSHarald Welte 
13120aa62aefSHarald Welte 	if (substream->number == 0)
13130aa62aefSHarald Welte 		playback_multi_pcm_prep_0(codec, stream_tag, format,
13140aa62aefSHarald Welte 					  substream);
13150aa62aefSHarald Welte 	else {
13160aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13170aa62aefSHarald Welte 		    spec->hp_independent_mode)
13180aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13190aa62aefSHarald Welte 						   stream_tag, 0, format);
13200aa62aefSHarald Welte 	}
13211f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
13220aa62aefSHarald Welte 	return 0;
13230aa62aefSHarald Welte }
13240aa62aefSHarald Welte 
13250aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
13260aa62aefSHarald Welte 				    struct hda_codec *codec,
13270aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
13280aa62aefSHarald Welte {
13290aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13300aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1331dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13320aa62aefSHarald Welte 	int i;
13330aa62aefSHarald Welte 
13340aa62aefSHarald Welte 	if (substream->number == 0) {
13350aa62aefSHarald Welte 		for (i = 0; i < mout->num_dacs; i++)
13360aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
13370aa62aefSHarald Welte 
13380aa62aefSHarald Welte 		if (mout->hp_nid && !spec->hp_independent_mode)
13390aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13400aa62aefSHarald Welte 						   0, 0, 0);
13410aa62aefSHarald Welte 
13420aa62aefSHarald Welte 		for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13430aa62aefSHarald Welte 			if (mout->extra_out_nid[i])
13440aa62aefSHarald Welte 				snd_hda_codec_setup_stream(codec,
13450aa62aefSHarald Welte 							mout->extra_out_nid[i],
13460aa62aefSHarald Welte 							0, 0, 0);
13470aa62aefSHarald Welte 		mutex_lock(&codec->spdif_mutex);
13480aa62aefSHarald Welte 		if (mout->dig_out_nid &&
13490aa62aefSHarald Welte 		    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13500aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13510aa62aefSHarald Welte 						   0, 0, 0);
13520aa62aefSHarald Welte 			mout->dig_out_used = 0;
13530aa62aefSHarald Welte 		}
13540aa62aefSHarald Welte 		mutex_unlock(&codec->spdif_mutex);
13550aa62aefSHarald Welte 	} else {
13560aa62aefSHarald Welte 		if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] &&
13570aa62aefSHarald Welte 		    spec->hp_independent_mode)
13580aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->hp_nid,
13590aa62aefSHarald Welte 						   0, 0, 0);
13600aa62aefSHarald Welte 	}
13611f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13620aa62aefSHarald Welte 	return 0;
13630aa62aefSHarald Welte }
13640aa62aefSHarald Welte 
1365c577b8a1SJoseph Chan /*
1366c577b8a1SJoseph Chan  * Digital out
1367c577b8a1SJoseph Chan  */
1368c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1369c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1370c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1371c577b8a1SJoseph Chan {
1372c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1373c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1374c577b8a1SJoseph Chan }
1375c577b8a1SJoseph Chan 
1376c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1377c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1378c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1379c577b8a1SJoseph Chan {
1380c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1381c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1382c577b8a1SJoseph Chan }
1383c577b8a1SJoseph Chan 
13845691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
138598aa34c0SHarald Welte 					struct hda_codec *codec,
138698aa34c0SHarald Welte 					unsigned int stream_tag,
138798aa34c0SHarald Welte 					unsigned int format,
138898aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
138998aa34c0SHarald Welte {
139098aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13919da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13929da29271STakashi Iwai 					     stream_tag, format, substream);
13939da29271STakashi Iwai }
13945691ec7fSHarald Welte 
13959da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
13969da29271STakashi Iwai 					struct hda_codec *codec,
13979da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13989da29271STakashi Iwai {
13999da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
14009da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
140198aa34c0SHarald Welte 	return 0;
140298aa34c0SHarald Welte }
140398aa34c0SHarald Welte 
1404c577b8a1SJoseph Chan /*
1405c577b8a1SJoseph Chan  * Analog capture
1406c577b8a1SJoseph Chan  */
1407c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1408c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1409c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1410c577b8a1SJoseph Chan 				   unsigned int format,
1411c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1412c577b8a1SJoseph Chan {
1413c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1414c577b8a1SJoseph Chan 
1415c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1416c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1417c577b8a1SJoseph Chan 	return 0;
1418c577b8a1SJoseph Chan }
1419c577b8a1SJoseph Chan 
1420c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1421c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1422c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1423c577b8a1SJoseph Chan {
1424c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1425888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1426c577b8a1SJoseph Chan 	return 0;
1427c577b8a1SJoseph Chan }
1428c577b8a1SJoseph Chan 
142990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_playback = {
14300aa62aefSHarald Welte 	.substreams = 2,
1431c577b8a1SJoseph Chan 	.channels_min = 2,
1432c577b8a1SJoseph Chan 	.channels_max = 8,
1433c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
1434c577b8a1SJoseph Chan 	.ops = {
1435c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
14360aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
14370aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1438c577b8a1SJoseph Chan 	},
1439c577b8a1SJoseph Chan };
1440c577b8a1SJoseph Chan 
144190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
1442c873cc25SLydia Wang 	.substreams = 2,
1443bc9b5623STakashi Iwai 	.channels_min = 2,
1444bc9b5623STakashi Iwai 	.channels_max = 8,
1445bc9b5623STakashi Iwai 	.nid = 0x10, /* NID to query formats and rates */
1446bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1447bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1448bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1449bc9b5623STakashi Iwai 	 */
1450bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1451bc9b5623STakashi Iwai 	.ops = {
1452bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
1453c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1454c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1455bc9b5623STakashi Iwai 	},
1456bc9b5623STakashi Iwai };
1457bc9b5623STakashi Iwai 
145890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_capture = {
1459c577b8a1SJoseph Chan 	.substreams = 2,
1460c577b8a1SJoseph Chan 	.channels_min = 2,
1461c577b8a1SJoseph Chan 	.channels_max = 2,
1462c577b8a1SJoseph Chan 	.nid = 0x15, /* NID to query formats and rates */
1463c577b8a1SJoseph Chan 	.ops = {
1464c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1465c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1466c577b8a1SJoseph Chan 	},
1467c577b8a1SJoseph Chan };
1468c577b8a1SJoseph Chan 
146990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_playback = {
1470c577b8a1SJoseph Chan 	.substreams = 1,
1471c577b8a1SJoseph Chan 	.channels_min = 2,
1472c577b8a1SJoseph Chan 	.channels_max = 2,
1473c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1474c577b8a1SJoseph Chan 	.ops = {
1475c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14766b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14779da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14789da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1479c577b8a1SJoseph Chan 	},
1480c577b8a1SJoseph Chan };
1481c577b8a1SJoseph Chan 
148290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_capture = {
1483c577b8a1SJoseph Chan 	.substreams = 1,
1484c577b8a1SJoseph Chan 	.channels_min = 2,
1485c577b8a1SJoseph Chan 	.channels_max = 2,
1486c577b8a1SJoseph Chan };
1487c577b8a1SJoseph Chan 
1488c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1489c577b8a1SJoseph Chan {
1490c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14915b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
149290dd48a1STakashi Iwai 	const struct snd_kcontrol_new *knew;
14935b0cb1d8SJaroslav Kysela 	int err, i;
1494c577b8a1SJoseph Chan 
149524088a58STakashi Iwai 	if (spec->set_widgets_power_state)
149624088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
149724088a58STakashi Iwai 			return -ENOMEM;
149824088a58STakashi Iwai 
1499c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1500c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1501c577b8a1SJoseph Chan 		if (err < 0)
1502c577b8a1SJoseph Chan 			return err;
1503c577b8a1SJoseph Chan 	}
1504c577b8a1SJoseph Chan 
1505c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1506c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
150774b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1508c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1509c577b8a1SJoseph Chan 		if (err < 0)
1510c577b8a1SJoseph Chan 			return err;
15119a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
15129a08160bSTakashi Iwai 						    &spec->multiout);
15139a08160bSTakashi Iwai 		if (err < 0)
15149a08160bSTakashi Iwai 			return err;
15159a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1516c577b8a1SJoseph Chan 	}
1517c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1518c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1519c577b8a1SJoseph Chan 		if (err < 0)
1520c577b8a1SJoseph Chan 			return err;
1521c577b8a1SJoseph Chan 	}
152217314379SLydia Wang 
15235b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15245b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15255b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
152621949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15275b0cb1d8SJaroslav Kysela 		if (err < 0)
15285b0cb1d8SJaroslav Kysela 			return err;
15295b0cb1d8SJaroslav Kysela 	}
15305b0cb1d8SJaroslav Kysela 
15315b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
15325b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
15335b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
15345b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
15355b0cb1d8SJaroslav Kysela 				continue;
15365b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
15375b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
15385b0cb1d8SJaroslav Kysela 				continue;
15395b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
15405b0cb1d8SJaroslav Kysela 					      knew->subdevice);
15415b0cb1d8SJaroslav Kysela 		}
15425b0cb1d8SJaroslav Kysela 	}
15435b0cb1d8SJaroslav Kysela 
154417314379SLydia Wang 	/* init power states */
15453e95b9abSLydia Wang 	set_widgets_power_state(codec);
154617314379SLydia Wang 	analog_low_current_mode(codec, 1);
154717314379SLydia Wang 
1548603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1549c577b8a1SJoseph Chan 	return 0;
1550c577b8a1SJoseph Chan }
1551c577b8a1SJoseph Chan 
1552c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1553c577b8a1SJoseph Chan {
1554c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1555c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1556c577b8a1SJoseph Chan 
1557c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1558c577b8a1SJoseph Chan 	codec->pcm_info = info;
1559c577b8a1SJoseph Chan 
156082673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
156182673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1562c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
1563377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1564377ff31aSLydia Wang 		*(spec->stream_analog_playback);
1565377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1566377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1567c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture);
1568c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1569c577b8a1SJoseph Chan 
1570c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1571c577b8a1SJoseph Chan 		spec->multiout.max_channels;
1572c577b8a1SJoseph Chan 
1573c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1574c577b8a1SJoseph Chan 		codec->num_pcms++;
1575c577b8a1SJoseph Chan 		info++;
157682673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
157782673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
157882673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1579c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
15807ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1581c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
1582c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1583c577b8a1SJoseph Chan 				*(spec->stream_digital_playback);
1584c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1585c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1586c577b8a1SJoseph Chan 		}
1587c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
1588c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1589c577b8a1SJoseph Chan 				*(spec->stream_digital_capture);
1590c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1591c577b8a1SJoseph Chan 				spec->dig_in_nid;
1592c577b8a1SJoseph Chan 		}
1593c577b8a1SJoseph Chan 	}
1594c577b8a1SJoseph Chan 
1595c577b8a1SJoseph Chan 	return 0;
1596c577b8a1SJoseph Chan }
1597c577b8a1SJoseph Chan 
1598c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1599c577b8a1SJoseph Chan {
1600c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1601c577b8a1SJoseph Chan 
1602c577b8a1SJoseph Chan 	if (!spec)
1603c577b8a1SJoseph Chan 		return;
1604c577b8a1SJoseph Chan 
1605603c4019STakashi Iwai 	via_free_kctls(codec);
16061f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1607c577b8a1SJoseph Chan 	kfree(codec->spec);
1608c577b8a1SJoseph Chan }
1609c577b8a1SJoseph Chan 
161064be285bSTakashi Iwai /* mute/unmute outputs */
161164be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
161264be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
161364be285bSTakashi Iwai {
161464be285bSTakashi Iwai 	int i;
161564be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
161664be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
161764be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
161864be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
161964be285bSTakashi Iwai }
162064be285bSTakashi Iwai 
162169e52a80SHarald Welte /* mute internal speaker if HP is plugged */
162269e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
162369e52a80SHarald Welte {
1624dcf34c8cSLydia Wang 	unsigned int present = 0;
162569e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
162669e52a80SHarald Welte 
1627d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1628dcf34c8cSLydia Wang 
162964be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
163064be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
163164be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
163264be285bSTakashi Iwai 				    present);
163369e52a80SHarald Welte }
163469e52a80SHarald Welte 
1635f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1636f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1637f3db423dSLydia Wang {
1638f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1639f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1640f3db423dSLydia Wang 
1641f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1642f3db423dSLydia Wang 		return;
1643f3db423dSLydia Wang 
1644d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1645d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1646f3db423dSLydia Wang 
1647f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1648f3db423dSLydia Wang 	if (lineout_present) {
16493e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
16503e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16513e0693e2STakashi Iwai 				    lineout_present ? 0 : PIN_OUT);
1652f3db423dSLydia Wang 		return;
1653f3db423dSLydia Wang 	}
1654f3db423dSLydia Wang 
1655d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1656f3db423dSLydia Wang 
1657f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
16583e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
16593e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16603e0693e2STakashi Iwai 				    hp_present ? 0 : PIN_OUT);
1661f3db423dSLydia Wang }
1662f3db423dSLydia Wang 
166369e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
166469e52a80SHarald Welte {
166569e52a80SHarald Welte 	unsigned int gpio_data;
166669e52a80SHarald Welte 	unsigned int vol_counter;
166769e52a80SHarald Welte 	unsigned int vol;
166869e52a80SHarald Welte 	unsigned int master_vol;
166969e52a80SHarald Welte 
167069e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
167169e52a80SHarald Welte 
167269e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
167369e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
167469e52a80SHarald Welte 
167569e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
167669e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
167769e52a80SHarald Welte 
167869e52a80SHarald Welte 	vol = vol_counter & 0x1F;
167969e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
168069e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
168169e52a80SHarald Welte 					AC_AMP_GET_INPUT);
168269e52a80SHarald Welte 
168369e52a80SHarald Welte 	if (gpio_data == 0x02) {
168469e52a80SHarald Welte 		/* unmute line out */
16853e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
16863e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16873e0693e2STakashi Iwai 				    PIN_OUT);
168869e52a80SHarald Welte 		if (vol_counter & 0x20) {
168969e52a80SHarald Welte 			/* decrease volume */
169069e52a80SHarald Welte 			if (vol > master_vol)
169169e52a80SHarald Welte 				vol = master_vol;
169269e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
169369e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
169469e52a80SHarald Welte 						 master_vol-vol);
169569e52a80SHarald Welte 		} else {
169669e52a80SHarald Welte 			/* increase volume */
169769e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
169869e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
169969e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
170069e52a80SHarald Welte 					  (master_vol+vol));
170169e52a80SHarald Welte 		}
170269e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
170369e52a80SHarald Welte 		/* mute line out */
17043e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
17053e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17063e0693e2STakashi Iwai 				    0);
170769e52a80SHarald Welte 	}
170869e52a80SHarald Welte }
170969e52a80SHarald Welte 
171025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
171125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
171225eaba2fSLydia Wang {
171325eaba2fSLydia Wang 	unsigned int hp_present;
171425eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
171525eaba2fSLydia Wang 
171627439ce7SLydia Wang 	if (!VT2002P_COMPATIBLE(spec))
171725eaba2fSLydia Wang 		return;
171825eaba2fSLydia Wang 
1719d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
172025eaba2fSLydia Wang 
172164be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
172264be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.speaker_outs,
172364be285bSTakashi Iwai 				    spec->autocfg.speaker_pins,
172464be285bSTakashi Iwai 				    hp_present);
172525eaba2fSLydia Wang }
172625eaba2fSLydia Wang 
172725eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
172825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
172925eaba2fSLydia Wang {
173064be285bSTakashi Iwai 	int present;
173125eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
173225eaba2fSLydia Wang 
173325eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
173425eaba2fSLydia Wang 		return;
173525eaba2fSLydia Wang 
173664be285bSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
173764be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
173864be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
173964be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
174064be285bSTakashi Iwai 				    present);
174125eaba2fSLydia Wang 
174264be285bSTakashi Iwai 	if (!present)
174364be285bSTakashi Iwai 		present = snd_hda_jack_detect(codec,
174464be285bSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
174525eaba2fSLydia Wang 
174625eaba2fSLydia Wang 	/* Speakers */
174764be285bSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
174864be285bSTakashi Iwai 			    spec->autocfg.speaker_pins,
174964be285bSTakashi Iwai 			    present);
175025eaba2fSLydia Wang }
175125eaba2fSLydia Wang 
175225eaba2fSLydia Wang 
175369e52a80SHarald Welte /* unsolicited event for jack sensing */
175469e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
175569e52a80SHarald Welte 				  unsigned int res)
175669e52a80SHarald Welte {
175769e52a80SHarald Welte 	res >>= 26;
1758ec7e7e42SLydia Wang 
1759a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
17603e95b9abSLydia Wang 		set_widgets_power_state(codec);
1761ec7e7e42SLydia Wang 
1762ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1763ec7e7e42SLydia Wang 
1764ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1765ec7e7e42SLydia Wang 		via_hp_automute(codec);
1766ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1767ec7e7e42SLydia Wang 		via_gpio_control(codec);
1768ec7e7e42SLydia Wang 	else if (res == VIA_MONO_EVENT)
1769f3db423dSLydia Wang 		via_mono_automute(codec);
1770ec7e7e42SLydia Wang 	else if (res == VIA_SPEAKER_EVENT)
177125eaba2fSLydia Wang 		via_speaker_automute(codec);
1772ec7e7e42SLydia Wang 	else if (res == VIA_BIND_HP_EVENT)
177325eaba2fSLydia Wang 		via_hp_bind_automute(codec);
177469e52a80SHarald Welte }
177569e52a80SHarald Welte 
1776c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1777c577b8a1SJoseph Chan {
1778c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
177969e52a80SHarald Welte 	int i;
178069e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
178169e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
178269e52a80SHarald Welte 
1783f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1784f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
178555d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
178655d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1787f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
178812b74c80STakashi Iwai 					    PIN_OUT);
178955d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1790f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1791f7278fd0SJosepch Chan 		}
179212b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
179312b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
179412b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1795f7278fd0SJosepch Chan 
17969da29271STakashi Iwai 	/* assign slave outs */
17979da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
17989da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
17995691ec7fSHarald Welte 
1800c577b8a1SJoseph Chan 	return 0;
1801c577b8a1SJoseph Chan }
1802c577b8a1SJoseph Chan 
18031f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18041f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
18051f2e99feSLydia Wang {
18061f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
18071f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
18081f2e99feSLydia Wang 	return 0;
18091f2e99feSLydia Wang }
18101f2e99feSLydia Wang #endif
18111f2e99feSLydia Wang 
1812cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1813cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1814cb53c626STakashi Iwai {
1815cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1816cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1817cb53c626STakashi Iwai }
1818cb53c626STakashi Iwai #endif
1819cb53c626STakashi Iwai 
1820c577b8a1SJoseph Chan /*
1821c577b8a1SJoseph Chan  */
182290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1823c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1824c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1825c577b8a1SJoseph Chan 	.init = via_init,
1826c577b8a1SJoseph Chan 	.free = via_free,
18271f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18281f2e99feSLydia Wang 	.suspend = via_suspend,
18291f2e99feSLydia Wang #endif
1830cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1831cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1832cb53c626STakashi Iwai #endif
1833c577b8a1SJoseph Chan };
1834c577b8a1SJoseph Chan 
1835*4a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1836c577b8a1SJoseph Chan {
1837*4a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1838*4a79616dSTakashi Iwai 	int i;
1839*4a79616dSTakashi Iwai 
1840*4a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
1841*4a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
1842*4a79616dSTakashi Iwai 			return false;
1843*4a79616dSTakashi Iwai 	}
1844*4a79616dSTakashi Iwai 	if (spec->multiout.hp_nid == dac)
1845*4a79616dSTakashi Iwai 		return false;
1846*4a79616dSTakashi Iwai 	return true;
1847*4a79616dSTakashi Iwai }
1848*4a79616dSTakashi Iwai 
1849*4a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1850*4a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
1851*4a79616dSTakashi Iwai 			      int depth, int wid_type)
1852*4a79616dSTakashi Iwai {
1853*4a79616dSTakashi Iwai 	hda_nid_t conn[8];
1854*4a79616dSTakashi Iwai 	int i, nums;
1855*4a79616dSTakashi Iwai 
1856*4a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1857*4a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
1858*4a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1859*4a79616dSTakashi Iwai 			continue;
1860*4a79616dSTakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1861*4a79616dSTakashi Iwai 			path->path[depth] = conn[i];
1862*4a79616dSTakashi Iwai 			path->idx[depth] = i;
1863*4a79616dSTakashi Iwai 			path->depth = ++depth;
1864*4a79616dSTakashi Iwai 			return true;
1865*4a79616dSTakashi Iwai 		}
1866*4a79616dSTakashi Iwai 	}
1867*4a79616dSTakashi Iwai 	if (depth > 4)
1868*4a79616dSTakashi Iwai 		return false;
1869*4a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
1870*4a79616dSTakashi Iwai 		unsigned int type;
1871*4a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
1872*4a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
1873*4a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
1874*4a79616dSTakashi Iwai 			continue;
1875*4a79616dSTakashi Iwai 		if (parse_output_path(codec, conn[i], target_dac,
1876*4a79616dSTakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL)) {
1877*4a79616dSTakashi Iwai 			path->path[depth] = conn[i];
1878*4a79616dSTakashi Iwai 			path->idx[depth] = i;
1879*4a79616dSTakashi Iwai 			return true;
1880*4a79616dSTakashi Iwai 		}
1881*4a79616dSTakashi Iwai 	}
1882*4a79616dSTakashi Iwai 	return false;
1883*4a79616dSTakashi Iwai }
1884*4a79616dSTakashi Iwai 
1885*4a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
1886*4a79616dSTakashi Iwai {
1887*4a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1888*4a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1889c577b8a1SJoseph Chan 	int i;
1890c577b8a1SJoseph Chan 	hda_nid_t nid;
1891c577b8a1SJoseph Chan 
1892c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
1893*4a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
1894*4a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1895c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
1896*4a79616dSTakashi Iwai 		if (!nid)
1897*4a79616dSTakashi Iwai 			continue;
1898*4a79616dSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1899*4a79616dSTakashi Iwai 			spec->private_dac_nids[i] =
1900*4a79616dSTakashi Iwai 				spec->out_path[i].path[spec->out_path[i].depth - 1];
1901c577b8a1SJoseph Chan 	}
1902c577b8a1SJoseph Chan 	return 0;
1903c577b8a1SJoseph Chan }
1904c577b8a1SJoseph Chan 
1905*4a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1906*4a79616dSTakashi Iwai 			  hda_nid_t pin, hda_nid_t dac, int chs)
1907c577b8a1SJoseph Chan {
1908*4a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1909c577b8a1SJoseph Chan 	char name[32];
1910*4a79616dSTakashi Iwai 	hda_nid_t nid;
1911*4a79616dSTakashi Iwai 	int err;
1912c577b8a1SJoseph Chan 
1913*4a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1914*4a79616dSTakashi Iwai 		nid = dac;
1915*4a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1916*4a79616dSTakashi Iwai 		nid = pin;
1917*4a79616dSTakashi Iwai 	else
1918*4a79616dSTakashi Iwai 		nid = 0;
1919*4a79616dSTakashi Iwai 	if (nid) {
1920*4a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1921c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1922*4a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1923c577b8a1SJoseph Chan 		if (err < 0)
1924c577b8a1SJoseph Chan 			return err;
1925c577b8a1SJoseph Chan 	}
1926*4a79616dSTakashi Iwai 
1927*4a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1928*4a79616dSTakashi Iwai 		nid = dac;
1929*4a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1930*4a79616dSTakashi Iwai 		nid = pin;
1931*4a79616dSTakashi Iwai 	else
1932*4a79616dSTakashi Iwai 		nid = 0;
1933*4a79616dSTakashi Iwai 	if (nid) {
1934*4a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
1935*4a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1936*4a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1937*4a79616dSTakashi Iwai 		if (err < 0)
1938*4a79616dSTakashi Iwai 			return err;
1939*4a79616dSTakashi Iwai 	}
1940*4a79616dSTakashi Iwai 	return 0;
1941*4a79616dSTakashi Iwai }
1942*4a79616dSTakashi Iwai 
1943*4a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
1944*4a79616dSTakashi Iwai 				hda_nid_t nid);
1945*4a79616dSTakashi Iwai 
1946*4a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
1947*4a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1948*4a79616dSTakashi Iwai {
1949*4a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1950*4a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1951*4a79616dSTakashi Iwai 	static const char * const chname[4] = {
1952*4a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
1953*4a79616dSTakashi Iwai 	};
1954*4a79616dSTakashi Iwai 	int i, idx, err;
1955*4a79616dSTakashi Iwai 
1956*4a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1957*4a79616dSTakashi Iwai 		hda_nid_t pin, dac;
1958*4a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
1959*4a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
1960*4a79616dSTakashi Iwai 		if (!pin || !dac)
1961*4a79616dSTakashi Iwai 			continue;
1962*4a79616dSTakashi Iwai 		if (i == AUTO_SEQ_CENLFE) {
1963*4a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "Center", pin, dac, 1);
1964*4a79616dSTakashi Iwai 			if (err < 0)
1965*4a79616dSTakashi Iwai 				return err;
1966*4a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "LFE", pin, dac, 2);
1967*4a79616dSTakashi Iwai 			if (err < 0)
1968*4a79616dSTakashi Iwai 				return err;
1969*4a79616dSTakashi Iwai 		} else {
1970*4a79616dSTakashi Iwai 			err = create_ch_ctls(codec, chname[i], pin, dac, 3);
1971*4a79616dSTakashi Iwai 			if (err < 0)
1972*4a79616dSTakashi Iwai 				return err;
1973*4a79616dSTakashi Iwai 		}
1974*4a79616dSTakashi Iwai 	}
1975*4a79616dSTakashi Iwai 
1976*4a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
1977*4a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
1978*4a79616dSTakashi Iwai 	if (idx >= 0) {
1979*4a79616dSTakashi Iwai 		/* add control to mixer */
1980*4a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1981*4a79616dSTakashi Iwai 				      "PCM Playback Volume",
1982*4a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1983*4a79616dSTakashi Iwai 							  idx, HDA_INPUT));
1984*4a79616dSTakashi Iwai 		if (err < 0)
1985*4a79616dSTakashi Iwai 			return err;
1986*4a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1987*4a79616dSTakashi Iwai 				      "PCM Playback Switch",
1988*4a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1989*4a79616dSTakashi Iwai 							  idx, HDA_INPUT));
1990*4a79616dSTakashi Iwai 		if (err < 0)
1991*4a79616dSTakashi Iwai 			return err;
1992c577b8a1SJoseph Chan 	}
1993c577b8a1SJoseph Chan 
1994c577b8a1SJoseph Chan 	return 0;
1995c577b8a1SJoseph Chan }
1996c577b8a1SJoseph Chan 
19970aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
19980aa62aefSHarald Welte {
19990aa62aefSHarald Welte 	int i;
20000aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
2001ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
20020aa62aefSHarald Welte 
20030aa62aefSHarald Welte 	/* for hp mode select */
200410a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
200510a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
20060aa62aefSHarald Welte 
20070aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
20080aa62aefSHarald Welte }
20090aa62aefSHarald Welte 
2010*4a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
2011c577b8a1SJoseph Chan {
2012*4a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
2013*4a79616dSTakashi Iwai 	hda_nid_t dac = 0;
2014c577b8a1SJoseph Chan 	int err;
2015c577b8a1SJoseph Chan 
2016c577b8a1SJoseph Chan 	if (!pin)
2017c577b8a1SJoseph Chan 		return 0;
2018c577b8a1SJoseph Chan 
2019*4a79616dSTakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2020*4a79616dSTakashi Iwai 			       &spec->hp_dep_path, 0, -1))
2021*4a79616dSTakashi Iwai 		return 0;
2022*4a79616dSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
2023*4a79616dSTakashi Iwai 		dac = spec->hp_path.path[spec->hp_path.depth - 1];
2024*4a79616dSTakashi Iwai 		spec->multiout.hp_nid = dac;
2025*4a79616dSTakashi Iwai 		spec->hp_independent_mode_index =
2026*4a79616dSTakashi Iwai 			spec->hp_path.idx[spec->hp_path.depth - 1];
20270aa62aefSHarald Welte 		create_hp_imux(spec);
2028*4a79616dSTakashi Iwai 	}
2029*4a79616dSTakashi Iwai 
2030*4a79616dSTakashi Iwai 	err = create_ch_ctls(codec, "Headphone", pin, dac, 3);
2031*4a79616dSTakashi Iwai 	if (err < 0)
2032*4a79616dSTakashi Iwai 		return err;
20330aa62aefSHarald Welte 
2034c577b8a1SJoseph Chan 	return 0;
2035c577b8a1SJoseph Chan }
2036c577b8a1SJoseph Chan 
2037a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
2038a766d0d7STakashi Iwai 				hda_nid_t nid)
2039a766d0d7STakashi Iwai {
2040a766d0d7STakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
2041a766d0d7STakashi Iwai 	int i, nums;
2042a766d0d7STakashi Iwai 
2043a766d0d7STakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
2044a766d0d7STakashi Iwai 	for (i = 0; i < nums; i++)
2045a766d0d7STakashi Iwai 		if (conn[i] == nid)
2046a766d0d7STakashi Iwai 			return i;
2047a766d0d7STakashi Iwai 	return -1;
2048a766d0d7STakashi Iwai }
2049a766d0d7STakashi Iwai 
2050a766d0d7STakashi Iwai /* look for ADCs */
2051a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2052a766d0d7STakashi Iwai {
2053a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2054a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2055a766d0d7STakashi Iwai 	int i;
2056a766d0d7STakashi Iwai 
2057a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2058a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2059a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2060a766d0d7STakashi Iwai 			continue;
2061a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2062a766d0d7STakashi Iwai 			continue;
2063a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2064a766d0d7STakashi Iwai 			continue;
2065a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2066a766d0d7STakashi Iwai 			return -ENOMEM;
2067a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2068a766d0d7STakashi Iwai 	}
2069a766d0d7STakashi Iwai 	return 0;
2070a766d0d7STakashi Iwai }
2071a766d0d7STakashi Iwai 
2072a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2073a766d0d7STakashi Iwai 
2074c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2075620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
2076620e2b28STakashi Iwai 					     const struct auto_pin_cfg *cfg)
2077c577b8a1SJoseph Chan {
207810a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
20790aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2080a766d0d7STakashi Iwai 	int i, err, idx, idx2, type, type_idx = 0;
2081a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
2082a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
2083a766d0d7STakashi Iwai 	int num_idxs;
2084a766d0d7STakashi Iwai 
2085a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2086a766d0d7STakashi Iwai 	if (err < 0)
2087a766d0d7STakashi Iwai 		return err;
2088a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2089a766d0d7STakashi Iwai 	if (err < 0)
2090a766d0d7STakashi Iwai 		return err;
2091a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
2092a766d0d7STakashi Iwai 
2093a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
2094a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
2095a766d0d7STakashi Iwai 	if (num_idxs <= 0)
2096a766d0d7STakashi Iwai 		return 0;
2097c577b8a1SJoseph Chan 
2098c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2099f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2100620e2b28STakashi Iwai 		if (pin_idxs[idx] == spec->aa_mix_nid) {
210110a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2102f3268512STakashi Iwai 			break;
2103f3268512STakashi Iwai 		}
2104f3268512STakashi Iwai 	}
2105c577b8a1SJoseph Chan 
21067b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
210710a20af7STakashi Iwai 		const char *label;
21087b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2109f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
21107b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2111c577b8a1SJoseph Chan 				break;
2112f3268512STakashi Iwai 		if (idx >= num_idxs)
2113f3268512STakashi Iwai 			continue;
21147b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
21157b315bb4STakashi Iwai 			type_idx++;
21167b315bb4STakashi Iwai 		else
21177b315bb4STakashi Iwai 			type_idx = 0;
211810a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2119620e2b28STakashi Iwai 		idx2 = get_connection_index(codec, spec->aa_mix_nid,
2120620e2b28STakashi Iwai 					    pin_idxs[idx]);
2121a766d0d7STakashi Iwai 		if (idx2 >= 0)
212216922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2123620e2b28STakashi Iwai 						   idx2, spec->aa_mix_nid);
2124c577b8a1SJoseph Chan 		if (err < 0)
2125c577b8a1SJoseph Chan 			return err;
212610a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2127c577b8a1SJoseph Chan 	}
2128c577b8a1SJoseph Chan 	return 0;
2129c577b8a1SJoseph Chan }
2130c577b8a1SJoseph Chan 
2131cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
213290dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = {
2133cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2134cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2135cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2136cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2137cb53c626STakashi Iwai 	{ } /* end */
2138cb53c626STakashi Iwai };
2139cb53c626STakashi Iwai #endif
2140cb53c626STakashi Iwai 
214176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
214276d9b0ddSHarald Welte {
214376d9b0ddSHarald Welte 	unsigned int def_conf;
214476d9b0ddSHarald Welte 	unsigned char seqassoc;
214576d9b0ddSHarald Welte 
21462f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
214776d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
214876d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
214982ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
215082ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
215176d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
21522f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
215376d9b0ddSHarald Welte 	}
215476d9b0ddSHarald Welte 
215576d9b0ddSHarald Welte 	return;
215676d9b0ddSHarald Welte }
215776d9b0ddSHarald Welte 
2158e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
21591f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
21601f2e99feSLydia Wang {
21611f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21621f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
21631f2e99feSLydia Wang 
21641f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21651f2e99feSLydia Wang 		return 0;
2166e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
21671f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2168e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
21691f2e99feSLydia Wang 	return 0;
21701f2e99feSLydia Wang }
21711f2e99feSLydia Wang 
2172e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
21731f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
21741f2e99feSLydia Wang {
21751f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21761f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
21771f2e99feSLydia Wang 	int change;
21781f2e99feSLydia Wang 
21791f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21801f2e99feSLydia Wang 		return 0;
2181e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
21821f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2183e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2184e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
21851f2e99feSLydia Wang 		mute_aa_path(codec, 1);
21861f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
21871f2e99feSLydia Wang 	}
21881f2e99feSLydia Wang 	return change;
21891f2e99feSLydia Wang }
21901f2e99feSLydia Wang 
2191e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
21921f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21931f2e99feSLydia Wang 	.name = "Jack Detect",
21941f2e99feSLydia Wang 	.count = 1,
21951f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2196e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2197e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
21981f2e99feSLydia Wang };
21991f2e99feSLydia Wang 
2200c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2201c577b8a1SJoseph Chan {
2202c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2203c577b8a1SJoseph Chan 	int err;
2204c577b8a1SJoseph Chan 
220576d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
220676d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
220776d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
220876d9b0ddSHarald Welte 
2209c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2210c577b8a1SJoseph Chan 	if (err < 0)
2211c577b8a1SJoseph Chan 		return err;
2212*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2213c577b8a1SJoseph Chan 	if (err < 0)
2214c577b8a1SJoseph Chan 		return err;
2215c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2216c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2217c577b8a1SJoseph Chan 
2218*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2219c577b8a1SJoseph Chan 	if (err < 0)
2220c577b8a1SJoseph Chan 		return err;
2221*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2222c577b8a1SJoseph Chan 	if (err < 0)
2223c577b8a1SJoseph Chan 		return err;
2224620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2225c577b8a1SJoseph Chan 	if (err < 0)
2226c577b8a1SJoseph Chan 		return err;
22271f2e99feSLydia Wang 	/* add jack detect on/off control */
2228e06e5a29STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2229e06e5a29STakashi Iwai 		return -ENOMEM;
2230c577b8a1SJoseph Chan 
2231c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2232c577b8a1SJoseph Chan 
22330852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2234c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
223555d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2236c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2237c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2238c577b8a1SJoseph Chan 
2239603c4019STakashi Iwai 	if (spec->kctls.list)
2240603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2241c577b8a1SJoseph Chan 
224269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2243c577b8a1SJoseph Chan 
22440aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
22450aa62aefSHarald Welte 
2246f8fdd495SHarald Welte 	if (spec->hp_mux)
22473d83e577STakashi Iwai 		via_hp_build(codec);
2248c577b8a1SJoseph Chan 
22495b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2250c577b8a1SJoseph Chan 	return 1;
2251c577b8a1SJoseph Chan }
2252c577b8a1SJoseph Chan 
2253c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2254c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2255c577b8a1SJoseph Chan {
225625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
225725eaba2fSLydia Wang 
2258c577b8a1SJoseph Chan 	via_init(codec);
2259c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2260c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2261c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
226211890956SLydia Wang 
226311890956SLydia Wang 	if (VT2002P_COMPATIBLE(spec)) {
226425eaba2fSLydia Wang 		via_hp_bind_automute(codec);
226525eaba2fSLydia Wang 	} else {
226625eaba2fSLydia Wang 		via_hp_automute(codec);
226725eaba2fSLydia Wang 		via_speaker_automute(codec);
226825eaba2fSLydia Wang 	}
226925eaba2fSLydia Wang 
2270c577b8a1SJoseph Chan 	return 0;
2271c577b8a1SJoseph Chan }
2272c577b8a1SJoseph Chan 
22731f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
22741f2e99feSLydia Wang {
22751f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
22761f2e99feSLydia Wang 					     vt1708_hp_work.work);
22771f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22781f2e99feSLydia Wang 		return;
22791f2e99feSLydia Wang 	/* if jack state toggled */
22801f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2281d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
22821f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
22831f2e99feSLydia Wang 		via_hp_automute(spec->codec);
22841f2e99feSLydia Wang 	}
22851f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
22861f2e99feSLydia Wang }
22871f2e99feSLydia Wang 
2288337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2289337b9d02STakashi Iwai {
2290337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2291337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2292337b9d02STakashi Iwai 	unsigned int type;
2293337b9d02STakashi Iwai 	int i, n;
2294337b9d02STakashi Iwai 
2295337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2296337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2297337b9d02STakashi Iwai 		while (nid) {
2298a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
22991c55d521STakashi Iwai 			if (type == AC_WID_PIN)
23001c55d521STakashi Iwai 				break;
2301337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2302337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2303337b9d02STakashi Iwai 			if (n <= 0)
2304337b9d02STakashi Iwai 				break;
2305337b9d02STakashi Iwai 			if (n > 1) {
2306337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2307337b9d02STakashi Iwai 				break;
2308337b9d02STakashi Iwai 			}
2309337b9d02STakashi Iwai 			nid = conn[0];
2310337b9d02STakashi Iwai 		}
2311337b9d02STakashi Iwai 	}
23121c55d521STakashi Iwai 	return 0;
2313337b9d02STakashi Iwai }
2314337b9d02STakashi Iwai 
2315c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2316c577b8a1SJoseph Chan {
2317c577b8a1SJoseph Chan 	struct via_spec *spec;
2318c577b8a1SJoseph Chan 	int err;
2319c577b8a1SJoseph Chan 
2320c577b8a1SJoseph Chan 	/* create a codec specific record */
23215b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2322c577b8a1SJoseph Chan 	if (spec == NULL)
2323c577b8a1SJoseph Chan 		return -ENOMEM;
2324c577b8a1SJoseph Chan 
2325620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2326620e2b28STakashi Iwai 
2327c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2328c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2329c577b8a1SJoseph Chan 	if (err < 0) {
2330c577b8a1SJoseph Chan 		via_free(codec);
2331c577b8a1SJoseph Chan 		return err;
2332c577b8a1SJoseph Chan 	} else if (!err) {
2333c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2334c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2335c577b8a1SJoseph Chan 	}
2336c577b8a1SJoseph Chan 
2337c577b8a1SJoseph Chan 
2338c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1708_pcm_analog_playback;
2339bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2340bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2341bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2342c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1708_pcm_analog_capture;
2343c577b8a1SJoseph Chan 
2344c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1708_pcm_digital_playback;
2345c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1708_pcm_digital_capture;
2346c577b8a1SJoseph Chan 
2347a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2348c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1708_capture_mixer;
2349c577b8a1SJoseph Chan 		spec->num_mixers++;
2350c577b8a1SJoseph Chan 	}
2351c577b8a1SJoseph Chan 
2352c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2353c577b8a1SJoseph Chan 
2354c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2355cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2356cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2357cb53c626STakashi Iwai #endif
23581f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2359c577b8a1SJoseph Chan 	return 0;
2360c577b8a1SJoseph Chan }
2361c577b8a1SJoseph Chan 
2362c577b8a1SJoseph Chan /* capture mixer elements */
236390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = {
2364c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT),
2365c577b8a1SJoseph Chan 	HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT),
2366c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT),
2367c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT),
2368c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT),
2369c577b8a1SJoseph Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT),
2370c577b8a1SJoseph Chan 	{
2371c577b8a1SJoseph Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2372c577b8a1SJoseph Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2373c577b8a1SJoseph Chan 		 * So call somewhat different..
2374c577b8a1SJoseph Chan 		 */
2375c577b8a1SJoseph Chan 		/* .name = "Capture Source", */
2376c577b8a1SJoseph Chan 		.name = "Input Source",
2377c577b8a1SJoseph Chan 		.count = 1,
2378c577b8a1SJoseph Chan 		.info = via_mux_enum_info,
2379c577b8a1SJoseph Chan 		.get = via_mux_enum_get,
2380c577b8a1SJoseph Chan 		.put = via_mux_enum_put,
2381c577b8a1SJoseph Chan 	},
2382c577b8a1SJoseph Chan 	{ } /* end */
2383c577b8a1SJoseph Chan };
2384c577b8a1SJoseph Chan 
238590dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = {
2386a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2387a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
238869e52a80SHarald Welte 	{ }
238969e52a80SHarald Welte };
239069e52a80SHarald Welte 
2391c577b8a1SJoseph Chan /*
2392c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2393c577b8a1SJoseph Chan  */
239490dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2395c577b8a1SJoseph Chan 	/*
2396c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2397c577b8a1SJoseph Chan 	 */
2398c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2399c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2400c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2401c577b8a1SJoseph Chan 
2402c577b8a1SJoseph Chan 
2403f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2404c577b8a1SJoseph Chan 	 * mixer widget
2405c577b8a1SJoseph Chan 	 */
2406c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2407f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2408f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2409f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2410f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2411f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2412c577b8a1SJoseph Chan 
2413c577b8a1SJoseph Chan 	/*
2414c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2415c577b8a1SJoseph Chan 	 */
2416c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2417c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2418c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2419c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2420c577b8a1SJoseph Chan 
2421c577b8a1SJoseph Chan 	/*
2422c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2423c577b8a1SJoseph Chan 	 */
2424c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2425c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2426c577b8a1SJoseph Chan 
2427bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2428bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2429c577b8a1SJoseph Chan 	/* PW9 Output enable */
2430c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2431c577b8a1SJoseph Chan 	{ }
2432c577b8a1SJoseph Chan };
2433c577b8a1SJoseph Chan 
243490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = {
2435c577b8a1SJoseph Chan 	.substreams = 1,
2436c577b8a1SJoseph Chan 	.channels_min = 2,
2437c577b8a1SJoseph Chan 	.channels_max = 10,
2438c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2439c577b8a1SJoseph Chan 	.ops = {
2440c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2441c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2442c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2443c577b8a1SJoseph Chan 	},
2444c577b8a1SJoseph Chan };
2445c577b8a1SJoseph Chan 
244690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = {
2447c577b8a1SJoseph Chan 	.substreams = 1,
2448c577b8a1SJoseph Chan 	.channels_min = 2,
2449c577b8a1SJoseph Chan 	.channels_max = 6,
2450c577b8a1SJoseph Chan 	.nid = 0x10, /* NID to query formats and rates */
2451c577b8a1SJoseph Chan 	.ops = {
2452c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
2453c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
2454c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
2455c577b8a1SJoseph Chan 	},
2456c577b8a1SJoseph Chan };
2457c577b8a1SJoseph Chan 
245890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_analog_capture = {
2459c577b8a1SJoseph Chan 	.substreams = 2,
2460c577b8a1SJoseph Chan 	.channels_min = 2,
2461c577b8a1SJoseph Chan 	.channels_max = 2,
2462c577b8a1SJoseph Chan 	.nid = 0x14, /* NID to query formats and rates */
2463c577b8a1SJoseph Chan 	.ops = {
2464c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
2465c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
2466c577b8a1SJoseph Chan 	},
2467c577b8a1SJoseph Chan };
2468c577b8a1SJoseph Chan 
246990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_playback = {
2470c577b8a1SJoseph Chan 	.substreams = 1,
2471c577b8a1SJoseph Chan 	.channels_min = 2,
2472c577b8a1SJoseph Chan 	.channels_max = 2,
2473c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
2474c577b8a1SJoseph Chan 	.ops = {
2475c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
2476c577b8a1SJoseph Chan 		.close = via_dig_playback_pcm_close
2477c577b8a1SJoseph Chan 	},
2478c577b8a1SJoseph Chan };
2479c577b8a1SJoseph Chan 
248090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_capture = {
2481c577b8a1SJoseph Chan 	.substreams = 1,
2482c577b8a1SJoseph Chan 	.channels_min = 2,
2483c577b8a1SJoseph Chan 	.channels_max = 2,
2484c577b8a1SJoseph Chan };
2485c577b8a1SJoseph Chan 
2486c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2487c577b8a1SJoseph Chan {
2488c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2489c577b8a1SJoseph Chan 	int err;
2490c577b8a1SJoseph Chan 
2491c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2492c577b8a1SJoseph Chan 	if (err < 0)
2493c577b8a1SJoseph Chan 		return err;
2494*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2495c577b8a1SJoseph Chan 	if (err < 0)
2496c577b8a1SJoseph Chan 		return err;
2497c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2498c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2499c577b8a1SJoseph Chan 
2500*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2501c577b8a1SJoseph Chan 	if (err < 0)
2502c577b8a1SJoseph Chan 		return err;
2503*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2504c577b8a1SJoseph Chan 	if (err < 0)
2505c577b8a1SJoseph Chan 		return err;
2506620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2507c577b8a1SJoseph Chan 	if (err < 0)
2508c577b8a1SJoseph Chan 		return err;
2509c577b8a1SJoseph Chan 
2510c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2511c577b8a1SJoseph Chan 
25120852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2513c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
251455d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2515c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2516c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2517c577b8a1SJoseph Chan 
2518603c4019STakashi Iwai 	if (spec->kctls.list)
2519603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2520c577b8a1SJoseph Chan 
25210aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2522c577b8a1SJoseph Chan 
2523f8fdd495SHarald Welte 	if (spec->hp_mux)
25243d83e577STakashi Iwai 		via_hp_build(codec);
2525f8fdd495SHarald Welte 
25265b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2527c577b8a1SJoseph Chan 	return 1;
2528c577b8a1SJoseph Chan }
2529c577b8a1SJoseph Chan 
2530cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
253190dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = {
2532cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2533cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2534cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2535cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2536cb53c626STakashi Iwai 	{ } /* end */
2537cb53c626STakashi Iwai };
2538cb53c626STakashi Iwai #endif
2539cb53c626STakashi Iwai 
2540c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2541c577b8a1SJoseph Chan {
2542c577b8a1SJoseph Chan 	struct via_spec *spec;
2543c577b8a1SJoseph Chan 	int err;
2544c577b8a1SJoseph Chan 
2545c577b8a1SJoseph Chan 	/* create a codec specific record */
25465b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2547c577b8a1SJoseph Chan 	if (spec == NULL)
2548c577b8a1SJoseph Chan 		return -ENOMEM;
2549c577b8a1SJoseph Chan 
2550620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2551620e2b28STakashi Iwai 
2552c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2553c577b8a1SJoseph Chan 	if (err < 0) {
2554c577b8a1SJoseph Chan 		via_free(codec);
2555c577b8a1SJoseph Chan 		return err;
2556c577b8a1SJoseph Chan 	} else if (!err) {
2557c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2558c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2559c577b8a1SJoseph Chan 	}
2560c577b8a1SJoseph Chan 
256169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
256269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2563c577b8a1SJoseph Chan 
2564c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback;
2565c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2566c577b8a1SJoseph Chan 
2567c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2568c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2569c577b8a1SJoseph Chan 
2570a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2571c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2572c577b8a1SJoseph Chan 		spec->num_mixers++;
2573c577b8a1SJoseph Chan 	}
2574c577b8a1SJoseph Chan 
2575c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2576c577b8a1SJoseph Chan 
2577c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
257869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2579cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2580cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2581cb53c626STakashi Iwai #endif
2582c577b8a1SJoseph Chan 
2583c577b8a1SJoseph Chan 	return 0;
2584c577b8a1SJoseph Chan }
2585c577b8a1SJoseph Chan /*
2586c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2587c577b8a1SJoseph Chan  */
258890dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2589c577b8a1SJoseph Chan 	/*
2590c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2591c577b8a1SJoseph Chan 	 */
2592c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2593c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2594c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2595c577b8a1SJoseph Chan 
2596c577b8a1SJoseph Chan 
2597c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2598c577b8a1SJoseph Chan 	 * mixer widget
2599c577b8a1SJoseph Chan 	 */
2600c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2601c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2602c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2603c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2604c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2605c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2606c577b8a1SJoseph Chan 
2607c577b8a1SJoseph Chan 	/*
2608c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2609c577b8a1SJoseph Chan 	 */
2610c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2611c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2612c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2613c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2614c577b8a1SJoseph Chan 
2615c577b8a1SJoseph Chan 	/*
2616c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2617c577b8a1SJoseph Chan 	 */
2618c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2619c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2620c577b8a1SJoseph Chan 
2621c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2622c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2623c577b8a1SJoseph Chan 	/* PW9 Output enable */
2624c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2625c577b8a1SJoseph Chan 	{ }
2626c577b8a1SJoseph Chan };
2627c577b8a1SJoseph Chan 
2628c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2629c577b8a1SJoseph Chan {
2630c577b8a1SJoseph Chan 	struct via_spec *spec;
2631c577b8a1SJoseph Chan 	int err;
2632c577b8a1SJoseph Chan 
2633c577b8a1SJoseph Chan 	/* create a codec specific record */
26345b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2635c577b8a1SJoseph Chan 	if (spec == NULL)
2636c577b8a1SJoseph Chan 		return -ENOMEM;
2637c577b8a1SJoseph Chan 
2638620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2639620e2b28STakashi Iwai 
2640c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2641c577b8a1SJoseph Chan 	if (err < 0) {
2642c577b8a1SJoseph Chan 		via_free(codec);
2643c577b8a1SJoseph Chan 		return err;
2644c577b8a1SJoseph Chan 	} else if (!err) {
2645c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2646c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2647c577b8a1SJoseph Chan 	}
2648c577b8a1SJoseph Chan 
264969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
265069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2651c577b8a1SJoseph Chan 
2652c577b8a1SJoseph Chan 	spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback;
2653c577b8a1SJoseph Chan 	spec->stream_analog_capture = &vt1709_pcm_analog_capture;
2654c577b8a1SJoseph Chan 
2655c577b8a1SJoseph Chan 	spec->stream_digital_playback = &vt1709_pcm_digital_playback;
2656c577b8a1SJoseph Chan 	spec->stream_digital_capture = &vt1709_pcm_digital_capture;
2657c577b8a1SJoseph Chan 
2658a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
2659c577b8a1SJoseph Chan 		spec->mixers[spec->num_mixers] = vt1709_capture_mixer;
2660c577b8a1SJoseph Chan 		spec->num_mixers++;
2661c577b8a1SJoseph Chan 	}
2662c577b8a1SJoseph Chan 
2663c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2664c577b8a1SJoseph Chan 
2665c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
266669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2667cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2668cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2669cb53c626STakashi Iwai #endif
2670f7278fd0SJosepch Chan 	return 0;
2671f7278fd0SJosepch Chan }
2672f7278fd0SJosepch Chan 
2673f7278fd0SJosepch Chan /* capture mixer elements */
267490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = {
2675f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
2676f7278fd0SJosepch Chan 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
2677f7278fd0SJosepch Chan 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
2678f7278fd0SJosepch Chan 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
2679f7278fd0SJosepch Chan 	{
2680f7278fd0SJosepch Chan 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2681f7278fd0SJosepch Chan 		/* The multiple "Capture Source" controls confuse alsamixer
2682f7278fd0SJosepch Chan 		 * So call somewhat different..
2683f7278fd0SJosepch Chan 		 */
2684f7278fd0SJosepch Chan 		/* .name = "Capture Source", */
2685f7278fd0SJosepch Chan 		.name = "Input Source",
2686f7278fd0SJosepch Chan 		.count = 1,
2687f7278fd0SJosepch Chan 		.info = via_mux_enum_info,
2688f7278fd0SJosepch Chan 		.get = via_mux_enum_get,
2689f7278fd0SJosepch Chan 		.put = via_mux_enum_put,
2690f7278fd0SJosepch Chan 	},
2691f7278fd0SJosepch Chan 	{ } /* end */
2692f7278fd0SJosepch Chan };
2693f7278fd0SJosepch Chan /*
2694f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2695f7278fd0SJosepch Chan  */
269690dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2697f7278fd0SJosepch Chan 	/*
2698f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2699f7278fd0SJosepch Chan 	 */
2700f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2701f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2702f7278fd0SJosepch Chan 
2703f7278fd0SJosepch Chan 
2704f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2705f7278fd0SJosepch Chan 	 * mixer widget
2706f7278fd0SJosepch Chan 	 */
2707f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2708f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2709f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2710f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2711f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2712f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2713f7278fd0SJosepch Chan 
2714f7278fd0SJosepch Chan 	/*
2715f7278fd0SJosepch Chan 	 * Set up output mixers
2716f7278fd0SJosepch Chan 	 */
2717f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2718f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2719f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2720f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2721f7278fd0SJosepch Chan 
2722f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2723bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2724f7278fd0SJosepch Chan 	/* PW9 Output enable */
2725f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2726f7278fd0SJosepch Chan 	/* PW10 Input enable */
2727f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2728f7278fd0SJosepch Chan 	{ }
2729f7278fd0SJosepch Chan };
2730f7278fd0SJosepch Chan 
273190dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2732f7278fd0SJosepch Chan 	/*
2733f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2734f7278fd0SJosepch Chan 	 */
2735f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2736f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2737f7278fd0SJosepch Chan 
2738f7278fd0SJosepch Chan 
2739f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2740f7278fd0SJosepch Chan 	 * mixer widget
2741f7278fd0SJosepch Chan 	 */
2742f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2743f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2744f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2745f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2746f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2747f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2748f7278fd0SJosepch Chan 
2749f7278fd0SJosepch Chan 	/*
2750f7278fd0SJosepch Chan 	 * Set up output mixers
2751f7278fd0SJosepch Chan 	 */
2752f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2753f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2754f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2755f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2756f7278fd0SJosepch Chan 
2757f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2758f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2759f7278fd0SJosepch Chan 	/* PW9 Output enable */
2760f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2761f7278fd0SJosepch Chan 	/* PW10 Input enable */
2762f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2763f7278fd0SJosepch Chan 	{ }
2764f7278fd0SJosepch Chan };
2765f7278fd0SJosepch Chan 
276690dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = {
2767a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2768a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2769a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2770a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2771a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2772a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2773a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2774a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2775a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
277669e52a80SHarald Welte 	{ }
277769e52a80SHarald Welte };
277869e52a80SHarald Welte 
277917314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo,
278017314379SLydia Wang 			      struct hda_codec *codec,
278117314379SLydia Wang 			      struct snd_pcm_substream *substream)
278217314379SLydia Wang {
278317314379SLydia Wang 	int idle = substream->pstr->substream_opened == 1
278417314379SLydia Wang 		&& substream->ref_count == 0;
278517314379SLydia Wang 
278617314379SLydia Wang 	analog_low_current_mode(codec, idle);
278717314379SLydia Wang 	return 0;
278817314379SLydia Wang }
278917314379SLydia Wang 
279090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = {
27910aa62aefSHarald Welte 	.substreams = 2,
2792f7278fd0SJosepch Chan 	.channels_min = 2,
2793f7278fd0SJosepch Chan 	.channels_max = 8,
2794f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2795f7278fd0SJosepch Chan 	.ops = {
2796f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
27970aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
279817314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
279917314379SLydia Wang 		.close = via_pcm_open_close
2800f7278fd0SJosepch Chan 	},
2801f7278fd0SJosepch Chan };
2802f7278fd0SJosepch Chan 
280390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = {
28040aa62aefSHarald Welte 	.substreams = 2,
2805f7278fd0SJosepch Chan 	.channels_min = 2,
2806f7278fd0SJosepch Chan 	.channels_max = 4,
2807f7278fd0SJosepch Chan 	.nid = 0x10, /* NID to query formats and rates */
2808f7278fd0SJosepch Chan 	.ops = {
2809f7278fd0SJosepch Chan 		.open = via_playback_pcm_open,
28100aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
28110aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
2812f7278fd0SJosepch Chan 	},
2813f7278fd0SJosepch Chan };
2814f7278fd0SJosepch Chan 
281590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_analog_capture = {
2816f7278fd0SJosepch Chan 	.substreams = 2,
2817f7278fd0SJosepch Chan 	.channels_min = 2,
2818f7278fd0SJosepch Chan 	.channels_max = 2,
2819f7278fd0SJosepch Chan 	.nid = 0x13, /* NID to query formats and rates */
2820f7278fd0SJosepch Chan 	.ops = {
282117314379SLydia Wang 		.open = via_pcm_open_close,
2822f7278fd0SJosepch Chan 		.prepare = via_capture_pcm_prepare,
282317314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
282417314379SLydia Wang 		.close = via_pcm_open_close
2825f7278fd0SJosepch Chan 	},
2826f7278fd0SJosepch Chan };
2827f7278fd0SJosepch Chan 
282890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_playback = {
2829f7278fd0SJosepch Chan 	.substreams = 1,
2830f7278fd0SJosepch Chan 	.channels_min = 2,
2831f7278fd0SJosepch Chan 	.channels_max = 2,
2832f7278fd0SJosepch Chan 	/* NID is set in via_build_pcms */
2833f7278fd0SJosepch Chan 	.ops = {
2834f7278fd0SJosepch Chan 		.open = via_dig_playback_pcm_open,
2835f7278fd0SJosepch Chan 		.close = via_dig_playback_pcm_close,
28369da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
28379da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
2838f7278fd0SJosepch Chan 	},
2839f7278fd0SJosepch Chan };
2840f7278fd0SJosepch Chan 
284190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_capture = {
2842f7278fd0SJosepch Chan 	.substreams = 1,
2843f7278fd0SJosepch Chan 	.channels_min = 2,
2844f7278fd0SJosepch Chan 	.channels_max = 2,
2845f7278fd0SJosepch Chan };
2846f7278fd0SJosepch Chan 
2847f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
2848f7278fd0SJosepch Chan {
2849f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
2850f7278fd0SJosepch Chan 	int err;
2851f7278fd0SJosepch Chan 
2852f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2853f7278fd0SJosepch Chan 	if (err < 0)
2854f7278fd0SJosepch Chan 		return err;
2855*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2856f7278fd0SJosepch Chan 	if (err < 0)
2857f7278fd0SJosepch Chan 		return err;
2858f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2859f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
2860f7278fd0SJosepch Chan 
2861*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2862f7278fd0SJosepch Chan 	if (err < 0)
2863f7278fd0SJosepch Chan 		return err;
2864*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2865f7278fd0SJosepch Chan 	if (err < 0)
2866f7278fd0SJosepch Chan 		return err;
2867620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2868f7278fd0SJosepch Chan 	if (err < 0)
2869f7278fd0SJosepch Chan 		return err;
2870f7278fd0SJosepch Chan 
2871f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2872f7278fd0SJosepch Chan 
28730852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2874f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
287555d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
2876f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
2877f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
2878f7278fd0SJosepch Chan 
2879603c4019STakashi Iwai 	if (spec->kctls.list)
2880603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2881f7278fd0SJosepch Chan 
28820aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
28830aa62aefSHarald Welte 
2884f8fdd495SHarald Welte 	if (spec->hp_mux)
28853d83e577STakashi Iwai 		via_hp_build(codec);
2886f7278fd0SJosepch Chan 
28875b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
2888f7278fd0SJosepch Chan 	return 1;
2889f7278fd0SJosepch Chan }
2890f7278fd0SJosepch Chan 
2891f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
289290dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = {
2893f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
2894f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
2895f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
2896f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
2897f7278fd0SJosepch Chan 	{ } /* end */
2898f7278fd0SJosepch Chan };
2899f7278fd0SJosepch Chan #endif
29003e95b9abSLydia Wang 
29013e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
29023e95b9abSLydia Wang {
29033e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
29043e95b9abSLydia Wang 	int imux_is_smixer;
29053e95b9abSLydia Wang 	unsigned int parm;
29063e95b9abSLydia Wang 	int is_8ch = 0;
2907bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2908bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
29093e95b9abSLydia Wang 		is_8ch = 1;
29103e95b9abSLydia Wang 
29113e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
29123e95b9abSLydia Wang 	imux_is_smixer =
29133e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
29143e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
29153e95b9abSLydia Wang 	/* inputs */
29163e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
29173e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29183e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
29193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
29203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
29213e95b9abSLydia Wang 	if (imux_is_smixer)
29223e95b9abSLydia Wang 		parm = AC_PWRST_D0;
29233e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
29243e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
29253e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
29263e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
29273e95b9abSLydia Wang 
29283e95b9abSLydia Wang 	/* outputs */
29293e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
29303e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29313e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
29323e95b9abSLydia Wang 	if (spec->smart51_enabled)
29333e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
29343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
29353e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
29363e95b9abSLydia Wang 
29373e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
29383e95b9abSLydia Wang 	if (is_8ch) {
29393e95b9abSLydia Wang 		parm = AC_PWRST_D3;
29403e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
29413e95b9abSLydia Wang 		if (spec->smart51_enabled)
29423e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
29433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
29443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29453e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
29463e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2947bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2948bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2949bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2950bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2951bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2952bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2953bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2954bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2955bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2956bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29573e95b9abSLydia Wang 	}
29583e95b9abSLydia Wang 
29593e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
29603e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29613e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
29623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
29633e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
29643e95b9abSLydia Wang 	if (is_8ch)
29653e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
29663e95b9abSLydia Wang 
29673e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
29683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
29693e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
29703e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
29713e95b9abSLydia Wang 	if (is_8ch) {
29723e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
29733e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29743e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
29753e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2976bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2977bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2978bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29793e95b9abSLydia Wang }
29803e95b9abSLydia Wang 
2981518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2982f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
2983f7278fd0SJosepch Chan {
2984f7278fd0SJosepch Chan 	struct via_spec *spec;
2985f7278fd0SJosepch Chan 	int err;
2986f7278fd0SJosepch Chan 
2987518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2988518bf3baSLydia Wang 		return patch_vt1708S(codec);
2989f7278fd0SJosepch Chan 	/* create a codec specific record */
29905b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2991f7278fd0SJosepch Chan 	if (spec == NULL)
2992f7278fd0SJosepch Chan 		return -ENOMEM;
2993f7278fd0SJosepch Chan 
2994620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2995620e2b28STakashi Iwai 
2996f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
2997f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
2998f7278fd0SJosepch Chan 	if (err < 0) {
2999f7278fd0SJosepch Chan 		via_free(codec);
3000f7278fd0SJosepch Chan 		return err;
3001f7278fd0SJosepch Chan 	} else if (!err) {
3002f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3003f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3004f7278fd0SJosepch Chan 	}
3005f7278fd0SJosepch Chan 
300669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
300769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3008f7278fd0SJosepch Chan 
3009f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback;
3010f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3011f7278fd0SJosepch Chan 
3012f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3013f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3014f7278fd0SJosepch Chan 
3015a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3016f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3017f7278fd0SJosepch Chan 		spec->num_mixers++;
3018f7278fd0SJosepch Chan 	}
3019f7278fd0SJosepch Chan 
3020f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3021f7278fd0SJosepch Chan 
3022f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
302369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3024f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3025f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3026f7278fd0SJosepch Chan #endif
3027f7278fd0SJosepch Chan 
30283e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
30293e95b9abSLydia Wang 
3030f7278fd0SJosepch Chan 	return 0;
3031f7278fd0SJosepch Chan }
3032f7278fd0SJosepch Chan 
3033f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
3034f7278fd0SJosepch Chan {
3035f7278fd0SJosepch Chan 	struct via_spec *spec;
3036f7278fd0SJosepch Chan 	int err;
3037f7278fd0SJosepch Chan 
3038f7278fd0SJosepch Chan 	/* create a codec specific record */
30395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3040f7278fd0SJosepch Chan 	if (spec == NULL)
3041f7278fd0SJosepch Chan 		return -ENOMEM;
3042f7278fd0SJosepch Chan 
3043f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
3044f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
3045f7278fd0SJosepch Chan 	if (err < 0) {
3046f7278fd0SJosepch Chan 		via_free(codec);
3047f7278fd0SJosepch Chan 		return err;
3048f7278fd0SJosepch Chan 	} else if (!err) {
3049f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3050f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
3051f7278fd0SJosepch Chan 	}
3052f7278fd0SJosepch Chan 
305369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
305469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
3055f7278fd0SJosepch Chan 
3056f7278fd0SJosepch Chan 	spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback;
3057f7278fd0SJosepch Chan 	spec->stream_analog_capture = &vt1708B_pcm_analog_capture;
3058f7278fd0SJosepch Chan 
3059f7278fd0SJosepch Chan 	spec->stream_digital_playback = &vt1708B_pcm_digital_playback;
3060f7278fd0SJosepch Chan 	spec->stream_digital_capture = &vt1708B_pcm_digital_capture;
3061f7278fd0SJosepch Chan 
3062a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3063f7278fd0SJosepch Chan 		spec->mixers[spec->num_mixers] = vt1708B_capture_mixer;
3064f7278fd0SJosepch Chan 		spec->num_mixers++;
3065f7278fd0SJosepch Chan 	}
3066f7278fd0SJosepch Chan 
3067f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3068f7278fd0SJosepch Chan 
3069f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
307069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3071f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
3072f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
3073f7278fd0SJosepch Chan #endif
3074c577b8a1SJoseph Chan 
30753e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
30763e95b9abSLydia Wang 
3077c577b8a1SJoseph Chan 	return 0;
3078c577b8a1SJoseph Chan }
3079c577b8a1SJoseph Chan 
3080d949cac1SHarald Welte /* Patch for VT1708S */
3081d949cac1SHarald Welte 
3082d949cac1SHarald Welte /* capture mixer elements */
308390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = {
3084d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3085d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3086d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3087d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
30886369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
30896369bcfcSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
30906369bcfcSLydia Wang 			 HDA_INPUT),
3091d949cac1SHarald Welte 	{
3092d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3093d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3094d949cac1SHarald Welte 		 * So call somewhat different..
3095d949cac1SHarald Welte 		 */
3096d949cac1SHarald Welte 		/* .name = "Capture Source", */
3097d949cac1SHarald Welte 		.name = "Input Source",
3098d949cac1SHarald Welte 		.count = 1,
3099d949cac1SHarald Welte 		.info = via_mux_enum_info,
3100d949cac1SHarald Welte 		.get = via_mux_enum_get,
3101d949cac1SHarald Welte 		.put = via_mux_enum_put,
3102d949cac1SHarald Welte 	},
3103d949cac1SHarald Welte 	{ } /* end */
3104d949cac1SHarald Welte };
3105d949cac1SHarald Welte 
310690dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = {
3107d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3108d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3109d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3110d949cac1SHarald Welte 
3111d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3112d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3113d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3114d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3115d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3116d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3117d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3118d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3119d949cac1SHarald Welte 
3120d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3121d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
31225691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3123d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
31245691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3125d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3126d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3127bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3128bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3129d949cac1SHarald Welte 	{ }
3130d949cac1SHarald Welte };
3131d949cac1SHarald Welte 
313290dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = {
3133a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3134a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3135a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3136a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3137a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3138a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3139a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3140a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3141a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
314269e52a80SHarald Welte 	{ }
314369e52a80SHarald Welte };
314469e52a80SHarald Welte 
314590dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = {
3146bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3147bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3148bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3149bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3150bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3151bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3152bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3153bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3154bc92df7fSLydia Wang 	{ }
3155bc92df7fSLydia Wang };
3156bc92df7fSLydia Wang 
315790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_playback = {
3158d949cac1SHarald Welte 	.substreams = 2,
3159d949cac1SHarald Welte 	.channels_min = 2,
3160d949cac1SHarald Welte 	.channels_max = 8,
3161d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3162d949cac1SHarald Welte 	.ops = {
3163d949cac1SHarald Welte 		.open = via_playback_pcm_open,
3164c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3165c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
316617314379SLydia Wang 		.close = via_pcm_open_close
3167d949cac1SHarald Welte 	},
3168d949cac1SHarald Welte };
3169d949cac1SHarald Welte 
317090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1705_pcm_analog_playback = {
3171bc92df7fSLydia Wang 	.substreams = 2,
3172bc92df7fSLydia Wang 	.channels_min = 2,
3173bc92df7fSLydia Wang 	.channels_max = 6,
3174bc92df7fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
3175bc92df7fSLydia Wang 	.ops = {
3176bc92df7fSLydia Wang 		.open = via_playback_pcm_open,
3177bc92df7fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3178bc92df7fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
3179bc92df7fSLydia Wang 		.close = via_pcm_open_close
3180bc92df7fSLydia Wang 	},
3181bc92df7fSLydia Wang };
3182bc92df7fSLydia Wang 
318390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_capture = {
3184d949cac1SHarald Welte 	.substreams = 2,
3185d949cac1SHarald Welte 	.channels_min = 2,
3186d949cac1SHarald Welte 	.channels_max = 2,
3187d949cac1SHarald Welte 	.nid = 0x13, /* NID to query formats and rates */
3188d949cac1SHarald Welte 	.ops = {
318917314379SLydia Wang 		.open = via_pcm_open_close,
3190d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
319117314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
319217314379SLydia Wang 		.close = via_pcm_open_close
3193d949cac1SHarald Welte 	},
3194d949cac1SHarald Welte };
3195d949cac1SHarald Welte 
319690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_digital_playback = {
31979da29271STakashi Iwai 	.substreams = 1,
3198d949cac1SHarald Welte 	.channels_min = 2,
3199d949cac1SHarald Welte 	.channels_max = 2,
3200d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3201d949cac1SHarald Welte 	.ops = {
3202d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3203d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
32049da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
32059da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3206d949cac1SHarald Welte 	},
3207d949cac1SHarald Welte };
3208d949cac1SHarald Welte 
32099da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
32109da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
32119da29271STakashi Iwai {
32129da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
32139da29271STakashi Iwai 	int i;
32149da29271STakashi Iwai 
32159da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
32169da29271STakashi Iwai 		hda_nid_t nid;
32179da29271STakashi Iwai 		int conn;
32189da29271STakashi Iwai 
32199da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
32209da29271STakashi Iwai 		if (!nid)
32219da29271STakashi Iwai 			continue;
32229da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
32239da29271STakashi Iwai 		if (conn < 1)
32249da29271STakashi Iwai 			continue;
32259da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
32269da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
32279da29271STakashi Iwai 		else {
32289da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
32299da29271STakashi Iwai 			break; /* at most two dig outs */
32309da29271STakashi Iwai 		}
32319da29271STakashi Iwai 	}
32329da29271STakashi Iwai }
32339da29271STakashi Iwai 
3234d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3235d949cac1SHarald Welte {
3236d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3237d949cac1SHarald Welte 	int err;
3238d949cac1SHarald Welte 
32399da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3240d949cac1SHarald Welte 	if (err < 0)
3241d949cac1SHarald Welte 		return err;
3242*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3243d949cac1SHarald Welte 	if (err < 0)
3244d949cac1SHarald Welte 		return err;
3245d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3246d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3247d949cac1SHarald Welte 
3248*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3249d949cac1SHarald Welte 	if (err < 0)
3250d949cac1SHarald Welte 		return err;
3251*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3252d949cac1SHarald Welte 	if (err < 0)
3253d949cac1SHarald Welte 		return err;
3254620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3255d949cac1SHarald Welte 	if (err < 0)
3256d949cac1SHarald Welte 		return err;
3257d949cac1SHarald Welte 
3258d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3259d949cac1SHarald Welte 
32609da29271STakashi Iwai 	fill_dig_outs(codec);
326198aa34c0SHarald Welte 
3262603c4019STakashi Iwai 	if (spec->kctls.list)
3263603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3264d949cac1SHarald Welte 
32650aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
32660aa62aefSHarald Welte 
3267f8fdd495SHarald Welte 	if (spec->hp_mux)
32683d83e577STakashi Iwai 		via_hp_build(codec);
3269d949cac1SHarald Welte 
32705b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3271d949cac1SHarald Welte 	return 1;
3272d949cac1SHarald Welte }
3273d949cac1SHarald Welte 
3274d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
327590dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = {
3276d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3277d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3278d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3279d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3280d949cac1SHarald Welte 	{ } /* end */
3281d949cac1SHarald Welte };
3282d949cac1SHarald Welte #endif
3283d949cac1SHarald Welte 
32846369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
32856369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
32866369bcfcSLydia Wang {
32876369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
32886369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
32896369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
32906369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
32916369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
32926369bcfcSLydia Wang }
32936369bcfcSLydia Wang 
3294d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3295d949cac1SHarald Welte {
3296d949cac1SHarald Welte 	struct via_spec *spec;
3297d949cac1SHarald Welte 	int err;
3298d949cac1SHarald Welte 
3299d949cac1SHarald Welte 	/* create a codec specific record */
33005b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3301d949cac1SHarald Welte 	if (spec == NULL)
3302d949cac1SHarald Welte 		return -ENOMEM;
3303d949cac1SHarald Welte 
3304620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3305620e2b28STakashi Iwai 
3306d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3307d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3308d949cac1SHarald Welte 	if (err < 0) {
3309d949cac1SHarald Welte 		via_free(codec);
3310d949cac1SHarald Welte 		return err;
3311d949cac1SHarald Welte 	} else if (!err) {
3312d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3313d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3314d949cac1SHarald Welte 	}
3315d949cac1SHarald Welte 
331669e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3317bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3318bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3319bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3320bc92df7fSLydia Wang 	else
3321bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3322bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3323d949cac1SHarald Welte 
3324bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3325bc92df7fSLydia Wang 		spec->stream_analog_playback = &vt1705_pcm_analog_playback;
3326bc92df7fSLydia Wang 	else
3327d949cac1SHarald Welte 		spec->stream_analog_playback = &vt1708S_pcm_analog_playback;
3328d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1708S_pcm_analog_capture;
3329d949cac1SHarald Welte 
3330d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1708S_pcm_digital_playback;
3331d949cac1SHarald Welte 
3332a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
33336369bcfcSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
33346369bcfcSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
3335d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1708S_capture_mixer;
3336d949cac1SHarald Welte 		spec->num_mixers++;
3337d949cac1SHarald Welte 	}
3338d949cac1SHarald Welte 
3339d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3340d949cac1SHarald Welte 
3341d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
334269e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3343d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3344d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3345d949cac1SHarald Welte #endif
3346d949cac1SHarald Welte 
3347518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3348518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3349518bf3baSLydia Wang 		kfree(codec->chip_name);
3350518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3351518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3352518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3353518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3354970f630fSLydia Wang 	}
3355bc92df7fSLydia Wang 	/* correct names for VT1705 */
3356bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3357bc92df7fSLydia Wang 		kfree(codec->chip_name);
3358bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3359bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3360bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3361bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3362bc92df7fSLydia Wang 	}
33633e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3364d949cac1SHarald Welte 	return 0;
3365d949cac1SHarald Welte }
3366d949cac1SHarald Welte 
3367d949cac1SHarald Welte /* Patch for VT1702 */
3368d949cac1SHarald Welte 
3369d949cac1SHarald Welte /* capture mixer elements */
337090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = {
3371d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT),
3372d949cac1SHarald Welte 	HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT),
3373d949cac1SHarald Welte 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT),
3374d949cac1SHarald Welte 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT),
3375d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT),
3376d949cac1SHarald Welte 	HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT),
3377d949cac1SHarald Welte 	HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0,
3378d949cac1SHarald Welte 			 HDA_INPUT),
3379d949cac1SHarald Welte 	{
3380d949cac1SHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3381d949cac1SHarald Welte 		/* The multiple "Capture Source" controls confuse alsamixer
3382d949cac1SHarald Welte 		 * So call somewhat different..
3383d949cac1SHarald Welte 		 */
3384d949cac1SHarald Welte 		/* .name = "Capture Source", */
3385d949cac1SHarald Welte 		.name = "Input Source",
3386d949cac1SHarald Welte 		.count = 1,
3387d949cac1SHarald Welte 		.info = via_mux_enum_info,
3388d949cac1SHarald Welte 		.get = via_mux_enum_get,
3389d949cac1SHarald Welte 		.put = via_mux_enum_put,
3390d949cac1SHarald Welte 	},
3391d949cac1SHarald Welte 	{ } /* end */
3392d949cac1SHarald Welte };
3393d949cac1SHarald Welte 
339490dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = {
3395d949cac1SHarald Welte 	/*
3396d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3397d949cac1SHarald Welte 	 */
3398d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3399d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3400d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3401d949cac1SHarald Welte 
3402d949cac1SHarald Welte 
3403d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3404d949cac1SHarald Welte 	 * mixer widget
3405d949cac1SHarald Welte 	 */
3406d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3407d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3408d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3409d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3410d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3411d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3412d949cac1SHarald Welte 
3413d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3414d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3415d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3416d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3417d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3418bc7e7e5cSLydia Wang 	/* mixer enable */
3419bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3420bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3421bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3422d949cac1SHarald Welte 	{ }
3423d949cac1SHarald Welte };
3424d949cac1SHarald Welte 
342590dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = {
3426a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3427a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3428a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3429a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3430a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3431a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
343269e52a80SHarald Welte 	{ }
343369e52a80SHarald Welte };
343469e52a80SHarald Welte 
343590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_playback = {
34360aa62aefSHarald Welte 	.substreams = 2,
3437d949cac1SHarald Welte 	.channels_min = 2,
3438d949cac1SHarald Welte 	.channels_max = 2,
3439d949cac1SHarald Welte 	.nid = 0x10, /* NID to query formats and rates */
3440d949cac1SHarald Welte 	.ops = {
3441d949cac1SHarald Welte 		.open = via_playback_pcm_open,
34420aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
344317314379SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
344417314379SLydia Wang 		.close = via_pcm_open_close
3445d949cac1SHarald Welte 	},
3446d949cac1SHarald Welte };
3447d949cac1SHarald Welte 
344890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_capture = {
3449d949cac1SHarald Welte 	.substreams = 3,
3450d949cac1SHarald Welte 	.channels_min = 2,
3451d949cac1SHarald Welte 	.channels_max = 2,
3452d949cac1SHarald Welte 	.nid = 0x12, /* NID to query formats and rates */
3453d949cac1SHarald Welte 	.ops = {
345417314379SLydia Wang 		.open = via_pcm_open_close,
3455d949cac1SHarald Welte 		.prepare = via_capture_pcm_prepare,
345617314379SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
345717314379SLydia Wang 		.close = via_pcm_open_close
3458d949cac1SHarald Welte 	},
3459d949cac1SHarald Welte };
3460d949cac1SHarald Welte 
346190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_digital_playback = {
34625691ec7fSHarald Welte 	.substreams = 2,
3463d949cac1SHarald Welte 	.channels_min = 2,
3464d949cac1SHarald Welte 	.channels_max = 2,
3465d949cac1SHarald Welte 	/* NID is set in via_build_pcms */
3466d949cac1SHarald Welte 	.ops = {
3467d949cac1SHarald Welte 		.open = via_dig_playback_pcm_open,
3468d949cac1SHarald Welte 		.close = via_dig_playback_pcm_close,
34699da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
34709da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
3471d949cac1SHarald Welte 	},
3472d949cac1SHarald Welte };
3473d949cac1SHarald Welte 
3474d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
3475d949cac1SHarald Welte {
3476d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3477d949cac1SHarald Welte 	int err;
3478d949cac1SHarald Welte 
34799da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3480d949cac1SHarald Welte 	if (err < 0)
3481d949cac1SHarald Welte 		return err;
3482*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3483d949cac1SHarald Welte 	if (err < 0)
3484d949cac1SHarald Welte 		return err;
3485d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3486d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3487d949cac1SHarald Welte 
3488*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3489d949cac1SHarald Welte 	if (err < 0)
3490d949cac1SHarald Welte 		return err;
3491*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3492d949cac1SHarald Welte 	if (err < 0)
3493d949cac1SHarald Welte 		return err;
3494c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
3495c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3496c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3497c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3498c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3499c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
3500620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3501d949cac1SHarald Welte 	if (err < 0)
3502d949cac1SHarald Welte 		return err;
3503d949cac1SHarald Welte 
3504d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3505d949cac1SHarald Welte 
35069da29271STakashi Iwai 	fill_dig_outs(codec);
350798aa34c0SHarald Welte 
3508603c4019STakashi Iwai 	if (spec->kctls.list)
3509603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3510d949cac1SHarald Welte 
35110aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
35120aa62aefSHarald Welte 
3513f8fdd495SHarald Welte 	if (spec->hp_mux)
35143d83e577STakashi Iwai 		via_hp_build(codec);
3515d949cac1SHarald Welte 
3516d949cac1SHarald Welte 	return 1;
3517d949cac1SHarald Welte }
3518d949cac1SHarald Welte 
3519d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
352090dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = {
3521d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
3522d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
3523d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
3524d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
3525d949cac1SHarald Welte 	{ } /* end */
3526d949cac1SHarald Welte };
3527d949cac1SHarald Welte #endif
3528d949cac1SHarald Welte 
35293e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
35303e95b9abSLydia Wang {
35313e95b9abSLydia Wang 	int imux_is_smixer =
35323e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
35333e95b9abSLydia Wang 	unsigned int parm;
35343e95b9abSLydia Wang 	/* inputs */
35353e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
35363e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
35383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
35393e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
35403e95b9abSLydia Wang 	if (imux_is_smixer)
35413e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
35423e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
35433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
35443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
35453e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
35463e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
35473e95b9abSLydia Wang 
35483e95b9abSLydia Wang 	/* outputs */
35493e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
35503e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
35523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
35533e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
35543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
35553e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
35563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
35573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
35583e95b9abSLydia Wang }
35593e95b9abSLydia Wang 
3560d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
3561d949cac1SHarald Welte {
3562d949cac1SHarald Welte 	struct via_spec *spec;
3563d949cac1SHarald Welte 	int err;
3564d949cac1SHarald Welte 
3565d949cac1SHarald Welte 	/* create a codec specific record */
35665b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3567d949cac1SHarald Welte 	if (spec == NULL)
3568d949cac1SHarald Welte 		return -ENOMEM;
3569d949cac1SHarald Welte 
3570620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
3571620e2b28STakashi Iwai 
3572d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3573d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
3574d949cac1SHarald Welte 	if (err < 0) {
3575d949cac1SHarald Welte 		via_free(codec);
3576d949cac1SHarald Welte 		return err;
3577d949cac1SHarald Welte 	} else if (!err) {
3578d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3579d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3580d949cac1SHarald Welte 	}
3581d949cac1SHarald Welte 
358269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
358369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
3584d949cac1SHarald Welte 
3585d949cac1SHarald Welte 	spec->stream_analog_playback = &vt1702_pcm_analog_playback;
3586d949cac1SHarald Welte 	spec->stream_analog_capture = &vt1702_pcm_analog_capture;
3587d949cac1SHarald Welte 
3588d949cac1SHarald Welte 	spec->stream_digital_playback = &vt1702_pcm_digital_playback;
3589d949cac1SHarald Welte 
3590a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3591d949cac1SHarald Welte 		spec->mixers[spec->num_mixers] = vt1702_capture_mixer;
3592d949cac1SHarald Welte 		spec->num_mixers++;
3593d949cac1SHarald Welte 	}
3594d949cac1SHarald Welte 
3595d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3596d949cac1SHarald Welte 
3597d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
359869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3599d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3600d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
3601d949cac1SHarald Welte #endif
3602d949cac1SHarald Welte 
36033e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
3604d949cac1SHarald Welte 	return 0;
3605d949cac1SHarald Welte }
3606d949cac1SHarald Welte 
3607eb7188caSLydia Wang /* Patch for VT1718S */
3608eb7188caSLydia Wang 
3609eb7188caSLydia Wang /* capture mixer elements */
361090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = {
3611eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
3612eb7188caSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
3613eb7188caSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
3614eb7188caSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
3615eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
3616eb7188caSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
3617eb7188caSLydia Wang 			 HDA_INPUT),
3618eb7188caSLydia Wang 	{
3619eb7188caSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3620eb7188caSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
3621eb7188caSLydia Wang 		 * So call somewhat different..
3622eb7188caSLydia Wang 		 */
3623eb7188caSLydia Wang 		.name = "Input Source",
3624eb7188caSLydia Wang 		.count = 2,
3625eb7188caSLydia Wang 		.info = via_mux_enum_info,
3626eb7188caSLydia Wang 		.get = via_mux_enum_get,
3627eb7188caSLydia Wang 		.put = via_mux_enum_put,
3628eb7188caSLydia Wang 	},
3629eb7188caSLydia Wang 	{ } /* end */
3630eb7188caSLydia Wang };
3631eb7188caSLydia Wang 
363290dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = {
3633eb7188caSLydia Wang 	/*
3634eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3635eb7188caSLydia Wang 	 */
3636eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3637eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3638eb7188caSLydia Wang 
36394ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
36404ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
3641eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3642eb7188caSLydia Wang 	 * mixer widget
3643eb7188caSLydia Wang 	 */
3644eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3645eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3646eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3647eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3648eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
36494ab2d53aSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
3650eb7188caSLydia Wang 
3651eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
3652eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3653eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
3654eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3655eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3656eb7188caSLydia Wang 	/* PW11 Input enable */
3657eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
3658eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
3659eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
3660eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
3661eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3662eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3663eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3664eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3665eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3666eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3667eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3668eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3669eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3670eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3671eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
3672eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
3673eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
3674eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
3675eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3676eb7188caSLydia Wang 	{ }
3677eb7188caSLydia Wang };
3678eb7188caSLydia Wang 
3679eb7188caSLydia Wang 
368090dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = {
3681eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
3682eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3683eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3684eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3685eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3686eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3687eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3688eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3689eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3690eb7188caSLydia Wang 	{ }
3691eb7188caSLydia Wang };
3692eb7188caSLydia Wang 
369390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_playback = {
3694eb7188caSLydia Wang 	.substreams = 2,
3695eb7188caSLydia Wang 	.channels_min = 2,
3696eb7188caSLydia Wang 	.channels_max = 10,
3697eb7188caSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
3698eb7188caSLydia Wang 	.ops = {
3699eb7188caSLydia Wang 		.open = via_playback_pcm_open,
3700eb7188caSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
3701eb7188caSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
3702eb7188caSLydia Wang 		.close = via_pcm_open_close,
3703eb7188caSLydia Wang 	},
3704eb7188caSLydia Wang };
3705eb7188caSLydia Wang 
370690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_capture = {
3707eb7188caSLydia Wang 	.substreams = 2,
3708eb7188caSLydia Wang 	.channels_min = 2,
3709eb7188caSLydia Wang 	.channels_max = 2,
3710eb7188caSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
3711eb7188caSLydia Wang 	.ops = {
3712eb7188caSLydia Wang 		.open = via_pcm_open_close,
3713eb7188caSLydia Wang 		.prepare = via_capture_pcm_prepare,
3714eb7188caSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
3715eb7188caSLydia Wang 		.close = via_pcm_open_close,
3716eb7188caSLydia Wang 	},
3717eb7188caSLydia Wang };
3718eb7188caSLydia Wang 
371990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_playback = {
3720eb7188caSLydia Wang 	.substreams = 2,
3721eb7188caSLydia Wang 	.channels_min = 2,
3722eb7188caSLydia Wang 	.channels_max = 2,
3723eb7188caSLydia Wang 	/* NID is set in via_build_pcms */
3724eb7188caSLydia Wang 	.ops = {
3725eb7188caSLydia Wang 		.open = via_dig_playback_pcm_open,
3726eb7188caSLydia Wang 		.close = via_dig_playback_pcm_close,
3727eb7188caSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
3728eb7188caSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
3729eb7188caSLydia Wang 	},
3730eb7188caSLydia Wang };
3731eb7188caSLydia Wang 
373290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_capture = {
3733eb7188caSLydia Wang 	.substreams = 1,
3734eb7188caSLydia Wang 	.channels_min = 2,
3735eb7188caSLydia Wang 	.channels_max = 2,
3736eb7188caSLydia Wang };
3737eb7188caSLydia Wang 
3738eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
3739eb7188caSLydia Wang {
3740eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
3741eb7188caSLydia Wang 	int err;
3742eb7188caSLydia Wang 
3743eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3744eb7188caSLydia Wang 
3745eb7188caSLydia Wang 	if (err < 0)
3746eb7188caSLydia Wang 		return err;
3747*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3748eb7188caSLydia Wang 	if (err < 0)
3749eb7188caSLydia Wang 		return err;
3750eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3751eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
3752eb7188caSLydia Wang 
3753*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3754eb7188caSLydia Wang 	if (err < 0)
3755eb7188caSLydia Wang 		return err;
3756*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3757eb7188caSLydia Wang 	if (err < 0)
3758eb7188caSLydia Wang 		return err;
3759620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3760eb7188caSLydia Wang 	if (err < 0)
3761eb7188caSLydia Wang 		return err;
3762eb7188caSLydia Wang 
3763eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3764eb7188caSLydia Wang 
3765eb7188caSLydia Wang 	fill_dig_outs(codec);
3766eb7188caSLydia Wang 
3767eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
3768eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
3769eb7188caSLydia Wang 
3770eb7188caSLydia Wang 	if (spec->kctls.list)
3771eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3772eb7188caSLydia Wang 
3773eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
3774eb7188caSLydia Wang 
3775eb7188caSLydia Wang 	if (spec->hp_mux)
37763d83e577STakashi Iwai 		via_hp_build(codec);
3777eb7188caSLydia Wang 
37785b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
3779eb7188caSLydia Wang 
3780eb7188caSLydia Wang 	return 1;
3781eb7188caSLydia Wang }
3782eb7188caSLydia Wang 
3783eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
378490dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = {
3785eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
3786eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
3787eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
3788eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
3789eb7188caSLydia Wang 	{ } /* end */
3790eb7188caSLydia Wang };
3791eb7188caSLydia Wang #endif
3792eb7188caSLydia Wang 
37933e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
37943e95b9abSLydia Wang {
37953e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
37963e95b9abSLydia Wang 	int imux_is_smixer;
37973e95b9abSLydia Wang 	unsigned int parm;
37983e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
37993e95b9abSLydia Wang 	imux_is_smixer =
38003e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
38013e95b9abSLydia Wang 	/* inputs */
38023e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
38033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
38053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
38063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
38073e95b9abSLydia Wang 	if (imux_is_smixer)
38083e95b9abSLydia Wang 		parm = AC_PWRST_D0;
38093e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
38103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
38113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
38123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
38133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
38143e95b9abSLydia Wang 
38153e95b9abSLydia Wang 	/* outputs */
38163e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
38173e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38183e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
38193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
38203e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
38213e95b9abSLydia Wang 
38223e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
38233e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
38253e95b9abSLydia Wang 	if (spec->smart51_enabled)
38263e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
38273e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
38283e95b9abSLydia Wang 
38293e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
38303e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38313e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
38323e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
38333e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
38343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
38353e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
38363e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
38373e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
38383e95b9abSLydia Wang 
38393e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
38403e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38413e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
38423e95b9abSLydia Wang 	if (spec->smart51_enabled)
38433e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
38443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
38453e95b9abSLydia Wang 
38463e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
38473e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
38483e95b9abSLydia Wang 		parm = AC_PWRST_D3;
38493e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
38503e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
38513e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
38523e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
38533e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
38543e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
38553e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
38563e95b9abSLydia Wang 	}
38573e95b9abSLydia Wang }
38583e95b9abSLydia Wang 
3859eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
3860eb7188caSLydia Wang {
3861eb7188caSLydia Wang 	struct via_spec *spec;
3862eb7188caSLydia Wang 	int err;
3863eb7188caSLydia Wang 
3864eb7188caSLydia Wang 	/* create a codec specific record */
38655b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3866eb7188caSLydia Wang 	if (spec == NULL)
3867eb7188caSLydia Wang 		return -ENOMEM;
3868eb7188caSLydia Wang 
3869620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3870620e2b28STakashi Iwai 
3871eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
3872eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
3873eb7188caSLydia Wang 	if (err < 0) {
3874eb7188caSLydia Wang 		via_free(codec);
3875eb7188caSLydia Wang 		return err;
3876eb7188caSLydia Wang 	} else if (!err) {
3877eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3878eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
3879eb7188caSLydia Wang 	}
3880eb7188caSLydia Wang 
3881eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
3882eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
3883eb7188caSLydia Wang 
3884eb7188caSLydia Wang 	spec->stream_analog_playback = &vt1718S_pcm_analog_playback;
3885eb7188caSLydia Wang 	spec->stream_analog_capture = &vt1718S_pcm_analog_capture;
3886eb7188caSLydia Wang 
3887eb7188caSLydia Wang 	spec->stream_digital_playback = &vt1718S_pcm_digital_playback;
3888bb3c6bfcSLydia Wang 	if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441)
3889eb7188caSLydia Wang 		spec->stream_digital_capture = &vt1718S_pcm_digital_capture;
3890eb7188caSLydia Wang 
3891a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
3892bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
3893bb3c6bfcSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
3894eb7188caSLydia Wang 		spec->mixers[spec->num_mixers] = vt1718S_capture_mixer;
3895eb7188caSLydia Wang 		spec->num_mixers++;
3896eb7188caSLydia Wang 	}
3897eb7188caSLydia Wang 
3898eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
3899eb7188caSLydia Wang 
3900eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
39010f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
3902eb7188caSLydia Wang 
3903eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3904eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
3905eb7188caSLydia Wang #endif
3906eb7188caSLydia Wang 
39073e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
39083e95b9abSLydia Wang 
3909eb7188caSLydia Wang 	return 0;
3910eb7188caSLydia Wang }
3911f3db423dSLydia Wang 
3912f3db423dSLydia Wang /* Patch for VT1716S */
3913f3db423dSLydia Wang 
3914f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3915f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
3916f3db423dSLydia Wang {
3917f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3918f3db423dSLydia Wang 	uinfo->count = 1;
3919f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
3920f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
3921f3db423dSLydia Wang 	return 0;
3922f3db423dSLydia Wang }
3923f3db423dSLydia Wang 
3924f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3925f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3926f3db423dSLydia Wang {
3927f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3928f3db423dSLydia Wang 	int index = 0;
3929f3db423dSLydia Wang 
3930f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
3931f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
3932f3db423dSLydia Wang 	if (index != -1)
3933f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
3934f3db423dSLydia Wang 
3935f3db423dSLydia Wang 	return 0;
3936f3db423dSLydia Wang }
3937f3db423dSLydia Wang 
3938f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3939f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3940f3db423dSLydia Wang {
3941f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3942f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3943f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
3944f3db423dSLydia Wang 
3945f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
3946f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
3947f3db423dSLydia Wang 	spec->dmic_enabled = index;
39483e95b9abSLydia Wang 	set_widgets_power_state(codec);
3949f3db423dSLydia Wang 	return 1;
3950f3db423dSLydia Wang }
3951f3db423dSLydia Wang 
3952f3db423dSLydia Wang /* capture mixer elements */
395390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = {
3954f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT),
3955f3db423dSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT),
3956f3db423dSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT),
3957f3db423dSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT),
3958f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT),
3959f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0,
3960f3db423dSLydia Wang 			 HDA_INPUT),
3961f3db423dSLydia Wang 	{
3962f3db423dSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3963f3db423dSLydia Wang 		.name = "Input Source",
3964f3db423dSLydia Wang 		.count = 1,
3965f3db423dSLydia Wang 		.info = via_mux_enum_info,
3966f3db423dSLydia Wang 		.get = via_mux_enum_get,
3967f3db423dSLydia Wang 		.put = via_mux_enum_put,
3968f3db423dSLydia Wang 	},
3969f3db423dSLydia Wang 	{ } /* end */
3970f3db423dSLydia Wang };
3971f3db423dSLydia Wang 
397290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
3973f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3974f3db423dSLydia Wang 	{
3975f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3976f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
39775b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
3978f3db423dSLydia Wang 	 .count = 1,
3979f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
3980f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
3981f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
3982f3db423dSLydia Wang 	 },
3983f3db423dSLydia Wang 	{}			/* end */
3984f3db423dSLydia Wang };
3985f3db423dSLydia Wang 
3986f3db423dSLydia Wang 
3987f3db423dSLydia Wang /* mono-out mixer elements */
398890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3989f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3990f3db423dSLydia Wang 	{ } /* end */
3991f3db423dSLydia Wang };
3992f3db423dSLydia Wang 
399390dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = {
3994f3db423dSLydia Wang 	/*
3995f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3996f3db423dSLydia Wang 	 */
3997f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3998f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3999f3db423dSLydia Wang 
4000f3db423dSLydia Wang 
4001f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4002f3db423dSLydia Wang 	 * mixer widget
4003f3db423dSLydia Wang 	 */
4004f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4005f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4006f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4007f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4008f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4009f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4010f3db423dSLydia Wang 
4011f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
4012f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
4013f3db423dSLydia Wang 
4014f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
4015f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
4016f3db423dSLydia Wang 
4017f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
4018f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
4019f3db423dSLydia Wang 
4020f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
4021f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
4022f3db423dSLydia Wang 
4023f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
4024f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4025f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4026f3db423dSLydia Wang 
4027f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
4028f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4029f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
4030f3db423dSLydia Wang 	/* PW12 Output enable */
4031f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
4032f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
4033f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
4034f3db423dSLydia Wang 	/* don't bybass mixer */
4035f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
4036f3db423dSLydia Wang 	/* Enable mono output */
4037f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
4038f3db423dSLydia Wang 	{ }
4039f3db423dSLydia Wang };
4040f3db423dSLydia Wang 
4041f3db423dSLydia Wang 
404290dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = {
4043f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
4044f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
4045f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4046f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4047f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4048f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
4049f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
4050f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4051f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4052f3db423dSLydia Wang 	{ }
4053f3db423dSLydia Wang };
4054f3db423dSLydia Wang 
405590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_playback = {
4056f3db423dSLydia Wang 	.substreams = 2,
4057f3db423dSLydia Wang 	.channels_min = 2,
4058f3db423dSLydia Wang 	.channels_max = 6,
4059f3db423dSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4060f3db423dSLydia Wang 	.ops = {
4061f3db423dSLydia Wang 		.open = via_playback_pcm_open,
4062f3db423dSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4063f3db423dSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4064f3db423dSLydia Wang 		.close = via_pcm_open_close,
4065f3db423dSLydia Wang 	},
4066f3db423dSLydia Wang };
4067f3db423dSLydia Wang 
406890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_capture = {
4069f3db423dSLydia Wang 	.substreams = 2,
4070f3db423dSLydia Wang 	.channels_min = 2,
4071f3db423dSLydia Wang 	.channels_max = 2,
4072f3db423dSLydia Wang 	.nid = 0x13, /* NID to query formats and rates */
4073f3db423dSLydia Wang 	.ops = {
4074f3db423dSLydia Wang 		.open = via_pcm_open_close,
4075f3db423dSLydia Wang 		.prepare = via_capture_pcm_prepare,
4076f3db423dSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4077f3db423dSLydia Wang 		.close = via_pcm_open_close,
4078f3db423dSLydia Wang 	},
4079f3db423dSLydia Wang };
4080f3db423dSLydia Wang 
408190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_digital_playback = {
4082f3db423dSLydia Wang 	.substreams = 2,
4083f3db423dSLydia Wang 	.channels_min = 2,
4084f3db423dSLydia Wang 	.channels_max = 2,
4085f3db423dSLydia Wang 	/* NID is set in via_build_pcms */
4086f3db423dSLydia Wang 	.ops = {
4087f3db423dSLydia Wang 		.open = via_dig_playback_pcm_open,
4088f3db423dSLydia Wang 		.close = via_dig_playback_pcm_close,
4089f3db423dSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4090f3db423dSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4091f3db423dSLydia Wang 	},
4092f3db423dSLydia Wang };
4093f3db423dSLydia Wang 
4094f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
4095f3db423dSLydia Wang {
4096f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
4097f3db423dSLydia Wang 	int err;
4098f3db423dSLydia Wang 
4099f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4100f3db423dSLydia Wang 	if (err < 0)
4101f3db423dSLydia Wang 		return err;
4102*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
4103f3db423dSLydia Wang 	if (err < 0)
4104f3db423dSLydia Wang 		return err;
4105f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
4106f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
4107f3db423dSLydia Wang 
4108*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
4109f3db423dSLydia Wang 	if (err < 0)
4110f3db423dSLydia Wang 		return err;
4111*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
4112f3db423dSLydia Wang 	if (err < 0)
4113f3db423dSLydia Wang 		return err;
4114620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
4115f3db423dSLydia Wang 	if (err < 0)
4116f3db423dSLydia Wang 		return err;
4117f3db423dSLydia Wang 
4118f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4119f3db423dSLydia Wang 
4120f3db423dSLydia Wang 	fill_dig_outs(codec);
4121f3db423dSLydia Wang 
4122f3db423dSLydia Wang 	if (spec->kctls.list)
4123f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4124f3db423dSLydia Wang 
4125f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
4126f3db423dSLydia Wang 
4127f3db423dSLydia Wang 	if (spec->hp_mux)
41283d83e577STakashi Iwai 		via_hp_build(codec);
4129f3db423dSLydia Wang 
41305b0cb1d8SJaroslav Kysela 	via_smart51_build(spec);
4131f3db423dSLydia Wang 
4132f3db423dSLydia Wang 	return 1;
4133f3db423dSLydia Wang }
4134f3db423dSLydia Wang 
4135f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
413690dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = {
4137f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
4138f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
4139f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
4140f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
4141f3db423dSLydia Wang 	{ } /* end */
4142f3db423dSLydia Wang };
4143f3db423dSLydia Wang #endif
4144f3db423dSLydia Wang 
41453e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
41463e95b9abSLydia Wang {
41473e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
41483e95b9abSLydia Wang 	int imux_is_smixer;
41493e95b9abSLydia Wang 	unsigned int parm;
41503e95b9abSLydia Wang 	unsigned int mono_out, present;
41513e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
41523e95b9abSLydia Wang 	imux_is_smixer =
41533e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
41543e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
41553e95b9abSLydia Wang 	/* inputs */
41563e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
41573e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
41593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
41603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
41613e95b9abSLydia Wang 	if (imux_is_smixer)
41623e95b9abSLydia Wang 		parm = AC_PWRST_D0;
41633e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
41643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
41653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
41663e95b9abSLydia Wang 
41673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
41693e95b9abSLydia Wang 	/* PW11 (22h) */
41703e95b9abSLydia Wang 	if (spec->dmic_enabled)
41713e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
41723e95b9abSLydia Wang 	else
41733e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
41743e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
41753e95b9abSLydia Wang 
41763e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
41773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
41783e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
41793e95b9abSLydia Wang 
41803e95b9abSLydia Wang 	/* outputs */
41813e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
41823e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
41843e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
41853e95b9abSLydia Wang 	if (spec->smart51_enabled)
41863e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
41873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
41883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
41893e95b9abSLydia Wang 
41903e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
41913e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41923e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
41933e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
41943e95b9abSLydia Wang 	if (spec->smart51_enabled)
41953e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
41963e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
41973e95b9abSLydia Wang 
41983e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
41993e95b9abSLydia Wang 	if (spec->smart51_enabled)
42003e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
42013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
42023e95b9abSLydia Wang 
42033e95b9abSLydia Wang 	/* Mono out */
42043e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
42053e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
42063e95b9abSLydia Wang 
42073e95b9abSLydia Wang 	if (present)
42083e95b9abSLydia Wang 		mono_out = 0;
42093e95b9abSLydia Wang 	else {
42103e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
42113e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
42123e95b9abSLydia Wang 			mono_out = 0;
42133e95b9abSLydia Wang 		else
42143e95b9abSLydia Wang 			mono_out = 1;
42153e95b9abSLydia Wang 	}
42163e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
42173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
42183e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
42193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
42203e95b9abSLydia Wang 
42213e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
42223e95b9abSLydia Wang 	parm = AC_PWRST_D3;
42233e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
42243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
42253e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
42263e95b9abSLydia Wang 	if (spec->hp_independent_mode)
42273e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
42283e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
42293e95b9abSLydia Wang 
42303e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
42313e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
42323e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
42333e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
42343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
42353e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
42363e95b9abSLydia Wang }
42373e95b9abSLydia Wang 
4238f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
4239f3db423dSLydia Wang {
4240f3db423dSLydia Wang 	struct via_spec *spec;
4241f3db423dSLydia Wang 	int err;
4242f3db423dSLydia Wang 
4243f3db423dSLydia Wang 	/* create a codec specific record */
42445b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4245f3db423dSLydia Wang 	if (spec == NULL)
4246f3db423dSLydia Wang 		return -ENOMEM;
4247f3db423dSLydia Wang 
4248620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
4249620e2b28STakashi Iwai 
4250f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
4251f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
4252f3db423dSLydia Wang 	if (err < 0) {
4253f3db423dSLydia Wang 		via_free(codec);
4254f3db423dSLydia Wang 		return err;
4255f3db423dSLydia Wang 	} else if (!err) {
4256f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4257f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4258f3db423dSLydia Wang 	}
4259f3db423dSLydia Wang 
4260f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
4261f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
4262f3db423dSLydia Wang 
4263f3db423dSLydia Wang 	spec->stream_analog_playback = &vt1716S_pcm_analog_playback;
4264f3db423dSLydia Wang 	spec->stream_analog_capture = &vt1716S_pcm_analog_capture;
4265f3db423dSLydia Wang 
4266f3db423dSLydia Wang 	spec->stream_digital_playback = &vt1716S_pcm_digital_playback;
4267f3db423dSLydia Wang 
4268a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
4269f3db423dSLydia Wang 		override_mic_boost(codec, 0x1a, 0, 3, 40);
4270f3db423dSLydia Wang 		override_mic_boost(codec, 0x1e, 0, 3, 40);
4271f3db423dSLydia Wang 		spec->mixers[spec->num_mixers] = vt1716S_capture_mixer;
4272f3db423dSLydia Wang 		spec->num_mixers++;
4273f3db423dSLydia Wang 	}
4274f3db423dSLydia Wang 
4275f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
4276f3db423dSLydia Wang 	spec->num_mixers++;
4277f3db423dSLydia Wang 
4278f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
4279f3db423dSLydia Wang 
4280f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
4281f3db423dSLydia Wang 
4282f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
42830f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4284f3db423dSLydia Wang 
4285f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4286f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
4287f3db423dSLydia Wang #endif
4288f3db423dSLydia Wang 
42893e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
4290f3db423dSLydia Wang 	return 0;
4291f3db423dSLydia Wang }
429225eaba2fSLydia Wang 
429325eaba2fSLydia Wang /* for vt2002P */
429425eaba2fSLydia Wang 
429525eaba2fSLydia Wang /* capture mixer elements */
429690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = {
429725eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
429825eaba2fSLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
429925eaba2fSLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
430025eaba2fSLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
430125eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
430225eaba2fSLydia Wang 	HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0,
430325eaba2fSLydia Wang 			 HDA_INPUT),
430425eaba2fSLydia Wang 	{
430525eaba2fSLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
430625eaba2fSLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
430725eaba2fSLydia Wang 		 * So call somewhat different..
430825eaba2fSLydia Wang 		 */
430925eaba2fSLydia Wang 		/* .name = "Capture Source", */
431025eaba2fSLydia Wang 		.name = "Input Source",
431125eaba2fSLydia Wang 		.count = 2,
431225eaba2fSLydia Wang 		.info = via_mux_enum_info,
431325eaba2fSLydia Wang 		.get = via_mux_enum_get,
431425eaba2fSLydia Wang 		.put = via_mux_enum_put,
431525eaba2fSLydia Wang 	},
431625eaba2fSLydia Wang 	{ } /* end */
431725eaba2fSLydia Wang };
431825eaba2fSLydia Wang 
431990dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = {
4320eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
4321eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
4322eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
4323eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
432425eaba2fSLydia Wang 	/*
432525eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
432625eaba2fSLydia Wang 	 */
432725eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
432825eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
432925eaba2fSLydia Wang 
433025eaba2fSLydia Wang 
433125eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
433225eaba2fSLydia Wang 	 * mixer widget
433325eaba2fSLydia Wang 	 */
433425eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
433525eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
433625eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
433725eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
433825eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
433925eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
434025eaba2fSLydia Wang 
434125eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
434225eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
434325eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
434425eaba2fSLydia Wang 
434525eaba2fSLydia Wang 	/* PW9 Output enable */
434625eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
434725eaba2fSLydia Wang 
434825eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
434925eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
435025eaba2fSLydia Wang 
435125eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
435225eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
435325eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
435425eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
435525eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
435625eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
435725eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
435825eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
435925eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
436025eaba2fSLydia Wang 
436125eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
436225eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
436325eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
436425eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
436525eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
436625eaba2fSLydia Wang 
436725eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
436825eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
436925eaba2fSLydia Wang 
437025eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
437125eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
437225eaba2fSLydia Wang 	{ }
437325eaba2fSLydia Wang };
437490dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = {
437511890956SLydia Wang 	/*
437611890956SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
437711890956SLydia Wang 	 */
437811890956SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
437911890956SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
438011890956SLydia Wang 
438111890956SLydia Wang 
438211890956SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
438311890956SLydia Wang 	 * mixer widget
438411890956SLydia Wang 	 */
438511890956SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
438611890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
438711890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
438811890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
438911890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
439011890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
439111890956SLydia Wang 
439211890956SLydia Wang 	/* MUX Indices: Mic = 0 */
439311890956SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
439411890956SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
439511890956SLydia Wang 
439611890956SLydia Wang 	/* PW9 Output enable */
439711890956SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
439811890956SLydia Wang 
439911890956SLydia Wang 	/* Enable Boost Volume backdoor */
440011890956SLydia Wang 	{0x1, 0xfb9, 0x24},
440111890956SLydia Wang 
440211890956SLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
440311890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
440411890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
440511890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
440611890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
440711890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
440811890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
440911890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
441011890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
441111890956SLydia Wang 
441211890956SLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
441311890956SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
441411890956SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
441511890956SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
441611890956SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
441711890956SLydia Wang 
441811890956SLydia Wang 	/* set PW0 index=0 (MW0) */
441911890956SLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
442011890956SLydia Wang 
442111890956SLydia Wang 	/* Enable AOW0 to MW9 */
442211890956SLydia Wang 	{0x1, 0xfb8, 0x88},
442311890956SLydia Wang 	{ }
442411890956SLydia Wang };
442525eaba2fSLydia Wang 
442625eaba2fSLydia Wang 
442790dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = {
442825eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
442925eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
443025eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
443125eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
443225eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
443325eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
443425eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
443525eaba2fSLydia Wang 	{ }
443625eaba2fSLydia Wang };
443790dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = {
443811890956SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
443911890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
444011890956SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
444111890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
444211890956SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
444311890956SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
444411890956SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
444511890956SLydia Wang 	{ }
444611890956SLydia Wang };
444725eaba2fSLydia Wang 
444890dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_playback = {
444925eaba2fSLydia Wang 	.substreams = 2,
445025eaba2fSLydia Wang 	.channels_min = 2,
445125eaba2fSLydia Wang 	.channels_max = 2,
445225eaba2fSLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
445325eaba2fSLydia Wang 	.ops = {
445425eaba2fSLydia Wang 		.open = via_playback_pcm_open,
445525eaba2fSLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
445625eaba2fSLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
445725eaba2fSLydia Wang 		.close = via_pcm_open_close,
445825eaba2fSLydia Wang 	},
445925eaba2fSLydia Wang };
446025eaba2fSLydia Wang 
446190dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_capture = {
446225eaba2fSLydia Wang 	.substreams = 2,
446325eaba2fSLydia Wang 	.channels_min = 2,
446425eaba2fSLydia Wang 	.channels_max = 2,
446525eaba2fSLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
446625eaba2fSLydia Wang 	.ops = {
446725eaba2fSLydia Wang 		.open = via_pcm_open_close,
446825eaba2fSLydia Wang 		.prepare = via_capture_pcm_prepare,
446925eaba2fSLydia Wang 		.cleanup = via_capture_pcm_cleanup,
447025eaba2fSLydia Wang 		.close = via_pcm_open_close,
447125eaba2fSLydia Wang 	},
447225eaba2fSLydia Wang };
447325eaba2fSLydia Wang 
447490dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_digital_playback = {
447525eaba2fSLydia Wang 	.substreams = 1,
447625eaba2fSLydia Wang 	.channels_min = 2,
447725eaba2fSLydia Wang 	.channels_max = 2,
447825eaba2fSLydia Wang 	/* NID is set in via_build_pcms */
447925eaba2fSLydia Wang 	.ops = {
448025eaba2fSLydia Wang 		.open = via_dig_playback_pcm_open,
448125eaba2fSLydia Wang 		.close = via_dig_playback_pcm_close,
448225eaba2fSLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
448325eaba2fSLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
448425eaba2fSLydia Wang 	},
448525eaba2fSLydia Wang };
448625eaba2fSLydia Wang 
448725eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
448825eaba2fSLydia Wang {
448925eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
449025eaba2fSLydia Wang 	int err;
449125eaba2fSLydia Wang 
449225eaba2fSLydia Wang 
449325eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
449425eaba2fSLydia Wang 	if (err < 0)
449525eaba2fSLydia Wang 		return err;
449625eaba2fSLydia Wang 
4497*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
449825eaba2fSLydia Wang 	if (err < 0)
449925eaba2fSLydia Wang 		return err;
450025eaba2fSLydia Wang 
450125eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
450225eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
450325eaba2fSLydia Wang 
4504*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
450525eaba2fSLydia Wang 	if (err < 0)
450625eaba2fSLydia Wang 		return err;
4507*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
450825eaba2fSLydia Wang 	if (err < 0)
450925eaba2fSLydia Wang 		return err;
4510620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
451125eaba2fSLydia Wang 	if (err < 0)
451225eaba2fSLydia Wang 		return err;
451325eaba2fSLydia Wang 
451425eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
451525eaba2fSLydia Wang 
451625eaba2fSLydia Wang 	fill_dig_outs(codec);
451725eaba2fSLydia Wang 
451825eaba2fSLydia Wang 	if (spec->kctls.list)
451925eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
452025eaba2fSLydia Wang 
452125eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
452225eaba2fSLydia Wang 
452325eaba2fSLydia Wang 	if (spec->hp_mux)
45243d83e577STakashi Iwai 		via_hp_build(codec);
452525eaba2fSLydia Wang 
452625eaba2fSLydia Wang 	return 1;
452725eaba2fSLydia Wang }
452825eaba2fSLydia Wang 
452925eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
453090dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = {
453125eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
453225eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
453325eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
453425eaba2fSLydia Wang 	{ } /* end */
453525eaba2fSLydia Wang };
453625eaba2fSLydia Wang #endif
453725eaba2fSLydia Wang 
45383e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
45393e95b9abSLydia Wang {
45403e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
45413e95b9abSLydia Wang 	int imux_is_smixer;
45423e95b9abSLydia Wang 	unsigned int parm;
45433e95b9abSLydia Wang 	unsigned int present;
45443e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
45453e95b9abSLydia Wang 	imux_is_smixer =
45463e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
45473e95b9abSLydia Wang 	/* inputs */
45483e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
45493e95b9abSLydia Wang 	parm = AC_PWRST_D3;
45503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
45513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
45523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
45533e95b9abSLydia Wang 	parm = AC_PWRST_D0;
45543e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
45553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
45563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
45573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
45583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
45593e95b9abSLydia Wang 
45603e95b9abSLydia Wang 	/* outputs */
45613e95b9abSLydia Wang 	/* AOW0 (8h)*/
45623e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
45633e95b9abSLydia Wang 
456411890956SLydia Wang 	if (spec->codec_type == VT1802) {
456511890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
456611890956SLydia Wang 		parm = AC_PWRST_D3;
456711890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
456811890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
456911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
457011890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
457111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
457211890956SLydia Wang 	} else {
45733e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
45743e95b9abSLydia Wang 		parm = AC_PWRST_D3;
45753e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
45763e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
45773e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
45783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
45793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
458011890956SLydia Wang 	}
45813e95b9abSLydia Wang 
458211890956SLydia Wang 	if (spec->codec_type == VT1802) {
458311890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
458411890956SLydia Wang 		parm = AC_PWRST_D3;
458511890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
458611890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
458711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
458811890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
458911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
459011890956SLydia Wang 	} else {
45913e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
45923e95b9abSLydia Wang 		parm = AC_PWRST_D3;
45933e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
45943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
45953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
45963e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
45973e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
459811890956SLydia Wang 	}
45993e95b9abSLydia Wang 
46003e95b9abSLydia Wang 	if (spec->hp_independent_mode)
46013e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
46023e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
46033e95b9abSLydia Wang 
46043e95b9abSLydia Wang 	/* Class-D */
46053e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
46063e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
46073e95b9abSLydia Wang 
46083e95b9abSLydia Wang 	parm = AC_PWRST_D3;
46093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
46103e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
461111890956SLydia Wang 	if (spec->codec_type == VT1802)
461211890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
461311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
461411890956SLydia Wang 	else
46153e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
46163e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
46183e95b9abSLydia Wang 
46193e95b9abSLydia Wang 	/* Mono Out */
46203e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
46213e95b9abSLydia Wang 
46223e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
462311890956SLydia Wang 	if (spec->codec_type == VT1802) {
462411890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
462511890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
462611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
462711890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
462811890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
462911890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
463011890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
463111890956SLydia Wang 	} else {
46323e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
46333e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
46343e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46353e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
46363e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
46373e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
46383e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
463911890956SLydia Wang 	}
46403e95b9abSLydia Wang 	/* MW9 (21h) */
46413e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
46423e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
46433e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
46443e95b9abSLydia Wang 	else
46453e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
46463e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
46473e95b9abSLydia Wang }
464825eaba2fSLydia Wang 
464925eaba2fSLydia Wang /* patch for vt2002P */
465025eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
465125eaba2fSLydia Wang {
465225eaba2fSLydia Wang 	struct via_spec *spec;
465325eaba2fSLydia Wang 	int err;
465425eaba2fSLydia Wang 
465525eaba2fSLydia Wang 	/* create a codec specific record */
46565b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
465725eaba2fSLydia Wang 	if (spec == NULL)
465825eaba2fSLydia Wang 		return -ENOMEM;
465925eaba2fSLydia Wang 
4660620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4661620e2b28STakashi Iwai 
466225eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
466325eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
466425eaba2fSLydia Wang 	if (err < 0) {
466525eaba2fSLydia Wang 		via_free(codec);
466625eaba2fSLydia Wang 		return err;
466725eaba2fSLydia Wang 	} else if (!err) {
466825eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
466925eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
467025eaba2fSLydia Wang 	}
467125eaba2fSLydia Wang 
467211890956SLydia Wang 	if (spec->codec_type == VT1802)
467311890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
467411890956SLydia Wang 			vt1802_volume_init_verbs;
467511890956SLydia Wang 	else
467611890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
467711890956SLydia Wang 			vt2002P_volume_init_verbs;
467825eaba2fSLydia Wang 
467911890956SLydia Wang 	if (spec->codec_type == VT1802)
468011890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
468111890956SLydia Wang 			vt1802_uniwill_init_verbs;
468211890956SLydia Wang 	else
468311890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
468411890956SLydia Wang 			vt2002P_uniwill_init_verbs;
468511890956SLydia Wang 
468625eaba2fSLydia Wang 	spec->stream_analog_playback = &vt2002P_pcm_analog_playback;
468725eaba2fSLydia Wang 	spec->stream_analog_capture = &vt2002P_pcm_analog_capture;
468825eaba2fSLydia Wang 
468925eaba2fSLydia Wang 	spec->stream_digital_playback = &vt2002P_pcm_digital_playback;
469025eaba2fSLydia Wang 
4691a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
469225eaba2fSLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
469325eaba2fSLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
469425eaba2fSLydia Wang 		spec->mixers[spec->num_mixers] = vt2002P_capture_mixer;
469525eaba2fSLydia Wang 		spec->num_mixers++;
469625eaba2fSLydia Wang 	}
469725eaba2fSLydia Wang 
469825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
469925eaba2fSLydia Wang 
470025eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
47010f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
470225eaba2fSLydia Wang 
470325eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
470425eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
470525eaba2fSLydia Wang #endif
470625eaba2fSLydia Wang 
47073e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
470825eaba2fSLydia Wang 	return 0;
470925eaba2fSLydia Wang }
4710ab6734e7SLydia Wang 
4711ab6734e7SLydia Wang /* for vt1812 */
4712ab6734e7SLydia Wang 
4713ab6734e7SLydia Wang /* capture mixer elements */
471490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = {
4715ab6734e7SLydia Wang 	HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT),
4716ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT),
4717ab6734e7SLydia Wang 	HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT),
4718ab6734e7SLydia Wang 	HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT),
4719ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT),
4720ab6734e7SLydia Wang 	HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0,
4721ab6734e7SLydia Wang 		       HDA_INPUT),
4722ab6734e7SLydia Wang 	{
4723ab6734e7SLydia Wang 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4724ab6734e7SLydia Wang 		/* The multiple "Capture Source" controls confuse alsamixer
4725ab6734e7SLydia Wang 		 * So call somewhat different..
4726ab6734e7SLydia Wang 		 */
4727ab6734e7SLydia Wang 		.name = "Input Source",
4728ab6734e7SLydia Wang 		.count = 2,
4729ab6734e7SLydia Wang 		.info = via_mux_enum_info,
4730ab6734e7SLydia Wang 		.get = via_mux_enum_get,
4731ab6734e7SLydia Wang 		.put = via_mux_enum_put,
4732ab6734e7SLydia Wang 	},
4733ab6734e7SLydia Wang 	{ } /* end */
4734ab6734e7SLydia Wang };
4735ab6734e7SLydia Wang 
473690dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = {
4737ab6734e7SLydia Wang 	/*
4738ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4739ab6734e7SLydia Wang 	 */
4740ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4741ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4742ab6734e7SLydia Wang 
4743ab6734e7SLydia Wang 
4744ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4745ab6734e7SLydia Wang 	 * mixer widget
4746ab6734e7SLydia Wang 	 */
4747ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4748ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4749ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4750ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4751ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4752ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4753ab6734e7SLydia Wang 
4754ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
4755ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
4756ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
4757ab6734e7SLydia Wang 
4758ab6734e7SLydia Wang 	/* PW9 Output enable */
4759ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4760ab6734e7SLydia Wang 
4761ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
4762ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
4763ab6734e7SLydia Wang 
4764ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
4765ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4766ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4767ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4768ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4769ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4770ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4771ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4772ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4773ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4774ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4775ab6734e7SLydia Wang 
4776ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
4777ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
4778ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
4779ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
4780ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
4781ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
4782ab6734e7SLydia Wang 
4783ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
4784ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
4785ab6734e7SLydia Wang 	{ }
4786ab6734e7SLydia Wang };
4787ab6734e7SLydia Wang 
4788ab6734e7SLydia Wang 
478990dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = {
4790ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
4791ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4792ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
4793ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4794ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4795ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4796ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4797ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4798ab6734e7SLydia Wang 	{ }
4799ab6734e7SLydia Wang };
4800ab6734e7SLydia Wang 
480190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_playback = {
4802ab6734e7SLydia Wang 	.substreams = 2,
4803ab6734e7SLydia Wang 	.channels_min = 2,
4804ab6734e7SLydia Wang 	.channels_max = 2,
4805ab6734e7SLydia Wang 	.nid = 0x8, /* NID to query formats and rates */
4806ab6734e7SLydia Wang 	.ops = {
4807ab6734e7SLydia Wang 		.open = via_playback_pcm_open,
4808ab6734e7SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
4809ab6734e7SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup,
4810ab6734e7SLydia Wang 		.close = via_pcm_open_close,
4811ab6734e7SLydia Wang 	},
4812ab6734e7SLydia Wang };
4813ab6734e7SLydia Wang 
481490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_capture = {
4815ab6734e7SLydia Wang 	.substreams = 2,
4816ab6734e7SLydia Wang 	.channels_min = 2,
4817ab6734e7SLydia Wang 	.channels_max = 2,
4818ab6734e7SLydia Wang 	.nid = 0x10, /* NID to query formats and rates */
4819ab6734e7SLydia Wang 	.ops = {
4820ab6734e7SLydia Wang 		.open = via_pcm_open_close,
4821ab6734e7SLydia Wang 		.prepare = via_capture_pcm_prepare,
4822ab6734e7SLydia Wang 		.cleanup = via_capture_pcm_cleanup,
4823ab6734e7SLydia Wang 		.close = via_pcm_open_close,
4824ab6734e7SLydia Wang 	},
4825ab6734e7SLydia Wang };
4826ab6734e7SLydia Wang 
482790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_digital_playback = {
4828ab6734e7SLydia Wang 	.substreams = 1,
4829ab6734e7SLydia Wang 	.channels_min = 2,
4830ab6734e7SLydia Wang 	.channels_max = 2,
4831ab6734e7SLydia Wang 	/* NID is set in via_build_pcms */
4832ab6734e7SLydia Wang 	.ops = {
4833ab6734e7SLydia Wang 		.open = via_dig_playback_pcm_open,
4834ab6734e7SLydia Wang 		.close = via_dig_playback_pcm_close,
4835ab6734e7SLydia Wang 		.prepare = via_dig_playback_pcm_prepare,
4836ab6734e7SLydia Wang 		.cleanup = via_dig_playback_pcm_cleanup
4837ab6734e7SLydia Wang 	},
4838ab6734e7SLydia Wang };
4839ab6734e7SLydia Wang 
4840ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
4841ab6734e7SLydia Wang {
4842ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
4843ab6734e7SLydia Wang 	int err;
4844ab6734e7SLydia Wang 
4845ab6734e7SLydia Wang 
4846ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4847ab6734e7SLydia Wang 	if (err < 0)
4848ab6734e7SLydia Wang 		return err;
4849ab6734e7SLydia Wang 	fill_dig_outs(codec);
4850*4a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
4851ab6734e7SLydia Wang 	if (err < 0)
4852ab6734e7SLydia Wang 		return err;
4853ab6734e7SLydia Wang 
4854ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
4855ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
4856ab6734e7SLydia Wang 
4857*4a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
4858ab6734e7SLydia Wang 	if (err < 0)
4859ab6734e7SLydia Wang 		return err;
4860*4a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
4861ab6734e7SLydia Wang 	if (err < 0)
4862ab6734e7SLydia Wang 		return err;
4863620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
4864ab6734e7SLydia Wang 	if (err < 0)
4865ab6734e7SLydia Wang 		return err;
4866ab6734e7SLydia Wang 
4867ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4868ab6734e7SLydia Wang 
4869ab6734e7SLydia Wang 	fill_dig_outs(codec);
4870ab6734e7SLydia Wang 
4871ab6734e7SLydia Wang 	if (spec->kctls.list)
4872ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4873ab6734e7SLydia Wang 
4874ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
4875ab6734e7SLydia Wang 
4876ab6734e7SLydia Wang 	if (spec->hp_mux)
48773d83e577STakashi Iwai 		via_hp_build(codec);
4878ab6734e7SLydia Wang 
4879ab6734e7SLydia Wang 	return 1;
4880ab6734e7SLydia Wang }
4881ab6734e7SLydia Wang 
4882ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
488390dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = {
4884ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
4885ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4886ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4887ab6734e7SLydia Wang 	{ } /* end */
4888ab6734e7SLydia Wang };
4889ab6734e7SLydia Wang #endif
4890ab6734e7SLydia Wang 
48913e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
48923e95b9abSLydia Wang {
48933e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
48943e95b9abSLydia Wang 	int imux_is_smixer =
48953e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
48963e95b9abSLydia Wang 	unsigned int parm;
48973e95b9abSLydia Wang 	unsigned int present;
48983e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
48993e95b9abSLydia Wang 	imux_is_smixer =
49003e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
49013e95b9abSLydia Wang 	/* inputs */
49023e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
49033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
49053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
49063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
49073e95b9abSLydia Wang 	parm = AC_PWRST_D0;
49083e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
49093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
49103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
49113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
49123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
49133e95b9abSLydia Wang 
49143e95b9abSLydia Wang 	/* outputs */
49153e95b9abSLydia Wang 	/* AOW0 (8h)*/
49163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
49173e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49183e95b9abSLydia Wang 
49193e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
49203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
49223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
49233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
49243e95b9abSLydia Wang 
49253e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
49263e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49273e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
49283e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
49293e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
49303e95b9abSLydia Wang 	if (spec->hp_independent_mode)
49313e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
49323e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49333e95b9abSLydia Wang 
49343e95b9abSLydia Wang 	/* Internal Speaker */
49353e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
49363e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
49373e95b9abSLydia Wang 
49383e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49393e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
49403e95b9abSLydia Wang 	if (present) {
49413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
49423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
49433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
49443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
49453e95b9abSLydia Wang 	} else {
49463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
49473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49483e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
49493e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49503e95b9abSLydia Wang 	}
49513e95b9abSLydia Wang 
49523e95b9abSLydia Wang 
49533e95b9abSLydia Wang 	/* Mono Out */
49543e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
49553e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
49563e95b9abSLydia Wang 
49573e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
49593e95b9abSLydia Wang 	if (present) {
49603e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
49613e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
49623e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
49633e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
49643e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
49653e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
49663e95b9abSLydia Wang 	} else {
49673e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
49683e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49693e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
49703e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49713e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
49723e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
49733e95b9abSLydia Wang 	}
49743e95b9abSLydia Wang 
49753e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
49763e95b9abSLydia Wang 	parm = AC_PWRST_D3;
49773e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
49783e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
49793e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
49803e95b9abSLydia Wang 
49813e95b9abSLydia Wang }
4982ab6734e7SLydia Wang 
4983ab6734e7SLydia Wang /* patch for vt1812 */
4984ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
4985ab6734e7SLydia Wang {
4986ab6734e7SLydia Wang 	struct via_spec *spec;
4987ab6734e7SLydia Wang 	int err;
4988ab6734e7SLydia Wang 
4989ab6734e7SLydia Wang 	/* create a codec specific record */
49905b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4991ab6734e7SLydia Wang 	if (spec == NULL)
4992ab6734e7SLydia Wang 		return -ENOMEM;
4993ab6734e7SLydia Wang 
4994620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4995620e2b28STakashi Iwai 
4996ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
4997ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
4998ab6734e7SLydia Wang 	if (err < 0) {
4999ab6734e7SLydia Wang 		via_free(codec);
5000ab6734e7SLydia Wang 		return err;
5001ab6734e7SLydia Wang 	} else if (!err) {
5002ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
5003ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
5004ab6734e7SLydia Wang 	}
5005ab6734e7SLydia Wang 
5006ab6734e7SLydia Wang 
5007ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
5008ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
5009ab6734e7SLydia Wang 
5010ab6734e7SLydia Wang 	spec->stream_analog_playback = &vt1812_pcm_analog_playback;
5011ab6734e7SLydia Wang 	spec->stream_analog_capture = &vt1812_pcm_analog_capture;
5012ab6734e7SLydia Wang 
5013ab6734e7SLydia Wang 	spec->stream_digital_playback = &vt1812_pcm_digital_playback;
5014ab6734e7SLydia Wang 
5015a766d0d7STakashi Iwai 	if (spec->adc_nids && spec->input_mux) {
5016ab6734e7SLydia Wang 		override_mic_boost(codec, 0x2b, 0, 3, 40);
5017ab6734e7SLydia Wang 		override_mic_boost(codec, 0x29, 0, 3, 40);
5018ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers] = vt1812_capture_mixer;
5019ab6734e7SLydia Wang 		spec->num_mixers++;
5020ab6734e7SLydia Wang 	}
5021ab6734e7SLydia Wang 
5022ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
5023ab6734e7SLydia Wang 
5024ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
50250f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
5026ab6734e7SLydia Wang 
5027ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
5028ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
5029ab6734e7SLydia Wang #endif
5030ab6734e7SLydia Wang 
50313e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
5032ab6734e7SLydia Wang 	return 0;
5033ab6734e7SLydia Wang }
5034ab6734e7SLydia Wang 
5035c577b8a1SJoseph Chan /*
5036c577b8a1SJoseph Chan  * patch entries
5037c577b8a1SJoseph Chan  */
503890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
50393218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
50403218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
50413218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
50423218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
50433218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
5044f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
50453218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
5046f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
50473218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
5048f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
50493218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
5050f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
50513218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
5052f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
50533218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
5054f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
50553218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
5056f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
50573218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
5058f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
50593218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
5060f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
50613218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
5062f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
50633218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
5064f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
50653218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
5066f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
50673218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
5068f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
50693218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
5070f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
50713218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
5072f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
50733218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
5074f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
50753218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
5076d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50773218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
5078d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50793218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
5080d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50813218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
5082d949cac1SHarald Welte 	  .patch = patch_vt1708S},
5083bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
5084d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50853218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
5086d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50873218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
5088d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50893218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
5090d949cac1SHarald Welte 	  .patch = patch_vt1708S},
50913218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
5092d949cac1SHarald Welte 	  .patch = patch_vt1702},
50933218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
5094d949cac1SHarald Welte 	  .patch = patch_vt1702},
50953218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
5096d949cac1SHarald Welte 	  .patch = patch_vt1702},
50973218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
5098d949cac1SHarald Welte 	  .patch = patch_vt1702},
50993218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
5100d949cac1SHarald Welte 	  .patch = patch_vt1702},
51013218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
5102d949cac1SHarald Welte 	  .patch = patch_vt1702},
51033218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
5104d949cac1SHarald Welte 	  .patch = patch_vt1702},
51053218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
5106d949cac1SHarald Welte 	  .patch = patch_vt1702},
5107eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
5108eb7188caSLydia Wang 	  .patch = patch_vt1718S},
5109eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
5110eb7188caSLydia Wang 	  .patch = patch_vt1718S},
5111bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
5112bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
5113bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
5114bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
5115f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
5116f3db423dSLydia Wang 	  .patch = patch_vt1716S},
5117f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
5118f3db423dSLydia Wang 	  .patch = patch_vt1716S},
511925eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
512025eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
5121ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
512236dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
512336dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
512411890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
512511890956SLydia Wang 		.patch = patch_vt2002P},
512611890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
512711890956SLydia Wang 		.patch = patch_vt2002P},
5128c577b8a1SJoseph Chan 	{} /* terminator */
5129c577b8a1SJoseph Chan };
51301289e9e8STakashi Iwai 
51311289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
51321289e9e8STakashi Iwai 
51331289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
51341289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
51351289e9e8STakashi Iwai 	.owner = THIS_MODULE,
51361289e9e8STakashi Iwai };
51371289e9e8STakashi Iwai 
51381289e9e8STakashi Iwai MODULE_LICENSE("GPL");
51391289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
51401289e9e8STakashi Iwai 
51411289e9e8STakashi Iwai static int __init patch_via_init(void)
51421289e9e8STakashi Iwai {
51431289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
51441289e9e8STakashi Iwai }
51451289e9e8STakashi Iwai 
51461289e9e8STakashi Iwai static void __exit patch_via_exit(void)
51471289e9e8STakashi Iwai {
51481289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
51491289e9e8STakashi Iwai }
51501289e9e8STakashi Iwai 
51511289e9e8STakashi Iwai module_init(patch_via_init)
51521289e9e8STakashi Iwai module_exit(patch_via_exit)
5153