xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision d7a99cce558f84477adacce9324ab22f52c951ba)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
575b0cb1d8SJaroslav Kysela #define NID_MAPPING		(-1)
585b0cb1d8SJaroslav Kysela 
59c577b8a1SJoseph Chan /* amp values */
60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan /* Pin Widget NID */
64c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
70c577b8a1SJoseph Chan 
71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
75f7278fd0SJosepch Chan 
76f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
80c577b8a1SJoseph Chan 
81d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
83d949cac1SHarald Welte 
84d949cac1SHarald Welte #define VT1702_HP_NID		0x17
85d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
86d949cac1SHarald Welte 
87d7426329SHarald Welte enum VIA_HDA_CODEC {
88d7426329SHarald Welte 	UNKNOWN = -1,
89d7426329SHarald Welte 	VT1708,
90d7426329SHarald Welte 	VT1709_10CH,
91d7426329SHarald Welte 	VT1709_6CH,
92d7426329SHarald Welte 	VT1708B_8CH,
93d7426329SHarald Welte 	VT1708B_4CH,
94d7426329SHarald Welte 	VT1708S,
95518bf3baSLydia Wang 	VT1708BCE,
96d7426329SHarald Welte 	VT1702,
97eb7188caSLydia Wang 	VT1718S,
98f3db423dSLydia Wang 	VT1716S,
9925eaba2fSLydia Wang 	VT2002P,
100ab6734e7SLydia Wang 	VT1812,
10111890956SLydia Wang 	VT1802,
102d7426329SHarald Welte 	CODEC_TYPES,
103d7426329SHarald Welte };
104d7426329SHarald Welte 
10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
10611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
10711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
10811890956SLydia Wang 	 (spec)->codec_type == VT1802)
10911890956SLydia Wang 
1104a79616dSTakashi Iwai struct nid_path {
1114a79616dSTakashi Iwai 	int depth;
1124a79616dSTakashi Iwai 	hda_nid_t path[5];
1134a79616dSTakashi Iwai 	short idx[5];
1144a79616dSTakashi Iwai };
1154a79616dSTakashi Iwai 
1161f2e99feSLydia Wang struct via_spec {
1171f2e99feSLydia Wang 	/* codec parameterization */
11890dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1191f2e99feSLydia Wang 	unsigned int num_mixers;
1201f2e99feSLydia Wang 
12190dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1221f2e99feSLydia Wang 	unsigned int num_iverbs;
1231f2e99feSLydia Wang 
12482673bc8STakashi Iwai 	char stream_name_analog[32];
1257eb56e84STakashi Iwai 	char stream_name_hp[32];
12690dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12790dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1281f2e99feSLydia Wang 
12982673bc8STakashi Iwai 	char stream_name_digital[32];
13090dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
13190dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1321f2e99feSLydia Wang 
1331f2e99feSLydia Wang 	/* playback */
1341f2e99feSLydia Wang 	struct hda_multi_out multiout;
1351f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
1361f2e99feSLydia Wang 
1374a79616dSTakashi Iwai 	struct nid_path out_path[4];
1384a79616dSTakashi Iwai 	struct nid_path hp_path;
1394a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
1404a79616dSTakashi Iwai 
1411f2e99feSLydia Wang 	/* capture */
1421f2e99feSLydia Wang 	unsigned int num_adc_nids;
143a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1441f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
145620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1461f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1471f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1481f2e99feSLydia Wang 
1491f2e99feSLydia Wang 	/* capture source */
1501f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1511f2e99feSLydia Wang 	unsigned int cur_mux[3];
1521f2e99feSLydia Wang 
1531f2e99feSLydia Wang 	/* PCM information */
1541f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1551f2e99feSLydia Wang 
1561f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1571f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1581f2e99feSLydia Wang 	struct snd_array kctls;
1591f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1601f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1611f2e99feSLydia Wang 
1621f2e99feSLydia Wang 	/* HP mode source */
1631f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1641f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1651f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
166f4a7828bSTakashi Iwai 	unsigned int can_smart51;
1671f2e99feSLydia Wang 	unsigned int smart51_enabled;
168f3db423dSLydia Wang 	unsigned int dmic_enabled;
16924088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1701f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1711f2e99feSLydia Wang 
1721f2e99feSLydia Wang 	/* work to check hp jack state */
1731f2e99feSLydia Wang 	struct hda_codec *codec;
1741f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
175e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1761f2e99feSLydia Wang 	int vt1708_hp_present;
1773e95b9abSLydia Wang 
1783e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1793e95b9abSLydia Wang 
1801f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1811f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1821f2e99feSLydia Wang #endif
1831f2e99feSLydia Wang };
1841f2e99feSLydia Wang 
1850341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1865b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1875b0cb1d8SJaroslav Kysela {
1885b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1895b0cb1d8SJaroslav Kysela 
1905b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1915b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1925b0cb1d8SJaroslav Kysela 		return NULL;
1935b0cb1d8SJaroslav Kysela 
1945b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1955b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1960341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1970341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1980341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1990341ccd7SLydia Wang 		spec->codec_type = VT1708S;
2005b0cb1d8SJaroslav Kysela 	return spec;
2015b0cb1d8SJaroslav Kysela }
2025b0cb1d8SJaroslav Kysela 
203744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
204d7426329SHarald Welte {
205744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
206d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
207d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
208d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
209d7426329SHarald Welte 
210d7426329SHarald Welte 	/* get codec type */
211d7426329SHarald Welte 	if (ven_id != 0x1106)
212d7426329SHarald Welte 		codec_type = UNKNOWN;
213d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
214d7426329SHarald Welte 		codec_type = VT1708;
215d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
216d7426329SHarald Welte 		codec_type = VT1709_10CH;
217d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
218d7426329SHarald Welte 		codec_type = VT1709_6CH;
219518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
220d7426329SHarald Welte 		codec_type = VT1708B_8CH;
221518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
222518bf3baSLydia Wang 			codec_type = VT1708BCE;
223518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
224d7426329SHarald Welte 		codec_type = VT1708B_4CH;
225d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
226d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
227d7426329SHarald Welte 		codec_type = VT1708S;
228d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
229d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
230d7426329SHarald Welte 		codec_type = VT1702;
231eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
232eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
233eb7188caSLydia Wang 		codec_type = VT1718S;
234f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
235f3db423dSLydia Wang 		codec_type = VT1716S;
236bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
237bb3c6bfcSLydia Wang 		codec_type = VT1718S;
23825eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
23925eaba2fSLydia Wang 		codec_type = VT2002P;
240ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
241ab6734e7SLydia Wang 		codec_type = VT1812;
24236dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
24336dd5c4aSLydia Wang 		codec_type = VT1708S;
24411890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
24511890956SLydia Wang 		codec_type = VT1802;
246d7426329SHarald Welte 	else
247d7426329SHarald Welte 		codec_type = UNKNOWN;
248d7426329SHarald Welte 	return codec_type;
249d7426329SHarald Welte };
250d7426329SHarald Welte 
251ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
25269e52a80SHarald Welte #define VIA_HP_EVENT		0x01
25369e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
254ec7e7e42SLydia Wang #define VIA_MONO_EVENT		0x03
255ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT	0x04
256ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT	0x05
25769e52a80SHarald Welte 
258c577b8a1SJoseph Chan enum {
259c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
260c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
261f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
26225eaba2fSLydia Wang 	VIA_CTL_WIDGET_BIND_PIN_MUTE,
263c577b8a1SJoseph Chan };
264c577b8a1SJoseph Chan 
265c577b8a1SJoseph Chan enum {
266eb14a46cSHarald Welte 	AUTO_SEQ_FRONT = 0,
267c577b8a1SJoseph Chan 	AUTO_SEQ_SURROUND,
268c577b8a1SJoseph Chan 	AUTO_SEQ_CENLFE,
269c577b8a1SJoseph Chan 	AUTO_SEQ_SIDE
270c577b8a1SJoseph Chan };
271c577b8a1SJoseph Chan 
272f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2731f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2741f2e99feSLydia Wang 
2751f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2761f2e99feSLydia Wang {
2771f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2781f2e99feSLydia Wang 		return;
2791f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
280e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2811f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2821f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2831f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2841f2e99feSLydia Wang }
2851f2e99feSLydia Wang 
2861f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2871f2e99feSLydia Wang {
2881f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2891f2e99feSLydia Wang 		return;
2901f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2911f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2921f2e99feSLydia Wang 		return;
2931f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
294e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2955b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2961f2e99feSLydia Wang }
297f5271101SLydia Wang 
2983e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2993e95b9abSLydia Wang {
3003e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
3013e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3023e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3033e95b9abSLydia Wang }
30425eaba2fSLydia Wang 
305f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
306f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
307f5271101SLydia Wang {
308f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
309f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
310f5271101SLydia Wang 
3113e95b9abSLydia Wang 	set_widgets_power_state(codec);
312f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
3131f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3141f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3151f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3161f2e99feSLydia Wang 		else
3171f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3181f2e99feSLydia Wang 	}
319f5271101SLydia Wang 	return change;
320f5271101SLydia Wang }
321f5271101SLydia Wang 
322f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
323f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
324f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
325f5271101SLydia Wang 			.name = NULL,					\
326f5271101SLydia Wang 			.index = 0,					\
327f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
328f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
329f5271101SLydia Wang 			.put = analog_input_switch_put,			\
330f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
331f5271101SLydia Wang 
33225eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec);
33325eaba2fSLydia Wang 
33425eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol,
33525eaba2fSLydia Wang 			       struct snd_ctl_elem_value *ucontrol)
33625eaba2fSLydia Wang {
33725eaba2fSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33825eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
33925eaba2fSLydia Wang 	int i;
34025eaba2fSLydia Wang 	int change = 0;
34125eaba2fSLydia Wang 
34225eaba2fSLydia Wang 	long *valp = ucontrol->value.integer.value;
34325eaba2fSLydia Wang 	int lmute, rmute;
34425eaba2fSLydia Wang 	if (strstr(kcontrol->id.name, "Switch") == NULL) {
34525eaba2fSLydia Wang 		snd_printd("Invalid control!\n");
34625eaba2fSLydia Wang 		return change;
34725eaba2fSLydia Wang 	}
34825eaba2fSLydia Wang 	change = snd_hda_mixer_amp_switch_put(kcontrol,
34925eaba2fSLydia Wang 					      ucontrol);
35025eaba2fSLydia Wang 	/* Get mute value */
35125eaba2fSLydia Wang 	lmute = *valp ? 0 : HDA_AMP_MUTE;
35225eaba2fSLydia Wang 	valp++;
35325eaba2fSLydia Wang 	rmute = *valp ? 0 : HDA_AMP_MUTE;
35425eaba2fSLydia Wang 
35525eaba2fSLydia Wang 	/* Set hp pins */
35625eaba2fSLydia Wang 	if (!spec->hp_independent_mode) {
35725eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.hp_outs; i++) {
35825eaba2fSLydia Wang 			snd_hda_codec_amp_update(
35925eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
36025eaba2fSLydia Wang 				0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36125eaba2fSLydia Wang 				lmute);
36225eaba2fSLydia Wang 			snd_hda_codec_amp_update(
36325eaba2fSLydia Wang 				codec, spec->autocfg.hp_pins[i],
36425eaba2fSLydia Wang 				1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
36525eaba2fSLydia Wang 				rmute);
36625eaba2fSLydia Wang 		}
36725eaba2fSLydia Wang 	}
36825eaba2fSLydia Wang 
36925eaba2fSLydia Wang 	if (!lmute && !rmute) {
37025eaba2fSLydia Wang 		/* Line Outs */
37125eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.line_outs; i++)
37225eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37325eaba2fSLydia Wang 				codec, spec->autocfg.line_out_pins[i],
37425eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
37525eaba2fSLydia Wang 		/* Speakers */
37625eaba2fSLydia Wang 		for (i = 0; i < spec->autocfg.speaker_outs; i++)
37725eaba2fSLydia Wang 			snd_hda_codec_amp_stereo(
37825eaba2fSLydia Wang 				codec, spec->autocfg.speaker_pins[i],
37925eaba2fSLydia Wang 				HDA_OUTPUT, 0, HDA_AMP_MUTE, 0);
38025eaba2fSLydia Wang 		/* unmute */
38125eaba2fSLydia Wang 		via_hp_bind_automute(codec);
38225eaba2fSLydia Wang 
38325eaba2fSLydia Wang 	} else {
38425eaba2fSLydia Wang 		if (lmute) {
38525eaba2fSLydia Wang 			/* Mute all left channels */
38625eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
38725eaba2fSLydia Wang 				snd_hda_codec_amp_update(
38825eaba2fSLydia Wang 					codec,
38925eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
39025eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39125eaba2fSLydia Wang 					lmute);
39225eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
39325eaba2fSLydia Wang 				snd_hda_codec_amp_update(
39425eaba2fSLydia Wang 					codec,
39525eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
39625eaba2fSLydia Wang 					0, HDA_OUTPUT, 0, HDA_AMP_MUTE,
39725eaba2fSLydia Wang 					lmute);
39825eaba2fSLydia Wang 		}
39925eaba2fSLydia Wang 		if (rmute) {
40025eaba2fSLydia Wang 			/* mute all right channels */
40125eaba2fSLydia Wang 			for (i = 1; i < spec->autocfg.line_outs; i++)
40225eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40325eaba2fSLydia Wang 					codec,
40425eaba2fSLydia Wang 					spec->autocfg.line_out_pins[i],
40525eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
40625eaba2fSLydia Wang 					rmute);
40725eaba2fSLydia Wang 			for (i = 0; i < spec->autocfg.speaker_outs; i++)
40825eaba2fSLydia Wang 				snd_hda_codec_amp_update(
40925eaba2fSLydia Wang 					codec,
41025eaba2fSLydia Wang 					spec->autocfg.speaker_pins[i],
41125eaba2fSLydia Wang 					1, HDA_OUTPUT, 0, HDA_AMP_MUTE,
41225eaba2fSLydia Wang 					rmute);
41325eaba2fSLydia Wang 		}
41425eaba2fSLydia Wang 	}
41525eaba2fSLydia Wang 	return change;
41625eaba2fSLydia Wang }
41725eaba2fSLydia Wang 
41825eaba2fSLydia Wang #define BIND_PIN_MUTE							\
41925eaba2fSLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
42025eaba2fSLydia Wang 			.name = NULL,					\
42125eaba2fSLydia Wang 			.index = 0,					\
42225eaba2fSLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
42325eaba2fSLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
42425eaba2fSLydia Wang 			.put = bind_pin_switch_put,			\
42525eaba2fSLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
42625eaba2fSLydia Wang 
42790dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
428c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
429c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
430f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
43125eaba2fSLydia Wang 	BIND_PIN_MUTE,
432c577b8a1SJoseph Chan };
433c577b8a1SJoseph Chan 
434ab6734e7SLydia Wang 
435c577b8a1SJoseph Chan /* add dynamic controls */
436291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
437291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
438291c9e33STakashi Iwai 				const char *name)
439c577b8a1SJoseph Chan {
440c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
441c577b8a1SJoseph Chan 
442603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
443603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
444c577b8a1SJoseph Chan 	if (!knew)
445291c9e33STakashi Iwai 		return NULL;
446291c9e33STakashi Iwai 	*knew = *tmpl;
447291c9e33STakashi Iwai 	if (!name)
448291c9e33STakashi Iwai 		name = tmpl->name;
449291c9e33STakashi Iwai 	if (name) {
450c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
451c577b8a1SJoseph Chan 		if (!knew->name)
452291c9e33STakashi Iwai 			return NULL;
453291c9e33STakashi Iwai 	}
454291c9e33STakashi Iwai 	return knew;
455291c9e33STakashi Iwai }
456291c9e33STakashi Iwai 
457291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
458291c9e33STakashi Iwai 			     int idx, unsigned long val)
459291c9e33STakashi Iwai {
460291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
461291c9e33STakashi Iwai 
462291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
463291c9e33STakashi Iwai 	if (!knew)
464c577b8a1SJoseph Chan 		return -ENOMEM;
465*d7a99cceSTakashi Iwai 	knew->index = idx;
4664d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4675e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
468c577b8a1SJoseph Chan 	knew->private_value = val;
469c577b8a1SJoseph Chan 	return 0;
470c577b8a1SJoseph Chan }
471c577b8a1SJoseph Chan 
4727b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4737b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4747b315bb4STakashi Iwai 
475291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
4765b0cb1d8SJaroslav Kysela 
477603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
478603c4019STakashi Iwai {
479603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
480603c4019STakashi Iwai 
481603c4019STakashi Iwai 	if (spec->kctls.list) {
482603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
483603c4019STakashi Iwai 		int i;
484603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
485603c4019STakashi Iwai 			kfree(kctl[i].name);
486603c4019STakashi Iwai 	}
487603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
488603c4019STakashi Iwai }
489603c4019STakashi Iwai 
490c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4919510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4927b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
493c577b8a1SJoseph Chan {
494c577b8a1SJoseph Chan 	char name[32];
495c577b8a1SJoseph Chan 	int err;
496c577b8a1SJoseph Chan 
497c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4987b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
499c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
500c577b8a1SJoseph Chan 	if (err < 0)
501c577b8a1SJoseph Chan 		return err;
502c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
5037b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
504c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
505c577b8a1SJoseph Chan 	if (err < 0)
506c577b8a1SJoseph Chan 		return err;
507c577b8a1SJoseph Chan 	return 0;
508c577b8a1SJoseph Chan }
509c577b8a1SJoseph Chan 
510c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec,
511c577b8a1SJoseph Chan 					   hda_nid_t nid, int pin_type,
512c577b8a1SJoseph Chan 					   int dac_idx)
513c577b8a1SJoseph Chan {
514c577b8a1SJoseph Chan 	/* set as output */
515c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
516c577b8a1SJoseph Chan 			    pin_type);
517c577b8a1SJoseph Chan 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
518c577b8a1SJoseph Chan 			    AMP_OUT_UNMUTE);
519d3a11e60STakashi Iwai 	if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)
520d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
521d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
522c577b8a1SJoseph Chan }
523c577b8a1SJoseph Chan 
524c577b8a1SJoseph Chan 
525c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
526c577b8a1SJoseph Chan {
527c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
528c577b8a1SJoseph Chan 	int i;
529c577b8a1SJoseph Chan 
530c577b8a1SJoseph Chan 	for (i = 0; i <= AUTO_SEQ_SIDE; i++) {
531c577b8a1SJoseph Chan 		hda_nid_t nid = spec->autocfg.line_out_pins[i];
532c577b8a1SJoseph Chan 		if (nid)
533c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i);
534c577b8a1SJoseph Chan 	}
535c577b8a1SJoseph Chan }
536c577b8a1SJoseph Chan 
537c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
538c577b8a1SJoseph Chan {
539c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
540c577b8a1SJoseph Chan 	hda_nid_t pin;
54125eaba2fSLydia Wang 	int i;
542c577b8a1SJoseph Chan 
54325eaba2fSLydia Wang 	for (i = 0; i < spec->autocfg.hp_outs; i++) {
54425eaba2fSLydia Wang 		pin = spec->autocfg.hp_pins[i];
545c577b8a1SJoseph Chan 		if (pin) /* connect to front */
546c577b8a1SJoseph Chan 			via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0);
547c577b8a1SJoseph Chan 	}
54825eaba2fSLydia Wang }
549c577b8a1SJoseph Chan 
550f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
55132e0191dSClemens Ladisch 
552c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
553c577b8a1SJoseph Chan {
554c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5557b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
55632e0191dSClemens Ladisch 	unsigned int ctl;
557c577b8a1SJoseph Chan 	int i;
558c577b8a1SJoseph Chan 
5597b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5607b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
561f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
56232e0191dSClemens Ladisch 			ctl = PIN_OUT;
56330649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
56432e0191dSClemens Ladisch 			ctl = PIN_VREF50;
56532e0191dSClemens Ladisch 		else
56632e0191dSClemens Ladisch 			ctl = PIN_IN;
567c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
56832e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
569c577b8a1SJoseph Chan 	}
570c577b8a1SJoseph Chan }
571f5271101SLydia Wang 
572f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
573f5271101SLydia Wang 				unsigned int *affected_parm)
574f5271101SLydia Wang {
575f5271101SLydia Wang 	unsigned parm;
576f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
577f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
578f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
579f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
5801564b287SLydia Wang 	struct via_spec *spec = codec->spec;
58124088a58STakashi Iwai 	unsigned present = 0;
58224088a58STakashi Iwai 
58324088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
58424088a58STakashi Iwai 	if (!no_presence)
58524088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
586f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
5871564b287SLydia Wang 	    || ((no_presence || present)
5881564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
589f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
590f5271101SLydia Wang 		parm = AC_PWRST_D0;
591f5271101SLydia Wang 	} else
592f5271101SLydia Wang 		parm = AC_PWRST_D3;
593f5271101SLydia Wang 
594f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
595f5271101SLydia Wang }
596f5271101SLydia Wang 
59724088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
59824088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
59924088a58STakashi Iwai {
60024088a58STakashi Iwai 	static const char * const texts[] = {
60124088a58STakashi Iwai 		"Disabled", "Enabled"
60224088a58STakashi Iwai 	};
60324088a58STakashi Iwai 
60424088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
60524088a58STakashi Iwai 	uinfo->count = 1;
60624088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
60724088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
60824088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
60924088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
61024088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
61124088a58STakashi Iwai 	return 0;
61224088a58STakashi Iwai }
61324088a58STakashi Iwai 
61424088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
61524088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
61624088a58STakashi Iwai {
61724088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
61824088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
61924088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
62024088a58STakashi Iwai 	return 0;
62124088a58STakashi Iwai }
62224088a58STakashi Iwai 
62324088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
62424088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
62524088a58STakashi Iwai {
62624088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
62724088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
62824088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
62924088a58STakashi Iwai 
63024088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
63124088a58STakashi Iwai 		return 0;
63224088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
63324088a58STakashi Iwai 	set_widgets_power_state(codec);
63424088a58STakashi Iwai 	return 1;
63524088a58STakashi Iwai }
63624088a58STakashi Iwai 
63724088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
63824088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
63924088a58STakashi Iwai 	.name = "Dynamic Power-Control",
64024088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
64124088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
64224088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
64324088a58STakashi Iwai };
64424088a58STakashi Iwai 
64524088a58STakashi Iwai 
646c577b8a1SJoseph Chan /*
647c577b8a1SJoseph Chan  * input MUX handling
648c577b8a1SJoseph Chan  */
649c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
650c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
651c577b8a1SJoseph Chan {
652c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
653c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
654c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
655c577b8a1SJoseph Chan }
656c577b8a1SJoseph Chan 
657c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
658c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
659c577b8a1SJoseph Chan {
660c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
661c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
662c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
663c577b8a1SJoseph Chan 
664c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
665c577b8a1SJoseph Chan 	return 0;
666c577b8a1SJoseph Chan }
667c577b8a1SJoseph Chan 
668c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
669c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
670c577b8a1SJoseph Chan {
671c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
672c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
673c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
674bff5fbf5SLydia Wang 	int ret;
675c577b8a1SJoseph Chan 
676337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
677337b9d02STakashi Iwai 		return -EINVAL;
678a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
679a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
680a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
681a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
682a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
683bff5fbf5SLydia Wang 
684bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
685bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
686bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
687a80e6e3cSLydia Wang 	/* update jack power state */
6883e95b9abSLydia Wang 	set_widgets_power_state(codec);
689a80e6e3cSLydia Wang 
690bff5fbf5SLydia Wang 	return ret;
691c577b8a1SJoseph Chan }
692c577b8a1SJoseph Chan 
6930aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6940aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6950aa62aefSHarald Welte {
6960aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6970aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6980aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
6990aa62aefSHarald Welte }
7000aa62aefSHarald Welte 
7010aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7020aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7030aa62aefSHarald Welte {
7040aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7055b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
706eb7188caSLydia Wang 	unsigned int pinsel;
707eb7188caSLydia Wang 
708eb7188caSLydia Wang 	/* use !! to translate conn sel 2 for VT1718S */
709eb7188caSLydia Wang 	pinsel = !!snd_hda_codec_read(codec, nid, 0,
7100aa62aefSHarald Welte 				      AC_VERB_GET_CONNECT_SEL,
7110aa62aefSHarald Welte 				      0x00);
7120aa62aefSHarald Welte 	ucontrol->value.enumerated.item[0] = pinsel;
7130aa62aefSHarald Welte 
7140aa62aefSHarald Welte 	return 0;
7150aa62aefSHarald Welte }
7160aa62aefSHarald Welte 
7170713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active)
7180713efebSLydia Wang {
7190713efebSLydia Wang 	struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name);
7200713efebSLydia Wang 	if (ctl) {
7210713efebSLydia Wang 		ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7220713efebSLydia Wang 		ctl->vd[0].access |= active
7230713efebSLydia Wang 			? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE;
7240713efebSLydia Wang 		snd_ctl_notify(codec->bus->card,
7250713efebSLydia Wang 			       SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id);
7260713efebSLydia Wang 	}
7270713efebSLydia Wang }
7280713efebSLydia Wang 
7295b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec)
7305b0cb1d8SJaroslav Kysela {
7315b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
7325b0cb1d8SJaroslav Kysela 	case VT1708:		return 0x1b;
7335b0cb1d8SJaroslav Kysela 	case VT1709_10CH:	return 0x29;
7345b0cb1d8SJaroslav Kysela 	case VT1708B_8CH:	/* fall thru */
7355b0cb1d8SJaroslav Kysela 	case VT1708S:		return 0x27;
736e87885feSLydia Wang 	case VT2002P:		return 0x19;
737e87885feSLydia Wang 	case VT1802:		return 0x15;
738e87885feSLydia Wang 	case VT1812:		return 0x15;
7395b0cb1d8SJaroslav Kysela 	default:		return 0;
7405b0cb1d8SJaroslav Kysela 	}
7415b0cb1d8SJaroslav Kysela }
7425b0cb1d8SJaroslav Kysela 
743cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec)
744cdc1784dSLydia Wang {
745cdc1784dSLydia Wang 	/* mute side channel */
746cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
747e87885feSLydia Wang 	unsigned int parm;
7485b0cb1d8SJaroslav Kysela 	hda_nid_t sw3 = side_mute_channel(spec);
749cdc1784dSLydia Wang 
750e87885feSLydia Wang 	if (sw3) {
751e87885feSLydia Wang 		if (VT2002P_COMPATIBLE(spec))
752e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
753e87885feSLydia Wang 			       AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1);
754e87885feSLydia Wang 		else
755e87885feSLydia Wang 			parm = spec->hp_independent_mode ?
756e87885feSLydia Wang 			       AMP_OUT_MUTE : AMP_OUT_UNMUTE;
757e87885feSLydia Wang 		snd_hda_codec_write(codec, sw3, 0,
758e87885feSLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, parm);
759e87885feSLydia Wang 		if (spec->codec_type == VT1812)
760e87885feSLydia Wang 			snd_hda_codec_write(codec, 0x1d, 0,
761e87885feSLydia Wang 					    AC_VERB_SET_AMP_GAIN_MUTE, parm);
762e87885feSLydia Wang 	}
763cdc1784dSLydia Wang 	return 0;
764cdc1784dSLydia Wang }
765cdc1784dSLydia Wang 
7660aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7670aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7680aa62aefSHarald Welte {
7690aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7700aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7715b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7720aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
773cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
774cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
775cdc1784dSLydia Wang 		? 1 : 0;
776ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1718S)
777ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
778ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0);
779ce0e5a9eSLydia Wang 	else
780ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, nid, 0,
781ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
7820aa62aefSHarald Welte 
783ce0e5a9eSLydia Wang 	if (spec->codec_type == VT1812)
784ce0e5a9eSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
785ce0e5a9eSLydia Wang 				    AC_VERB_SET_CONNECT_SEL, pinsel);
786cdc1784dSLydia Wang 	if (spec->multiout.hp_nid && spec->multiout.hp_nid
787cdc1784dSLydia Wang 	    != spec->multiout.dac_nids[HDA_FRONT])
788cdc1784dSLydia Wang 		snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid,
7890aa62aefSHarald Welte 					   0, 0, 0);
7900aa62aefSHarald Welte 
791cdc1784dSLydia Wang 	update_side_mute_status(codec);
7920713efebSLydia Wang 	/* update HP volume/swtich active state */
7930713efebSLydia Wang 	if (spec->codec_type == VT1708S
794eb7188caSLydia Wang 	    || spec->codec_type == VT1702
795f3db423dSLydia Wang 	    || spec->codec_type == VT1718S
79625eaba2fSLydia Wang 	    || spec->codec_type == VT1716S
79711890956SLydia Wang 	    || VT2002P_COMPATIBLE(spec)) {
7980713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Volume",
7990713efebSLydia Wang 			     spec->hp_independent_mode);
8000713efebSLydia Wang 		activate_ctl(codec, "Headphone Playback Switch",
8010713efebSLydia Wang 			     spec->hp_independent_mode);
8020713efebSLydia Wang 	}
803ce0e5a9eSLydia Wang 	/* update jack power state */
8043e95b9abSLydia Wang 	set_widgets_power_state(codec);
8050aa62aefSHarald Welte 	return 0;
8060aa62aefSHarald Welte }
8070aa62aefSHarald Welte 
80890dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = {
8090aa62aefSHarald Welte 	{
8100aa62aefSHarald Welte 		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8110aa62aefSHarald Welte 		.name = "Independent HP",
8120aa62aefSHarald Welte 		.info = via_independent_hp_info,
8130aa62aefSHarald Welte 		.get = via_independent_hp_get,
8140aa62aefSHarald Welte 		.put = via_independent_hp_put,
8150aa62aefSHarald Welte 	},
8165b0cb1d8SJaroslav Kysela 	{
8175b0cb1d8SJaroslav Kysela 		.iface = NID_MAPPING,
8185b0cb1d8SJaroslav Kysela 		.name = "Independent HP",
8195b0cb1d8SJaroslav Kysela 	},
8200aa62aefSHarald Welte };
8210aa62aefSHarald Welte 
8223d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
8235b0cb1d8SJaroslav Kysela {
8243d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8255b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8265b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8273d83e577STakashi Iwai 	int nums;
8283d83e577STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
8295b0cb1d8SJaroslav Kysela 
8305b0cb1d8SJaroslav Kysela 	switch (spec->codec_type) {
8315b0cb1d8SJaroslav Kysela 	case VT1718S:
8325b0cb1d8SJaroslav Kysela 		nid = 0x34;
8335b0cb1d8SJaroslav Kysela 		break;
8345b0cb1d8SJaroslav Kysela 	case VT2002P:
83511890956SLydia Wang 	case VT1802:
8365b0cb1d8SJaroslav Kysela 		nid = 0x35;
8375b0cb1d8SJaroslav Kysela 		break;
8385b0cb1d8SJaroslav Kysela 	case VT1812:
8395b0cb1d8SJaroslav Kysela 		nid = 0x3d;
8405b0cb1d8SJaroslav Kysela 		break;
8415b0cb1d8SJaroslav Kysela 	default:
8425b0cb1d8SJaroslav Kysela 		nid = spec->autocfg.hp_pins[0];
8435b0cb1d8SJaroslav Kysela 		break;
8445b0cb1d8SJaroslav Kysela 	}
8455b0cb1d8SJaroslav Kysela 
846ee3c35c0SLydia Wang 	if (spec->codec_type != VT1708) {
847ee3c35c0SLydia Wang 		nums = snd_hda_get_connections(codec, nid,
848ee3c35c0SLydia Wang 					       conn, HDA_MAX_CONNECTIONS);
8493d83e577STakashi Iwai 		if (nums <= 1)
8503d83e577STakashi Iwai 			return 0;
851ee3c35c0SLydia Wang 	}
8523d83e577STakashi Iwai 
8533d83e577STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer[0]);
8543d83e577STakashi Iwai 	if (knew == NULL)
8553d83e577STakashi Iwai 		return -ENOMEM;
8563d83e577STakashi Iwai 
8575b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8585b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
8595b0cb1d8SJaroslav Kysela 
8605b0cb1d8SJaroslav Kysela 	knew = via_clone_control(spec, &via_hp_mixer[1]);
8615b0cb1d8SJaroslav Kysela 	if (knew == NULL)
8625b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8635b0cb1d8SJaroslav Kysela 	knew->subdevice = side_mute_channel(spec);
8645b0cb1d8SJaroslav Kysela 
8655b0cb1d8SJaroslav Kysela 	return 0;
8665b0cb1d8SJaroslav Kysela }
8675b0cb1d8SJaroslav Kysela 
8681564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8691564b287SLydia Wang {
8701564b287SLydia Wang 	int i;
8711564b287SLydia Wang 	struct snd_ctl_elem_id id;
872525566cbSLydia Wang 	const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
873525566cbSLydia Wang 	struct snd_kcontrol *ctl;
8741564b287SLydia Wang 
8751564b287SLydia Wang 	memset(&id, 0, sizeof(id));
8761564b287SLydia Wang 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
8771564b287SLydia Wang 	for (i = 0; i < ARRAY_SIZE(labels); i++) {
8781564b287SLydia Wang 		sprintf(id.name, "%s Playback Volume", labels[i]);
879525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
880525566cbSLydia Wang 		if (ctl)
881525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
882525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
883525566cbSLydia Wang 					&ctl->id);
8841564b287SLydia Wang 	}
8851564b287SLydia Wang }
8861564b287SLydia Wang 
8871564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8881564b287SLydia Wang {
8891564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8901564b287SLydia Wang 	int start_idx;
8911564b287SLydia Wang 	int end_idx;
8921564b287SLydia Wang 	int i;
8931564b287SLydia Wang 	/* get nid of MW0 and start & end index */
8941564b287SLydia Wang 	switch (spec->codec_type) {
8951564b287SLydia Wang 	case VT1708:
8961564b287SLydia Wang 		start_idx = 2;
8971564b287SLydia Wang 		end_idx = 4;
8981564b287SLydia Wang 		break;
8991564b287SLydia Wang 	case VT1709_10CH:
9001564b287SLydia Wang 	case VT1709_6CH:
9011564b287SLydia Wang 		start_idx = 2;
9021564b287SLydia Wang 		end_idx = 4;
9031564b287SLydia Wang 		break;
9041564b287SLydia Wang 	case VT1708B_8CH:
9051564b287SLydia Wang 	case VT1708B_4CH:
9061564b287SLydia Wang 	case VT1708S:
907f3db423dSLydia Wang 	case VT1716S:
9081564b287SLydia Wang 		start_idx = 2;
9091564b287SLydia Wang 		end_idx = 4;
9101564b287SLydia Wang 		break;
911ab657e0cSLydia Wang 	case VT1718S:
912ab657e0cSLydia Wang 		start_idx = 1;
913ab657e0cSLydia Wang 		end_idx = 3;
914ab657e0cSLydia Wang 		break;
9151564b287SLydia Wang 	default:
9161564b287SLydia Wang 		return;
9171564b287SLydia Wang 	}
9181564b287SLydia Wang 	/* check AA path's mute status */
9191564b287SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
9201564b287SLydia Wang 		int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
921620e2b28STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i,
9221564b287SLydia Wang 					 HDA_AMP_MUTE, val);
9231564b287SLydia Wang 	}
9241564b287SLydia Wang }
925f4a7828bSTakashi Iwai 
926f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
9271564b287SLydia Wang {
928f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
9297b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9307b315bb4STakashi Iwai 	int i;
9317b315bb4STakashi Iwai 
9327b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
933f4a7828bSTakashi Iwai 		unsigned int defcfg;
934f4a7828bSTakashi Iwai 		if (pin != cfg->inputs[i].pin)
935f4a7828bSTakashi Iwai 			continue;
936f4a7828bSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
937f4a7828bSTakashi Iwai 			return false;
938f4a7828bSTakashi Iwai 		defcfg = snd_hda_codec_get_pincfg(codec, pin);
939f4a7828bSTakashi Iwai 		if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
940f4a7828bSTakashi Iwai 			return false;
941f4a7828bSTakashi Iwai 		return true;
9421564b287SLydia Wang 	}
943f4a7828bSTakashi Iwai 	return false;
9441564b287SLydia Wang }
9451564b287SLydia Wang 
9461564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
9471564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
9481564b287SLydia Wang {
9491564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
9501564b287SLydia Wang 	uinfo->count = 1;
9511564b287SLydia Wang 	uinfo->value.integer.min = 0;
9521564b287SLydia Wang 	uinfo->value.integer.max = 1;
9531564b287SLydia Wang 	return 0;
9541564b287SLydia Wang }
9551564b287SLydia Wang 
9561564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
9571564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9581564b287SLydia Wang {
9591564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9601564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9617b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9621564b287SLydia Wang 	int on = 1;
9631564b287SLydia Wang 	int i;
9641564b287SLydia Wang 
9657b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9667b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
967f4a7828bSTakashi Iwai 		unsigned int ctl;
96886e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9697b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9701564b287SLydia Wang 			continue; /* ignore FMic for independent HP */
971f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, nid))
972f4a7828bSTakashi Iwai 			continue;
973f4a7828bSTakashi Iwai 		ctl = snd_hda_codec_read(codec, nid, 0,
974f4a7828bSTakashi Iwai 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
9757b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
9761564b287SLydia Wang 			on = 0;
9771564b287SLydia Wang 	}
9781564b287SLydia Wang 	*ucontrol->value.integer.value = on;
9791564b287SLydia Wang 	return 0;
9801564b287SLydia Wang }
9811564b287SLydia Wang 
9821564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
9831564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
9841564b287SLydia Wang {
9851564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
9861564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9877b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
9881564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
9891564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
9901564b287SLydia Wang 	int i;
9911564b287SLydia Wang 
9927b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
9937b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
9947b315bb4STakashi Iwai 		unsigned int parm;
9957b315bb4STakashi Iwai 
99686e2959aSTakashi Iwai 		if (cfg->inputs[i].type == AUTO_PIN_MIC &&
9977b315bb4STakashi Iwai 		    spec->hp_independent_mode && spec->codec_type != VT1718S)
9981564b287SLydia Wang 			continue; /* don't retask FMic for independent HP */
999f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, nid))
1000f4a7828bSTakashi Iwai 			continue;
10017b315bb4STakashi Iwai 
10027b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
10031564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
10041564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
10051564b287SLydia Wang 		parm |= out_in;
10061564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
10071564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
10081564b287SLydia Wang 				    parm);
10091564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
10101564b287SLydia Wang 			mute_aa_path(codec, 1);
10111564b287SLydia Wang 			notify_aa_path_ctls(codec);
10121564b287SLydia Wang 		}
10131564b287SLydia Wang 	}
10141564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
10153e95b9abSLydia Wang 	set_widgets_power_state(codec);
10161564b287SLydia Wang 	return 1;
10171564b287SLydia Wang }
10181564b287SLydia Wang 
10195f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
10201564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10211564b287SLydia Wang 	.name = "Smart 5.1",
10221564b287SLydia Wang 	.count = 1,
10231564b287SLydia Wang 	.info = via_smart51_info,
10241564b287SLydia Wang 	.get = via_smart51_get,
10251564b287SLydia Wang 	.put = via_smart51_put,
10261564b287SLydia Wang };
10271564b287SLydia Wang 
1028f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
10295b0cb1d8SJaroslav Kysela {
1030f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
10315b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
10327b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
10335b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
10345b0cb1d8SJaroslav Kysela 	int i;
10355b0cb1d8SJaroslav Kysela 
1036f4a7828bSTakashi Iwai 	if (!spec->can_smart51)
1037cb34c207SLydia Wang 		return 0;
1038cb34c207SLydia Wang 
10395f4b36d6STakashi Iwai 	knew = via_clone_control(spec, &via_smart51_mixer);
10405b0cb1d8SJaroslav Kysela 	if (knew == NULL)
10415b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10425b0cb1d8SJaroslav Kysela 
10437b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
10447b315bb4STakashi Iwai 		nid = cfg->inputs[i].pin;
1045f4a7828bSTakashi Iwai 		if (is_smart51_pins(codec, nid)) {
10465f4b36d6STakashi Iwai 			knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
10477b315bb4STakashi Iwai 			break;
10485b0cb1d8SJaroslav Kysela 		}
10495b0cb1d8SJaroslav Kysela 	}
10505b0cb1d8SJaroslav Kysela 
10515b0cb1d8SJaroslav Kysela 	return 0;
10525b0cb1d8SJaroslav Kysela }
10535b0cb1d8SJaroslav Kysela 
1054f5271101SLydia Wang /* check AA path's mute statue */
1055f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
1056f5271101SLydia Wang {
1057f5271101SLydia Wang 	int mute = 1;
1058f5271101SLydia Wang 	int start_idx;
1059f5271101SLydia Wang 	int end_idx;
1060f5271101SLydia Wang 	int i;
1061f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1062f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
1063f5271101SLydia Wang 	switch (spec->codec_type) {
1064f5271101SLydia Wang 	case VT1708B_8CH:
1065f5271101SLydia Wang 	case VT1708B_4CH:
1066f5271101SLydia Wang 	case VT1708S:
1067f3db423dSLydia Wang 	case VT1716S:
1068f5271101SLydia Wang 		start_idx = 2;
1069f5271101SLydia Wang 		end_idx = 4;
1070f5271101SLydia Wang 		break;
1071f5271101SLydia Wang 	case VT1702:
1072f5271101SLydia Wang 		start_idx = 1;
1073f5271101SLydia Wang 		end_idx = 3;
1074f5271101SLydia Wang 		break;
1075eb7188caSLydia Wang 	case VT1718S:
1076eb7188caSLydia Wang 		start_idx = 1;
1077eb7188caSLydia Wang 		end_idx = 3;
1078eb7188caSLydia Wang 		break;
107925eaba2fSLydia Wang 	case VT2002P:
1080ab6734e7SLydia Wang 	case VT1812:
108111890956SLydia Wang 	case VT1802:
108225eaba2fSLydia Wang 		start_idx = 0;
108325eaba2fSLydia Wang 		end_idx = 2;
108425eaba2fSLydia Wang 		break;
1085f5271101SLydia Wang 	default:
1086f5271101SLydia Wang 		return 0;
1087f5271101SLydia Wang 	}
1088f5271101SLydia Wang 	/* check AA path's mute status */
1089f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
1090f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
1091620e2b28STakashi Iwai 			codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
1092f5271101SLydia Wang 		int shift = 8 * (i % 4);
1093f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
1094f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
1095f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1096f5271101SLydia Wang 			/* check mute status while the pin is connected */
1097620e2b28STakashi Iwai 			int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
1098f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1099620e2b28STakashi Iwai 			int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
1100f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
1101f5271101SLydia Wang 			if (!mute_l || !mute_r) {
1102f5271101SLydia Wang 				mute = 0;
1103f5271101SLydia Wang 				break;
1104f5271101SLydia Wang 			}
1105f5271101SLydia Wang 		}
1106f5271101SLydia Wang 	}
1107f5271101SLydia Wang 	return mute;
1108f5271101SLydia Wang }
1109f5271101SLydia Wang 
1110f5271101SLydia Wang /* enter/exit analog low-current mode */
1111f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1112f5271101SLydia Wang {
1113f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1114f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
1115f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
1116f5271101SLydia Wang 	unsigned int verb = 0;
1117f5271101SLydia Wang 	unsigned int parm = 0;
1118f5271101SLydia Wang 
1119f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
1120f5271101SLydia Wang 		enable = enable && saved_stream_idle;
1121f5271101SLydia Wang 	else {
1122f5271101SLydia Wang 		enable = enable && stream_idle;
1123f5271101SLydia Wang 		saved_stream_idle = stream_idle;
1124f5271101SLydia Wang 	}
1125f5271101SLydia Wang 
1126f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1127f5271101SLydia Wang 	switch (spec->codec_type) {
1128f5271101SLydia Wang 	case VT1708B_8CH:
1129f5271101SLydia Wang 	case VT1708B_4CH:
1130f5271101SLydia Wang 		verb = 0xf70;
1131f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1132f5271101SLydia Wang 		break;
1133f5271101SLydia Wang 	case VT1708S:
1134eb7188caSLydia Wang 	case VT1718S:
1135f3db423dSLydia Wang 	case VT1716S:
1136f5271101SLydia Wang 		verb = 0xf73;
1137f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1138f5271101SLydia Wang 		break;
1139f5271101SLydia Wang 	case VT1702:
1140f5271101SLydia Wang 		verb = 0xf73;
1141f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1142f5271101SLydia Wang 		break;
114325eaba2fSLydia Wang 	case VT2002P:
1144ab6734e7SLydia Wang 	case VT1812:
114511890956SLydia Wang 	case VT1802:
114625eaba2fSLydia Wang 		verb = 0xf93;
114725eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
114825eaba2fSLydia Wang 		break;
1149f5271101SLydia Wang 	default:
1150f5271101SLydia Wang 		return;		/* other codecs are not supported */
1151f5271101SLydia Wang 	}
1152f5271101SLydia Wang 	/* send verb */
1153f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1154f5271101SLydia Wang }
1155f5271101SLydia Wang 
1156c577b8a1SJoseph Chan /*
1157c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1158c577b8a1SJoseph Chan  */
115990dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = {
1160c577b8a1SJoseph Chan 	/*
1161c577b8a1SJoseph Chan 	 * Unmute ADC0-1 and set the default input to mic-in
1162c577b8a1SJoseph Chan 	 */
1163c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1164c577b8a1SJoseph Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1165c577b8a1SJoseph Chan 
1166c577b8a1SJoseph Chan 
1167f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
1168c577b8a1SJoseph Chan 	 * mixer widget
1169c577b8a1SJoseph Chan 	 */
1170c577b8a1SJoseph Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
1171f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
1172f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
1173f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
1174f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
1175f7278fd0SJosepch Chan 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
1176c577b8a1SJoseph Chan 
1177c577b8a1SJoseph Chan 	/*
1178c577b8a1SJoseph Chan 	 * Set up output mixers (0x19 - 0x1b)
1179c577b8a1SJoseph Chan 	 */
1180c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
1181c577b8a1SJoseph Chan 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1182c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1183c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
1184c577b8a1SJoseph Chan 
1185bfdc675aSLydia Wang 	/* Setup default input MW0 to PW4 */
1186bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
1187c577b8a1SJoseph Chan 	/* PW9 Output enable */
1188c577b8a1SJoseph Chan 	{0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
1189aa266fccSLydia Wang 	/* power down jack detect function */
1190aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1191f7278fd0SJosepch Chan 	{ }
1192c577b8a1SJoseph Chan };
1193c577b8a1SJoseph Chan 
11947eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec,
11957eb56e84STakashi Iwai 			       struct snd_pcm_substream *substream)
11967eb56e84STakashi Iwai {
11977eb56e84STakashi Iwai 	int idle = substream->pstr->substream_opened == 1
11987eb56e84STakashi Iwai 		&& substream->ref_count == 0;
11997eb56e84STakashi Iwai 	analog_low_current_mode(codec, idle);
12007eb56e84STakashi Iwai }
12017eb56e84STakashi Iwai 
1202c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo,
1203c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1204c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1205c577b8a1SJoseph Chan {
1206c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
12077eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
12089a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
12099a08160bSTakashi Iwai 					     hinfo);
1210c577b8a1SJoseph Chan }
1211c577b8a1SJoseph Chan 
12129af74210STakashi Iwai static int via_playback_pcm_close(struct hda_pcm_stream *hinfo,
12139af74210STakashi Iwai 				  struct hda_codec *codec,
12149af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
12159af74210STakashi Iwai {
12167eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
12179af74210STakashi Iwai 	return 0;
12189af74210STakashi Iwai }
12199af74210STakashi Iwai 
12207eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
12217eb56e84STakashi Iwai 				    struct hda_codec *codec,
12227eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
12237eb56e84STakashi Iwai {
12247eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
12257eb56e84STakashi Iwai 	struct hda_multi_out *mout = &spec->multiout;
12267eb56e84STakashi Iwai 
12277eb56e84STakashi Iwai 	if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] ||
12287eb56e84STakashi Iwai 	    !spec->hp_independent_mode)
12297eb56e84STakashi Iwai 		return -EINVAL;
12307eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
12317eb56e84STakashi Iwai 	return 0;
12327eb56e84STakashi Iwai }
12337eb56e84STakashi Iwai 
12347eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
12357eb56e84STakashi Iwai 					  struct hda_codec *codec,
12360aa62aefSHarald Welte 					  unsigned int stream_tag,
12370aa62aefSHarald Welte 					  unsigned int format,
12380aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
12390aa62aefSHarald Welte {
12400aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12410aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1242dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
12430aa62aefSHarald Welte 	int chs = substream->runtime->channels;
12440aa62aefSHarald Welte 	int i;
12457c935976SStephen Warren 	struct hda_spdif_out *spdif =
12467c935976SStephen Warren 		snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid);
12470aa62aefSHarald Welte 
12480aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
12490aa62aefSHarald Welte 	if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
12500aa62aefSHarald Welte 		if (chs == 2 &&
12510aa62aefSHarald Welte 		    snd_hda_is_supported_format(codec, mout->dig_out_nid,
12520aa62aefSHarald Welte 						format) &&
12537c935976SStephen Warren 		    !(spdif->status & IEC958_AES0_NONAUDIO)) {
12540aa62aefSHarald Welte 			mout->dig_out_used = HDA_DIG_ANALOG_DUP;
12550aa62aefSHarald Welte 			/* turn off SPDIF once; otherwise the IEC958 bits won't
12560aa62aefSHarald Welte 			 * be updated */
12577c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12580aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12590aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12607c935976SStephen Warren 						    spdif->ctls &
12610aa62aefSHarald Welte 							~AC_DIG1_ENABLE & 0xff);
12620aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12630aa62aefSHarald Welte 						   stream_tag, 0, format);
12640aa62aefSHarald Welte 			/* turn on again (if needed) */
12657c935976SStephen Warren 			if (spdif->ctls & AC_DIG1_ENABLE)
12660aa62aefSHarald Welte 				snd_hda_codec_write(codec, mout->dig_out_nid, 0,
12670aa62aefSHarald Welte 						    AC_VERB_SET_DIGI_CONVERT_1,
12687c935976SStephen Warren 						    spdif->ctls & 0xff);
12690aa62aefSHarald Welte 		} else {
12700aa62aefSHarald Welte 			mout->dig_out_used = 0;
12710aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
12720aa62aefSHarald Welte 						   0, 0, 0);
12730aa62aefSHarald Welte 		}
12740aa62aefSHarald Welte 	}
12750aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
12760aa62aefSHarald Welte 
12770aa62aefSHarald Welte 	/* front */
12780aa62aefSHarald Welte 	snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag,
12790aa62aefSHarald Welte 				   0, format);
12800aa62aefSHarald Welte 
1281eb7188caSLydia Wang 	if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT]
1282eb7188caSLydia Wang 	    && !spec->hp_independent_mode)
12830aa62aefSHarald Welte 		/* headphone out will just decode front left/right (stereo) */
12840aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag,
12850aa62aefSHarald Welte 					   0, format);
12860aa62aefSHarald Welte 
12870aa62aefSHarald Welte 	/* extra outputs copied from front */
12880aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
12890aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
12900aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
12910aa62aefSHarald Welte 						   mout->extra_out_nid[i],
12920aa62aefSHarald Welte 						   stream_tag, 0, format);
12930aa62aefSHarald Welte 
12940aa62aefSHarald Welte 	/* surrounds */
12950aa62aefSHarald Welte 	for (i = 1; i < mout->num_dacs; i++) {
12960aa62aefSHarald Welte 		if (chs >= (i + 1) * 2) /* independent out */
12970aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
12980aa62aefSHarald Welte 						   i * 2, format);
12990aa62aefSHarald Welte 		else /* copy front */
13000aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec, nids[i], stream_tag,
13010aa62aefSHarald Welte 						   0, format);
13020aa62aefSHarald Welte 	}
13037eb56e84STakashi Iwai 	vt1708_start_hp_work(spec);
13047eb56e84STakashi Iwai 	return 0;
13050aa62aefSHarald Welte }
13060aa62aefSHarald Welte 
13077eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
13080aa62aefSHarald Welte 				       struct hda_codec *codec,
13090aa62aefSHarald Welte 				       unsigned int stream_tag,
13100aa62aefSHarald Welte 				       unsigned int format,
13110aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
13120aa62aefSHarald Welte {
13130aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13140aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
13150aa62aefSHarald Welte 
13167eb56e84STakashi Iwai 	snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format);
13171f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
13180aa62aefSHarald Welte 	return 0;
13190aa62aefSHarald Welte }
13200aa62aefSHarald Welte 
13210aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
13220aa62aefSHarald Welte 				    struct hda_codec *codec,
13230aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
13240aa62aefSHarald Welte {
13250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
13260aa62aefSHarald Welte 	struct hda_multi_out *mout = &spec->multiout;
1327dda14410STakashi Iwai 	const hda_nid_t *nids = mout->dac_nids;
13280aa62aefSHarald Welte 	int i;
13290aa62aefSHarald Welte 
13300aa62aefSHarald Welte 	for (i = 0; i < mout->num_dacs; i++)
13310aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0);
13320aa62aefSHarald Welte 
13330aa62aefSHarald Welte 	if (mout->hp_nid && !spec->hp_independent_mode)
13340aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->hp_nid,
13350aa62aefSHarald Welte 					   0, 0, 0);
13360aa62aefSHarald Welte 
13370aa62aefSHarald Welte 	for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++)
13380aa62aefSHarald Welte 		if (mout->extra_out_nid[i])
13390aa62aefSHarald Welte 			snd_hda_codec_setup_stream(codec,
13400aa62aefSHarald Welte 						   mout->extra_out_nid[i],
13410aa62aefSHarald Welte 						   0, 0, 0);
13420aa62aefSHarald Welte 	mutex_lock(&codec->spdif_mutex);
13430aa62aefSHarald Welte 	if (mout->dig_out_nid &&
13440aa62aefSHarald Welte 	    mout->dig_out_used == HDA_DIG_ANALOG_DUP) {
13450aa62aefSHarald Welte 		snd_hda_codec_setup_stream(codec, mout->dig_out_nid,
13460aa62aefSHarald Welte 					   0, 0, 0);
13470aa62aefSHarald Welte 		mout->dig_out_used = 0;
13480aa62aefSHarald Welte 	}
13490aa62aefSHarald Welte 	mutex_unlock(&codec->spdif_mutex);
13507eb56e84STakashi Iwai 	vt1708_stop_hp_work(spec);
13517eb56e84STakashi Iwai 	return 0;
13520aa62aefSHarald Welte }
13537eb56e84STakashi Iwai 
13547eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
13557eb56e84STakashi Iwai 				       struct hda_codec *codec,
13567eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
13577eb56e84STakashi Iwai {
13587eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
13597eb56e84STakashi Iwai 	struct hda_multi_out *mout = &spec->multiout;
13607eb56e84STakashi Iwai 
13617eb56e84STakashi Iwai 	snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0);
13621f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
13630aa62aefSHarald Welte 	return 0;
13640aa62aefSHarald Welte }
13650aa62aefSHarald Welte 
1366c577b8a1SJoseph Chan /*
1367c577b8a1SJoseph Chan  * Digital out
1368c577b8a1SJoseph Chan  */
1369c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1370c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1371c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1372c577b8a1SJoseph Chan {
1373c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1374c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1375c577b8a1SJoseph Chan }
1376c577b8a1SJoseph Chan 
1377c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1378c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1379c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1380c577b8a1SJoseph Chan {
1381c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1382c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1383c577b8a1SJoseph Chan }
1384c577b8a1SJoseph Chan 
13855691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
138698aa34c0SHarald Welte 					struct hda_codec *codec,
138798aa34c0SHarald Welte 					unsigned int stream_tag,
138898aa34c0SHarald Welte 					unsigned int format,
138998aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
139098aa34c0SHarald Welte {
139198aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
13929da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
13939da29271STakashi Iwai 					     stream_tag, format, substream);
13949da29271STakashi Iwai }
13955691ec7fSHarald Welte 
13969da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
13979da29271STakashi Iwai 					struct hda_codec *codec,
13989da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13999da29271STakashi Iwai {
14009da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
14019da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
140298aa34c0SHarald Welte 	return 0;
140398aa34c0SHarald Welte }
140498aa34c0SHarald Welte 
1405c577b8a1SJoseph Chan /*
1406c577b8a1SJoseph Chan  * Analog capture
1407c577b8a1SJoseph Chan  */
1408c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1409c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1410c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1411c577b8a1SJoseph Chan 				   unsigned int format,
1412c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1413c577b8a1SJoseph Chan {
1414c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1415c577b8a1SJoseph Chan 
1416c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1417c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1418c577b8a1SJoseph Chan 	return 0;
1419c577b8a1SJoseph Chan }
1420c577b8a1SJoseph Chan 
1421c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1422c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1423c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1424c577b8a1SJoseph Chan {
1425c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1426888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1427c577b8a1SJoseph Chan 	return 0;
1428c577b8a1SJoseph Chan }
1429c577b8a1SJoseph Chan 
14309af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
14317eb56e84STakashi Iwai 	.substreams = 1,
1432c577b8a1SJoseph Chan 	.channels_min = 2,
1433c577b8a1SJoseph Chan 	.channels_max = 8,
14349af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1435c577b8a1SJoseph Chan 	.ops = {
1436c577b8a1SJoseph Chan 		.open = via_playback_pcm_open,
14379af74210STakashi Iwai 		.close = via_playback_pcm_close,
14380aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
14390aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1440c577b8a1SJoseph Chan 	},
1441c577b8a1SJoseph Chan };
1442c577b8a1SJoseph Chan 
14437eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
14447eb56e84STakashi Iwai 	.substreams = 1,
14457eb56e84STakashi Iwai 	.channels_min = 2,
14467eb56e84STakashi Iwai 	.channels_max = 2,
14477eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
14487eb56e84STakashi Iwai 	.ops = {
14497eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
14507eb56e84STakashi Iwai 		.close = via_playback_pcm_close,
14517eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
14527eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
14537eb56e84STakashi Iwai 	},
14547eb56e84STakashi Iwai };
14557eb56e84STakashi Iwai 
145690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
14577eb56e84STakashi Iwai 	.substreams = 1,
1458bc9b5623STakashi Iwai 	.channels_min = 2,
1459bc9b5623STakashi Iwai 	.channels_max = 8,
14609af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1461bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1462bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1463bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1464bc9b5623STakashi Iwai 	 */
1465bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1466bc9b5623STakashi Iwai 	.ops = {
1467bc9b5623STakashi Iwai 		.open = via_playback_pcm_open,
14689af74210STakashi Iwai 		.close = via_playback_pcm_close,
1469c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1470c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1471bc9b5623STakashi Iwai 	},
1472bc9b5623STakashi Iwai };
1473bc9b5623STakashi Iwai 
14749af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
14757eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1476c577b8a1SJoseph Chan 	.channels_min = 2,
1477c577b8a1SJoseph Chan 	.channels_max = 2,
14789af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1479c577b8a1SJoseph Chan 	.ops = {
14809af74210STakashi Iwai 		.open = via_playback_pcm_open,
14819af74210STakashi Iwai 		.close = via_playback_pcm_close,
1482c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1483c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1484c577b8a1SJoseph Chan 	},
1485c577b8a1SJoseph Chan };
1486c577b8a1SJoseph Chan 
14879af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1488c577b8a1SJoseph Chan 	.substreams = 1,
1489c577b8a1SJoseph Chan 	.channels_min = 2,
1490c577b8a1SJoseph Chan 	.channels_max = 2,
1491c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1492c577b8a1SJoseph Chan 	.ops = {
1493c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14946b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14959da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14969da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1497c577b8a1SJoseph Chan 	},
1498c577b8a1SJoseph Chan };
1499c577b8a1SJoseph Chan 
15009af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1501c577b8a1SJoseph Chan 	.substreams = 1,
1502c577b8a1SJoseph Chan 	.channels_min = 2,
1503c577b8a1SJoseph Chan 	.channels_max = 2,
1504c577b8a1SJoseph Chan };
1505c577b8a1SJoseph Chan 
1506c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1507c577b8a1SJoseph Chan {
1508c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
15095b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
151090dd48a1STakashi Iwai 	const struct snd_kcontrol_new *knew;
15115b0cb1d8SJaroslav Kysela 	int err, i;
1512c577b8a1SJoseph Chan 
151324088a58STakashi Iwai 	if (spec->set_widgets_power_state)
151424088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
151524088a58STakashi Iwai 			return -ENOMEM;
151624088a58STakashi Iwai 
1517c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1518c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1519c577b8a1SJoseph Chan 		if (err < 0)
1520c577b8a1SJoseph Chan 			return err;
1521c577b8a1SJoseph Chan 	}
1522c577b8a1SJoseph Chan 
1523c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1524c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
152574b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1526c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1527c577b8a1SJoseph Chan 		if (err < 0)
1528c577b8a1SJoseph Chan 			return err;
15299a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
15309a08160bSTakashi Iwai 						    &spec->multiout);
15319a08160bSTakashi Iwai 		if (err < 0)
15329a08160bSTakashi Iwai 			return err;
15339a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1534c577b8a1SJoseph Chan 	}
1535c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1536c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1537c577b8a1SJoseph Chan 		if (err < 0)
1538c577b8a1SJoseph Chan 			return err;
1539c577b8a1SJoseph Chan 	}
154017314379SLydia Wang 
15415b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15425b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15435b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
154421949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15455b0cb1d8SJaroslav Kysela 		if (err < 0)
15465b0cb1d8SJaroslav Kysela 			return err;
15475b0cb1d8SJaroslav Kysela 	}
15485b0cb1d8SJaroslav Kysela 
15495b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
15505b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
15515b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
15525b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
15535b0cb1d8SJaroslav Kysela 				continue;
15545b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
15555b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
15565b0cb1d8SJaroslav Kysela 				continue;
15575b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
15585b0cb1d8SJaroslav Kysela 					      knew->subdevice);
15595b0cb1d8SJaroslav Kysela 		}
15605b0cb1d8SJaroslav Kysela 	}
15615b0cb1d8SJaroslav Kysela 
156217314379SLydia Wang 	/* init power states */
15633e95b9abSLydia Wang 	set_widgets_power_state(codec);
156417314379SLydia Wang 	analog_low_current_mode(codec, 1);
156517314379SLydia Wang 
1566603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1567c577b8a1SJoseph Chan 	return 0;
1568c577b8a1SJoseph Chan }
1569c577b8a1SJoseph Chan 
1570c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1571c577b8a1SJoseph Chan {
1572c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1573c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1574c577b8a1SJoseph Chan 
1575c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1576c577b8a1SJoseph Chan 	codec->pcm_info = info;
1577c577b8a1SJoseph Chan 
157882673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
157982673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1580c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
15819af74210STakashi Iwai 
15829af74210STakashi Iwai 	if (!spec->stream_analog_playback)
15839af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1584377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
15859af74210STakashi Iwai 		*spec->stream_analog_playback;
1586377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1587377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1588c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1589c577b8a1SJoseph Chan 		spec->multiout.max_channels;
15909af74210STakashi Iwai 
15919af74210STakashi Iwai 	if (!spec->stream_analog_capture)
15929af74210STakashi Iwai 		spec->stream_analog_capture = &via_pcm_analog_capture;
15939af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
15949af74210STakashi Iwai 		*spec->stream_analog_capture;
15959af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
15969af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
15979af74210STakashi Iwai 		spec->num_adc_nids;
1598c577b8a1SJoseph Chan 
1599c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1600c577b8a1SJoseph Chan 		codec->num_pcms++;
1601c577b8a1SJoseph Chan 		info++;
160282673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
160382673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
160482673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1605c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
16067ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1607c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
16089af74210STakashi Iwai 			if (!spec->stream_digital_playback)
16099af74210STakashi Iwai 				spec->stream_digital_playback =
16109af74210STakashi Iwai 					&via_pcm_digital_playback;
1611c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
16129af74210STakashi Iwai 				*spec->stream_digital_playback;
1613c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1614c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1615c577b8a1SJoseph Chan 		}
1616c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
16179af74210STakashi Iwai 			if (!spec->stream_digital_capture)
16189af74210STakashi Iwai 				spec->stream_digital_capture =
16199af74210STakashi Iwai 					&via_pcm_digital_capture;
1620c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
16219af74210STakashi Iwai 				*spec->stream_digital_capture;
1622c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1623c577b8a1SJoseph Chan 				spec->dig_in_nid;
1624c577b8a1SJoseph Chan 		}
1625c577b8a1SJoseph Chan 	}
1626c577b8a1SJoseph Chan 
16277eb56e84STakashi Iwai 	if (spec->multiout.hp_nid) {
16287eb56e84STakashi Iwai 		codec->num_pcms++;
16297eb56e84STakashi Iwai 		info++;
16307eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
16317eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
16327eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
16337eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
16347eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
16357eb56e84STakashi Iwai 			spec->multiout.hp_nid;
16367eb56e84STakashi Iwai 	}
1637c577b8a1SJoseph Chan 	return 0;
1638c577b8a1SJoseph Chan }
1639c577b8a1SJoseph Chan 
1640c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1641c577b8a1SJoseph Chan {
1642c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1643c577b8a1SJoseph Chan 
1644c577b8a1SJoseph Chan 	if (!spec)
1645c577b8a1SJoseph Chan 		return;
1646c577b8a1SJoseph Chan 
1647603c4019STakashi Iwai 	via_free_kctls(codec);
16481f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1649c577b8a1SJoseph Chan 	kfree(codec->spec);
1650c577b8a1SJoseph Chan }
1651c577b8a1SJoseph Chan 
165264be285bSTakashi Iwai /* mute/unmute outputs */
165364be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
165464be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
165564be285bSTakashi Iwai {
165664be285bSTakashi Iwai 	int i;
165764be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
165864be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
165964be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
166064be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
166164be285bSTakashi Iwai }
166264be285bSTakashi Iwai 
166369e52a80SHarald Welte /* mute internal speaker if HP is plugged */
166469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
166569e52a80SHarald Welte {
1666dcf34c8cSLydia Wang 	unsigned int present = 0;
166769e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
166869e52a80SHarald Welte 
1669d56757abSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1670dcf34c8cSLydia Wang 
167164be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
167264be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
167364be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
167464be285bSTakashi Iwai 				    present);
167569e52a80SHarald Welte }
167669e52a80SHarald Welte 
1677f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */
1678f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec)
1679f3db423dSLydia Wang {
1680f3db423dSLydia Wang 	unsigned int hp_present, lineout_present;
1681f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1682f3db423dSLydia Wang 
1683f3db423dSLydia Wang 	if (spec->codec_type != VT1716S)
1684f3db423dSLydia Wang 		return;
1685f3db423dSLydia Wang 
1686d56757abSTakashi Iwai 	lineout_present = snd_hda_jack_detect(codec,
1687d56757abSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
1688f3db423dSLydia Wang 
1689f3db423dSLydia Wang 	/* Mute Mono Out if Line Out is plugged */
1690f3db423dSLydia Wang 	if (lineout_present) {
16913e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
16923e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
16933e0693e2STakashi Iwai 				    lineout_present ? 0 : PIN_OUT);
1694f3db423dSLydia Wang 		return;
1695f3db423dSLydia Wang 	}
1696f3db423dSLydia Wang 
1697d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1698f3db423dSLydia Wang 
1699f3db423dSLydia Wang 	if (!spec->hp_independent_mode)
17003e0693e2STakashi Iwai 		snd_hda_codec_write(codec, 0x2A, 0,
17013e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17023e0693e2STakashi Iwai 				    hp_present ? 0 : PIN_OUT);
1703f3db423dSLydia Wang }
1704f3db423dSLydia Wang 
170569e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
170669e52a80SHarald Welte {
170769e52a80SHarald Welte 	unsigned int gpio_data;
170869e52a80SHarald Welte 	unsigned int vol_counter;
170969e52a80SHarald Welte 	unsigned int vol;
171069e52a80SHarald Welte 	unsigned int master_vol;
171169e52a80SHarald Welte 
171269e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
171369e52a80SHarald Welte 
171469e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
171569e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
171669e52a80SHarald Welte 
171769e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
171869e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
171969e52a80SHarald Welte 
172069e52a80SHarald Welte 	vol = vol_counter & 0x1F;
172169e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
172269e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
172369e52a80SHarald Welte 					AC_AMP_GET_INPUT);
172469e52a80SHarald Welte 
172569e52a80SHarald Welte 	if (gpio_data == 0x02) {
172669e52a80SHarald Welte 		/* unmute line out */
17273e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
17283e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17293e0693e2STakashi Iwai 				    PIN_OUT);
173069e52a80SHarald Welte 		if (vol_counter & 0x20) {
173169e52a80SHarald Welte 			/* decrease volume */
173269e52a80SHarald Welte 			if (vol > master_vol)
173369e52a80SHarald Welte 				vol = master_vol;
173469e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
173569e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
173669e52a80SHarald Welte 						 master_vol-vol);
173769e52a80SHarald Welte 		} else {
173869e52a80SHarald Welte 			/* increase volume */
173969e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
174069e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
174169e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
174269e52a80SHarald Welte 					  (master_vol+vol));
174369e52a80SHarald Welte 		}
174469e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
174569e52a80SHarald Welte 		/* mute line out */
17463e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
17473e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
17483e0693e2STakashi Iwai 				    0);
174969e52a80SHarald Welte 	}
175069e52a80SHarald Welte }
175169e52a80SHarald Welte 
175225eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */
175325eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec)
175425eaba2fSLydia Wang {
175525eaba2fSLydia Wang 	unsigned int hp_present;
175625eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
175725eaba2fSLydia Wang 
175827439ce7SLydia Wang 	if (!VT2002P_COMPATIBLE(spec))
175925eaba2fSLydia Wang 		return;
176025eaba2fSLydia Wang 
1761d56757abSTakashi Iwai 	hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
176225eaba2fSLydia Wang 
176364be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
176464be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.speaker_outs,
176564be285bSTakashi Iwai 				    spec->autocfg.speaker_pins,
176664be285bSTakashi Iwai 				    hp_present);
176725eaba2fSLydia Wang }
176825eaba2fSLydia Wang 
176925eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */
177025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec)
177125eaba2fSLydia Wang {
177264be285bSTakashi Iwai 	int present;
177325eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
177425eaba2fSLydia Wang 
177525eaba2fSLydia Wang 	if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0])
177625eaba2fSLydia Wang 		return;
177725eaba2fSLydia Wang 
177864be285bSTakashi Iwai 	present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
177964be285bSTakashi Iwai 	if (!spec->hp_independent_mode)
178064be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
178164be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
178264be285bSTakashi Iwai 				    present);
178325eaba2fSLydia Wang 
178464be285bSTakashi Iwai 	if (!present)
178564be285bSTakashi Iwai 		present = snd_hda_jack_detect(codec,
178664be285bSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
178725eaba2fSLydia Wang 
178825eaba2fSLydia Wang 	/* Speakers */
178964be285bSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
179064be285bSTakashi Iwai 			    spec->autocfg.speaker_pins,
179164be285bSTakashi Iwai 			    present);
179225eaba2fSLydia Wang }
179325eaba2fSLydia Wang 
179425eaba2fSLydia Wang 
179569e52a80SHarald Welte /* unsolicited event for jack sensing */
179669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
179769e52a80SHarald Welte 				  unsigned int res)
179869e52a80SHarald Welte {
179969e52a80SHarald Welte 	res >>= 26;
1800ec7e7e42SLydia Wang 
1801a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
18023e95b9abSLydia Wang 		set_widgets_power_state(codec);
1803ec7e7e42SLydia Wang 
1804ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1805ec7e7e42SLydia Wang 
1806ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1807ec7e7e42SLydia Wang 		via_hp_automute(codec);
1808ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1809ec7e7e42SLydia Wang 		via_gpio_control(codec);
1810ec7e7e42SLydia Wang 	else if (res == VIA_MONO_EVENT)
1811f3db423dSLydia Wang 		via_mono_automute(codec);
1812ec7e7e42SLydia Wang 	else if (res == VIA_SPEAKER_EVENT)
181325eaba2fSLydia Wang 		via_speaker_automute(codec);
1814ec7e7e42SLydia Wang 	else if (res == VIA_BIND_HP_EVENT)
181525eaba2fSLydia Wang 		via_hp_bind_automute(codec);
181669e52a80SHarald Welte }
181769e52a80SHarald Welte 
1818c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec)
1819c577b8a1SJoseph Chan {
1820c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
182169e52a80SHarald Welte 	int i;
182269e52a80SHarald Welte 	for (i = 0; i < spec->num_iverbs; i++)
182369e52a80SHarald Welte 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
182469e52a80SHarald Welte 
1825f7278fd0SJosepch Chan 	/* Lydia Add for EAPD enable */
1826f7278fd0SJosepch Chan 	if (!spec->dig_in_nid) { /* No Digital In connection */
182755d1d6c1STakashi Iwai 		if (spec->dig_in_pin) {
182855d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1829f7278fd0SJosepch Chan 					    AC_VERB_SET_PIN_WIDGET_CONTROL,
183012b74c80STakashi Iwai 					    PIN_OUT);
183155d1d6c1STakashi Iwai 			snd_hda_codec_write(codec, spec->dig_in_pin, 0,
1832f7278fd0SJosepch Chan 					    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
1833f7278fd0SJosepch Chan 		}
183412b74c80STakashi Iwai 	} else /* enable SPDIF-input pin */
183512b74c80STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
183612b74c80STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
1837f7278fd0SJosepch Chan 
18389da29271STakashi Iwai 	/* assign slave outs */
18399da29271STakashi Iwai 	if (spec->slave_dig_outs[0])
18409da29271STakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
18415691ec7fSHarald Welte 
1842c577b8a1SJoseph Chan 	return 0;
1843c577b8a1SJoseph Chan }
1844c577b8a1SJoseph Chan 
18451f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18461f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
18471f2e99feSLydia Wang {
18481f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
18491f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
18501f2e99feSLydia Wang 	return 0;
18511f2e99feSLydia Wang }
18521f2e99feSLydia Wang #endif
18531f2e99feSLydia Wang 
1854cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1855cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1856cb53c626STakashi Iwai {
1857cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1858cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1859cb53c626STakashi Iwai }
1860cb53c626STakashi Iwai #endif
1861cb53c626STakashi Iwai 
1862c577b8a1SJoseph Chan /*
1863c577b8a1SJoseph Chan  */
186490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1865c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1866c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1867c577b8a1SJoseph Chan 	.init = via_init,
1868c577b8a1SJoseph Chan 	.free = via_free,
18691f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
18701f2e99feSLydia Wang 	.suspend = via_suspend,
18711f2e99feSLydia Wang #endif
1872cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1873cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1874cb53c626STakashi Iwai #endif
1875c577b8a1SJoseph Chan };
1876c577b8a1SJoseph Chan 
18774a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1878c577b8a1SJoseph Chan {
18794a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
18804a79616dSTakashi Iwai 	int i;
18814a79616dSTakashi Iwai 
18824a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
18834a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
18844a79616dSTakashi Iwai 			return false;
18854a79616dSTakashi Iwai 	}
18864a79616dSTakashi Iwai 	if (spec->multiout.hp_nid == dac)
18874a79616dSTakashi Iwai 		return false;
18884a79616dSTakashi Iwai 	return true;
18894a79616dSTakashi Iwai }
18904a79616dSTakashi Iwai 
18914a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
18924a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
18934a79616dSTakashi Iwai 			      int depth, int wid_type)
18944a79616dSTakashi Iwai {
18954a79616dSTakashi Iwai 	hda_nid_t conn[8];
18964a79616dSTakashi Iwai 	int i, nums;
18974a79616dSTakashi Iwai 
18984a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
18994a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
19004a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
19014a79616dSTakashi Iwai 			continue;
19024a79616dSTakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
19034a79616dSTakashi Iwai 			path->path[depth] = conn[i];
19044a79616dSTakashi Iwai 			path->idx[depth] = i;
19054a79616dSTakashi Iwai 			path->depth = ++depth;
19064a79616dSTakashi Iwai 			return true;
19074a79616dSTakashi Iwai 		}
19084a79616dSTakashi Iwai 	}
19094a79616dSTakashi Iwai 	if (depth > 4)
19104a79616dSTakashi Iwai 		return false;
19114a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
19124a79616dSTakashi Iwai 		unsigned int type;
19134a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
19144a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
19154a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
19164a79616dSTakashi Iwai 			continue;
19174a79616dSTakashi Iwai 		if (parse_output_path(codec, conn[i], target_dac,
19184a79616dSTakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL)) {
19194a79616dSTakashi Iwai 			path->path[depth] = conn[i];
19204a79616dSTakashi Iwai 			path->idx[depth] = i;
19214a79616dSTakashi Iwai 			return true;
19224a79616dSTakashi Iwai 		}
19234a79616dSTakashi Iwai 	}
19244a79616dSTakashi Iwai 	return false;
19254a79616dSTakashi Iwai }
19264a79616dSTakashi Iwai 
19274a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
19284a79616dSTakashi Iwai {
19294a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
19304a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1931c577b8a1SJoseph Chan 	int i;
1932c577b8a1SJoseph Chan 	hda_nid_t nid;
1933c577b8a1SJoseph Chan 
1934c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
19354a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
19364a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1937c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
19384a79616dSTakashi Iwai 		if (!nid)
19394a79616dSTakashi Iwai 			continue;
19404a79616dSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
19414a79616dSTakashi Iwai 			spec->private_dac_nids[i] =
19424a79616dSTakashi Iwai 				spec->out_path[i].path[spec->out_path[i].depth - 1];
1943c577b8a1SJoseph Chan 	}
1944c577b8a1SJoseph Chan 	return 0;
1945c577b8a1SJoseph Chan }
1946c577b8a1SJoseph Chan 
19474a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
19484a79616dSTakashi Iwai 			  hda_nid_t pin, hda_nid_t dac, int chs)
1949c577b8a1SJoseph Chan {
19504a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1951c577b8a1SJoseph Chan 	char name[32];
19524a79616dSTakashi Iwai 	hda_nid_t nid;
19534a79616dSTakashi Iwai 	int err;
1954c577b8a1SJoseph Chan 
19554a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
19564a79616dSTakashi Iwai 		nid = dac;
19574a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
19584a79616dSTakashi Iwai 		nid = pin;
19594a79616dSTakashi Iwai 	else
19604a79616dSTakashi Iwai 		nid = 0;
19614a79616dSTakashi Iwai 	if (nid) {
19624a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1963c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
19644a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1965c577b8a1SJoseph Chan 		if (err < 0)
1966c577b8a1SJoseph Chan 			return err;
1967c577b8a1SJoseph Chan 	}
19684a79616dSTakashi Iwai 
19694a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
19704a79616dSTakashi Iwai 		nid = dac;
19714a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
19724a79616dSTakashi Iwai 		nid = pin;
19734a79616dSTakashi Iwai 	else
19744a79616dSTakashi Iwai 		nid = 0;
19754a79616dSTakashi Iwai 	if (nid) {
19764a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
19774a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
19784a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
19794a79616dSTakashi Iwai 		if (err < 0)
19804a79616dSTakashi Iwai 			return err;
19814a79616dSTakashi Iwai 	}
19824a79616dSTakashi Iwai 	return 0;
19834a79616dSTakashi Iwai }
19844a79616dSTakashi Iwai 
19854a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
19864a79616dSTakashi Iwai 				hda_nid_t nid);
19874a79616dSTakashi Iwai 
1988f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1989f4a7828bSTakashi Iwai {
1990f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1991f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
1992f4a7828bSTakashi Iwai 	int i;
1993f4a7828bSTakashi Iwai 
1994f4a7828bSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1995f4a7828bSTakashi Iwai 		if (!is_smart51_pins(codec, cfg->inputs[i].pin))
1996f4a7828bSTakashi Iwai 			continue;
1997f4a7828bSTakashi Iwai 		spec->can_smart51 = 1;
1998f4a7828bSTakashi Iwai 		cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1999f4a7828bSTakashi Iwai 		if (cfg->line_outs == 3)
2000f4a7828bSTakashi Iwai 			break;
2001f4a7828bSTakashi Iwai 	}
2002f4a7828bSTakashi Iwai }
2003f4a7828bSTakashi Iwai 
20044a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
20054a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
20064a79616dSTakashi Iwai {
20074a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
2008f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
20094a79616dSTakashi Iwai 	static const char * const chname[4] = {
20104a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
20114a79616dSTakashi Iwai 	};
20124a79616dSTakashi Iwai 	int i, idx, err;
2013f4a7828bSTakashi Iwai 	int old_line_outs;
2014f4a7828bSTakashi Iwai 
2015f4a7828bSTakashi Iwai 	/* check smart51 */
2016f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
2017f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
2018f4a7828bSTakashi Iwai 		mangle_smart51(codec);
20194a79616dSTakashi Iwai 
20204a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
20214a79616dSTakashi Iwai 		hda_nid_t pin, dac;
20224a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
20234a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
20244a79616dSTakashi Iwai 		if (!pin || !dac)
20254a79616dSTakashi Iwai 			continue;
20264a79616dSTakashi Iwai 		if (i == AUTO_SEQ_CENLFE) {
20274a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "Center", pin, dac, 1);
20284a79616dSTakashi Iwai 			if (err < 0)
20294a79616dSTakashi Iwai 				return err;
20304a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "LFE", pin, dac, 2);
20314a79616dSTakashi Iwai 			if (err < 0)
20324a79616dSTakashi Iwai 				return err;
20334a79616dSTakashi Iwai 		} else {
20344a79616dSTakashi Iwai 			err = create_ch_ctls(codec, chname[i], pin, dac, 3);
20354a79616dSTakashi Iwai 			if (err < 0)
20364a79616dSTakashi Iwai 				return err;
20374a79616dSTakashi Iwai 		}
20384a79616dSTakashi Iwai 	}
20394a79616dSTakashi Iwai 
20404a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
20414a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
20424a79616dSTakashi Iwai 	if (idx >= 0) {
20434a79616dSTakashi Iwai 		/* add control to mixer */
20444a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
20454a79616dSTakashi Iwai 				      "PCM Playback Volume",
20464a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20474a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20484a79616dSTakashi Iwai 		if (err < 0)
20494a79616dSTakashi Iwai 			return err;
20504a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
20514a79616dSTakashi Iwai 				      "PCM Playback Switch",
20524a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20534a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20544a79616dSTakashi Iwai 		if (err < 0)
20554a79616dSTakashi Iwai 			return err;
2056c577b8a1SJoseph Chan 	}
2057c577b8a1SJoseph Chan 
2058f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
2059f4a7828bSTakashi Iwai 
2060c577b8a1SJoseph Chan 	return 0;
2061c577b8a1SJoseph Chan }
2062c577b8a1SJoseph Chan 
20630aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
20640aa62aefSHarald Welte {
20650aa62aefSHarald Welte 	int i;
20660aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
2067ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
20680aa62aefSHarald Welte 
20690aa62aefSHarald Welte 	/* for hp mode select */
207010a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
207110a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
20720aa62aefSHarald Welte 
20730aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
20740aa62aefSHarald Welte }
20750aa62aefSHarald Welte 
20764a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
2077c577b8a1SJoseph Chan {
20784a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
20794a79616dSTakashi Iwai 	hda_nid_t dac = 0;
2080c577b8a1SJoseph Chan 	int err;
2081c577b8a1SJoseph Chan 
2082c577b8a1SJoseph Chan 	if (!pin)
2083c577b8a1SJoseph Chan 		return 0;
2084c577b8a1SJoseph Chan 
20854a79616dSTakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
20864a79616dSTakashi Iwai 			       &spec->hp_dep_path, 0, -1))
20874a79616dSTakashi Iwai 		return 0;
20884a79616dSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
20894a79616dSTakashi Iwai 		dac = spec->hp_path.path[spec->hp_path.depth - 1];
20904a79616dSTakashi Iwai 		spec->multiout.hp_nid = dac;
20914a79616dSTakashi Iwai 		spec->hp_independent_mode_index =
20924a79616dSTakashi Iwai 			spec->hp_path.idx[spec->hp_path.depth - 1];
20930aa62aefSHarald Welte 		create_hp_imux(spec);
20944a79616dSTakashi Iwai 	}
20954a79616dSTakashi Iwai 
20964a79616dSTakashi Iwai 	err = create_ch_ctls(codec, "Headphone", pin, dac, 3);
20974a79616dSTakashi Iwai 	if (err < 0)
20984a79616dSTakashi Iwai 		return err;
20990aa62aefSHarald Welte 
2100c577b8a1SJoseph Chan 	return 0;
2101c577b8a1SJoseph Chan }
2102c577b8a1SJoseph Chan 
2103a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux,
2104a766d0d7STakashi Iwai 				hda_nid_t nid)
2105a766d0d7STakashi Iwai {
2106a766d0d7STakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
2107a766d0d7STakashi Iwai 	int i, nums;
2108a766d0d7STakashi Iwai 
2109a766d0d7STakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
2110a766d0d7STakashi Iwai 	for (i = 0; i < nums; i++)
2111a766d0d7STakashi Iwai 		if (conn[i] == nid)
2112a766d0d7STakashi Iwai 			return i;
2113a766d0d7STakashi Iwai 	return -1;
2114a766d0d7STakashi Iwai }
2115a766d0d7STakashi Iwai 
2116a766d0d7STakashi Iwai /* look for ADCs */
2117a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2118a766d0d7STakashi Iwai {
2119a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2120a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2121a766d0d7STakashi Iwai 	int i;
2122a766d0d7STakashi Iwai 
2123a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2124a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2125a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2126a766d0d7STakashi Iwai 			continue;
2127a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2128a766d0d7STakashi Iwai 			continue;
2129a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2130a766d0d7STakashi Iwai 			continue;
2131a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2132a766d0d7STakashi Iwai 			return -ENOMEM;
2133a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2134a766d0d7STakashi Iwai 	}
2135a766d0d7STakashi Iwai 	return 0;
2136a766d0d7STakashi Iwai }
2137a766d0d7STakashi Iwai 
2138a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2139a766d0d7STakashi Iwai 
2140*d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
2141*d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2142*d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
2143*d7a99cceSTakashi Iwai 	 * So call somewhat different..
2144*d7a99cceSTakashi Iwai 	 */
2145*d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
2146*d7a99cceSTakashi Iwai 	.name = "Input Source",
2147*d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
2148*d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
2149*d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
2150*d7a99cceSTakashi Iwai };
2151*d7a99cceSTakashi Iwai 
2152c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
2153620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
2154620e2b28STakashi Iwai 					     const struct auto_pin_cfg *cfg)
2155c577b8a1SJoseph Chan {
215610a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
21570aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
2158a766d0d7STakashi Iwai 	int i, err, idx, idx2, type, type_idx = 0;
2159a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
2160a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
2161a766d0d7STakashi Iwai 	int num_idxs;
2162a766d0d7STakashi Iwai 
2163a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2164a766d0d7STakashi Iwai 	if (err < 0)
2165a766d0d7STakashi Iwai 		return err;
2166a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2167a766d0d7STakashi Iwai 	if (err < 0)
2168a766d0d7STakashi Iwai 		return err;
2169a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
2170a766d0d7STakashi Iwai 
2171a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
2172a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
2173a766d0d7STakashi Iwai 	if (num_idxs <= 0)
2174a766d0d7STakashi Iwai 		return 0;
2175c577b8a1SJoseph Chan 
2176c577b8a1SJoseph Chan 	/* for internal loopback recording select */
2177f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
2178620e2b28STakashi Iwai 		if (pin_idxs[idx] == spec->aa_mix_nid) {
217910a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
2180f3268512STakashi Iwai 			break;
2181f3268512STakashi Iwai 		}
2182f3268512STakashi Iwai 	}
2183c577b8a1SJoseph Chan 
21847b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
218510a20af7STakashi Iwai 		const char *label;
21867b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
2187f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
21887b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
2189c577b8a1SJoseph Chan 				break;
2190f3268512STakashi Iwai 		if (idx >= num_idxs)
2191f3268512STakashi Iwai 			continue;
21927b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
21937b315bb4STakashi Iwai 			type_idx++;
21947b315bb4STakashi Iwai 		else
21957b315bb4STakashi Iwai 			type_idx = 0;
219610a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2197620e2b28STakashi Iwai 		idx2 = get_connection_index(codec, spec->aa_mix_nid,
2198620e2b28STakashi Iwai 					    pin_idxs[idx]);
2199a766d0d7STakashi Iwai 		if (idx2 >= 0)
220016922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2201620e2b28STakashi Iwai 						   idx2, spec->aa_mix_nid);
2202c577b8a1SJoseph Chan 		if (err < 0)
2203c577b8a1SJoseph Chan 			return err;
220410a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
2205c577b8a1SJoseph Chan 	}
2206*d7a99cceSTakashi Iwai 
2207*d7a99cceSTakashi Iwai 	/* create capture mixer elements */
2208*d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2209*d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
2210*d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2211*d7a99cceSTakashi Iwai 					"Capture Volume", i,
2212*d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2213*d7a99cceSTakashi Iwai 							    HDA_INPUT));
2214*d7a99cceSTakashi Iwai 		if (err < 0)
2215*d7a99cceSTakashi Iwai 			return err;
2216*d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2217*d7a99cceSTakashi Iwai 					"Capture Switch", i,
2218*d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2219*d7a99cceSTakashi Iwai 							    HDA_INPUT));
2220*d7a99cceSTakashi Iwai 		if (err < 0)
2221*d7a99cceSTakashi Iwai 			return err;
2222*d7a99cceSTakashi Iwai 	}
2223*d7a99cceSTakashi Iwai 
2224*d7a99cceSTakashi Iwai 	/* input-source control */
2225*d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2226*d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
2227*d7a99cceSTakashi Iwai 			break;
2228*d7a99cceSTakashi Iwai 	if (i) {
2229*d7a99cceSTakashi Iwai 		struct snd_kcontrol_new *knew;
2230*d7a99cceSTakashi Iwai 		knew = via_clone_control(spec, &via_input_src_ctl);
2231*d7a99cceSTakashi Iwai 		if (!knew)
2232*d7a99cceSTakashi Iwai 			return -ENOMEM;
2233*d7a99cceSTakashi Iwai 		knew->count = i;
2234*d7a99cceSTakashi Iwai 	}
2235*d7a99cceSTakashi Iwai 
2236*d7a99cceSTakashi Iwai 	/* mic-boosts */
2237*d7a99cceSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2238*d7a99cceSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2239*d7a99cceSTakashi Iwai 		unsigned int caps;
2240*d7a99cceSTakashi Iwai 		const char *label;
2241*d7a99cceSTakashi Iwai 		char name[32];
2242*d7a99cceSTakashi Iwai 
2243*d7a99cceSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
2244*d7a99cceSTakashi Iwai 			continue;
2245*d7a99cceSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
2246*d7a99cceSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2247*d7a99cceSTakashi Iwai 			continue;
2248*d7a99cceSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2249*d7a99cceSTakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2250*d7a99cceSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2251*d7a99cceSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2252*d7a99cceSTakashi Iwai 		if (err < 0)
2253*d7a99cceSTakashi Iwai 			return err;
2254*d7a99cceSTakashi Iwai 	}
2255*d7a99cceSTakashi Iwai 
2256c577b8a1SJoseph Chan 	return 0;
2257c577b8a1SJoseph Chan }
2258c577b8a1SJoseph Chan 
2259cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
226090dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = {
2261cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2262cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2263cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2264cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2265cb53c626STakashi Iwai 	{ } /* end */
2266cb53c626STakashi Iwai };
2267cb53c626STakashi Iwai #endif
2268cb53c626STakashi Iwai 
226976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
227076d9b0ddSHarald Welte {
227176d9b0ddSHarald Welte 	unsigned int def_conf;
227276d9b0ddSHarald Welte 	unsigned char seqassoc;
227376d9b0ddSHarald Welte 
22742f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
227576d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
227676d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
227782ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
227882ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
227976d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
22802f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
228176d9b0ddSHarald Welte 	}
228276d9b0ddSHarald Welte 
228376d9b0ddSHarald Welte 	return;
228476d9b0ddSHarald Welte }
228576d9b0ddSHarald Welte 
2286e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
22871f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22881f2e99feSLydia Wang {
22891f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22901f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22911f2e99feSLydia Wang 
22921f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22931f2e99feSLydia Wang 		return 0;
2294e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
22951f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2296e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
22971f2e99feSLydia Wang 	return 0;
22981f2e99feSLydia Wang }
22991f2e99feSLydia Wang 
2300e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
23011f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
23021f2e99feSLydia Wang {
23031f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
23041f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
23051f2e99feSLydia Wang 	int change;
23061f2e99feSLydia Wang 
23071f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
23081f2e99feSLydia Wang 		return 0;
2309e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
23101f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2311e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2312e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
23131f2e99feSLydia Wang 		mute_aa_path(codec, 1);
23141f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
23151f2e99feSLydia Wang 	}
23161f2e99feSLydia Wang 	return change;
23171f2e99feSLydia Wang }
23181f2e99feSLydia Wang 
2319e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
23201f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
23211f2e99feSLydia Wang 	.name = "Jack Detect",
23221f2e99feSLydia Wang 	.count = 1,
23231f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2324e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2325e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
23261f2e99feSLydia Wang };
23271f2e99feSLydia Wang 
2328c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec)
2329c577b8a1SJoseph Chan {
2330c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2331c577b8a1SJoseph Chan 	int err;
2332c577b8a1SJoseph Chan 
233376d9b0ddSHarald Welte 	/* Add HP and CD pin config connect bit re-config action */
233476d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
233576d9b0ddSHarald Welte 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
233676d9b0ddSHarald Welte 
2337c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2338c577b8a1SJoseph Chan 	if (err < 0)
2339c577b8a1SJoseph Chan 		return err;
23404a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2341c577b8a1SJoseph Chan 	if (err < 0)
2342c577b8a1SJoseph Chan 		return err;
2343c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2344c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2345c577b8a1SJoseph Chan 
23464a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2347c577b8a1SJoseph Chan 	if (err < 0)
2348c577b8a1SJoseph Chan 		return err;
23494a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2350c577b8a1SJoseph Chan 	if (err < 0)
2351c577b8a1SJoseph Chan 		return err;
2352620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2353c577b8a1SJoseph Chan 	if (err < 0)
2354c577b8a1SJoseph Chan 		return err;
23551f2e99feSLydia Wang 	/* add jack detect on/off control */
2356e06e5a29STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2357e06e5a29STakashi Iwai 		return -ENOMEM;
2358c577b8a1SJoseph Chan 
2359c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2360c577b8a1SJoseph Chan 
23610852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2362c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1708_DIGOUT_NID;
236355d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708_DIGIN_PIN;
2364c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2365c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1708_DIGIN_NID;
2366c577b8a1SJoseph Chan 
2367603c4019STakashi Iwai 	if (spec->kctls.list)
2368603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2369c577b8a1SJoseph Chan 
237069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs;
2371c577b8a1SJoseph Chan 
23720aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
23730aa62aefSHarald Welte 
2374f8fdd495SHarald Welte 	if (spec->hp_mux)
23753d83e577STakashi Iwai 		via_hp_build(codec);
2376c577b8a1SJoseph Chan 
2377f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2378f4a7828bSTakashi Iwai 	if (err < 0)
2379f4a7828bSTakashi Iwai 		return err;
2380f4a7828bSTakashi Iwai 
2381c577b8a1SJoseph Chan 	return 1;
2382c577b8a1SJoseph Chan }
2383c577b8a1SJoseph Chan 
2384c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */
2385c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec)
2386c577b8a1SJoseph Chan {
238725eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
238825eaba2fSLydia Wang 
2389c577b8a1SJoseph Chan 	via_init(codec);
2390c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2391c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
2392c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
239311890956SLydia Wang 
239411890956SLydia Wang 	if (VT2002P_COMPATIBLE(spec)) {
239525eaba2fSLydia Wang 		via_hp_bind_automute(codec);
239625eaba2fSLydia Wang 	} else {
239725eaba2fSLydia Wang 		via_hp_automute(codec);
239825eaba2fSLydia Wang 		via_speaker_automute(codec);
239925eaba2fSLydia Wang 	}
240025eaba2fSLydia Wang 
2401c577b8a1SJoseph Chan 	return 0;
2402c577b8a1SJoseph Chan }
2403c577b8a1SJoseph Chan 
24041f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
24051f2e99feSLydia Wang {
24061f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
24071f2e99feSLydia Wang 					     vt1708_hp_work.work);
24081f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24091f2e99feSLydia Wang 		return;
24101f2e99feSLydia Wang 	/* if jack state toggled */
24111f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2412d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
24131f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
24141f2e99feSLydia Wang 		via_hp_automute(spec->codec);
24151f2e99feSLydia Wang 	}
24161f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
24171f2e99feSLydia Wang }
24181f2e99feSLydia Wang 
2419337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2420337b9d02STakashi Iwai {
2421337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2422337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2423337b9d02STakashi Iwai 	unsigned int type;
2424337b9d02STakashi Iwai 	int i, n;
2425337b9d02STakashi Iwai 
2426337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2427337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2428337b9d02STakashi Iwai 		while (nid) {
2429a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
24301c55d521STakashi Iwai 			if (type == AC_WID_PIN)
24311c55d521STakashi Iwai 				break;
2432337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2433337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2434337b9d02STakashi Iwai 			if (n <= 0)
2435337b9d02STakashi Iwai 				break;
2436337b9d02STakashi Iwai 			if (n > 1) {
2437337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2438337b9d02STakashi Iwai 				break;
2439337b9d02STakashi Iwai 			}
2440337b9d02STakashi Iwai 			nid = conn[0];
2441337b9d02STakashi Iwai 		}
2442337b9d02STakashi Iwai 	}
24431c55d521STakashi Iwai 	return 0;
2444337b9d02STakashi Iwai }
2445337b9d02STakashi Iwai 
2446c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2447c577b8a1SJoseph Chan {
2448c577b8a1SJoseph Chan 	struct via_spec *spec;
2449c577b8a1SJoseph Chan 	int err;
2450c577b8a1SJoseph Chan 
2451c577b8a1SJoseph Chan 	/* create a codec specific record */
24525b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2453c577b8a1SJoseph Chan 	if (spec == NULL)
2454c577b8a1SJoseph Chan 		return -ENOMEM;
2455c577b8a1SJoseph Chan 
2456620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2457620e2b28STakashi Iwai 
2458c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
2459c577b8a1SJoseph Chan 	err = vt1708_parse_auto_config(codec);
2460c577b8a1SJoseph Chan 	if (err < 0) {
2461c577b8a1SJoseph Chan 		via_free(codec);
2462c577b8a1SJoseph Chan 		return err;
2463c577b8a1SJoseph Chan 	} else if (!err) {
2464c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2465c577b8a1SJoseph Chan 		       "from BIOS.  Using genenic mode...\n");
2466c577b8a1SJoseph Chan 	}
2467c577b8a1SJoseph Chan 
2468c577b8a1SJoseph Chan 
2469bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2470bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2471bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2472c577b8a1SJoseph Chan 
2473c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2474c577b8a1SJoseph Chan 
2475c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
2476cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2477cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2478cb53c626STakashi Iwai #endif
24791f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2480c577b8a1SJoseph Chan 	return 0;
2481c577b8a1SJoseph Chan }
2482c577b8a1SJoseph Chan 
248390dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = {
2484a34df19aSLydia Wang 	{0x20, AC_VERB_SET_UNSOLICITED_ENABLE,
2485a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
248669e52a80SHarald Welte 	{ }
248769e52a80SHarald Welte };
248869e52a80SHarald Welte 
2489c577b8a1SJoseph Chan /*
2490c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2491c577b8a1SJoseph Chan  */
249290dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = {
2493c577b8a1SJoseph Chan 	/*
2494c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2495c577b8a1SJoseph Chan 	 */
2496c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2497c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2498c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2499c577b8a1SJoseph Chan 
2500c577b8a1SJoseph Chan 
2501f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2502c577b8a1SJoseph Chan 	 * mixer widget
2503c577b8a1SJoseph Chan 	 */
2504c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2505f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2506f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2507f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2508f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2509f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2510c577b8a1SJoseph Chan 
2511c577b8a1SJoseph Chan 	/*
2512c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2513c577b8a1SJoseph Chan 	 */
2514c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2515c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2516c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2517c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2518c577b8a1SJoseph Chan 
2519c577b8a1SJoseph Chan 	/*
2520c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2521c577b8a1SJoseph Chan 	 */
2522c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2523c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2524c577b8a1SJoseph Chan 
2525bfdc675aSLydia Wang 	/* Set input of PW4 as MW0 */
2526bfdc675aSLydia Wang 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2527c577b8a1SJoseph Chan 	/* PW9 Output enable */
2528c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2529c577b8a1SJoseph Chan 	{ }
2530c577b8a1SJoseph Chan };
2531c577b8a1SJoseph Chan 
2532c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec)
2533c577b8a1SJoseph Chan {
2534c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2535c577b8a1SJoseph Chan 	int err;
2536c577b8a1SJoseph Chan 
2537c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2538c577b8a1SJoseph Chan 	if (err < 0)
2539c577b8a1SJoseph Chan 		return err;
25404a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2541c577b8a1SJoseph Chan 	if (err < 0)
2542c577b8a1SJoseph Chan 		return err;
2543c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2544c577b8a1SJoseph Chan 		return 0; /* can't find valid BIOS pin config */
2545c577b8a1SJoseph Chan 
25464a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2547c577b8a1SJoseph Chan 	if (err < 0)
2548c577b8a1SJoseph Chan 		return err;
25494a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2550c577b8a1SJoseph Chan 	if (err < 0)
2551c577b8a1SJoseph Chan 		return err;
2552620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2553c577b8a1SJoseph Chan 	if (err < 0)
2554c577b8a1SJoseph Chan 		return err;
2555c577b8a1SJoseph Chan 
2556c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2557c577b8a1SJoseph Chan 
25580852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2559c577b8a1SJoseph Chan 		spec->multiout.dig_out_nid = VT1709_DIGOUT_NID;
256055d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1709_DIGIN_PIN;
2561c577b8a1SJoseph Chan 	if (spec->autocfg.dig_in_pin)
2562c577b8a1SJoseph Chan 		spec->dig_in_nid = VT1709_DIGIN_NID;
2563c577b8a1SJoseph Chan 
2564603c4019STakashi Iwai 	if (spec->kctls.list)
2565603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2566c577b8a1SJoseph Chan 
25670aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
2568c577b8a1SJoseph Chan 
2569f8fdd495SHarald Welte 	if (spec->hp_mux)
25703d83e577STakashi Iwai 		via_hp_build(codec);
2571f8fdd495SHarald Welte 
2572f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2573f4a7828bSTakashi Iwai 	if (err < 0)
2574f4a7828bSTakashi Iwai 		return err;
2575f4a7828bSTakashi Iwai 
2576c577b8a1SJoseph Chan 	return 1;
2577c577b8a1SJoseph Chan }
2578c577b8a1SJoseph Chan 
2579cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
258090dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = {
2581cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2582cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2583cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2584cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2585cb53c626STakashi Iwai 	{ } /* end */
2586cb53c626STakashi Iwai };
2587cb53c626STakashi Iwai #endif
2588cb53c626STakashi Iwai 
2589c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2590c577b8a1SJoseph Chan {
2591c577b8a1SJoseph Chan 	struct via_spec *spec;
2592c577b8a1SJoseph Chan 	int err;
2593c577b8a1SJoseph Chan 
2594c577b8a1SJoseph Chan 	/* create a codec specific record */
25955b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2596c577b8a1SJoseph Chan 	if (spec == NULL)
2597c577b8a1SJoseph Chan 		return -ENOMEM;
2598c577b8a1SJoseph Chan 
2599620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2600620e2b28STakashi Iwai 
2601c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2602c577b8a1SJoseph Chan 	if (err < 0) {
2603c577b8a1SJoseph Chan 		via_free(codec);
2604c577b8a1SJoseph Chan 		return err;
2605c577b8a1SJoseph Chan 	} else if (!err) {
2606c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2607c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2608c577b8a1SJoseph Chan 	}
2609c577b8a1SJoseph Chan 
261069e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs;
261169e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2612c577b8a1SJoseph Chan 
2613c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2614c577b8a1SJoseph Chan 
2615c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
261669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2617cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2618cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2619cb53c626STakashi Iwai #endif
2620c577b8a1SJoseph Chan 
2621c577b8a1SJoseph Chan 	return 0;
2622c577b8a1SJoseph Chan }
2623c577b8a1SJoseph Chan /*
2624c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2625c577b8a1SJoseph Chan  */
262690dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = {
2627c577b8a1SJoseph Chan 	/*
2628c577b8a1SJoseph Chan 	 * Unmute ADC0-2 and set the default input to mic-in
2629c577b8a1SJoseph Chan 	 */
2630c577b8a1SJoseph Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2631c577b8a1SJoseph Chan 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2632c577b8a1SJoseph Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2633c577b8a1SJoseph Chan 
2634c577b8a1SJoseph Chan 
2635c577b8a1SJoseph Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2636c577b8a1SJoseph Chan 	 * mixer widget
2637c577b8a1SJoseph Chan 	 */
2638c577b8a1SJoseph Chan 	/* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2639c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2640c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2641c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2642c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2643c577b8a1SJoseph Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2644c577b8a1SJoseph Chan 
2645c577b8a1SJoseph Chan 	/*
2646c577b8a1SJoseph Chan 	 * Set up output selector (0x1a, 0x1b, 0x29)
2647c577b8a1SJoseph Chan 	 */
2648c577b8a1SJoseph Chan 	/* set vol=0 to output mixers */
2649c577b8a1SJoseph Chan 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2650c577b8a1SJoseph Chan 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2651c577b8a1SJoseph Chan 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2652c577b8a1SJoseph Chan 
2653c577b8a1SJoseph Chan 	/*
2654c577b8a1SJoseph Chan 	 *  Unmute PW3 and PW4
2655c577b8a1SJoseph Chan 	 */
2656c577b8a1SJoseph Chan 	{0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2657c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2658c577b8a1SJoseph Chan 
2659c577b8a1SJoseph Chan 	/* Set input of PW4 as MW0 */
2660c577b8a1SJoseph Chan 	{0x20, AC_VERB_SET_CONNECT_SEL, 0},
2661c577b8a1SJoseph Chan 	/* PW9 Output enable */
2662c577b8a1SJoseph Chan 	{0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2663c577b8a1SJoseph Chan 	{ }
2664c577b8a1SJoseph Chan };
2665c577b8a1SJoseph Chan 
2666c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2667c577b8a1SJoseph Chan {
2668c577b8a1SJoseph Chan 	struct via_spec *spec;
2669c577b8a1SJoseph Chan 	int err;
2670c577b8a1SJoseph Chan 
2671c577b8a1SJoseph Chan 	/* create a codec specific record */
26725b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2673c577b8a1SJoseph Chan 	if (spec == NULL)
2674c577b8a1SJoseph Chan 		return -ENOMEM;
2675c577b8a1SJoseph Chan 
2676620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2677620e2b28STakashi Iwai 
2678c577b8a1SJoseph Chan 	err = vt1709_parse_auto_config(codec);
2679c577b8a1SJoseph Chan 	if (err < 0) {
2680c577b8a1SJoseph Chan 		via_free(codec);
2681c577b8a1SJoseph Chan 		return err;
2682c577b8a1SJoseph Chan 	} else if (!err) {
2683c577b8a1SJoseph Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration.  "
2684c577b8a1SJoseph Chan 		       "Using genenic mode...\n");
2685c577b8a1SJoseph Chan 	}
2686c577b8a1SJoseph Chan 
268769e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs;
268869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs;
2689c577b8a1SJoseph Chan 
2690c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2691c577b8a1SJoseph Chan 
2692c577b8a1SJoseph Chan 	codec->patch_ops.init = via_auto_init;
269369e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2694cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2695cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2696cb53c626STakashi Iwai #endif
2697f7278fd0SJosepch Chan 	return 0;
2698f7278fd0SJosepch Chan }
2699f7278fd0SJosepch Chan 
2700f7278fd0SJosepch Chan /*
2701f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2702f7278fd0SJosepch Chan  */
270390dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = {
2704f7278fd0SJosepch Chan 	/*
2705f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2706f7278fd0SJosepch Chan 	 */
2707f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2708f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2709f7278fd0SJosepch Chan 
2710f7278fd0SJosepch Chan 
2711f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2712f7278fd0SJosepch Chan 	 * mixer widget
2713f7278fd0SJosepch Chan 	 */
2714f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2715f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2716f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2717f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2718f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2719f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2720f7278fd0SJosepch Chan 
2721f7278fd0SJosepch Chan 	/*
2722f7278fd0SJosepch Chan 	 * Set up output mixers
2723f7278fd0SJosepch Chan 	 */
2724f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2725f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2726f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2727f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2728f7278fd0SJosepch Chan 
2729f7278fd0SJosepch Chan 	/* Setup default input to PW4 */
2730bfdc675aSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0},
2731f7278fd0SJosepch Chan 	/* PW9 Output enable */
2732f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2733f7278fd0SJosepch Chan 	/* PW10 Input enable */
2734f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2735f7278fd0SJosepch Chan 	{ }
2736f7278fd0SJosepch Chan };
2737f7278fd0SJosepch Chan 
273890dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = {
2739f7278fd0SJosepch Chan 	/*
2740f7278fd0SJosepch Chan 	 * Unmute ADC0-1 and set the default input to mic-in
2741f7278fd0SJosepch Chan 	 */
2742f7278fd0SJosepch Chan 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2743f7278fd0SJosepch Chan 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2744f7278fd0SJosepch Chan 
2745f7278fd0SJosepch Chan 
2746f7278fd0SJosepch Chan 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
2747f7278fd0SJosepch Chan 	 * mixer widget
2748f7278fd0SJosepch Chan 	 */
2749f7278fd0SJosepch Chan 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
2750f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
2751f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
2752f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
2753f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
2754f7278fd0SJosepch Chan 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
2755f7278fd0SJosepch Chan 
2756f7278fd0SJosepch Chan 	/*
2757f7278fd0SJosepch Chan 	 * Set up output mixers
2758f7278fd0SJosepch Chan 	 */
2759f7278fd0SJosepch Chan 	/* set vol=0 to output mixers */
2760f7278fd0SJosepch Chan 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2761f7278fd0SJosepch Chan 	{0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2762f7278fd0SJosepch Chan 	{0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
2763f7278fd0SJosepch Chan 
2764f7278fd0SJosepch Chan 	/* Setup default input of PW4 to MW0 */
2765f7278fd0SJosepch Chan 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
2766f7278fd0SJosepch Chan 	/* PW9 Output enable */
2767f7278fd0SJosepch Chan 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
2768f7278fd0SJosepch Chan 	/* PW10 Input enable */
2769f7278fd0SJosepch Chan 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20},
2770f7278fd0SJosepch Chan 	{ }
2771f7278fd0SJosepch Chan };
2772f7278fd0SJosepch Chan 
277390dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = {
2774a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
2775a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
2776a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2777a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2778a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2779a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2780a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2781a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
2782a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
278369e52a80SHarald Welte 	{ }
278469e52a80SHarald Welte };
278569e52a80SHarald Welte 
2786f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec)
2787f7278fd0SJosepch Chan {
2788f7278fd0SJosepch Chan 	struct via_spec *spec = codec->spec;
2789f7278fd0SJosepch Chan 	int err;
2790f7278fd0SJosepch Chan 
2791f7278fd0SJosepch Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2792f7278fd0SJosepch Chan 	if (err < 0)
2793f7278fd0SJosepch Chan 		return err;
27944a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
2795f7278fd0SJosepch Chan 	if (err < 0)
2796f7278fd0SJosepch Chan 		return err;
2797f7278fd0SJosepch Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
2798f7278fd0SJosepch Chan 		return 0; /* can't find valid BIOS pin config */
2799f7278fd0SJosepch Chan 
28004a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2801f7278fd0SJosepch Chan 	if (err < 0)
2802f7278fd0SJosepch Chan 		return err;
28034a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2804f7278fd0SJosepch Chan 	if (err < 0)
2805f7278fd0SJosepch Chan 		return err;
2806620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2807f7278fd0SJosepch Chan 	if (err < 0)
2808f7278fd0SJosepch Chan 		return err;
2809f7278fd0SJosepch Chan 
2810f7278fd0SJosepch Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2811f7278fd0SJosepch Chan 
28120852d7a6STakashi Iwai 	if (spec->autocfg.dig_outs)
2813f7278fd0SJosepch Chan 		spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID;
281455d1d6c1STakashi Iwai 	spec->dig_in_pin = VT1708B_DIGIN_PIN;
2815f7278fd0SJosepch Chan 	if (spec->autocfg.dig_in_pin)
2816f7278fd0SJosepch Chan 		spec->dig_in_nid = VT1708B_DIGIN_NID;
2817f7278fd0SJosepch Chan 
2818603c4019STakashi Iwai 	if (spec->kctls.list)
2819603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2820f7278fd0SJosepch Chan 
28210aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
28220aa62aefSHarald Welte 
2823f8fdd495SHarald Welte 	if (spec->hp_mux)
28243d83e577STakashi Iwai 		via_hp_build(codec);
2825f7278fd0SJosepch Chan 
2826f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2827f4a7828bSTakashi Iwai 	if (err < 0)
2828f4a7828bSTakashi Iwai 		return err;
2829f4a7828bSTakashi Iwai 
2830f7278fd0SJosepch Chan 	return 1;
2831f7278fd0SJosepch Chan }
2832f7278fd0SJosepch Chan 
2833f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
283490dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = {
2835f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
2836f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
2837f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
2838f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
2839f7278fd0SJosepch Chan 	{ } /* end */
2840f7278fd0SJosepch Chan };
2841f7278fd0SJosepch Chan #endif
28423e95b9abSLydia Wang 
28433e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
28443e95b9abSLydia Wang {
28453e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
28463e95b9abSLydia Wang 	int imux_is_smixer;
28473e95b9abSLydia Wang 	unsigned int parm;
28483e95b9abSLydia Wang 	int is_8ch = 0;
2849bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2850bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
28513e95b9abSLydia Wang 		is_8ch = 1;
28523e95b9abSLydia Wang 
28533e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
28543e95b9abSLydia Wang 	imux_is_smixer =
28553e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
28563e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
28573e95b9abSLydia Wang 	/* inputs */
28583e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
28593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
28613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
28623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
28633e95b9abSLydia Wang 	if (imux_is_smixer)
28643e95b9abSLydia Wang 		parm = AC_PWRST_D0;
28653e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
28663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
28673e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
28683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
28693e95b9abSLydia Wang 
28703e95b9abSLydia Wang 	/* outputs */
28713e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
28723e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28733e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
28743e95b9abSLydia Wang 	if (spec->smart51_enabled)
28753e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
28763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
28773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
28783e95b9abSLydia Wang 
28793e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
28803e95b9abSLydia Wang 	if (is_8ch) {
28813e95b9abSLydia Wang 		parm = AC_PWRST_D3;
28823e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
28833e95b9abSLydia Wang 		if (spec->smart51_enabled)
28843e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
28853e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
28863e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28873e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
28883e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2889bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2890bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2891bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2892bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2893bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2894bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2895bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2896bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2897bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2898bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28993e95b9abSLydia Wang 	}
29003e95b9abSLydia Wang 
29013e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
29023e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29033e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
29043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
29053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
29063e95b9abSLydia Wang 	if (is_8ch)
29073e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
29083e95b9abSLydia Wang 
29093e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
29103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
29113e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
29123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
29133e95b9abSLydia Wang 	if (is_8ch) {
29143e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
29153e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29163e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
29173e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2918bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2919bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2920bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29213e95b9abSLydia Wang }
29223e95b9abSLydia Wang 
2923518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2924f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
2925f7278fd0SJosepch Chan {
2926f7278fd0SJosepch Chan 	struct via_spec *spec;
2927f7278fd0SJosepch Chan 	int err;
2928f7278fd0SJosepch Chan 
2929518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2930518bf3baSLydia Wang 		return patch_vt1708S(codec);
2931f7278fd0SJosepch Chan 	/* create a codec specific record */
29325b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2933f7278fd0SJosepch Chan 	if (spec == NULL)
2934f7278fd0SJosepch Chan 		return -ENOMEM;
2935f7278fd0SJosepch Chan 
2936620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2937620e2b28STakashi Iwai 
2938f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
2939f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
2940f7278fd0SJosepch Chan 	if (err < 0) {
2941f7278fd0SJosepch Chan 		via_free(codec);
2942f7278fd0SJosepch Chan 		return err;
2943f7278fd0SJosepch Chan 	} else if (!err) {
2944f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2945f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
2946f7278fd0SJosepch Chan 	}
2947f7278fd0SJosepch Chan 
294869e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs;
294969e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
2950f7278fd0SJosepch Chan 
2951f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2952f7278fd0SJosepch Chan 
2953f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
295469e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2955f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2956f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2957f7278fd0SJosepch Chan #endif
2958f7278fd0SJosepch Chan 
29593e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
29603e95b9abSLydia Wang 
2961f7278fd0SJosepch Chan 	return 0;
2962f7278fd0SJosepch Chan }
2963f7278fd0SJosepch Chan 
2964f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
2965f7278fd0SJosepch Chan {
2966f7278fd0SJosepch Chan 	struct via_spec *spec;
2967f7278fd0SJosepch Chan 	int err;
2968f7278fd0SJosepch Chan 
2969f7278fd0SJosepch Chan 	/* create a codec specific record */
29705b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2971f7278fd0SJosepch Chan 	if (spec == NULL)
2972f7278fd0SJosepch Chan 		return -ENOMEM;
2973f7278fd0SJosepch Chan 
2974f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
2975f7278fd0SJosepch Chan 	err = vt1708B_parse_auto_config(codec);
2976f7278fd0SJosepch Chan 	if (err < 0) {
2977f7278fd0SJosepch Chan 		via_free(codec);
2978f7278fd0SJosepch Chan 		return err;
2979f7278fd0SJosepch Chan 	} else if (!err) {
2980f7278fd0SJosepch Chan 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
2981f7278fd0SJosepch Chan 		       "from BIOS.  Using genenic mode...\n");
2982f7278fd0SJosepch Chan 	}
2983f7278fd0SJosepch Chan 
298469e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs;
298569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs;
2986f7278fd0SJosepch Chan 
2987f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2988f7278fd0SJosepch Chan 
2989f7278fd0SJosepch Chan 	codec->patch_ops.init = via_auto_init;
299069e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
2991f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2992f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2993f7278fd0SJosepch Chan #endif
2994c577b8a1SJoseph Chan 
29953e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
29963e95b9abSLydia Wang 
2997c577b8a1SJoseph Chan 	return 0;
2998c577b8a1SJoseph Chan }
2999c577b8a1SJoseph Chan 
3000d949cac1SHarald Welte /* Patch for VT1708S */
3001d949cac1SHarald Welte 
300290dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = {
3003d949cac1SHarald Welte 	/* Unmute ADC0-1 and set the default input to mic-in */
3004d949cac1SHarald Welte 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3005d949cac1SHarald Welte 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3006d949cac1SHarald Welte 
3007d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the
3008d949cac1SHarald Welte 	 * analog-loopback mixer widget */
3009d949cac1SHarald Welte 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3010d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3011d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3012d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3013d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3014d949cac1SHarald Welte 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)},
3015d949cac1SHarald Welte 
3016d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3017d949cac1SHarald Welte 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
30185691ec7fSHarald Welte 	/* PW9, PW10  Output enable */
3019d949cac1SHarald Welte 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
30205691ec7fSHarald Welte 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3021d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3022d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3023bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3024bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3025d949cac1SHarald Welte 	{ }
3026d949cac1SHarald Welte };
3027d949cac1SHarald Welte 
302890dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = {
3029a34df19aSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3030a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3031a34df19aSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3032a34df19aSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3033a34df19aSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3034a34df19aSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3035a34df19aSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3036a34df19aSLydia Wang 	{0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3037a34df19aSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
303869e52a80SHarald Welte 	{ }
303969e52a80SHarald Welte };
304069e52a80SHarald Welte 
304190dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = {
3042bc92df7fSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3043bc92df7fSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3044bc92df7fSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3045bc92df7fSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3046bc92df7fSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3047bc92df7fSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3048bc92df7fSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3049bc92df7fSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3050bc92df7fSLydia Wang 	{ }
3051bc92df7fSLydia Wang };
3052bc92df7fSLydia Wang 
30539da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
30549da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
30559da29271STakashi Iwai {
30569da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
30579da29271STakashi Iwai 	int i;
30589da29271STakashi Iwai 
30599da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
30609da29271STakashi Iwai 		hda_nid_t nid;
30619da29271STakashi Iwai 		int conn;
30629da29271STakashi Iwai 
30639da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
30649da29271STakashi Iwai 		if (!nid)
30659da29271STakashi Iwai 			continue;
30669da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
30679da29271STakashi Iwai 		if (conn < 1)
30689da29271STakashi Iwai 			continue;
30699da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
30709da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
30719da29271STakashi Iwai 		else {
30729da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
30739da29271STakashi Iwai 			break; /* at most two dig outs */
30749da29271STakashi Iwai 		}
30759da29271STakashi Iwai 	}
30769da29271STakashi Iwai }
30779da29271STakashi Iwai 
3078d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec)
3079d949cac1SHarald Welte {
3080d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3081d949cac1SHarald Welte 	int err;
3082d949cac1SHarald Welte 
30839da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3084d949cac1SHarald Welte 	if (err < 0)
3085d949cac1SHarald Welte 		return err;
30864a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3087d949cac1SHarald Welte 	if (err < 0)
3088d949cac1SHarald Welte 		return err;
3089d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3090d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3091d949cac1SHarald Welte 
30924a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3093d949cac1SHarald Welte 	if (err < 0)
3094d949cac1SHarald Welte 		return err;
30954a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3096d949cac1SHarald Welte 	if (err < 0)
3097d949cac1SHarald Welte 		return err;
3098620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3099d949cac1SHarald Welte 	if (err < 0)
3100d949cac1SHarald Welte 		return err;
3101d949cac1SHarald Welte 
3102d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3103d949cac1SHarald Welte 
31049da29271STakashi Iwai 	fill_dig_outs(codec);
310598aa34c0SHarald Welte 
3106603c4019STakashi Iwai 	if (spec->kctls.list)
3107603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3108d949cac1SHarald Welte 
31090aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
31100aa62aefSHarald Welte 
3111f8fdd495SHarald Welte 	if (spec->hp_mux)
31123d83e577STakashi Iwai 		via_hp_build(codec);
3113d949cac1SHarald Welte 
3114f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3115f4a7828bSTakashi Iwai 	if (err < 0)
3116f4a7828bSTakashi Iwai 		return err;
3117f4a7828bSTakashi Iwai 
3118d949cac1SHarald Welte 	return 1;
3119d949cac1SHarald Welte }
3120d949cac1SHarald Welte 
3121d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
312290dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = {
3123d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
3124d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
3125d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
3126d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
3127d949cac1SHarald Welte 	{ } /* end */
3128d949cac1SHarald Welte };
3129d949cac1SHarald Welte #endif
3130d949cac1SHarald Welte 
31316369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
31326369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
31336369bcfcSLydia Wang {
31346369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
31356369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
31366369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
31376369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
31386369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
31396369bcfcSLydia Wang }
31406369bcfcSLydia Wang 
3141d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3142d949cac1SHarald Welte {
3143d949cac1SHarald Welte 	struct via_spec *spec;
3144d949cac1SHarald Welte 	int err;
3145d949cac1SHarald Welte 
3146d949cac1SHarald Welte 	/* create a codec specific record */
31475b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3148d949cac1SHarald Welte 	if (spec == NULL)
3149d949cac1SHarald Welte 		return -ENOMEM;
3150d949cac1SHarald Welte 
3151620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3152*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3153*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3154620e2b28STakashi Iwai 
3155d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3156d949cac1SHarald Welte 	err = vt1708S_parse_auto_config(codec);
3157d949cac1SHarald Welte 	if (err < 0) {
3158d949cac1SHarald Welte 		via_free(codec);
3159d949cac1SHarald Welte 		return err;
3160d949cac1SHarald Welte 	} else if (!err) {
3161d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3162d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3163d949cac1SHarald Welte 	}
3164d949cac1SHarald Welte 
316569e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs;
3166bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)
3167bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3168bc92df7fSLydia Wang 			vt1705_uniwill_init_verbs;
3169bc92df7fSLydia Wang 	else
3170bc92df7fSLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
3171bc92df7fSLydia Wang 			vt1708S_uniwill_init_verbs;
3172d949cac1SHarald Welte 
3173d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3174d949cac1SHarald Welte 
3175d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
317669e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3177d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3178d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
3179d949cac1SHarald Welte #endif
3180d949cac1SHarald Welte 
3181518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3182518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3183518bf3baSLydia Wang 		kfree(codec->chip_name);
3184518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3185518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3186518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3187518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3188970f630fSLydia Wang 	}
3189bc92df7fSLydia Wang 	/* correct names for VT1705 */
3190bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3191bc92df7fSLydia Wang 		kfree(codec->chip_name);
3192bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3193bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3194bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3195bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3196bc92df7fSLydia Wang 	}
31973e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3198d949cac1SHarald Welte 	return 0;
3199d949cac1SHarald Welte }
3200d949cac1SHarald Welte 
3201d949cac1SHarald Welte /* Patch for VT1702 */
3202d949cac1SHarald Welte 
320390dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = {
3204d949cac1SHarald Welte 	/*
3205d949cac1SHarald Welte 	 * Unmute ADC0-1 and set the default input to mic-in
3206d949cac1SHarald Welte 	 */
3207d949cac1SHarald Welte 	{0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3208d949cac1SHarald Welte 	{0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3209d949cac1SHarald Welte 	{0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3210d949cac1SHarald Welte 
3211d949cac1SHarald Welte 
3212d949cac1SHarald Welte 	/* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3213d949cac1SHarald Welte 	 * mixer widget
3214d949cac1SHarald Welte 	 */
3215d949cac1SHarald Welte 	/* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */
3216d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3217d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3218d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)},
3219d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)},
3220d949cac1SHarald Welte 	{0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3221d949cac1SHarald Welte 
3222d949cac1SHarald Welte 	/* Setup default input of PW4 to MW0 */
3223d949cac1SHarald Welte 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x1},
3224d949cac1SHarald Welte 	/* PW6 PW7 Output enable */
3225d949cac1SHarald Welte 	{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3226d949cac1SHarald Welte 	{0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3227bc7e7e5cSLydia Wang 	/* mixer enable */
3228bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3229bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3230bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3231d949cac1SHarald Welte 	{ }
3232d949cac1SHarald Welte };
3233d949cac1SHarald Welte 
323490dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = {
3235a34df19aSLydia Wang 	{0x17, AC_VERB_SET_UNSOLICITED_ENABLE,
3236a34df19aSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3237a34df19aSLydia Wang 	{0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3238a34df19aSLydia Wang 	{0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3239a34df19aSLydia Wang 	{0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3240a34df19aSLydia Wang 	{0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
324169e52a80SHarald Welte 	{ }
324269e52a80SHarald Welte };
324369e52a80SHarald Welte 
3244d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec)
3245d949cac1SHarald Welte {
3246d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
3247d949cac1SHarald Welte 	int err;
3248d949cac1SHarald Welte 
32499da29271STakashi Iwai 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3250d949cac1SHarald Welte 	if (err < 0)
3251d949cac1SHarald Welte 		return err;
32524a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3253d949cac1SHarald Welte 	if (err < 0)
3254d949cac1SHarald Welte 		return err;
3255d949cac1SHarald Welte 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3256d949cac1SHarald Welte 		return 0; /* can't find valid BIOS pin config */
3257d949cac1SHarald Welte 
32584a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3259d949cac1SHarald Welte 	if (err < 0)
3260d949cac1SHarald Welte 		return err;
32614a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3262d949cac1SHarald Welte 	if (err < 0)
3263d949cac1SHarald Welte 		return err;
3264c2c02ea3SLydia Wang 	/* limit AA path volume to 0 dB */
3265c2c02ea3SLydia Wang 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3266c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3267c2c02ea3SLydia Wang 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3268c2c02ea3SLydia Wang 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3269c2c02ea3SLydia Wang 				  (1 << AC_AMPCAP_MUTE_SHIFT));
3270620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3271d949cac1SHarald Welte 	if (err < 0)
3272d949cac1SHarald Welte 		return err;
3273d949cac1SHarald Welte 
3274d949cac1SHarald Welte 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3275d949cac1SHarald Welte 
32769da29271STakashi Iwai 	fill_dig_outs(codec);
327798aa34c0SHarald Welte 
3278603c4019STakashi Iwai 	if (spec->kctls.list)
3279603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3280d949cac1SHarald Welte 
32810aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
32820aa62aefSHarald Welte 
3283f8fdd495SHarald Welte 	if (spec->hp_mux)
32843d83e577STakashi Iwai 		via_hp_build(codec);
3285d949cac1SHarald Welte 
3286d949cac1SHarald Welte 	return 1;
3287d949cac1SHarald Welte }
3288d949cac1SHarald Welte 
3289d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
329090dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = {
3291d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
3292d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
3293d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
3294d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
3295d949cac1SHarald Welte 	{ } /* end */
3296d949cac1SHarald Welte };
3297d949cac1SHarald Welte #endif
3298d949cac1SHarald Welte 
32993e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
33003e95b9abSLydia Wang {
33013e95b9abSLydia Wang 	int imux_is_smixer =
33023e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
33033e95b9abSLydia Wang 	unsigned int parm;
33043e95b9abSLydia Wang 	/* inputs */
33053e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
33063e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
33083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
33093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
33103e95b9abSLydia Wang 	if (imux_is_smixer)
33113e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
33123e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
33133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
33143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
33153e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
33163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
33173e95b9abSLydia Wang 
33183e95b9abSLydia Wang 	/* outputs */
33193e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
33203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
33223e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
33233e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
33243e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
33253e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
33263e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
33273e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
33283e95b9abSLydia Wang }
33293e95b9abSLydia Wang 
3330d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
3331d949cac1SHarald Welte {
3332d949cac1SHarald Welte 	struct via_spec *spec;
3333d949cac1SHarald Welte 	int err;
3334d949cac1SHarald Welte 
3335d949cac1SHarald Welte 	/* create a codec specific record */
33365b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3337d949cac1SHarald Welte 	if (spec == NULL)
3338d949cac1SHarald Welte 		return -ENOMEM;
3339d949cac1SHarald Welte 
3340620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
3341620e2b28STakashi Iwai 
3342d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
3343d949cac1SHarald Welte 	err = vt1702_parse_auto_config(codec);
3344d949cac1SHarald Welte 	if (err < 0) {
3345d949cac1SHarald Welte 		via_free(codec);
3346d949cac1SHarald Welte 		return err;
3347d949cac1SHarald Welte 	} else if (!err) {
3348d949cac1SHarald Welte 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3349d949cac1SHarald Welte 		       "from BIOS.  Using genenic mode...\n");
3350d949cac1SHarald Welte 	}
3351d949cac1SHarald Welte 
335269e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs;
335369e52a80SHarald Welte 	spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs;
3354d949cac1SHarald Welte 
3355d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3356d949cac1SHarald Welte 
3357d949cac1SHarald Welte 	codec->patch_ops.init = via_auto_init;
335869e52a80SHarald Welte 	codec->patch_ops.unsol_event = via_unsol_event;
3359d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
3360d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
3361d949cac1SHarald Welte #endif
3362d949cac1SHarald Welte 
33633e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
3364d949cac1SHarald Welte 	return 0;
3365d949cac1SHarald Welte }
3366d949cac1SHarald Welte 
3367eb7188caSLydia Wang /* Patch for VT1718S */
3368eb7188caSLydia Wang 
336990dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = {
3370eb7188caSLydia Wang 	/*
3371eb7188caSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3372eb7188caSLydia Wang 	 */
3373eb7188caSLydia Wang 	{0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3374eb7188caSLydia Wang 	{0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3375eb7188caSLydia Wang 
33764ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
33774ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
3378eb7188caSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3379eb7188caSLydia Wang 	 * mixer widget
3380eb7188caSLydia Wang 	 */
3381eb7188caSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3382eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3383eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3384eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3385eb7188caSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
33864ab2d53aSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)},
3387eb7188caSLydia Wang 
3388eb7188caSLydia Wang 	/* Setup default input of Front HP to MW9 */
3389eb7188caSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3390eb7188caSLydia Wang 	/* PW9 PW10 Output enable */
3391eb7188caSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3392eb7188caSLydia Wang 	{0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
3393eb7188caSLydia Wang 	/* PW11 Input enable */
3394eb7188caSLydia Wang 	{0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN},
3395eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
3396eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
3397eb7188caSLydia Wang 	/* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */
3398eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3399eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3400eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3401eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3402eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3403eb7188caSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3404eb7188caSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3405eb7188caSLydia Wang 	{0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3406eb7188caSLydia Wang 	{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
3407eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3408eb7188caSLydia Wang 	/* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */
3409eb7188caSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0x2},
3410eb7188caSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0x1},
3411eb7188caSLydia Wang 	/* Unmute MW4's index 0 */
3412eb7188caSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3413eb7188caSLydia Wang 	{ }
3414eb7188caSLydia Wang };
3415eb7188caSLydia Wang 
3416eb7188caSLydia Wang 
341790dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = {
3418eb7188caSLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
3419eb7188caSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3420eb7188caSLydia Wang 	{0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3421eb7188caSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3422eb7188caSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3423eb7188caSLydia Wang 	{0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3424eb7188caSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3425eb7188caSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3426eb7188caSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3427eb7188caSLydia Wang 	{ }
3428eb7188caSLydia Wang };
3429eb7188caSLydia Wang 
3430eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec)
3431eb7188caSLydia Wang {
3432eb7188caSLydia Wang 	struct via_spec *spec = codec->spec;
3433eb7188caSLydia Wang 	int err;
3434eb7188caSLydia Wang 
3435eb7188caSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3436eb7188caSLydia Wang 
3437eb7188caSLydia Wang 	if (err < 0)
3438eb7188caSLydia Wang 		return err;
34394a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3440eb7188caSLydia Wang 	if (err < 0)
3441eb7188caSLydia Wang 		return err;
3442eb7188caSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3443eb7188caSLydia Wang 		return 0; /* can't find valid BIOS pin config */
3444eb7188caSLydia Wang 
34454a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3446eb7188caSLydia Wang 	if (err < 0)
3447eb7188caSLydia Wang 		return err;
34484a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3449eb7188caSLydia Wang 	if (err < 0)
3450eb7188caSLydia Wang 		return err;
3451620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3452eb7188caSLydia Wang 	if (err < 0)
3453eb7188caSLydia Wang 		return err;
3454eb7188caSLydia Wang 
3455eb7188caSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3456eb7188caSLydia Wang 
3457eb7188caSLydia Wang 	fill_dig_outs(codec);
3458eb7188caSLydia Wang 
3459eb7188caSLydia Wang 	if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428)
3460eb7188caSLydia Wang 		spec->dig_in_nid = 0x13;
3461eb7188caSLydia Wang 
3462eb7188caSLydia Wang 	if (spec->kctls.list)
3463eb7188caSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3464eb7188caSLydia Wang 
3465eb7188caSLydia Wang 	spec->input_mux = &spec->private_imux[0];
3466eb7188caSLydia Wang 
3467eb7188caSLydia Wang 	if (spec->hp_mux)
34683d83e577STakashi Iwai 		via_hp_build(codec);
3469eb7188caSLydia Wang 
3470f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3471f4a7828bSTakashi Iwai 	if (err < 0)
3472f4a7828bSTakashi Iwai 		return err;
3473eb7188caSLydia Wang 
3474eb7188caSLydia Wang 	return 1;
3475eb7188caSLydia Wang }
3476eb7188caSLydia Wang 
3477eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
347890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = {
3479eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
3480eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
3481eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
3482eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
3483eb7188caSLydia Wang 	{ } /* end */
3484eb7188caSLydia Wang };
3485eb7188caSLydia Wang #endif
3486eb7188caSLydia Wang 
34873e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
34883e95b9abSLydia Wang {
34893e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
34903e95b9abSLydia Wang 	int imux_is_smixer;
34913e95b9abSLydia Wang 	unsigned int parm;
34923e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
34933e95b9abSLydia Wang 	imux_is_smixer =
34943e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
34953e95b9abSLydia Wang 	/* inputs */
34963e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
34973e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
34993e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
35003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
35013e95b9abSLydia Wang 	if (imux_is_smixer)
35023e95b9abSLydia Wang 		parm = AC_PWRST_D0;
35033e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
35043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
35053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
35063e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
35073e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
35083e95b9abSLydia Wang 
35093e95b9abSLydia Wang 	/* outputs */
35103e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
35113e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
35133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
35143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
35153e95b9abSLydia Wang 
35163e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
35173e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35183e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
35193e95b9abSLydia Wang 	if (spec->smart51_enabled)
35203e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
35213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
35223e95b9abSLydia Wang 
35233e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
35243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
35263e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
35273e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
35283e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
35293e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
35303e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
35313e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
35323e95b9abSLydia Wang 
35333e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
35343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
35363e95b9abSLydia Wang 	if (spec->smart51_enabled)
35373e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
35383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
35393e95b9abSLydia Wang 
35403e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
35413e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
35423e95b9abSLydia Wang 		parm = AC_PWRST_D3;
35433e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
35443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
35453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
35473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35483e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
35493e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35503e95b9abSLydia Wang 	}
35513e95b9abSLydia Wang }
35523e95b9abSLydia Wang 
3553eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
3554eb7188caSLydia Wang {
3555eb7188caSLydia Wang 	struct via_spec *spec;
3556eb7188caSLydia Wang 	int err;
3557eb7188caSLydia Wang 
3558eb7188caSLydia Wang 	/* create a codec specific record */
35595b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3560eb7188caSLydia Wang 	if (spec == NULL)
3561eb7188caSLydia Wang 		return -ENOMEM;
3562eb7188caSLydia Wang 
3563620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3564*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3565*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3566620e2b28STakashi Iwai 
3567eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
3568eb7188caSLydia Wang 	err = vt1718S_parse_auto_config(codec);
3569eb7188caSLydia Wang 	if (err < 0) {
3570eb7188caSLydia Wang 		via_free(codec);
3571eb7188caSLydia Wang 		return err;
3572eb7188caSLydia Wang 	} else if (!err) {
3573eb7188caSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3574eb7188caSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
3575eb7188caSLydia Wang 	}
3576eb7188caSLydia Wang 
3577eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs;
3578eb7188caSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs;
3579eb7188caSLydia Wang 
3580eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
3581eb7188caSLydia Wang 
3582eb7188caSLydia Wang 	codec->patch_ops.init = via_auto_init;
35830f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
3584eb7188caSLydia Wang 
3585eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3586eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
3587eb7188caSLydia Wang #endif
3588eb7188caSLydia Wang 
35893e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
35903e95b9abSLydia Wang 
3591eb7188caSLydia Wang 	return 0;
3592eb7188caSLydia Wang }
3593f3db423dSLydia Wang 
3594f3db423dSLydia Wang /* Patch for VT1716S */
3595f3db423dSLydia Wang 
3596f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3597f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
3598f3db423dSLydia Wang {
3599f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3600f3db423dSLydia Wang 	uinfo->count = 1;
3601f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
3602f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
3603f3db423dSLydia Wang 	return 0;
3604f3db423dSLydia Wang }
3605f3db423dSLydia Wang 
3606f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3607f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3608f3db423dSLydia Wang {
3609f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3610f3db423dSLydia Wang 	int index = 0;
3611f3db423dSLydia Wang 
3612f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
3613f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
3614f3db423dSLydia Wang 	if (index != -1)
3615f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
3616f3db423dSLydia Wang 
3617f3db423dSLydia Wang 	return 0;
3618f3db423dSLydia Wang }
3619f3db423dSLydia Wang 
3620f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3621f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3622f3db423dSLydia Wang {
3623f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3624f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3625f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
3626f3db423dSLydia Wang 
3627f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
3628f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
3629f3db423dSLydia Wang 	spec->dmic_enabled = index;
36303e95b9abSLydia Wang 	set_widgets_power_state(codec);
3631f3db423dSLydia Wang 	return 1;
3632f3db423dSLydia Wang }
3633f3db423dSLydia Wang 
363490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
3635f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3636f3db423dSLydia Wang 	{
3637f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3638f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
36395b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
3640f3db423dSLydia Wang 	 .count = 1,
3641f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
3642f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
3643f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
3644f3db423dSLydia Wang 	 },
3645f3db423dSLydia Wang 	{}			/* end */
3646f3db423dSLydia Wang };
3647f3db423dSLydia Wang 
3648f3db423dSLydia Wang 
3649f3db423dSLydia Wang /* mono-out mixer elements */
365090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3651f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3652f3db423dSLydia Wang 	{ } /* end */
3653f3db423dSLydia Wang };
3654f3db423dSLydia Wang 
365590dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = {
3656f3db423dSLydia Wang 	/*
3657f3db423dSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
3658f3db423dSLydia Wang 	 */
3659f3db423dSLydia Wang 	{0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3660f3db423dSLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3661f3db423dSLydia Wang 
3662f3db423dSLydia Wang 
3663f3db423dSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
3664f3db423dSLydia Wang 	 * mixer widget
3665f3db423dSLydia Wang 	 */
3666f3db423dSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
3667f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
3668f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
3669f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
3670f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
3671f3db423dSLydia Wang 	{0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
3672f3db423dSLydia Wang 
3673f3db423dSLydia Wang 	/* MUX Indices: Stereo Mixer = 5 */
3674f3db423dSLydia Wang 	{0x17, AC_VERB_SET_CONNECT_SEL, 0x5},
3675f3db423dSLydia Wang 
3676f3db423dSLydia Wang 	/* Setup default input of PW4 to MW0 */
3677f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_CONNECT_SEL, 0x0},
3678f3db423dSLydia Wang 
3679f3db423dSLydia Wang 	/* Setup default input of SW1 as MW0 */
3680f3db423dSLydia Wang 	{0x18, AC_VERB_SET_CONNECT_SEL, 0x1},
3681f3db423dSLydia Wang 
3682f3db423dSLydia Wang 	/* Setup default input of SW4 as AOW0 */
3683f3db423dSLydia Wang 	{0x28, AC_VERB_SET_CONNECT_SEL, 0x1},
3684f3db423dSLydia Wang 
3685f3db423dSLydia Wang 	/* PW9 PW10 Output enable */
3686f3db423dSLydia Wang 	{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3687f3db423dSLydia Wang 	{0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3688f3db423dSLydia Wang 
3689f3db423dSLydia Wang 	/* Unmute SW1, PW12 */
3690f3db423dSLydia Wang 	{0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
3691f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
3692f3db423dSLydia Wang 	/* PW12 Output enable */
3693f3db423dSLydia Wang 	{0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40},
3694f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
3695f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
3696f3db423dSLydia Wang 	/* don't bybass mixer */
3697f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
3698f3db423dSLydia Wang 	/* Enable mono output */
3699f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
3700f3db423dSLydia Wang 	{ }
3701f3db423dSLydia Wang };
3702f3db423dSLydia Wang 
3703f3db423dSLydia Wang 
370490dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = {
3705f3db423dSLydia Wang 	{0x1d, AC_VERB_SET_UNSOLICITED_ENABLE,
3706f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT},
3707f3db423dSLydia Wang 	{0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3708f3db423dSLydia Wang 	{0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3709f3db423dSLydia Wang 	{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3710f3db423dSLydia Wang 	{0x1c, AC_VERB_SET_UNSOLICITED_ENABLE,
3711f3db423dSLydia Wang 	 AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT},
3712f3db423dSLydia Wang 	{0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3713f3db423dSLydia Wang 	{0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
3714f3db423dSLydia Wang 	{ }
3715f3db423dSLydia Wang };
3716f3db423dSLydia Wang 
3717f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec)
3718f3db423dSLydia Wang {
3719f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3720f3db423dSLydia Wang 	int err;
3721f3db423dSLydia Wang 
3722f3db423dSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
3723f3db423dSLydia Wang 	if (err < 0)
3724f3db423dSLydia Wang 		return err;
37254a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
3726f3db423dSLydia Wang 	if (err < 0)
3727f3db423dSLydia Wang 		return err;
3728f3db423dSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
3729f3db423dSLydia Wang 		return 0; /* can't find valid BIOS pin config */
3730f3db423dSLydia Wang 
37314a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
3732f3db423dSLydia Wang 	if (err < 0)
3733f3db423dSLydia Wang 		return err;
37344a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
3735f3db423dSLydia Wang 	if (err < 0)
3736f3db423dSLydia Wang 		return err;
3737620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
3738f3db423dSLydia Wang 	if (err < 0)
3739f3db423dSLydia Wang 		return err;
3740f3db423dSLydia Wang 
3741f3db423dSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
3742f3db423dSLydia Wang 
3743f3db423dSLydia Wang 	fill_dig_outs(codec);
3744f3db423dSLydia Wang 
3745f3db423dSLydia Wang 	if (spec->kctls.list)
3746f3db423dSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
3747f3db423dSLydia Wang 
3748f3db423dSLydia Wang 	spec->input_mux = &spec->private_imux[0];
3749f3db423dSLydia Wang 
3750f3db423dSLydia Wang 	if (spec->hp_mux)
37513d83e577STakashi Iwai 		via_hp_build(codec);
3752f3db423dSLydia Wang 
3753f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
3754f4a7828bSTakashi Iwai 	if (err < 0)
3755f4a7828bSTakashi Iwai 		return err;
3756f3db423dSLydia Wang 
3757f3db423dSLydia Wang 	return 1;
3758f3db423dSLydia Wang }
3759f3db423dSLydia Wang 
3760f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
376190dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = {
3762f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
3763f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
3764f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
3765f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
3766f3db423dSLydia Wang 	{ } /* end */
3767f3db423dSLydia Wang };
3768f3db423dSLydia Wang #endif
3769f3db423dSLydia Wang 
37703e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
37713e95b9abSLydia Wang {
37723e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
37733e95b9abSLydia Wang 	int imux_is_smixer;
37743e95b9abSLydia Wang 	unsigned int parm;
37753e95b9abSLydia Wang 	unsigned int mono_out, present;
37763e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
37773e95b9abSLydia Wang 	imux_is_smixer =
37783e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
37793e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
37803e95b9abSLydia Wang 	/* inputs */
37813e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
37823e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
37843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
37853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
37863e95b9abSLydia Wang 	if (imux_is_smixer)
37873e95b9abSLydia Wang 		parm = AC_PWRST_D0;
37883e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
37893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
37903e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
37913e95b9abSLydia Wang 
37923e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
37943e95b9abSLydia Wang 	/* PW11 (22h) */
37953e95b9abSLydia Wang 	if (spec->dmic_enabled)
37963e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
37973e95b9abSLydia Wang 	else
37983e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
37993e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
38003e95b9abSLydia Wang 
38013e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
38023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
38033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
38043e95b9abSLydia Wang 
38053e95b9abSLydia Wang 	/* outputs */
38063e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
38073e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
38093e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
38103e95b9abSLydia Wang 	if (spec->smart51_enabled)
38113e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
38123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 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 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
38163e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38173e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
38183e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
38193e95b9abSLydia Wang 	if (spec->smart51_enabled)
38203e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
38213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
38223e95b9abSLydia Wang 
38233e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
38243e95b9abSLydia Wang 	if (spec->smart51_enabled)
38253e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
38263e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
38273e95b9abSLydia Wang 
38283e95b9abSLydia Wang 	/* Mono out */
38293e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
38303e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
38313e95b9abSLydia Wang 
38323e95b9abSLydia Wang 	if (present)
38333e95b9abSLydia Wang 		mono_out = 0;
38343e95b9abSLydia Wang 	else {
38353e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
38363e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
38373e95b9abSLydia Wang 			mono_out = 0;
38383e95b9abSLydia Wang 		else
38393e95b9abSLydia Wang 			mono_out = 1;
38403e95b9abSLydia Wang 	}
38413e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
38423e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
38433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
38443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
38453e95b9abSLydia Wang 
38463e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
38473e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
38493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
38503e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
38513e95b9abSLydia Wang 	if (spec->hp_independent_mode)
38523e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
38533e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
38543e95b9abSLydia Wang 
38553e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
38563e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
38573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
38583e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
38593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
38603e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
38613e95b9abSLydia Wang }
38623e95b9abSLydia Wang 
3863f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3864f3db423dSLydia Wang {
3865f3db423dSLydia Wang 	struct via_spec *spec;
3866f3db423dSLydia Wang 	int err;
3867f3db423dSLydia Wang 
3868f3db423dSLydia Wang 	/* create a codec specific record */
38695b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3870f3db423dSLydia Wang 	if (spec == NULL)
3871f3db423dSLydia Wang 		return -ENOMEM;
3872f3db423dSLydia Wang 
3873620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3874*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3875*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3876620e2b28STakashi Iwai 
3877f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
3878f3db423dSLydia Wang 	err = vt1716S_parse_auto_config(codec);
3879f3db423dSLydia Wang 	if (err < 0) {
3880f3db423dSLydia Wang 		via_free(codec);
3881f3db423dSLydia Wang 		return err;
3882f3db423dSLydia Wang 	} else if (!err) {
3883f3db423dSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
3884f3db423dSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
3885f3db423dSLydia Wang 	}
3886f3db423dSLydia Wang 
3887f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_volume_init_verbs;
3888f3db423dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs;
3889f3db423dSLydia Wang 
3890f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3891f3db423dSLydia Wang 	spec->num_mixers++;
3892f3db423dSLydia Wang 
3893f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3894f3db423dSLydia Wang 
3895f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3896f3db423dSLydia Wang 
3897f3db423dSLydia Wang 	codec->patch_ops.init = via_auto_init;
38980f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
3899f3db423dSLydia Wang 
3900f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3901f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
3902f3db423dSLydia Wang #endif
3903f3db423dSLydia Wang 
39043e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
3905f3db423dSLydia Wang 	return 0;
3906f3db423dSLydia Wang }
390725eaba2fSLydia Wang 
390825eaba2fSLydia Wang /* for vt2002P */
390925eaba2fSLydia Wang 
391090dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = {
3911eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
3912eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
3913eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
3914eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
391525eaba2fSLydia Wang 	/*
391625eaba2fSLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
391725eaba2fSLydia Wang 	 */
391825eaba2fSLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
391925eaba2fSLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
392025eaba2fSLydia Wang 
392125eaba2fSLydia Wang 
392225eaba2fSLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
392325eaba2fSLydia Wang 	 * mixer widget
392425eaba2fSLydia Wang 	 */
392525eaba2fSLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
392625eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
392725eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
392825eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
392925eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
393025eaba2fSLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
393125eaba2fSLydia Wang 
393225eaba2fSLydia Wang 	/* MUX Indices: Mic = 0 */
393325eaba2fSLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
393425eaba2fSLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
393525eaba2fSLydia Wang 
393625eaba2fSLydia Wang 	/* PW9 Output enable */
393725eaba2fSLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
393825eaba2fSLydia Wang 
393925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
394025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
394125eaba2fSLydia Wang 
394225eaba2fSLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
394325eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
394425eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
394525eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
394625eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
394725eaba2fSLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
394825eaba2fSLydia Wang 	{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
394925eaba2fSLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
395025eaba2fSLydia Wang 	{0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
395125eaba2fSLydia Wang 
395225eaba2fSLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
395325eaba2fSLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
395425eaba2fSLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
395525eaba2fSLydia Wang 	{0x37, AC_VERB_SET_CONNECT_SEL, 0},
395625eaba2fSLydia Wang 	{0x3b, AC_VERB_SET_CONNECT_SEL, 0},
395725eaba2fSLydia Wang 
395825eaba2fSLydia Wang 	/* set PW0 index=0 (MW0) */
395925eaba2fSLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
396025eaba2fSLydia Wang 
396125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
396225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
396325eaba2fSLydia Wang 	{ }
396425eaba2fSLydia Wang };
396590dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = {
396611890956SLydia Wang 	/*
396711890956SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
396811890956SLydia Wang 	 */
396911890956SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
397011890956SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
397111890956SLydia Wang 
397211890956SLydia Wang 
397311890956SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
397411890956SLydia Wang 	 * mixer widget
397511890956SLydia Wang 	 */
397611890956SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
397711890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
397811890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
397911890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
398011890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
398111890956SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
398211890956SLydia Wang 
398311890956SLydia Wang 	/* MUX Indices: Mic = 0 */
398411890956SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
398511890956SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
398611890956SLydia Wang 
398711890956SLydia Wang 	/* PW9 Output enable */
398811890956SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
398911890956SLydia Wang 
399011890956SLydia Wang 	/* Enable Boost Volume backdoor */
399111890956SLydia Wang 	{0x1, 0xfb9, 0x24},
399211890956SLydia Wang 
399311890956SLydia Wang 	/* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
399411890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
399511890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
399611890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
399711890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
399811890956SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
399911890956SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
400011890956SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
400111890956SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
400211890956SLydia Wang 
400311890956SLydia Wang 	/* set MUX0/1/4/8 = 0 (AOW0) */
400411890956SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
400511890956SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
400611890956SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
400711890956SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
400811890956SLydia Wang 
400911890956SLydia Wang 	/* set PW0 index=0 (MW0) */
401011890956SLydia Wang 	{0x24, AC_VERB_SET_CONNECT_SEL, 0},
401111890956SLydia Wang 
401211890956SLydia Wang 	/* Enable AOW0 to MW9 */
401311890956SLydia Wang 	{0x1, 0xfb8, 0x88},
401411890956SLydia Wang 	{ }
401511890956SLydia Wang };
401625eaba2fSLydia Wang 
401725eaba2fSLydia Wang 
401890dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = {
401925eaba2fSLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
402025eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
402125eaba2fSLydia Wang 	{0x26, AC_VERB_SET_UNSOLICITED_ENABLE,
402225eaba2fSLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
402325eaba2fSLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
402425eaba2fSLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
402525eaba2fSLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
402625eaba2fSLydia Wang 	{ }
402725eaba2fSLydia Wang };
402890dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = {
402911890956SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE,
403011890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
403111890956SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
403211890956SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
403311890956SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
403411890956SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
403511890956SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
403611890956SLydia Wang 	{ }
403711890956SLydia Wang };
403825eaba2fSLydia Wang 
403925eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec)
404025eaba2fSLydia Wang {
404125eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
404225eaba2fSLydia Wang 	int err;
404325eaba2fSLydia Wang 
404425eaba2fSLydia Wang 
404525eaba2fSLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
404625eaba2fSLydia Wang 	if (err < 0)
404725eaba2fSLydia Wang 		return err;
404825eaba2fSLydia Wang 
40494a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
405025eaba2fSLydia Wang 	if (err < 0)
405125eaba2fSLydia Wang 		return err;
405225eaba2fSLydia Wang 
405325eaba2fSLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
405425eaba2fSLydia Wang 		return 0; /* can't find valid BIOS pin config */
405525eaba2fSLydia Wang 
40564a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
405725eaba2fSLydia Wang 	if (err < 0)
405825eaba2fSLydia Wang 		return err;
40594a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
406025eaba2fSLydia Wang 	if (err < 0)
406125eaba2fSLydia Wang 		return err;
4062620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
406325eaba2fSLydia Wang 	if (err < 0)
406425eaba2fSLydia Wang 		return err;
406525eaba2fSLydia Wang 
406625eaba2fSLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
406725eaba2fSLydia Wang 
406825eaba2fSLydia Wang 	fill_dig_outs(codec);
406925eaba2fSLydia Wang 
407025eaba2fSLydia Wang 	if (spec->kctls.list)
407125eaba2fSLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
407225eaba2fSLydia Wang 
407325eaba2fSLydia Wang 	spec->input_mux = &spec->private_imux[0];
407425eaba2fSLydia Wang 
407525eaba2fSLydia Wang 	if (spec->hp_mux)
40763d83e577STakashi Iwai 		via_hp_build(codec);
407725eaba2fSLydia Wang 
407825eaba2fSLydia Wang 	return 1;
407925eaba2fSLydia Wang }
408025eaba2fSLydia Wang 
408125eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
408290dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = {
408325eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
408425eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
408525eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
408625eaba2fSLydia Wang 	{ } /* end */
408725eaba2fSLydia Wang };
408825eaba2fSLydia Wang #endif
408925eaba2fSLydia Wang 
40903e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
40913e95b9abSLydia Wang {
40923e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
40933e95b9abSLydia Wang 	int imux_is_smixer;
40943e95b9abSLydia Wang 	unsigned int parm;
40953e95b9abSLydia Wang 	unsigned int present;
40963e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
40973e95b9abSLydia Wang 	imux_is_smixer =
40983e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
40993e95b9abSLydia Wang 	/* inputs */
41003e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
41013e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41023e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
41033e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
41043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
41053e95b9abSLydia Wang 	parm = AC_PWRST_D0;
41063e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
41073e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
41083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
41093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
41103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
41113e95b9abSLydia Wang 
41123e95b9abSLydia Wang 	/* outputs */
41133e95b9abSLydia Wang 	/* AOW0 (8h)*/
41143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
41153e95b9abSLydia Wang 
411611890956SLydia Wang 	if (spec->codec_type == VT1802) {
411711890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
411811890956SLydia Wang 		parm = AC_PWRST_D3;
411911890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
412011890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
412111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
412211890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
412311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
412411890956SLydia Wang 	} else {
41253e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
41263e95b9abSLydia Wang 		parm = AC_PWRST_D3;
41273e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
41283e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
41293e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
41303e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
41313e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
413211890956SLydia Wang 	}
41333e95b9abSLydia Wang 
413411890956SLydia Wang 	if (spec->codec_type == VT1802) {
413511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
413611890956SLydia Wang 		parm = AC_PWRST_D3;
413711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
413811890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
413911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
414011890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
414111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
414211890956SLydia Wang 	} else {
41433e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
41443e95b9abSLydia Wang 		parm = AC_PWRST_D3;
41453e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
41463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
41473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
41483e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
41493e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
415011890956SLydia Wang 	}
41513e95b9abSLydia Wang 
41523e95b9abSLydia Wang 	if (spec->hp_independent_mode)
41533e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
41543e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
41553e95b9abSLydia Wang 
41563e95b9abSLydia Wang 	/* Class-D */
41573e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
41583e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
41593e95b9abSLydia Wang 
41603e95b9abSLydia Wang 	parm = AC_PWRST_D3;
41613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
41623e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
416311890956SLydia Wang 	if (spec->codec_type == VT1802)
416411890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
416511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
416611890956SLydia Wang 	else
41673e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
41683e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
41693e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
41703e95b9abSLydia Wang 
41713e95b9abSLydia Wang 	/* Mono Out */
41723e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
41733e95b9abSLydia Wang 
41743e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
417511890956SLydia Wang 	if (spec->codec_type == VT1802) {
417611890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
417711890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
417811890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
417911890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
418011890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
418111890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
418211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
418311890956SLydia Wang 	} else {
41843e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
41853e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
41863e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
41873e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
41883e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
41893e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
41903e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
419111890956SLydia Wang 	}
41923e95b9abSLydia Wang 	/* MW9 (21h) */
41933e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
41943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
41953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
41963e95b9abSLydia Wang 	else
41973e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
41983e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
41993e95b9abSLydia Wang }
420025eaba2fSLydia Wang 
420125eaba2fSLydia Wang /* patch for vt2002P */
420225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
420325eaba2fSLydia Wang {
420425eaba2fSLydia Wang 	struct via_spec *spec;
420525eaba2fSLydia Wang 	int err;
420625eaba2fSLydia Wang 
420725eaba2fSLydia Wang 	/* create a codec specific record */
42085b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
420925eaba2fSLydia Wang 	if (spec == NULL)
421025eaba2fSLydia Wang 		return -ENOMEM;
421125eaba2fSLydia Wang 
4212620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4213*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
4214*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
4215620e2b28STakashi Iwai 
421625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
421725eaba2fSLydia Wang 	err = vt2002P_parse_auto_config(codec);
421825eaba2fSLydia Wang 	if (err < 0) {
421925eaba2fSLydia Wang 		via_free(codec);
422025eaba2fSLydia Wang 		return err;
422125eaba2fSLydia Wang 	} else if (!err) {
422225eaba2fSLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
422325eaba2fSLydia Wang 		       "from BIOS.  Using genenic mode...\n");
422425eaba2fSLydia Wang 	}
422525eaba2fSLydia Wang 
422611890956SLydia Wang 	if (spec->codec_type == VT1802)
422711890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
422811890956SLydia Wang 			vt1802_volume_init_verbs;
422911890956SLydia Wang 	else
423011890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++]  =
423111890956SLydia Wang 			vt2002P_volume_init_verbs;
423225eaba2fSLydia Wang 
423311890956SLydia Wang 	if (spec->codec_type == VT1802)
423411890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
423511890956SLydia Wang 			vt1802_uniwill_init_verbs;
423611890956SLydia Wang 	else
423711890956SLydia Wang 		spec->init_verbs[spec->num_iverbs++] =
423811890956SLydia Wang 			vt2002P_uniwill_init_verbs;
423911890956SLydia Wang 
424025eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
424125eaba2fSLydia Wang 
424225eaba2fSLydia Wang 	codec->patch_ops.init = via_auto_init;
42430f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
424425eaba2fSLydia Wang 
424525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
424625eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
424725eaba2fSLydia Wang #endif
424825eaba2fSLydia Wang 
42493e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
425025eaba2fSLydia Wang 	return 0;
425125eaba2fSLydia Wang }
4252ab6734e7SLydia Wang 
4253ab6734e7SLydia Wang /* for vt1812 */
4254ab6734e7SLydia Wang 
425590dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = {
4256ab6734e7SLydia Wang 	/*
4257ab6734e7SLydia Wang 	 * Unmute ADC0-1 and set the default input to mic-in
4258ab6734e7SLydia Wang 	 */
4259ab6734e7SLydia Wang 	{0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4260ab6734e7SLydia Wang 	{0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4261ab6734e7SLydia Wang 
4262ab6734e7SLydia Wang 
4263ab6734e7SLydia Wang 	/* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback
4264ab6734e7SLydia Wang 	 * mixer widget
4265ab6734e7SLydia Wang 	 */
4266ab6734e7SLydia Wang 	/* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */
4267ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
4268ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
4269ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
4270ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
4271ab6734e7SLydia Wang 	{0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
4272ab6734e7SLydia Wang 
4273ab6734e7SLydia Wang 	/* MUX Indices: Mic = 0 */
4274ab6734e7SLydia Wang 	{0x1e, AC_VERB_SET_CONNECT_SEL, 0},
4275ab6734e7SLydia Wang 	{0x1f, AC_VERB_SET_CONNECT_SEL, 0},
4276ab6734e7SLydia Wang 
4277ab6734e7SLydia Wang 	/* PW9 Output enable */
4278ab6734e7SLydia Wang 	{0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN},
4279ab6734e7SLydia Wang 
4280ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
4281ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
4282ab6734e7SLydia Wang 
4283ab6734e7SLydia Wang 	/* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */
4284ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4285ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4286ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4287ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4288ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
4289ab6734e7SLydia Wang 	{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4290ab6734e7SLydia Wang 	{0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4291ab6734e7SLydia Wang 	{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4292ab6734e7SLydia Wang 	{0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4293ab6734e7SLydia Wang 	{0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
4294ab6734e7SLydia Wang 
4295ab6734e7SLydia Wang 	/* set MUX0/1/4/13/15 = 0 (AOW0) */
4296ab6734e7SLydia Wang 	{0x34, AC_VERB_SET_CONNECT_SEL, 0},
4297ab6734e7SLydia Wang 	{0x35, AC_VERB_SET_CONNECT_SEL, 0},
4298ab6734e7SLydia Wang 	{0x38, AC_VERB_SET_CONNECT_SEL, 0},
4299ab6734e7SLydia Wang 	{0x3c, AC_VERB_SET_CONNECT_SEL, 0},
4300ab6734e7SLydia Wang 	{0x3d, AC_VERB_SET_CONNECT_SEL, 0},
4301ab6734e7SLydia Wang 
4302ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
4303ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
4304ab6734e7SLydia Wang 	{ }
4305ab6734e7SLydia Wang };
4306ab6734e7SLydia Wang 
4307ab6734e7SLydia Wang 
430890dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = {
4309ab6734e7SLydia Wang 	{0x33, AC_VERB_SET_UNSOLICITED_ENABLE,
4310ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4311ab6734e7SLydia Wang 	{0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT },
4312ab6734e7SLydia Wang 	{0x28, AC_VERB_SET_UNSOLICITED_ENABLE,
4313ab6734e7SLydia Wang 	 AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT},
4314ab6734e7SLydia Wang 	{0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4315ab6734e7SLydia Wang 	{0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4316ab6734e7SLydia Wang 	{0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT},
4317ab6734e7SLydia Wang 	{ }
4318ab6734e7SLydia Wang };
4319ab6734e7SLydia Wang 
4320ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec)
4321ab6734e7SLydia Wang {
4322ab6734e7SLydia Wang 	struct via_spec *spec = codec->spec;
4323ab6734e7SLydia Wang 	int err;
4324ab6734e7SLydia Wang 
4325ab6734e7SLydia Wang 
4326ab6734e7SLydia Wang 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
4327ab6734e7SLydia Wang 	if (err < 0)
4328ab6734e7SLydia Wang 		return err;
4329ab6734e7SLydia Wang 	fill_dig_outs(codec);
43304a79616dSTakashi Iwai 	err = via_auto_fill_dac_nids(codec);
4331ab6734e7SLydia Wang 	if (err < 0)
4332ab6734e7SLydia Wang 		return err;
4333ab6734e7SLydia Wang 
4334ab6734e7SLydia Wang 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs)
4335ab6734e7SLydia Wang 		return 0; /* can't find valid BIOS pin config */
4336ab6734e7SLydia Wang 
43374a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
4338ab6734e7SLydia Wang 	if (err < 0)
4339ab6734e7SLydia Wang 		return err;
43404a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
4341ab6734e7SLydia Wang 	if (err < 0)
4342ab6734e7SLydia Wang 		return err;
4343620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
4344ab6734e7SLydia Wang 	if (err < 0)
4345ab6734e7SLydia Wang 		return err;
4346ab6734e7SLydia Wang 
4347ab6734e7SLydia Wang 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
4348ab6734e7SLydia Wang 
4349ab6734e7SLydia Wang 	fill_dig_outs(codec);
4350ab6734e7SLydia Wang 
4351ab6734e7SLydia Wang 	if (spec->kctls.list)
4352ab6734e7SLydia Wang 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
4353ab6734e7SLydia Wang 
4354ab6734e7SLydia Wang 	spec->input_mux = &spec->private_imux[0];
4355ab6734e7SLydia Wang 
4356ab6734e7SLydia Wang 	if (spec->hp_mux)
43573d83e577STakashi Iwai 		via_hp_build(codec);
4358ab6734e7SLydia Wang 
4359ab6734e7SLydia Wang 	return 1;
4360ab6734e7SLydia Wang }
4361ab6734e7SLydia Wang 
4362ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
436390dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = {
4364ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
4365ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
4366ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
4367ab6734e7SLydia Wang 	{ } /* end */
4368ab6734e7SLydia Wang };
4369ab6734e7SLydia Wang #endif
4370ab6734e7SLydia Wang 
43713e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
43723e95b9abSLydia Wang {
43733e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
43743e95b9abSLydia Wang 	int imux_is_smixer =
43753e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
43763e95b9abSLydia Wang 	unsigned int parm;
43773e95b9abSLydia Wang 	unsigned int present;
43783e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
43793e95b9abSLydia Wang 	imux_is_smixer =
43803e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
43813e95b9abSLydia Wang 	/* inputs */
43823e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
43833e95b9abSLydia Wang 	parm = AC_PWRST_D3;
43843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
43853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
43863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
43873e95b9abSLydia Wang 	parm = AC_PWRST_D0;
43883e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
43893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
43903e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
43913e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
43923e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
43933e95b9abSLydia Wang 
43943e95b9abSLydia Wang 	/* outputs */
43953e95b9abSLydia Wang 	/* AOW0 (8h)*/
43963e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
43973e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
43983e95b9abSLydia Wang 
43993e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
44003e95b9abSLydia Wang 	parm = AC_PWRST_D3;
44013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
44023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
44033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
44043e95b9abSLydia Wang 
44053e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
44063e95b9abSLydia Wang 	parm = AC_PWRST_D3;
44073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
44083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
44093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
44103e95b9abSLydia Wang 	if (spec->hp_independent_mode)
44113e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
44123e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44133e95b9abSLydia Wang 
44143e95b9abSLydia Wang 	/* Internal Speaker */
44153e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
44163e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
44173e95b9abSLydia Wang 
44183e95b9abSLydia Wang 	parm = AC_PWRST_D3;
44193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
44203e95b9abSLydia Wang 	if (present) {
44213e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
44223e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
44233e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
44243e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
44253e95b9abSLydia Wang 	} else {
44263e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
44273e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44283e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
44293e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44303e95b9abSLydia Wang 	}
44313e95b9abSLydia Wang 
44323e95b9abSLydia Wang 
44333e95b9abSLydia Wang 	/* Mono Out */
44343e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
44353e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
44363e95b9abSLydia Wang 
44373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
44383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
44393e95b9abSLydia Wang 	if (present) {
44403e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
44413e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
44423e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
44433e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
44443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
44453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
44463e95b9abSLydia Wang 	} else {
44473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
44483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
44503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44513e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
44523e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
44533e95b9abSLydia Wang 	}
44543e95b9abSLydia Wang 
44553e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
44563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
44573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
44583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
44593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
44603e95b9abSLydia Wang 
44613e95b9abSLydia Wang }
4462ab6734e7SLydia Wang 
4463ab6734e7SLydia Wang /* patch for vt1812 */
4464ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
4465ab6734e7SLydia Wang {
4466ab6734e7SLydia Wang 	struct via_spec *spec;
4467ab6734e7SLydia Wang 	int err;
4468ab6734e7SLydia Wang 
4469ab6734e7SLydia Wang 	/* create a codec specific record */
44705b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
4471ab6734e7SLydia Wang 	if (spec == NULL)
4472ab6734e7SLydia Wang 		return -ENOMEM;
4473ab6734e7SLydia Wang 
4474620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
4475*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
4476*d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
4477620e2b28STakashi Iwai 
4478ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
4479ab6734e7SLydia Wang 	err = vt1812_parse_auto_config(codec);
4480ab6734e7SLydia Wang 	if (err < 0) {
4481ab6734e7SLydia Wang 		via_free(codec);
4482ab6734e7SLydia Wang 		return err;
4483ab6734e7SLydia Wang 	} else if (!err) {
4484ab6734e7SLydia Wang 		printk(KERN_INFO "hda_codec: Cannot set up configuration "
4485ab6734e7SLydia Wang 		       "from BIOS.  Using genenic mode...\n");
4486ab6734e7SLydia Wang 	}
4487ab6734e7SLydia Wang 
4488ab6734e7SLydia Wang 
4489ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++]  = vt1812_volume_init_verbs;
4490ab6734e7SLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs;
4491ab6734e7SLydia Wang 
4492ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
4493ab6734e7SLydia Wang 
4494ab6734e7SLydia Wang 	codec->patch_ops.init = via_auto_init;
44950f48327eSStephen Rothwell 	codec->patch_ops.unsol_event = via_unsol_event;
4496ab6734e7SLydia Wang 
4497ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
4498ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
4499ab6734e7SLydia Wang #endif
4500ab6734e7SLydia Wang 
45013e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
4502ab6734e7SLydia Wang 	return 0;
4503ab6734e7SLydia Wang }
4504ab6734e7SLydia Wang 
4505c577b8a1SJoseph Chan /*
4506c577b8a1SJoseph Chan  * patch entries
4507c577b8a1SJoseph Chan  */
450890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
45093218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
45103218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
45113218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
45123218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
45133218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
4514f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
45153218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
4516f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
45173218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
4518f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
45193218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
4520f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
45213218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
4522f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
45233218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
4524f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
45253218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
4526f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
45273218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
4528f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
45293218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
4530f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
45313218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
4532f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
45333218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
4534f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
45353218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
4536f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
45373218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
4538f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
45393218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
4540f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
45413218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
4542f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
45433218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
4544f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
45453218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
4546d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45473218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
4548d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45493218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
4550d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45513218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
4552d949cac1SHarald Welte 	  .patch = patch_vt1708S},
4553bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
4554d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45553218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
4556d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45573218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
4558d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45593218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
4560d949cac1SHarald Welte 	  .patch = patch_vt1708S},
45613218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
4562d949cac1SHarald Welte 	  .patch = patch_vt1702},
45633218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
4564d949cac1SHarald Welte 	  .patch = patch_vt1702},
45653218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
4566d949cac1SHarald Welte 	  .patch = patch_vt1702},
45673218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
4568d949cac1SHarald Welte 	  .patch = patch_vt1702},
45693218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
4570d949cac1SHarald Welte 	  .patch = patch_vt1702},
45713218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
4572d949cac1SHarald Welte 	  .patch = patch_vt1702},
45733218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
4574d949cac1SHarald Welte 	  .patch = patch_vt1702},
45753218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
4576d949cac1SHarald Welte 	  .patch = patch_vt1702},
4577eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
4578eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4579eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
4580eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4581bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
4582bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4583bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
4584bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4585f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
4586f3db423dSLydia Wang 	  .patch = patch_vt1716S},
4587f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
4588f3db423dSLydia Wang 	  .patch = patch_vt1716S},
458925eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
459025eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
4591ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
459236dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
459336dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
459411890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
459511890956SLydia Wang 		.patch = patch_vt2002P},
459611890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
459711890956SLydia Wang 		.patch = patch_vt2002P},
4598c577b8a1SJoseph Chan 	{} /* terminator */
4599c577b8a1SJoseph Chan };
46001289e9e8STakashi Iwai 
46011289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
46021289e9e8STakashi Iwai 
46031289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
46041289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
46051289e9e8STakashi Iwai 	.owner = THIS_MODULE,
46061289e9e8STakashi Iwai };
46071289e9e8STakashi Iwai 
46081289e9e8STakashi Iwai MODULE_LICENSE("GPL");
46091289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
46101289e9e8STakashi Iwai 
46111289e9e8STakashi Iwai static int __init patch_via_init(void)
46121289e9e8STakashi Iwai {
46131289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
46141289e9e8STakashi Iwai }
46151289e9e8STakashi Iwai 
46161289e9e8STakashi Iwai static void __exit patch_via_exit(void)
46171289e9e8STakashi Iwai {
46181289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
46191289e9e8STakashi Iwai }
46201289e9e8STakashi Iwai 
46211289e9e8STakashi Iwai module_init(patch_via_init)
46221289e9e8STakashi Iwai module_exit(patch_via_exit)
4623