xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 43737e0ae97ea87285686ffd07e30c82d897beb5)
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>
52da155d5bSPaul Gortmaker #include <linux/module.h>
53c577b8a1SJoseph Chan #include <sound/core.h>
540aa62aefSHarald Welte #include <sound/asoundef.h>
55c577b8a1SJoseph Chan #include "hda_codec.h"
56c577b8a1SJoseph Chan #include "hda_local.h"
57128bc4baSTakashi Iwai #include "hda_auto_parser.h"
581835a0f9STakashi Iwai #include "hda_jack.h"
59c577b8a1SJoseph Chan 
60c577b8a1SJoseph Chan /* Pin Widget NID */
6176d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6276d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
63c577b8a1SJoseph Chan 
64d7426329SHarald Welte enum VIA_HDA_CODEC {
65d7426329SHarald Welte 	UNKNOWN = -1,
66d7426329SHarald Welte 	VT1708,
67d7426329SHarald Welte 	VT1709_10CH,
68d7426329SHarald Welte 	VT1709_6CH,
69d7426329SHarald Welte 	VT1708B_8CH,
70d7426329SHarald Welte 	VT1708B_4CH,
71d7426329SHarald Welte 	VT1708S,
72518bf3baSLydia Wang 	VT1708BCE,
73d7426329SHarald Welte 	VT1702,
74eb7188caSLydia Wang 	VT1718S,
75f3db423dSLydia Wang 	VT1716S,
7625eaba2fSLydia Wang 	VT2002P,
77ab6734e7SLydia Wang 	VT1812,
7811890956SLydia Wang 	VT1802,
79*43737e0aSLydia Wang 	VT1705CF,
80d7426329SHarald Welte 	CODEC_TYPES,
81d7426329SHarald Welte };
82d7426329SHarald Welte 
8311890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8411890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8511890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8611890956SLydia Wang 	 (spec)->codec_type == VT1802)
8711890956SLydia Wang 
888e3679dcSTakashi Iwai #define MAX_NID_PATH_DEPTH	5
898e3679dcSTakashi Iwai 
9009a9ad69STakashi Iwai /* output-path: DAC -> ... -> pin
9109a9ad69STakashi Iwai  * idx[] contains the source index number of the next widget;
9209a9ad69STakashi Iwai  * e.g. idx[0] is the index of the DAC selected by path[1] widget
9309a9ad69STakashi Iwai  * multi[] indicates whether it's a selector widget with multi-connectors
9409a9ad69STakashi Iwai  * (i.e. the connection selection is mandatory)
9509a9ad69STakashi Iwai  * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
9609a9ad69STakashi Iwai  */
974a79616dSTakashi Iwai struct nid_path {
984a79616dSTakashi Iwai 	int depth;
998e3679dcSTakashi Iwai 	hda_nid_t path[MAX_NID_PATH_DEPTH];
10009a9ad69STakashi Iwai 	unsigned char idx[MAX_NID_PATH_DEPTH];
10109a9ad69STakashi Iwai 	unsigned char multi[MAX_NID_PATH_DEPTH];
10209a9ad69STakashi Iwai 	unsigned int vol_ctl;
10309a9ad69STakashi Iwai 	unsigned int mute_ctl;
1044a79616dSTakashi Iwai };
1054a79616dSTakashi Iwai 
106a86a88eaSTakashi Iwai /* input-path */
107a86a88eaSTakashi Iwai struct via_input {
108a86a88eaSTakashi Iwai 	hda_nid_t pin;	/* input-pin or aa-mix */
109a86a88eaSTakashi Iwai 	int adc_idx;	/* ADC index to be used */
110a86a88eaSTakashi Iwai 	int mux_idx;	/* MUX index (if any) */
111a86a88eaSTakashi Iwai 	const char *label;	/* input-source label */
112a86a88eaSTakashi Iwai };
113a86a88eaSTakashi Iwai 
114de6c74f3STakashi Iwai #define VIA_MAX_ADCS	3
115de6c74f3STakashi Iwai 
1163b607e3dSTakashi Iwai enum {
1173b607e3dSTakashi Iwai 	STREAM_MULTI_OUT = (1 << 0),
1183b607e3dSTakashi Iwai 	STREAM_INDEP_HP = (1 << 1),
1193b607e3dSTakashi Iwai };
1203b607e3dSTakashi Iwai 
1211f2e99feSLydia Wang struct via_spec {
1227819d1c7STakashi Iwai 	struct hda_gen_spec gen;
1237819d1c7STakashi Iwai 
1241f2e99feSLydia Wang 	/* codec parameterization */
12590dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1261f2e99feSLydia Wang 	unsigned int num_mixers;
1271f2e99feSLydia Wang 
12890dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1291f2e99feSLydia Wang 	unsigned int num_iverbs;
1301f2e99feSLydia Wang 
13182673bc8STakashi Iwai 	char stream_name_analog[32];
1327eb56e84STakashi Iwai 	char stream_name_hp[32];
13390dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
13490dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1351f2e99feSLydia Wang 
13682673bc8STakashi Iwai 	char stream_name_digital[32];
13790dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
13890dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1391f2e99feSLydia Wang 
1401f2e99feSLydia Wang 	/* playback */
1411f2e99feSLydia Wang 	struct hda_multi_out multiout;
1421f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
143ece8d043STakashi Iwai 	hda_nid_t hp_dac_nid;
1443214b966STakashi Iwai 	hda_nid_t speaker_dac_nid;
1453214b966STakashi Iwai 	int hp_indep_shared;	/* indep HP-DAC is shared with side ch */
1463b607e3dSTakashi Iwai 	int opened_streams;	/* STREAM_* bits */
1473b607e3dSTakashi Iwai 	int active_streams;	/* STREAM_* bits */
1483214b966STakashi Iwai 	int aamix_mode;		/* loopback is enabled for output-path? */
1491f2e99feSLydia Wang 
1503214b966STakashi Iwai 	/* Output-paths:
1513214b966STakashi Iwai 	 * There are different output-paths depending on the setup.
1523214b966STakashi Iwai 	 * out_path, hp_path and speaker_path are primary paths.  If both
1533214b966STakashi Iwai 	 * direct DAC and aa-loopback routes are available, these contain
1543214b966STakashi Iwai 	 * the former paths.  Meanwhile *_mix_path contain the paths with
1553214b966STakashi Iwai 	 * loopback mixer.  (Since the loopback is only for front channel,
1563214b966STakashi Iwai 	 * no out_mix_path for surround channels.)
1573214b966STakashi Iwai 	 * The HP output has another path, hp_indep_path, which is used in
1583214b966STakashi Iwai 	 * the independent-HP mode.
1593214b966STakashi Iwai 	 */
160de6c74f3STakashi Iwai 	struct nid_path out_path[HDA_SIDE + 1];
1613214b966STakashi Iwai 	struct nid_path out_mix_path;
1624a79616dSTakashi Iwai 	struct nid_path hp_path;
1633214b966STakashi Iwai 	struct nid_path hp_mix_path;
1643214b966STakashi Iwai 	struct nid_path hp_indep_path;
1654a918ffeSTakashi Iwai 	struct nid_path speaker_path;
1663214b966STakashi Iwai 	struct nid_path speaker_mix_path;
1674a79616dSTakashi Iwai 
1681f2e99feSLydia Wang 	/* capture */
1691f2e99feSLydia Wang 	unsigned int num_adc_nids;
170de6c74f3STakashi Iwai 	hda_nid_t adc_nids[VIA_MAX_ADCS];
171de6c74f3STakashi Iwai 	hda_nid_t mux_nids[VIA_MAX_ADCS];
172620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1731f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1741f2e99feSLydia Wang 
1751f2e99feSLydia Wang 	/* capture source */
176a86a88eaSTakashi Iwai 	bool dyn_adc_switch;
177a86a88eaSTakashi Iwai 	int num_inputs;
178a86a88eaSTakashi Iwai 	struct via_input inputs[AUTO_CFG_MAX_INS + 1];
179de6c74f3STakashi Iwai 	unsigned int cur_mux[VIA_MAX_ADCS];
1801f2e99feSLydia Wang 
1813b607e3dSTakashi Iwai 	/* dynamic DAC switching */
1823b607e3dSTakashi Iwai 	unsigned int cur_dac_stream_tag;
1833b607e3dSTakashi Iwai 	unsigned int cur_dac_format;
1843b607e3dSTakashi Iwai 	unsigned int cur_hp_stream_tag;
1853b607e3dSTakashi Iwai 	unsigned int cur_hp_format;
1863b607e3dSTakashi Iwai 
187a86a88eaSTakashi Iwai 	/* dynamic ADC switching */
188a86a88eaSTakashi Iwai 	hda_nid_t cur_adc;
189a86a88eaSTakashi Iwai 	unsigned int cur_adc_stream_tag;
190a86a88eaSTakashi Iwai 	unsigned int cur_adc_format;
191a86a88eaSTakashi Iwai 
1921f2e99feSLydia Wang 	/* PCM information */
1931f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1941f2e99feSLydia Wang 
1951f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1961f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1971f2e99feSLydia Wang 	struct snd_array kctls;
1981f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1991f2e99feSLydia Wang 
2001f2e99feSLydia Wang 	/* HP mode source */
2011f2e99feSLydia Wang 	unsigned int hp_independent_mode;
202f3db423dSLydia Wang 	unsigned int dmic_enabled;
20324088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
2041f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
2051f2e99feSLydia Wang 
206e9d010c2STakashi Iwai 	/* analog low-power control */
207e9d010c2STakashi Iwai 	bool alc_mode;
208e9d010c2STakashi Iwai 
209e3d7a143STakashi Iwai 	/* smart51 setup */
210e3d7a143STakashi Iwai 	unsigned int smart51_nums;
211e3d7a143STakashi Iwai 	hda_nid_t smart51_pins[2];
212e3d7a143STakashi Iwai 	int smart51_idxs[2];
213e3d7a143STakashi Iwai 	const char *smart51_labels[2];
214e3d7a143STakashi Iwai 	unsigned int smart51_enabled;
215e3d7a143STakashi Iwai 
2161f2e99feSLydia Wang 	/* work to check hp jack state */
2171f2e99feSLydia Wang 	struct hda_codec *codec;
2181f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
219187d333eSTakashi Iwai 	int hp_work_active;
220e06e5a29STakashi Iwai 	int vt1708_jack_detect;
2211f2e99feSLydia Wang 	int vt1708_hp_present;
2223e95b9abSLydia Wang 
2233e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
224*43737e0aSLydia Wang 	unsigned int dac_stream_tag[4];
2253e95b9abSLydia Wang 
2261f2e99feSLydia Wang 	struct hda_loopback_check loopback;
22713af8e77STakashi Iwai 	int num_loopbacks;
22813af8e77STakashi Iwai 	struct hda_amp_list loopback_list[8];
229a86a88eaSTakashi Iwai 
230a86a88eaSTakashi Iwai 	/* bind capture-volume */
231a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_vol;
232a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_sw;
2333b607e3dSTakashi Iwai 
2343b607e3dSTakashi Iwai 	struct mutex config_mutex;
2351f2e99feSLydia Wang };
2361f2e99feSLydia Wang 
2370341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
2385b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
2395b0cb1d8SJaroslav Kysela {
2405b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
2415b0cb1d8SJaroslav Kysela 
2425b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2435b0cb1d8SJaroslav Kysela 	if (spec == NULL)
2445b0cb1d8SJaroslav Kysela 		return NULL;
2455b0cb1d8SJaroslav Kysela 
246361dab3eSTakashi Iwai 	snd_array_init(&spec->kctls, sizeof(struct snd_kcontrol_new), 32);
2473b607e3dSTakashi Iwai 	mutex_init(&spec->config_mutex);
2485b0cb1d8SJaroslav Kysela 	codec->spec = spec;
2495b0cb1d8SJaroslav Kysela 	spec->codec = codec;
2500341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
2510341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
2520341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
2530341ccd7SLydia Wang 		spec->codec_type = VT1708S;
2547819d1c7STakashi Iwai 	snd_hda_gen_init(&spec->gen);
2555b0cb1d8SJaroslav Kysela 	return spec;
2565b0cb1d8SJaroslav Kysela }
2575b0cb1d8SJaroslav Kysela 
258744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
259d7426329SHarald Welte {
260744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
261d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
262d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
263d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
264d7426329SHarald Welte 
265d7426329SHarald Welte 	/* get codec type */
266d7426329SHarald Welte 	if (ven_id != 0x1106)
267d7426329SHarald Welte 		codec_type = UNKNOWN;
268d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
269d7426329SHarald Welte 		codec_type = VT1708;
270d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
271d7426329SHarald Welte 		codec_type = VT1709_10CH;
272d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
273d7426329SHarald Welte 		codec_type = VT1709_6CH;
274518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
275d7426329SHarald Welte 		codec_type = VT1708B_8CH;
276518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
277518bf3baSLydia Wang 			codec_type = VT1708BCE;
278518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
279d7426329SHarald Welte 		codec_type = VT1708B_4CH;
280d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
281d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
282d7426329SHarald Welte 		codec_type = VT1708S;
283d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
284d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
285d7426329SHarald Welte 		codec_type = VT1702;
286eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
287eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
288eb7188caSLydia Wang 		codec_type = VT1718S;
289f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
290f3db423dSLydia Wang 		codec_type = VT1716S;
291bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
292bb3c6bfcSLydia Wang 		codec_type = VT1718S;
29325eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
29425eaba2fSLydia Wang 		codec_type = VT2002P;
295ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
296ab6734e7SLydia Wang 		codec_type = VT1812;
29736dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
29836dd5c4aSLydia Wang 		codec_type = VT1708S;
29911890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
30011890956SLydia Wang 		codec_type = VT1802;
301*43737e0aSLydia Wang 	else if (dev_id == 0x4760)
302*43737e0aSLydia Wang 		codec_type = VT1705CF;
303d7426329SHarald Welte 	else
304d7426329SHarald Welte 		codec_type = UNKNOWN;
305d7426329SHarald Welte 	return codec_type;
306d7426329SHarald Welte };
307d7426329SHarald Welte 
308ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
30969e52a80SHarald Welte #define VIA_HP_EVENT		0x01
3104a918ffeSTakashi Iwai #define VIA_LINE_EVENT		0x03
31169e52a80SHarald Welte 
312c577b8a1SJoseph Chan enum {
313c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
314c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
315f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
316c577b8a1SJoseph Chan };
317c577b8a1SJoseph Chan 
318ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
319ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
3201f2e99feSLydia Wang 
321187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
322187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
323187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
3241f2e99feSLydia Wang 
3251f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
3261f2e99feSLydia Wang {
3271f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
3281f2e99feSLydia Wang 		return;
329187d333eSTakashi Iwai 	if (spec->hp_work_active) {
330187d333eSTakashi Iwai 		snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
3315b84ba26STejun Heo 		cancel_delayed_work_sync(&spec->vt1708_hp_work);
332187d333eSTakashi Iwai 		spec->hp_work_active = 0;
333187d333eSTakashi Iwai 	}
334187d333eSTakashi Iwai }
335187d333eSTakashi Iwai 
336187d333eSTakashi Iwai static void vt1708_update_hp_work(struct via_spec *spec)
337187d333eSTakashi Iwai {
338187d333eSTakashi Iwai 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
339187d333eSTakashi Iwai 		return;
340187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect &&
341187d333eSTakashi Iwai 	    (spec->active_streams || hp_detect_with_aa(spec->codec))) {
342187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
343187d333eSTakashi Iwai 			snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
344187d333eSTakashi Iwai 			schedule_delayed_work(&spec->vt1708_hp_work,
345187d333eSTakashi Iwai 					      msecs_to_jiffies(100));
346187d333eSTakashi Iwai 			spec->hp_work_active = 1;
347187d333eSTakashi Iwai 		}
348187d333eSTakashi Iwai 	} else if (!hp_detect_with_aa(spec->codec))
349187d333eSTakashi Iwai 		vt1708_stop_hp_work(spec);
3501f2e99feSLydia Wang }
351f5271101SLydia Wang 
3523e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
3533e95b9abSLydia Wang {
3543e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
3553e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3563e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3573e95b9abSLydia Wang }
35825eaba2fSLydia Wang 
359f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
360f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
361f5271101SLydia Wang {
362f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
363f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
364f5271101SLydia Wang 
3653e95b9abSLydia Wang 	set_widgets_power_state(codec);
366ada509ecSTakashi Iwai 	analog_low_current_mode(snd_kcontrol_chip(kcontrol));
367187d333eSTakashi Iwai 	vt1708_update_hp_work(codec->spec);
368f5271101SLydia Wang 	return change;
369f5271101SLydia Wang }
370f5271101SLydia Wang 
371f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
372f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
373f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
374f5271101SLydia Wang 			.name = NULL,					\
375f5271101SLydia Wang 			.index = 0,					\
376f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
377f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
378f5271101SLydia Wang 			.put = analog_input_switch_put,			\
379f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
380f5271101SLydia Wang 
38190dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
382c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
383c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
384f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
385c577b8a1SJoseph Chan };
386c577b8a1SJoseph Chan 
387ab6734e7SLydia Wang 
388c577b8a1SJoseph Chan /* add dynamic controls */
389291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
390291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
391291c9e33STakashi Iwai 				const char *name)
392c577b8a1SJoseph Chan {
393c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
394c577b8a1SJoseph Chan 
395603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
396c577b8a1SJoseph Chan 	if (!knew)
397291c9e33STakashi Iwai 		return NULL;
398291c9e33STakashi Iwai 	*knew = *tmpl;
399291c9e33STakashi Iwai 	if (!name)
400291c9e33STakashi Iwai 		name = tmpl->name;
401291c9e33STakashi Iwai 	if (name) {
402c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
403c577b8a1SJoseph Chan 		if (!knew->name)
404291c9e33STakashi Iwai 			return NULL;
405291c9e33STakashi Iwai 	}
406291c9e33STakashi Iwai 	return knew;
407291c9e33STakashi Iwai }
408291c9e33STakashi Iwai 
409291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
410291c9e33STakashi Iwai 			     int idx, unsigned long val)
411291c9e33STakashi Iwai {
412291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
413291c9e33STakashi Iwai 
414291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
415291c9e33STakashi Iwai 	if (!knew)
416c577b8a1SJoseph Chan 		return -ENOMEM;
417d7a99cceSTakashi Iwai 	knew->index = idx;
4184d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
4195e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
420c577b8a1SJoseph Chan 	knew->private_value = val;
421c577b8a1SJoseph Chan 	return 0;
422c577b8a1SJoseph Chan }
423c577b8a1SJoseph Chan 
4247b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
4257b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
4267b315bb4STakashi Iwai 
427291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
4285b0cb1d8SJaroslav Kysela 
429603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
430603c4019STakashi Iwai {
431603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
432603c4019STakashi Iwai 
433603c4019STakashi Iwai 	if (spec->kctls.list) {
434603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
435603c4019STakashi Iwai 		int i;
436603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
437603c4019STakashi Iwai 			kfree(kctl[i].name);
438603c4019STakashi Iwai 	}
439603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
440603c4019STakashi Iwai }
441603c4019STakashi Iwai 
442c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4439510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4447b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
445c577b8a1SJoseph Chan {
446c577b8a1SJoseph Chan 	char name[32];
447c577b8a1SJoseph Chan 	int err;
448c577b8a1SJoseph Chan 
449c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4507b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
451c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
452c577b8a1SJoseph Chan 	if (err < 0)
453c577b8a1SJoseph Chan 		return err;
454c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
4557b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
456c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
457c577b8a1SJoseph Chan 	if (err < 0)
458c577b8a1SJoseph Chan 		return err;
459c577b8a1SJoseph Chan 	return 0;
460c577b8a1SJoseph Chan }
461c577b8a1SJoseph Chan 
4625d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \
4638d087c76STakashi Iwai 	snd_hda_get_conn_index(codec, mux, nid, 0)
4645d41762aSTakashi Iwai 
4658df2a312STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
4668df2a312STakashi Iwai 			   unsigned int mask)
4678df2a312STakashi Iwai {
468a934d5a9STakashi Iwai 	unsigned int caps;
469a934d5a9STakashi Iwai 	if (!nid)
470a934d5a9STakashi Iwai 		return false;
471a934d5a9STakashi Iwai 	caps = get_wcaps(codec, nid);
4728df2a312STakashi Iwai 	if (dir == HDA_INPUT)
4738df2a312STakashi Iwai 		caps &= AC_WCAP_IN_AMP;
4748df2a312STakashi Iwai 	else
4758df2a312STakashi Iwai 		caps &= AC_WCAP_OUT_AMP;
4768df2a312STakashi Iwai 	if (!caps)
4778df2a312STakashi Iwai 		return false;
4788df2a312STakashi Iwai 	if (query_amp_caps(codec, nid, dir) & mask)
4798df2a312STakashi Iwai 		return true;
4808df2a312STakashi Iwai 	return false;
4818df2a312STakashi Iwai }
4828df2a312STakashi Iwai 
48309a9ad69STakashi Iwai #define have_mute(codec, nid, dir) \
48409a9ad69STakashi Iwai 	check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
4858df2a312STakashi Iwai 
486d69607b3SLydia Wang /* enable/disable the output-route mixers */
487d69607b3SLydia Wang static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
4883214b966STakashi Iwai 				hda_nid_t mix_nid, int idx, bool enable)
489d69607b3SLydia Wang {
490d69607b3SLydia Wang 	int i, num, val;
491d69607b3SLydia Wang 
492d69607b3SLydia Wang 	if (!path)
493d69607b3SLydia Wang 		return;
49409cf03b8STakashi Iwai 	num = snd_hda_get_num_conns(codec, mix_nid);
495d69607b3SLydia Wang 	for (i = 0; i < num; i++) {
4963214b966STakashi Iwai 		if (i == idx)
497d69607b3SLydia Wang 			val = AMP_IN_UNMUTE(i);
498d69607b3SLydia Wang 		else
499d69607b3SLydia Wang 			val = AMP_IN_MUTE(i);
500d69607b3SLydia Wang 		snd_hda_codec_write(codec, mix_nid, 0,
501d69607b3SLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, val);
502d69607b3SLydia Wang 	}
503d69607b3SLydia Wang }
504d69607b3SLydia Wang 
50509a9ad69STakashi Iwai /* enable/disable the output-route */
50609a9ad69STakashi Iwai static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
50709a9ad69STakashi Iwai 				 bool enable, bool force)
5085d41762aSTakashi Iwai {
509d69607b3SLydia Wang 	struct via_spec *spec = codec->spec;
5103214b966STakashi Iwai 	int i;
51109a9ad69STakashi Iwai 	for (i = 0; i < path->depth; i++) {
51209a9ad69STakashi Iwai 		hda_nid_t src, dst;
51309a9ad69STakashi Iwai 		int idx = path->idx[i];
51409a9ad69STakashi Iwai 		src = path->path[i];
51509a9ad69STakashi Iwai 		if (i < path->depth - 1)
51609a9ad69STakashi Iwai 			dst = path->path[i + 1];
51709a9ad69STakashi Iwai 		else
51809a9ad69STakashi Iwai 			dst = 0;
51909a9ad69STakashi Iwai 		if (enable && path->multi[i])
52009a9ad69STakashi Iwai 			snd_hda_codec_write(codec, dst, 0,
5215d41762aSTakashi Iwai 					    AC_VERB_SET_CONNECT_SEL, idx);
5223214b966STakashi Iwai 		if (!force && (dst == spec->aa_mix_nid))
523e5e14681SLydia Wang 			continue;
5243214b966STakashi Iwai 		if (have_mute(codec, dst, HDA_INPUT))
5253214b966STakashi Iwai 			activate_output_mix(codec, path, dst, idx, enable);
52609a9ad69STakashi Iwai 		if (!force && (src == path->vol_ctl || src == path->mute_ctl))
52709a9ad69STakashi Iwai 			continue;
52809a9ad69STakashi Iwai 		if (have_mute(codec, src, HDA_OUTPUT)) {
52909a9ad69STakashi Iwai 			int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
53009a9ad69STakashi Iwai 			snd_hda_codec_write(codec, src, 0,
53109a9ad69STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE, val);
53209a9ad69STakashi Iwai 		}
53309a9ad69STakashi Iwai 	}
5345d41762aSTakashi Iwai }
5355d41762aSTakashi Iwai 
5365d41762aSTakashi Iwai /* set the given pin as output */
5375d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
5385d41762aSTakashi Iwai 			    int pin_type)
5395d41762aSTakashi Iwai {
5405d41762aSTakashi Iwai 	if (!pin)
5415d41762aSTakashi Iwai 		return;
542cdd03cedSTakashi Iwai 	snd_hda_set_pin_ctl(codec, pin, pin_type);
5435d41762aSTakashi Iwai 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
5445d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0,
545d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
546c577b8a1SJoseph Chan }
547c577b8a1SJoseph Chan 
54809a9ad69STakashi Iwai static void via_auto_init_output(struct hda_codec *codec,
549a353fbb1STakashi Iwai 				 struct nid_path *path, int pin_type)
5505d41762aSTakashi Iwai {
5515d41762aSTakashi Iwai 	unsigned int caps;
552d69607b3SLydia Wang 	hda_nid_t pin;
5535d41762aSTakashi Iwai 
55409a9ad69STakashi Iwai 	if (!path->depth)
5555d41762aSTakashi Iwai 		return;
55609a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
5575d41762aSTakashi Iwai 
5585d41762aSTakashi Iwai 	init_output_pin(codec, pin, pin_type);
55977e314f7STakashi Iwai 	if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
5605d41762aSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_OUTPUT);
56177e314f7STakashi Iwai 	else
56277e314f7STakashi Iwai 		caps = 0;
5635d41762aSTakashi Iwai 	if (caps & AC_AMPCAP_MUTE) {
5645d41762aSTakashi Iwai 		unsigned int val;
5655d41762aSTakashi Iwai 		val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
5665d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
5675d41762aSTakashi Iwai 				    AMP_OUT_MUTE | val);
5685d41762aSTakashi Iwai 	}
569a353fbb1STakashi Iwai 	activate_output_path(codec, path, true, true); /* force on */
57009a9ad69STakashi Iwai }
571c577b8a1SJoseph Chan 
572c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
573c577b8a1SJoseph Chan {
574c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5753214b966STakashi Iwai 	struct nid_path *path;
576c577b8a1SJoseph Chan 	int i;
577c577b8a1SJoseph Chan 
5783214b966STakashi Iwai 	for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
5793214b966STakashi Iwai 		path = &spec->out_path[i];
5803214b966STakashi Iwai 		if (!i && spec->aamix_mode && spec->out_mix_path.depth)
5813214b966STakashi Iwai 			path = &spec->out_mix_path;
582a353fbb1STakashi Iwai 		via_auto_init_output(codec, path, PIN_OUT);
5833214b966STakashi Iwai 	}
584c577b8a1SJoseph Chan }
585c577b8a1SJoseph Chan 
586020066d1STakashi Iwai /* deactivate the inactive headphone-paths */
587020066d1STakashi Iwai static void deactivate_hp_paths(struct hda_codec *codec)
588c577b8a1SJoseph Chan {
589c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5903214b966STakashi Iwai 	int shared = spec->hp_indep_shared;
591c577b8a1SJoseph Chan 
59209a9ad69STakashi Iwai 	if (spec->hp_independent_mode) {
59309a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
5943214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, false, false);
5953214b966STakashi Iwai 		if (shared)
5963214b966STakashi Iwai 			activate_output_path(codec, &spec->out_path[shared],
5973214b966STakashi Iwai 					     false, false);
598020066d1STakashi Iwai 	} else if (spec->aamix_mode || !spec->hp_path.depth) {
599020066d1STakashi Iwai 		activate_output_path(codec, &spec->hp_indep_path, false, false);
6003214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
6013214b966STakashi Iwai 	} else {
602020066d1STakashi Iwai 		activate_output_path(codec, &spec->hp_indep_path, false, false);
6033214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, false, false);
60409a9ad69STakashi Iwai 	}
60525eaba2fSLydia Wang }
606c577b8a1SJoseph Chan 
607020066d1STakashi Iwai static void via_auto_init_hp_out(struct hda_codec *codec)
608020066d1STakashi Iwai {
609020066d1STakashi Iwai 	struct via_spec *spec = codec->spec;
610020066d1STakashi Iwai 
611020066d1STakashi Iwai 	if (!spec->hp_path.depth) {
612a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
613020066d1STakashi Iwai 		return;
614020066d1STakashi Iwai 	}
615020066d1STakashi Iwai 	deactivate_hp_paths(codec);
616020066d1STakashi Iwai 	if (spec->hp_independent_mode)
617a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
618020066d1STakashi Iwai 	else if (spec->aamix_mode)
619a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
620020066d1STakashi Iwai 	else
621a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->hp_path, PIN_HP);
622020066d1STakashi Iwai }
623020066d1STakashi Iwai 
6244a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec)
6254a918ffeSTakashi Iwai {
6264a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
6274a918ffeSTakashi Iwai 
6283214b966STakashi Iwai 	if (!spec->autocfg.speaker_outs)
6293214b966STakashi Iwai 		return;
6303214b966STakashi Iwai 	if (!spec->speaker_path.depth) {
631a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
6323214b966STakashi Iwai 		return;
6333214b966STakashi Iwai 	}
6343214b966STakashi Iwai 	if (!spec->aamix_mode) {
6353214b966STakashi Iwai 		activate_output_path(codec, &spec->speaker_mix_path,
6363214b966STakashi Iwai 				     false, false);
637a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
6383214b966STakashi Iwai 	} else {
6393214b966STakashi Iwai 		activate_output_path(codec, &spec->speaker_path, false, false);
640a353fbb1STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
6413214b966STakashi Iwai 	}
6424a918ffeSTakashi Iwai }
6434a918ffeSTakashi Iwai 
644f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
6456e969d91STakashi Iwai static void via_hp_automute(struct hda_codec *codec);
64632e0191dSClemens Ladisch 
647c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
648c577b8a1SJoseph Chan {
649c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
6507b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
651096a8854STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
65232e0191dSClemens Ladisch 	unsigned int ctl;
653096a8854STakashi Iwai 	int i, num_conns;
654c577b8a1SJoseph Chan 
655096a8854STakashi Iwai 	/* init ADCs */
656096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
65777e314f7STakashi Iwai 		hda_nid_t nid = spec->adc_nids[i];
65877e314f7STakashi Iwai 		if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) ||
65977e314f7STakashi Iwai 		    !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE))
66077e314f7STakashi Iwai 			continue;
661096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
662096a8854STakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
663096a8854STakashi Iwai 				    AMP_IN_UNMUTE(0));
664096a8854STakashi Iwai 	}
665096a8854STakashi Iwai 
666096a8854STakashi Iwai 	/* init pins */
6677b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
6687b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
669f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
67032e0191dSClemens Ladisch 			ctl = PIN_OUT;
6714740860bSTakashi Iwai 		else {
67232e0191dSClemens Ladisch 			ctl = PIN_IN;
6734740860bSTakashi Iwai 			if (cfg->inputs[i].type == AUTO_PIN_MIC)
6744740860bSTakashi Iwai 				ctl |= snd_hda_get_default_vref(codec, nid);
6754740860bSTakashi Iwai 		}
676cdd03cedSTakashi Iwai 		snd_hda_set_pin_ctl(codec, nid, ctl);
677c577b8a1SJoseph Chan 	}
678096a8854STakashi Iwai 
679096a8854STakashi Iwai 	/* init input-src */
680096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
681a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
682fc1156c0STakashi Iwai 		/* secondary ADCs must have the unique MUX */
683fc1156c0STakashi Iwai 		if (i > 0 && !spec->mux_nids[i])
684fc1156c0STakashi Iwai 			break;
685a86a88eaSTakashi Iwai 		if (spec->mux_nids[adc_idx]) {
686a86a88eaSTakashi Iwai 			int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
687a86a88eaSTakashi Iwai 			snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
688096a8854STakashi Iwai 					    AC_VERB_SET_CONNECT_SEL,
689a86a88eaSTakashi Iwai 					    mux_idx);
690a86a88eaSTakashi Iwai 		}
691a86a88eaSTakashi Iwai 		if (spec->dyn_adc_switch)
692a86a88eaSTakashi Iwai 			break; /* only one input-src */
693096a8854STakashi Iwai 	}
694096a8854STakashi Iwai 
695096a8854STakashi Iwai 	/* init aa-mixer */
696096a8854STakashi Iwai 	if (!spec->aa_mix_nid)
697096a8854STakashi Iwai 		return;
698096a8854STakashi Iwai 	num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
699096a8854STakashi Iwai 					    ARRAY_SIZE(conn));
700096a8854STakashi Iwai 	for (i = 0; i < num_conns; i++) {
701096a8854STakashi Iwai 		unsigned int caps = get_wcaps(codec, conn[i]);
702096a8854STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_PIN)
703096a8854STakashi Iwai 			snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
704096a8854STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
705096a8854STakashi Iwai 					    AMP_IN_MUTE(i));
706096a8854STakashi Iwai 	}
707c577b8a1SJoseph Chan }
708f5271101SLydia Wang 
709054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
710054d867eSTakashi Iwai 			       unsigned int parm)
711054d867eSTakashi Iwai {
712054d867eSTakashi Iwai 	if (snd_hda_codec_read(codec, nid, 0,
713054d867eSTakashi Iwai 			       AC_VERB_GET_POWER_STATE, 0) == parm)
714054d867eSTakashi Iwai 		return;
715054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
716054d867eSTakashi Iwai }
717054d867eSTakashi Iwai 
718*43737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
719*43737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
720*43737e0aSLydia Wang {
721*43737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
722*43737e0aSLydia Wang 	unsigned int format;
723*43737e0aSLydia Wang 	if (snd_hda_codec_read(codec, nid, 0,
724*43737e0aSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0) == parm)
725*43737e0aSLydia Wang 		return;
726*43737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
727*43737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
728*43737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
729*43737e0aSLydia Wang 
730*43737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
731*43737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
732*43737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
733*43737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
734*43737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
735*43737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
736*43737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
737*43737e0aSLydia Wang 	}
738*43737e0aSLydia Wang }
739*43737e0aSLydia Wang 
740f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
741f5271101SLydia Wang 				unsigned int *affected_parm)
742f5271101SLydia Wang {
743f5271101SLydia Wang 	unsigned parm;
744f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
745f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
746f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
747f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
7481564b287SLydia Wang 	struct via_spec *spec = codec->spec;
74924088a58STakashi Iwai 	unsigned present = 0;
75024088a58STakashi Iwai 
75124088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
75224088a58STakashi Iwai 	if (!no_presence)
75324088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
754f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
7551564b287SLydia Wang 	    || ((no_presence || present)
7561564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
757f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
758f5271101SLydia Wang 		parm = AC_PWRST_D0;
759f5271101SLydia Wang 	} else
760f5271101SLydia Wang 		parm = AC_PWRST_D3;
761f5271101SLydia Wang 
762054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
763f5271101SLydia Wang }
764f5271101SLydia Wang 
76524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
76624088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
76724088a58STakashi Iwai {
768dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
76924088a58STakashi Iwai }
77024088a58STakashi Iwai 
77124088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
77224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
77324088a58STakashi Iwai {
77424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
77524088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
77624088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
77724088a58STakashi Iwai 	return 0;
77824088a58STakashi Iwai }
77924088a58STakashi Iwai 
78024088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
78124088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
78224088a58STakashi Iwai {
78324088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
78424088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
78524088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
78624088a58STakashi Iwai 
78724088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
78824088a58STakashi Iwai 		return 0;
78924088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
79024088a58STakashi Iwai 	set_widgets_power_state(codec);
791e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
79224088a58STakashi Iwai 	return 1;
79324088a58STakashi Iwai }
79424088a58STakashi Iwai 
79524088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
79624088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
79724088a58STakashi Iwai 	.name = "Dynamic Power-Control",
79824088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
79924088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
80024088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
80124088a58STakashi Iwai };
80224088a58STakashi Iwai 
80324088a58STakashi Iwai 
8040aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
8050aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
8060aa62aefSHarald Welte {
8078df2a312STakashi Iwai 	static const char * const texts[] = { "OFF", "ON" };
8088df2a312STakashi Iwai 
8098df2a312STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
8108df2a312STakashi Iwai 	uinfo->count = 1;
8118df2a312STakashi Iwai 	uinfo->value.enumerated.items = 2;
8128df2a312STakashi Iwai 	if (uinfo->value.enumerated.item >= 2)
8138df2a312STakashi Iwai 		uinfo->value.enumerated.item = 1;
8148df2a312STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
8158df2a312STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
8168df2a312STakashi Iwai 	return 0;
8170aa62aefSHarald Welte }
8180aa62aefSHarald Welte 
8190aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
8200aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
8210aa62aefSHarald Welte {
8220aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
823cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
824cdc1784dSLydia Wang 
825ece8d043STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
826cdc1784dSLydia Wang 	return 0;
827cdc1784dSLydia Wang }
828cdc1784dSLydia Wang 
8293b607e3dSTakashi Iwai /* adjust spec->multiout setup according to the current flags */
8303b607e3dSTakashi Iwai static void setup_playback_multi_pcm(struct via_spec *spec)
8313b607e3dSTakashi Iwai {
8323b607e3dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
8333b607e3dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
8343b607e3dSTakashi Iwai 	spec->multiout.hp_nid = 0;
8353b607e3dSTakashi Iwai 	if (!spec->hp_independent_mode) {
8363b607e3dSTakashi Iwai 		if (!spec->hp_indep_shared)
8373b607e3dSTakashi Iwai 			spec->multiout.hp_nid = spec->hp_dac_nid;
8383b607e3dSTakashi Iwai 	} else {
8393b607e3dSTakashi Iwai 		if (spec->hp_indep_shared)
8403b607e3dSTakashi Iwai 			spec->multiout.num_dacs = cfg->line_outs - 1;
8413b607e3dSTakashi Iwai 	}
8423b607e3dSTakashi Iwai }
8433b607e3dSTakashi Iwai 
8443b607e3dSTakashi Iwai /* update DAC setups according to indep-HP switch;
8453b607e3dSTakashi Iwai  * this function is called only when indep-HP is modified
8463b607e3dSTakashi Iwai  */
8473b607e3dSTakashi Iwai static void switch_indep_hp_dacs(struct hda_codec *codec)
8483b607e3dSTakashi Iwai {
8493b607e3dSTakashi Iwai 	struct via_spec *spec = codec->spec;
8503b607e3dSTakashi Iwai 	int shared = spec->hp_indep_shared;
8513b607e3dSTakashi Iwai 	hda_nid_t shared_dac, hp_dac;
8523b607e3dSTakashi Iwai 
8533b607e3dSTakashi Iwai 	if (!spec->opened_streams)
8543b607e3dSTakashi Iwai 		return;
8553b607e3dSTakashi Iwai 
8563b607e3dSTakashi Iwai 	shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
8573b607e3dSTakashi Iwai 	hp_dac = spec->hp_dac_nid;
8583b607e3dSTakashi Iwai 	if (spec->hp_independent_mode) {
8593b607e3dSTakashi Iwai 		/* switch to indep-HP mode */
8603b607e3dSTakashi Iwai 		if (spec->active_streams & STREAM_MULTI_OUT) {
8613b607e3dSTakashi Iwai 			__snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
8623b607e3dSTakashi Iwai 			__snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
8633b607e3dSTakashi Iwai 		}
8643b607e3dSTakashi Iwai 		if (spec->active_streams & STREAM_INDEP_HP)
8653b607e3dSTakashi Iwai 			snd_hda_codec_setup_stream(codec, hp_dac,
8663b607e3dSTakashi Iwai 						   spec->cur_hp_stream_tag, 0,
8673b607e3dSTakashi Iwai 						   spec->cur_hp_format);
8683b607e3dSTakashi Iwai 	} else {
8693b607e3dSTakashi Iwai 		/* back to HP or shared-DAC */
8703b607e3dSTakashi Iwai 		if (spec->active_streams & STREAM_INDEP_HP)
8713b607e3dSTakashi Iwai 			__snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
8723b607e3dSTakashi Iwai 		if (spec->active_streams & STREAM_MULTI_OUT) {
8733b607e3dSTakashi Iwai 			hda_nid_t dac;
8743b607e3dSTakashi Iwai 			int ch;
8753b607e3dSTakashi Iwai 			if (shared_dac) { /* reset mutli-ch DAC */
8763b607e3dSTakashi Iwai 				dac = shared_dac;
8773b607e3dSTakashi Iwai 				ch = shared * 2;
8783b607e3dSTakashi Iwai 			} else { /* reset HP DAC */
8793b607e3dSTakashi Iwai 				dac = hp_dac;
8803b607e3dSTakashi Iwai 				ch = 0;
8813b607e3dSTakashi Iwai 			}
8823b607e3dSTakashi Iwai 			snd_hda_codec_setup_stream(codec, dac,
8833b607e3dSTakashi Iwai 						   spec->cur_dac_stream_tag, ch,
8843b607e3dSTakashi Iwai 						   spec->cur_dac_format);
8853b607e3dSTakashi Iwai 		}
8863b607e3dSTakashi Iwai 	}
8873b607e3dSTakashi Iwai 	setup_playback_multi_pcm(spec);
8883b607e3dSTakashi Iwai }
8893b607e3dSTakashi Iwai 
8900aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
8910aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
8920aa62aefSHarald Welte {
8930aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8940aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
8953214b966STakashi Iwai 	int cur, shared;
8968df2a312STakashi Iwai 
8973b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
89825250505STakashi Iwai 	cur = !!ucontrol->value.enumerated.item[0];
8993b607e3dSTakashi Iwai 	if (spec->hp_independent_mode == cur) {
9003b607e3dSTakashi Iwai 		mutex_unlock(&spec->config_mutex);
90125250505STakashi Iwai 		return 0;
9023b607e3dSTakashi Iwai 	}
90325250505STakashi Iwai 	spec->hp_independent_mode = cur;
9043214b966STakashi Iwai 	shared = spec->hp_indep_shared;
905020066d1STakashi Iwai 	deactivate_hp_paths(codec);
906020066d1STakashi Iwai 	if (cur)
907020066d1STakashi Iwai 		activate_output_path(codec, &spec->hp_indep_path, true, false);
908020066d1STakashi Iwai 	else {
9093214b966STakashi Iwai 		if (shared)
9103214b966STakashi Iwai 			activate_output_path(codec, &spec->out_path[shared],
91125250505STakashi Iwai 					     true, false);
912020066d1STakashi Iwai 		if (spec->aamix_mode || !spec->hp_path.depth)
913020066d1STakashi Iwai 			activate_output_path(codec, &spec->hp_mix_path,
914020066d1STakashi Iwai 					     true, false);
915020066d1STakashi Iwai 		else
916020066d1STakashi Iwai 			activate_output_path(codec, &spec->hp_path,
917020066d1STakashi Iwai 					     true, false);
9188df2a312STakashi Iwai 	}
9190aa62aefSHarald Welte 
9203b607e3dSTakashi Iwai 	switch_indep_hp_dacs(codec);
9213b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
9223b607e3dSTakashi Iwai 
923ce0e5a9eSLydia Wang 	/* update jack power state */
9243e95b9abSLydia Wang 	set_widgets_power_state(codec);
9256e969d91STakashi Iwai 	via_hp_automute(codec);
92625250505STakashi Iwai 	return 1;
9270aa62aefSHarald Welte }
9280aa62aefSHarald Welte 
929ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = {
9300aa62aefSHarald Welte 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9310aa62aefSHarald Welte 	.name = "Independent HP",
9320aa62aefSHarald Welte 	.info = via_independent_hp_info,
9330aa62aefSHarald Welte 	.get = via_independent_hp_get,
9340aa62aefSHarald Welte 	.put = via_independent_hp_put,
9350aa62aefSHarald Welte };
9360aa62aefSHarald Welte 
9373d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
9385b0cb1d8SJaroslav Kysela {
9393d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
9405b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
9415b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
9425b0cb1d8SJaroslav Kysela 
9435b0cb1d8SJaroslav Kysela 	nid = spec->autocfg.hp_pins[0];
944ece8d043STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer);
9453d83e577STakashi Iwai 	if (knew == NULL)
9463d83e577STakashi Iwai 		return -ENOMEM;
9473d83e577STakashi Iwai 
9485b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
9495b0cb1d8SJaroslav Kysela 
9505b0cb1d8SJaroslav Kysela 	return 0;
9515b0cb1d8SJaroslav Kysela }
9525b0cb1d8SJaroslav Kysela 
9531564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
9541564b287SLydia Wang {
955e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
9561564b287SLydia Wang 	int i;
9571564b287SLydia Wang 
958e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
959e3d7a143STakashi Iwai 		struct snd_kcontrol *ctl;
960e3d7a143STakashi Iwai 		struct snd_ctl_elem_id id;
9611564b287SLydia Wang 		memset(&id, 0, sizeof(id));
9621564b287SLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
963e3d7a143STakashi Iwai 		sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
964525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
965525566cbSLydia Wang 		if (ctl)
966525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
967525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
968525566cbSLydia Wang 					&ctl->id);
9691564b287SLydia Wang 	}
9701564b287SLydia Wang }
9711564b287SLydia Wang 
9721564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
9731564b287SLydia Wang {
9741564b287SLydia Wang 	struct via_spec *spec = codec->spec;
9751564b287SLydia Wang 	int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
976e3d7a143STakashi Iwai 	int i;
977e3d7a143STakashi Iwai 
978e3d7a143STakashi Iwai 	/* check AA path's mute status */
979e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
980e3d7a143STakashi Iwai 		if (spec->smart51_idxs[i] < 0)
981e3d7a143STakashi Iwai 			continue;
982e3d7a143STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
983e3d7a143STakashi Iwai 					 HDA_INPUT, spec->smart51_idxs[i],
9841564b287SLydia Wang 					 HDA_AMP_MUTE, val);
9851564b287SLydia Wang 	}
9861564b287SLydia Wang }
987f4a7828bSTakashi Iwai 
988e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
989e3d7a143STakashi Iwai {
990e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
991e3d7a143STakashi Iwai 	int i;
992e3d7a143STakashi Iwai 
993e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++)
994e3d7a143STakashi Iwai 		if (spec->smart51_pins[i] == pin)
995e3d7a143STakashi Iwai 			return true;
996e3d7a143STakashi Iwai 	return false;
997e3d7a143STakashi Iwai }
998e3d7a143STakashi Iwai 
9991564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
10001564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
10011564b287SLydia Wang {
10021564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
10031564b287SLydia Wang 	struct via_spec *spec = codec->spec;
10041564b287SLydia Wang 
1005f2b1c9f0STakashi Iwai 	*ucontrol->value.integer.value = spec->smart51_enabled;
10061564b287SLydia Wang 	return 0;
10071564b287SLydia Wang }
10081564b287SLydia Wang 
10091564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
10101564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
10111564b287SLydia Wang {
10121564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
10131564b287SLydia Wang 	struct via_spec *spec = codec->spec;
10141564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
10151564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
10161564b287SLydia Wang 	int i;
10171564b287SLydia Wang 
1018e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
1019e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
10207b315bb4STakashi Iwai 		unsigned int parm;
10217b315bb4STakashi Iwai 
10227b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
10231564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
10241564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
10251564b287SLydia Wang 		parm |= out_in;
1026cdd03cedSTakashi Iwai 		snd_hda_set_pin_ctl(codec, nid, parm);
10271564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
10281564b287SLydia Wang 			mute_aa_path(codec, 1);
10291564b287SLydia Wang 			notify_aa_path_ctls(codec);
10301564b287SLydia Wang 		}
10311564b287SLydia Wang 	}
10321564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
10333e95b9abSLydia Wang 	set_widgets_power_state(codec);
10341564b287SLydia Wang 	return 1;
10351564b287SLydia Wang }
10361564b287SLydia Wang 
10375f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
10381564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
10391564b287SLydia Wang 	.name = "Smart 5.1",
10401564b287SLydia Wang 	.count = 1,
1041f2b1c9f0STakashi Iwai 	.info = snd_ctl_boolean_mono_info,
10421564b287SLydia Wang 	.get = via_smart51_get,
10431564b287SLydia Wang 	.put = via_smart51_put,
10441564b287SLydia Wang };
10451564b287SLydia Wang 
1046f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
10475b0cb1d8SJaroslav Kysela {
1048f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
10495b0cb1d8SJaroslav Kysela 
1050e3d7a143STakashi Iwai 	if (!spec->smart51_nums)
1051cb34c207SLydia Wang 		return 0;
1052e3d7a143STakashi Iwai 	if (!via_clone_control(spec, &via_smart51_mixer))
10535b0cb1d8SJaroslav Kysela 		return -ENOMEM;
10545b0cb1d8SJaroslav Kysela 	return 0;
10555b0cb1d8SJaroslav Kysela }
10565b0cb1d8SJaroslav Kysela 
1057f5271101SLydia Wang /* check AA path's mute status */
1058ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
1059ada509ecSTakashi Iwai {
1060ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
1061ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
1062ada509ecSTakashi Iwai 	int i, ch, v;
1063ada509ecSTakashi Iwai 
1064ada509ecSTakashi Iwai 	for (i = 0; i < spec->num_loopbacks; i++) {
1065ada509ecSTakashi Iwai 		p = &spec->loopback_list[i];
1066ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
1067ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1068ada509ecSTakashi Iwai 						   p->idx);
1069ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
1070ada509ecSTakashi Iwai 				return false;
1071f5271101SLydia Wang 		}
1072f5271101SLydia Wang 	}
1073ada509ecSTakashi Iwai 	return true;
1074f5271101SLydia Wang }
1075f5271101SLydia Wang 
1076f5271101SLydia Wang /* enter/exit analog low-current mode */
1077e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
1078f5271101SLydia Wang {
1079f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
1080ada509ecSTakashi Iwai 	bool enable;
1081ada509ecSTakashi Iwai 	unsigned int verb, parm;
1082f5271101SLydia Wang 
1083e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
1084e9d010c2STakashi Iwai 		enable = false;
1085e9d010c2STakashi Iwai 	else
108692433923STakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->opened_streams;
1087e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
1088e9d010c2STakashi Iwai 		return;
1089e9d010c2STakashi Iwai 	spec->alc_mode = enable;
1090f5271101SLydia Wang 
1091f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
1092f5271101SLydia Wang 	switch (spec->codec_type) {
1093f5271101SLydia Wang 	case VT1708B_8CH:
1094f5271101SLydia Wang 	case VT1708B_4CH:
1095f5271101SLydia Wang 		verb = 0xf70;
1096f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1097f5271101SLydia Wang 		break;
1098f5271101SLydia Wang 	case VT1708S:
1099eb7188caSLydia Wang 	case VT1718S:
1100f3db423dSLydia Wang 	case VT1716S:
1101f5271101SLydia Wang 		verb = 0xf73;
1102f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1103f5271101SLydia Wang 		break;
1104f5271101SLydia Wang 	case VT1702:
1105f5271101SLydia Wang 		verb = 0xf73;
1106f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1107f5271101SLydia Wang 		break;
110825eaba2fSLydia Wang 	case VT2002P:
1109ab6734e7SLydia Wang 	case VT1812:
111011890956SLydia Wang 	case VT1802:
111125eaba2fSLydia Wang 		verb = 0xf93;
111225eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
111325eaba2fSLydia Wang 		break;
1114*43737e0aSLydia Wang 	case VT1705CF:
1115*43737e0aSLydia Wang 		verb = 0xf82;
1116*43737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
1117*43737e0aSLydia Wang 		break;
1118f5271101SLydia Wang 	default:
1119f5271101SLydia Wang 		return;		/* other codecs are not supported */
1120f5271101SLydia Wang 	}
1121f5271101SLydia Wang 	/* send verb */
1122f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1123f5271101SLydia Wang }
1124f5271101SLydia Wang 
1125e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
1126e9d010c2STakashi Iwai {
1127e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
1128e9d010c2STakashi Iwai }
1129e9d010c2STakashi Iwai 
1130c577b8a1SJoseph Chan /*
1131c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1132c577b8a1SJoseph Chan  */
1133096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
1134aa266fccSLydia Wang 	/* power down jack detect function */
1135aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1136f7278fd0SJosepch Chan 	{ }
1137c577b8a1SJoseph Chan };
1138c577b8a1SJoseph Chan 
11393b607e3dSTakashi Iwai static void set_stream_open(struct hda_codec *codec, int bit, bool active)
11407eb56e84STakashi Iwai {
1141ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
1142ada509ecSTakashi Iwai 
1143ada509ecSTakashi Iwai 	if (active)
11443b607e3dSTakashi Iwai 		spec->opened_streams |= bit;
1145ada509ecSTakashi Iwai 	else
11463b607e3dSTakashi Iwai 		spec->opened_streams &= ~bit;
1147ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
11487eb56e84STakashi Iwai }
11497eb56e84STakashi Iwai 
1150ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
1151c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1152c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1153c577b8a1SJoseph Chan {
1154c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
115525250505STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1156ada509ecSTakashi Iwai 	int err;
1157ece8d043STakashi Iwai 
115825250505STakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
115925250505STakashi Iwai 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
11603b607e3dSTakashi Iwai 	set_stream_open(codec, STREAM_MULTI_OUT, true);
1161ada509ecSTakashi Iwai 	err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
11629a08160bSTakashi Iwai 					    hinfo);
1163ada509ecSTakashi Iwai 	if (err < 0) {
11643b607e3dSTakashi Iwai 		set_stream_open(codec, STREAM_MULTI_OUT, false);
1165ada509ecSTakashi Iwai 		return err;
1166ada509ecSTakashi Iwai 	}
1167ada509ecSTakashi Iwai 	return 0;
1168c577b8a1SJoseph Chan }
1169c577b8a1SJoseph Chan 
1170ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
11719af74210STakashi Iwai 				  struct hda_codec *codec,
11729af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
11739af74210STakashi Iwai {
11743b607e3dSTakashi Iwai 	set_stream_open(codec, STREAM_MULTI_OUT, false);
11759af74210STakashi Iwai 	return 0;
11769af74210STakashi Iwai }
11779af74210STakashi Iwai 
11787eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
11797eb56e84STakashi Iwai 				    struct hda_codec *codec,
11807eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
11817eb56e84STakashi Iwai {
11827eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
11837eb56e84STakashi Iwai 
1184ece8d043STakashi Iwai 	if (snd_BUG_ON(!spec->hp_dac_nid))
11857eb56e84STakashi Iwai 		return -EINVAL;
11863b607e3dSTakashi Iwai 	set_stream_open(codec, STREAM_INDEP_HP, true);
1187ece8d043STakashi Iwai 	return 0;
1188ece8d043STakashi Iwai }
1189ece8d043STakashi Iwai 
1190ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1191ece8d043STakashi Iwai 				     struct hda_codec *codec,
1192ece8d043STakashi Iwai 				     struct snd_pcm_substream *substream)
1193ece8d043STakashi Iwai {
11943b607e3dSTakashi Iwai 	set_stream_open(codec, STREAM_INDEP_HP, false);
11957eb56e84STakashi Iwai 	return 0;
11967eb56e84STakashi Iwai }
11977eb56e84STakashi Iwai 
11987eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
11997eb56e84STakashi Iwai 					  struct hda_codec *codec,
12000aa62aefSHarald Welte 					  unsigned int stream_tag,
12010aa62aefSHarald Welte 					  unsigned int format,
12020aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
12030aa62aefSHarald Welte {
12040aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12050aa62aefSHarald Welte 
12063b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
12073b607e3dSTakashi Iwai 	setup_playback_multi_pcm(spec);
1208ece8d043STakashi Iwai 	snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1209ece8d043STakashi Iwai 					 format, substream);
12103b607e3dSTakashi Iwai 	/* remember for dynamic DAC switch with indep-HP */
12113b607e3dSTakashi Iwai 	spec->active_streams |= STREAM_MULTI_OUT;
12123b607e3dSTakashi Iwai 	spec->cur_dac_stream_tag = stream_tag;
12133b607e3dSTakashi Iwai 	spec->cur_dac_format = format;
12143b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1215187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
12167eb56e84STakashi Iwai 	return 0;
12170aa62aefSHarald Welte }
12180aa62aefSHarald Welte 
12197eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
12200aa62aefSHarald Welte 				       struct hda_codec *codec,
12210aa62aefSHarald Welte 				       unsigned int stream_tag,
12220aa62aefSHarald Welte 				       unsigned int format,
12230aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
12240aa62aefSHarald Welte {
12250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12260aa62aefSHarald Welte 
12273b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
12283b607e3dSTakashi Iwai 	if (spec->hp_independent_mode)
1229ece8d043STakashi Iwai 		snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1230ece8d043STakashi Iwai 					   stream_tag, 0, format);
12313b607e3dSTakashi Iwai 	spec->active_streams |= STREAM_INDEP_HP;
12323b607e3dSTakashi Iwai 	spec->cur_hp_stream_tag = stream_tag;
12333b607e3dSTakashi Iwai 	spec->cur_hp_format = format;
12343b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1235187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
12360aa62aefSHarald Welte 	return 0;
12370aa62aefSHarald Welte }
12380aa62aefSHarald Welte 
12390aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
12400aa62aefSHarald Welte 				    struct hda_codec *codec,
12410aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
12420aa62aefSHarald Welte {
12430aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
12440aa62aefSHarald Welte 
12453b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
1246ece8d043STakashi Iwai 	snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
12473b607e3dSTakashi Iwai 	spec->active_streams &= ~STREAM_MULTI_OUT;
12483b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1249187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
12507eb56e84STakashi Iwai 	return 0;
12510aa62aefSHarald Welte }
12527eb56e84STakashi Iwai 
12537eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
12547eb56e84STakashi Iwai 				       struct hda_codec *codec,
12557eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
12567eb56e84STakashi Iwai {
12577eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
12587eb56e84STakashi Iwai 
12593b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
12603b607e3dSTakashi Iwai 	if (spec->hp_independent_mode)
1261ece8d043STakashi Iwai 		snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
12623b607e3dSTakashi Iwai 	spec->active_streams &= ~STREAM_INDEP_HP;
12633b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1264187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
12650aa62aefSHarald Welte 	return 0;
12660aa62aefSHarald Welte }
12670aa62aefSHarald Welte 
1268c577b8a1SJoseph Chan /*
1269c577b8a1SJoseph Chan  * Digital out
1270c577b8a1SJoseph Chan  */
1271c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1272c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1273c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1274c577b8a1SJoseph Chan {
1275c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1276c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1277c577b8a1SJoseph Chan }
1278c577b8a1SJoseph Chan 
1279c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1280c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1281c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1282c577b8a1SJoseph Chan {
1283c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1284c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1285c577b8a1SJoseph Chan }
1286c577b8a1SJoseph Chan 
12875691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
128898aa34c0SHarald Welte 					struct hda_codec *codec,
128998aa34c0SHarald Welte 					unsigned int stream_tag,
129098aa34c0SHarald Welte 					unsigned int format,
129198aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
129298aa34c0SHarald Welte {
129398aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
12949da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
12959da29271STakashi Iwai 					     stream_tag, format, substream);
12969da29271STakashi Iwai }
12975691ec7fSHarald Welte 
12989da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
12999da29271STakashi Iwai 					struct hda_codec *codec,
13009da29271STakashi Iwai 					struct snd_pcm_substream *substream)
13019da29271STakashi Iwai {
13029da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
13039da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
130498aa34c0SHarald Welte 	return 0;
130598aa34c0SHarald Welte }
130698aa34c0SHarald Welte 
1307c577b8a1SJoseph Chan /*
1308c577b8a1SJoseph Chan  * Analog capture
1309c577b8a1SJoseph Chan  */
1310c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1311c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1312c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1313c577b8a1SJoseph Chan 				   unsigned int format,
1314c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1315c577b8a1SJoseph Chan {
1316c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1317c577b8a1SJoseph Chan 
1318c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1319c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1320c577b8a1SJoseph Chan 	return 0;
1321c577b8a1SJoseph Chan }
1322c577b8a1SJoseph Chan 
1323c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1324c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1325c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1326c577b8a1SJoseph Chan {
1327c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1328888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1329c577b8a1SJoseph Chan 	return 0;
1330c577b8a1SJoseph Chan }
1331c577b8a1SJoseph Chan 
1332a86a88eaSTakashi Iwai /* analog capture with dynamic ADC switching */
1333a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1334a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1335a86a88eaSTakashi Iwai 					   unsigned int stream_tag,
1336a86a88eaSTakashi Iwai 					   unsigned int format,
1337a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1338a86a88eaSTakashi Iwai {
1339a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1340a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1341a86a88eaSTakashi Iwai 
13423b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
1343a86a88eaSTakashi Iwai 	spec->cur_adc = spec->adc_nids[adc_idx];
1344a86a88eaSTakashi Iwai 	spec->cur_adc_stream_tag = stream_tag;
1345a86a88eaSTakashi Iwai 	spec->cur_adc_format = format;
1346a86a88eaSTakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
13473b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1348a86a88eaSTakashi Iwai 	return 0;
1349a86a88eaSTakashi Iwai }
1350a86a88eaSTakashi Iwai 
1351a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1352a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1353a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1354a86a88eaSTakashi Iwai {
1355a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1356a86a88eaSTakashi Iwai 
13573b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
1358a86a88eaSTakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1359a86a88eaSTakashi Iwai 	spec->cur_adc = 0;
13603b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
1361a86a88eaSTakashi Iwai 	return 0;
1362a86a88eaSTakashi Iwai }
1363a86a88eaSTakashi Iwai 
1364a86a88eaSTakashi Iwai /* re-setup the stream if running; called from input-src put */
1365a86a88eaSTakashi Iwai static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1366a86a88eaSTakashi Iwai {
1367a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1368a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[cur].adc_idx;
1369a86a88eaSTakashi Iwai 	hda_nid_t adc = spec->adc_nids[adc_idx];
13703b607e3dSTakashi Iwai 	bool ret = false;
1371a86a88eaSTakashi Iwai 
13723b607e3dSTakashi Iwai 	mutex_lock(&spec->config_mutex);
1373a86a88eaSTakashi Iwai 	if (spec->cur_adc && spec->cur_adc != adc) {
1374a86a88eaSTakashi Iwai 		/* stream is running, let's swap the current ADC */
1375a86a88eaSTakashi Iwai 		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1376a86a88eaSTakashi Iwai 		spec->cur_adc = adc;
1377a86a88eaSTakashi Iwai 		snd_hda_codec_setup_stream(codec, adc,
1378a86a88eaSTakashi Iwai 					   spec->cur_adc_stream_tag, 0,
1379a86a88eaSTakashi Iwai 					   spec->cur_adc_format);
13803b607e3dSTakashi Iwai 		ret = true;
1381a86a88eaSTakashi Iwai 	}
13823b607e3dSTakashi Iwai 	mutex_unlock(&spec->config_mutex);
13833b607e3dSTakashi Iwai 	return ret;
1384a86a88eaSTakashi Iwai }
1385a86a88eaSTakashi Iwai 
13869af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
13877eb56e84STakashi Iwai 	.substreams = 1,
1388c577b8a1SJoseph Chan 	.channels_min = 2,
1389c577b8a1SJoseph Chan 	.channels_max = 8,
13909af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1391c577b8a1SJoseph Chan 	.ops = {
1392ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1393ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
13940aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
13950aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1396c577b8a1SJoseph Chan 	},
1397c577b8a1SJoseph Chan };
1398c577b8a1SJoseph Chan 
13997eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
14007eb56e84STakashi Iwai 	.substreams = 1,
14017eb56e84STakashi Iwai 	.channels_min = 2,
14027eb56e84STakashi Iwai 	.channels_max = 2,
14037eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
14047eb56e84STakashi Iwai 	.ops = {
14057eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
1406ece8d043STakashi Iwai 		.close = via_playback_hp_pcm_close,
14077eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
14087eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
14097eb56e84STakashi Iwai 	},
14107eb56e84STakashi Iwai };
14117eb56e84STakashi Iwai 
141290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
14137eb56e84STakashi Iwai 	.substreams = 1,
1414bc9b5623STakashi Iwai 	.channels_min = 2,
1415bc9b5623STakashi Iwai 	.channels_max = 8,
14169af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1417bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1418bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1419bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1420bc9b5623STakashi Iwai 	 */
1421bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1422bc9b5623STakashi Iwai 	.ops = {
1423ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1424ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
1425c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1426c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1427bc9b5623STakashi Iwai 	},
1428bc9b5623STakashi Iwai };
1429bc9b5623STakashi Iwai 
14309af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
14317eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1432c577b8a1SJoseph Chan 	.channels_min = 2,
1433c577b8a1SJoseph Chan 	.channels_max = 2,
14349af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1435c577b8a1SJoseph Chan 	.ops = {
1436c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1437c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1438c577b8a1SJoseph Chan 	},
1439c577b8a1SJoseph Chan };
1440c577b8a1SJoseph Chan 
1441a86a88eaSTakashi Iwai static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1442a86a88eaSTakashi Iwai 	.substreams = 1,
1443a86a88eaSTakashi Iwai 	.channels_min = 2,
1444a86a88eaSTakashi Iwai 	.channels_max = 2,
1445a86a88eaSTakashi Iwai 	/* NID is set in via_build_pcms */
1446a86a88eaSTakashi Iwai 	.ops = {
1447a86a88eaSTakashi Iwai 		.prepare = via_dyn_adc_capture_pcm_prepare,
1448a86a88eaSTakashi Iwai 		.cleanup = via_dyn_adc_capture_pcm_cleanup,
1449a86a88eaSTakashi Iwai 	},
1450a86a88eaSTakashi Iwai };
1451a86a88eaSTakashi Iwai 
14529af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1453c577b8a1SJoseph Chan 	.substreams = 1,
1454c577b8a1SJoseph Chan 	.channels_min = 2,
1455c577b8a1SJoseph Chan 	.channels_max = 2,
1456c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1457c577b8a1SJoseph Chan 	.ops = {
1458c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
14596b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
14609da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
14619da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1462c577b8a1SJoseph Chan 	},
1463c577b8a1SJoseph Chan };
1464c577b8a1SJoseph Chan 
14659af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1466c577b8a1SJoseph Chan 	.substreams = 1,
1467c577b8a1SJoseph Chan 	.channels_min = 2,
1468c577b8a1SJoseph Chan 	.channels_max = 2,
1469c577b8a1SJoseph Chan };
1470c577b8a1SJoseph Chan 
1471370bafbdSTakashi Iwai /*
1472370bafbdSTakashi Iwai  * slave controls for virtual master
1473370bafbdSTakashi Iwai  */
14749322ca54STakashi Iwai static const char * const via_slave_pfxs[] = {
14759322ca54STakashi Iwai 	"Front", "Surround", "Center", "LFE", "Side",
1476f37bc7a8STakashi Iwai 	"Headphone", "Speaker", "Bass Speaker",
1477370bafbdSTakashi Iwai 	NULL,
1478370bafbdSTakashi Iwai };
1479370bafbdSTakashi Iwai 
1480c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1481c577b8a1SJoseph Chan {
1482c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
14835b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
14845b0cb1d8SJaroslav Kysela 	int err, i;
1485c577b8a1SJoseph Chan 
1486b5bcc189STakashi Iwai 	spec->no_pin_power_ctl = 1;
148724088a58STakashi Iwai 	if (spec->set_widgets_power_state)
148824088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
148924088a58STakashi Iwai 			return -ENOMEM;
149024088a58STakashi Iwai 
1491c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1492c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1493c577b8a1SJoseph Chan 		if (err < 0)
1494c577b8a1SJoseph Chan 			return err;
1495c577b8a1SJoseph Chan 	}
1496c577b8a1SJoseph Chan 
1497c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1498c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
149974b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1500c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1501c577b8a1SJoseph Chan 		if (err < 0)
1502c577b8a1SJoseph Chan 			return err;
15039a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
15049a08160bSTakashi Iwai 						    &spec->multiout);
15059a08160bSTakashi Iwai 		if (err < 0)
15069a08160bSTakashi Iwai 			return err;
15079a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1508c577b8a1SJoseph Chan 	}
1509c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1510c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1511c577b8a1SJoseph Chan 		if (err < 0)
1512c577b8a1SJoseph Chan 			return err;
1513c577b8a1SJoseph Chan 	}
151417314379SLydia Wang 
1515370bafbdSTakashi Iwai 	/* if we have no master control, let's create it */
1516370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1517370bafbdSTakashi Iwai 		unsigned int vmaster_tlv[4];
1518370bafbdSTakashi Iwai 		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1519370bafbdSTakashi Iwai 					HDA_OUTPUT, vmaster_tlv);
1520370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
15219322ca54STakashi Iwai 					  vmaster_tlv, via_slave_pfxs,
15229322ca54STakashi Iwai 					  "Playback Volume");
1523370bafbdSTakashi Iwai 		if (err < 0)
1524370bafbdSTakashi Iwai 			return err;
1525370bafbdSTakashi Iwai 	}
1526370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1527370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
15289322ca54STakashi Iwai 					  NULL, via_slave_pfxs,
15299322ca54STakashi Iwai 					  "Playback Switch");
1530370bafbdSTakashi Iwai 		if (err < 0)
1531370bafbdSTakashi Iwai 			return err;
1532370bafbdSTakashi Iwai 	}
1533370bafbdSTakashi Iwai 
15345b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
15355b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
15365b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
153777e314f7STakashi Iwai 		if (!spec->mux_nids[i])
153877e314f7STakashi Iwai 			continue;
153921949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
15405b0cb1d8SJaroslav Kysela 		if (err < 0)
15415b0cb1d8SJaroslav Kysela 			return err;
15425b0cb1d8SJaroslav Kysela 	}
15435b0cb1d8SJaroslav Kysela 
1544603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
154501a61e12STakashi Iwai 
154601a61e12STakashi Iwai 	err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
154701a61e12STakashi Iwai 	if (err < 0)
154801a61e12STakashi Iwai 		return err;
154901a61e12STakashi Iwai 
1550c577b8a1SJoseph Chan 	return 0;
1551c577b8a1SJoseph Chan }
1552c577b8a1SJoseph Chan 
1553c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1554c577b8a1SJoseph Chan {
1555c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1556c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1557c577b8a1SJoseph Chan 
1558a5973103STakashi Iwai 	codec->num_pcms = 0;
1559c577b8a1SJoseph Chan 	codec->pcm_info = info;
1560c577b8a1SJoseph Chan 
1561a5973103STakashi Iwai 	if (spec->multiout.num_dacs || spec->num_adc_nids) {
1562a5973103STakashi Iwai 		snprintf(spec->stream_name_analog,
1563a5973103STakashi Iwai 			 sizeof(spec->stream_name_analog),
156482673bc8STakashi Iwai 			 "%s Analog", codec->chip_name);
1565c577b8a1SJoseph Chan 		info->name = spec->stream_name_analog;
15669af74210STakashi Iwai 
1567a5973103STakashi Iwai 		if (spec->multiout.num_dacs) {
15689af74210STakashi Iwai 			if (!spec->stream_analog_playback)
1569a5973103STakashi Iwai 				spec->stream_analog_playback =
1570a5973103STakashi Iwai 					&via_pcm_analog_playback;
1571377ff31aSLydia Wang 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
15729af74210STakashi Iwai 				*spec->stream_analog_playback;
1573377ff31aSLydia Wang 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1574377ff31aSLydia Wang 				spec->multiout.dac_nids[0];
1575c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1576c577b8a1SJoseph Chan 				spec->multiout.max_channels;
1577ee81abb6STakashi Iwai 			if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT
1578ee81abb6STakashi Iwai 			    && spec->autocfg.line_outs == 2)
1579ee81abb6STakashi Iwai 				info->stream[SNDRV_PCM_STREAM_PLAYBACK].chmap =
1580ee81abb6STakashi Iwai 					snd_pcm_2_1_chmaps;
1581a5973103STakashi Iwai 		}
15829af74210STakashi Iwai 
1583a86a88eaSTakashi Iwai 		if (!spec->stream_analog_capture) {
1584a86a88eaSTakashi Iwai 			if (spec->dyn_adc_switch)
1585a86a88eaSTakashi Iwai 				spec->stream_analog_capture =
1586a86a88eaSTakashi Iwai 					&via_pcm_dyn_adc_analog_capture;
1587a86a88eaSTakashi Iwai 			else
1588a5973103STakashi Iwai 				spec->stream_analog_capture =
1589a5973103STakashi Iwai 					&via_pcm_analog_capture;
1590a86a88eaSTakashi Iwai 		}
1591a5973103STakashi Iwai 		if (spec->num_adc_nids) {
15929af74210STakashi Iwai 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
15939af74210STakashi Iwai 				*spec->stream_analog_capture;
1594a5973103STakashi Iwai 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1595a5973103STakashi Iwai 				spec->adc_nids[0];
1596a86a88eaSTakashi Iwai 			if (!spec->dyn_adc_switch)
15979af74210STakashi Iwai 				info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
15989af74210STakashi Iwai 					spec->num_adc_nids;
1599a5973103STakashi Iwai 		}
1600c577b8a1SJoseph Chan 		codec->num_pcms++;
1601c577b8a1SJoseph Chan 		info++;
1602a5973103STakashi Iwai 	}
1603a5973103STakashi Iwai 
1604a5973103STakashi Iwai 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
160582673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
160682673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
160782673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1608c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
16097ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1610c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
16119af74210STakashi Iwai 			if (!spec->stream_digital_playback)
16129af74210STakashi Iwai 				spec->stream_digital_playback =
16139af74210STakashi Iwai 					&via_pcm_digital_playback;
1614c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
16159af74210STakashi Iwai 				*spec->stream_digital_playback;
1616c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1617c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1618c577b8a1SJoseph Chan 		}
1619c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
16209af74210STakashi Iwai 			if (!spec->stream_digital_capture)
16219af74210STakashi Iwai 				spec->stream_digital_capture =
16229af74210STakashi Iwai 					&via_pcm_digital_capture;
1623c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
16249af74210STakashi Iwai 				*spec->stream_digital_capture;
1625c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1626c577b8a1SJoseph Chan 				spec->dig_in_nid;
1627c577b8a1SJoseph Chan 		}
1628a5973103STakashi Iwai 		codec->num_pcms++;
1629a5973103STakashi Iwai 		info++;
1630c577b8a1SJoseph Chan 	}
1631c577b8a1SJoseph Chan 
1632ece8d043STakashi Iwai 	if (spec->hp_dac_nid) {
16337eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
16347eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
16357eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
16367eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
16377eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1638ece8d043STakashi Iwai 			spec->hp_dac_nid;
1639a5973103STakashi Iwai 		codec->num_pcms++;
1640a5973103STakashi Iwai 		info++;
16417eb56e84STakashi Iwai 	}
1642c577b8a1SJoseph Chan 	return 0;
1643c577b8a1SJoseph Chan }
1644c577b8a1SJoseph Chan 
1645c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1646c577b8a1SJoseph Chan {
1647c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1648c577b8a1SJoseph Chan 
1649c577b8a1SJoseph Chan 	if (!spec)
1650c577b8a1SJoseph Chan 		return;
1651c577b8a1SJoseph Chan 
1652603c4019STakashi Iwai 	via_free_kctls(codec);
16531f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1654a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_vol);
1655a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_sw);
16567819d1c7STakashi Iwai 	snd_hda_gen_free(&spec->gen);
1657a86a88eaSTakashi Iwai 	kfree(spec);
1658c577b8a1SJoseph Chan }
1659c577b8a1SJoseph Chan 
166064be285bSTakashi Iwai /* mute/unmute outputs */
166164be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
166264be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
166364be285bSTakashi Iwai {
166464be285bSTakashi Iwai 	int i;
166594994734STakashi Iwai 	for (i = 0; i < num_pins; i++) {
166694994734STakashi Iwai 		unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
166794994734STakashi Iwai 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
166894994734STakashi Iwai 		if (parm & AC_PINCTL_IN_EN)
166994994734STakashi Iwai 			continue;
167094994734STakashi Iwai 		if (mute)
167194994734STakashi Iwai 			parm &= ~AC_PINCTL_OUT_EN;
167294994734STakashi Iwai 		else
167394994734STakashi Iwai 			parm |= AC_PINCTL_OUT_EN;
1674cdd03cedSTakashi Iwai 		snd_hda_set_pin_ctl(codec, pins[i], parm);
167594994734STakashi Iwai 	}
167664be285bSTakashi Iwai }
167764be285bSTakashi Iwai 
16784a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */
16794a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present)
16804a918ffeSTakashi Iwai {
16814a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
16824a918ffeSTakashi Iwai 
16834a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs)
16844a918ffeSTakashi Iwai 		return;
16854a918ffeSTakashi Iwai 	if (!present)
16864a918ffeSTakashi Iwai 		present = snd_hda_jack_detect(codec,
16874a918ffeSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
16884a918ffeSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
16894a918ffeSTakashi Iwai 			    spec->autocfg.speaker_pins,
16904a918ffeSTakashi Iwai 			    present);
16914a918ffeSTakashi Iwai }
16924a918ffeSTakashi Iwai 
169369e52a80SHarald Welte /* mute internal speaker if HP is plugged */
169469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
169569e52a80SHarald Welte {
16964a918ffeSTakashi Iwai 	int present = 0;
16976e969d91STakashi Iwai 	int nums;
169869e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
169969e52a80SHarald Welte 
1700187d333eSTakashi Iwai 	if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1701cf55e904SHerton Ronaldo Krzesinski 	    (spec->codec_type != VT1708 || spec->vt1708_jack_detect) &&
1702cf55e904SHerton Ronaldo Krzesinski 	    is_jack_detectable(codec, spec->autocfg.hp_pins[0]))
1703d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
17046e969d91STakashi Iwai 
1705f2b1c9f0STakashi Iwai 	if (spec->smart51_enabled)
1706f2b1c9f0STakashi Iwai 		nums = spec->autocfg.line_outs + spec->smart51_nums;
1707f2b1c9f0STakashi Iwai 	else
1708f2b1c9f0STakashi Iwai 		nums = spec->autocfg.line_outs;
17096e969d91STakashi Iwai 	toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
17106e969d91STakashi Iwai 
17114a918ffeSTakashi Iwai 	via_line_automute(codec, present);
1712f3db423dSLydia Wang }
1713f3db423dSLydia Wang 
17142a43952aSTakashi Iwai #ifdef CONFIG_PM
171568cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
17161f2e99feSLydia Wang {
17171f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
17181f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
171994c142a1SDavid Henningsson 
172094c142a1SDavid Henningsson 	if (spec->codec_type == VT1802) {
172194c142a1SDavid Henningsson 		/* Fix pop noise on headphones */
172294c142a1SDavid Henningsson 		int i;
172394c142a1SDavid Henningsson 		for (i = 0; i < spec->autocfg.hp_outs; i++)
172494c142a1SDavid Henningsson 			snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0);
172594c142a1SDavid Henningsson 	}
172694c142a1SDavid Henningsson 
17271f2e99feSLydia Wang 	return 0;
17281f2e99feSLydia Wang }
17291f2e99feSLydia Wang #endif
17301f2e99feSLydia Wang 
173183012a7cSTakashi Iwai #ifdef CONFIG_PM
1732cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1733cb53c626STakashi Iwai {
1734cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1735cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1736cb53c626STakashi Iwai }
1737cb53c626STakashi Iwai #endif
1738cb53c626STakashi Iwai 
1739c577b8a1SJoseph Chan /*
1740c577b8a1SJoseph Chan  */
17415d41762aSTakashi Iwai 
17425d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
17435d41762aSTakashi Iwai 
174490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1745c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1746c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1747c577b8a1SJoseph Chan 	.init = via_init,
1748c577b8a1SJoseph Chan 	.free = via_free,
17494e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
17502a43952aSTakashi Iwai #ifdef CONFIG_PM
17511f2e99feSLydia Wang 	.suspend = via_suspend,
1752cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1753cb53c626STakashi Iwai #endif
1754c577b8a1SJoseph Chan };
1755c577b8a1SJoseph Chan 
17564a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1757c577b8a1SJoseph Chan {
17584a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
17594a79616dSTakashi Iwai 	int i;
17604a79616dSTakashi Iwai 
17614a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
17624a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
17634a79616dSTakashi Iwai 			return false;
17644a79616dSTakashi Iwai 	}
1765ece8d043STakashi Iwai 	if (spec->hp_dac_nid == dac)
17664a79616dSTakashi Iwai 		return false;
17674a79616dSTakashi Iwai 	return true;
17684a79616dSTakashi Iwai }
17694a79616dSTakashi Iwai 
17708e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
17713214b966STakashi Iwai 				hda_nid_t target_dac, int with_aa_mix,
17723214b966STakashi Iwai 				struct nid_path *path, int depth)
17734a79616dSTakashi Iwai {
17743214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
17754a79616dSTakashi Iwai 	hda_nid_t conn[8];
17764a79616dSTakashi Iwai 	int i, nums;
17774a79616dSTakashi Iwai 
17783214b966STakashi Iwai 	if (nid == spec->aa_mix_nid) {
17793214b966STakashi Iwai 		if (!with_aa_mix)
17803214b966STakashi Iwai 			return false;
17813214b966STakashi Iwai 		with_aa_mix = 2; /* mark aa-mix is included */
17823214b966STakashi Iwai 	}
17833214b966STakashi Iwai 
17844a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
17854a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
17864a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
17874a79616dSTakashi Iwai 			continue;
17883214b966STakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
17893214b966STakashi Iwai 			/* aa-mix is requested but not included? */
17903214b966STakashi Iwai 			if (!(spec->aa_mix_nid && with_aa_mix == 1))
179109a9ad69STakashi Iwai 				goto found;
17924a79616dSTakashi Iwai 		}
17933214b966STakashi Iwai 	}
17948e3679dcSTakashi Iwai 	if (depth >= MAX_NID_PATH_DEPTH)
17954a79616dSTakashi Iwai 		return false;
17964a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
17974a79616dSTakashi Iwai 		unsigned int type;
17984a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
17993214b966STakashi Iwai 		if (type == AC_WID_AUD_OUT)
18004a79616dSTakashi Iwai 			continue;
18018e3679dcSTakashi Iwai 		if (__parse_output_path(codec, conn[i], target_dac,
18023214b966STakashi Iwai 					with_aa_mix, path, depth + 1))
180309a9ad69STakashi Iwai 			goto found;
18044a79616dSTakashi Iwai 	}
18054a79616dSTakashi Iwai 	return false;
180609a9ad69STakashi Iwai 
180709a9ad69STakashi Iwai  found:
180809a9ad69STakashi Iwai 	path->path[path->depth] = conn[i];
180909a9ad69STakashi Iwai 	path->idx[path->depth] = i;
181009a9ad69STakashi Iwai 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
181109a9ad69STakashi Iwai 		path->multi[path->depth] = 1;
181209a9ad69STakashi Iwai 	path->depth++;
181309a9ad69STakashi Iwai 	return true;
18144a79616dSTakashi Iwai }
18154a79616dSTakashi Iwai 
18168e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
18173214b966STakashi Iwai 			      hda_nid_t target_dac, int with_aa_mix,
18183214b966STakashi Iwai 			      struct nid_path *path)
18198e3679dcSTakashi Iwai {
18203214b966STakashi Iwai 	if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
18218e3679dcSTakashi Iwai 		path->path[path->depth] = nid;
18228e3679dcSTakashi Iwai 		path->depth++;
18233214b966STakashi Iwai 		snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
18243214b966STakashi Iwai 			    path->depth, path->path[0], path->path[1],
18253214b966STakashi Iwai 			    path->path[2], path->path[3], path->path[4]);
18268e3679dcSTakashi Iwai 		return true;
18278e3679dcSTakashi Iwai 	}
18288e3679dcSTakashi Iwai 	return false;
18298e3679dcSTakashi Iwai }
18308e3679dcSTakashi Iwai 
18314a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
18324a79616dSTakashi Iwai {
18334a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
18344a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
18355b376195STakashi Iwai 	int i;
1836c577b8a1SJoseph Chan 	hda_nid_t nid;
1837c577b8a1SJoseph Chan 
18385b376195STakashi Iwai 	spec->multiout.num_dacs = 0;
1839c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
18404a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
18413214b966STakashi Iwai 		hda_nid_t dac = 0;
1842c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
18434a79616dSTakashi Iwai 		if (!nid)
18444a79616dSTakashi Iwai 			continue;
18453214b966STakashi Iwai 		if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
18463214b966STakashi Iwai 			dac = spec->out_path[i].path[0];
18473214b966STakashi Iwai 		if (!i && parse_output_path(codec, nid, dac, 1,
18483214b966STakashi Iwai 					    &spec->out_mix_path))
18493214b966STakashi Iwai 			dac = spec->out_mix_path.path[0];
18505b376195STakashi Iwai 		if (dac)
18515b376195STakashi Iwai 			spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
18525c9a5615SLydia Wang 	}
18533214b966STakashi Iwai 	if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
18543214b966STakashi Iwai 		spec->out_path[0] = spec->out_mix_path;
18553214b966STakashi Iwai 		spec->out_mix_path.depth = 0;
18563214b966STakashi Iwai 	}
1857c577b8a1SJoseph Chan 	return 0;
1858c577b8a1SJoseph Chan }
1859c577b8a1SJoseph Chan 
18604a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
186109a9ad69STakashi Iwai 			  int chs, bool check_dac, struct nid_path *path)
1862c577b8a1SJoseph Chan {
18634a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1864c577b8a1SJoseph Chan 	char name[32];
186509a9ad69STakashi Iwai 	hda_nid_t dac, pin, sel, nid;
186609a9ad69STakashi Iwai 	int err;
1867a934d5a9STakashi Iwai 
186809a9ad69STakashi Iwai 	dac = check_dac ? path->path[0] : 0;
186909a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
187009a9ad69STakashi Iwai 	sel = path->depth > 1 ? path->path[1] : 0;
1871c577b8a1SJoseph Chan 
18728df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
18734a79616dSTakashi Iwai 		nid = dac;
18748df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
18754a79616dSTakashi Iwai 		nid = pin;
1876a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1877a934d5a9STakashi Iwai 		nid = sel;
18784a79616dSTakashi Iwai 	else
18794a79616dSTakashi Iwai 		nid = 0;
18804a79616dSTakashi Iwai 	if (nid) {
18814a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1882c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1883a00a5fadSLydia Wang 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1884c577b8a1SJoseph Chan 		if (err < 0)
1885c577b8a1SJoseph Chan 			return err;
188609a9ad69STakashi Iwai 		path->vol_ctl = nid;
1887c577b8a1SJoseph Chan 	}
18884a79616dSTakashi Iwai 
18898df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
18904a79616dSTakashi Iwai 		nid = dac;
18918df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
18924a79616dSTakashi Iwai 		nid = pin;
1893a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1894a934d5a9STakashi Iwai 		nid = sel;
18954a79616dSTakashi Iwai 	else
18964a79616dSTakashi Iwai 		nid = 0;
18974a79616dSTakashi Iwai 	if (nid) {
18984a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
18994a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
19004a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
19014a79616dSTakashi Iwai 		if (err < 0)
19024a79616dSTakashi Iwai 			return err;
190309a9ad69STakashi Iwai 		path->mute_ctl = nid;
19044a79616dSTakashi Iwai 	}
19054a79616dSTakashi Iwai 	return 0;
19064a79616dSTakashi Iwai }
19074a79616dSTakashi Iwai 
1908f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1909f4a7828bSTakashi Iwai {
1910f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1911f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
19120f98c24bSTakashi Iwai 	struct auto_pin_cfg_item *ins = cfg->inputs;
19130f98c24bSTakashi Iwai 	int i, j, nums, attr;
19140f98c24bSTakashi Iwai 	int pins[AUTO_CFG_MAX_INS];
1915f4a7828bSTakashi Iwai 
19160f98c24bSTakashi Iwai 	for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
19170f98c24bSTakashi Iwai 		nums = 0;
1918f4a7828bSTakashi Iwai 		for (i = 0; i < cfg->num_inputs; i++) {
19190f98c24bSTakashi Iwai 			unsigned int def;
19200f98c24bSTakashi Iwai 			if (ins[i].type > AUTO_PIN_LINE_IN)
19210f98c24bSTakashi Iwai 				continue;
19220f98c24bSTakashi Iwai 			def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
19230f98c24bSTakashi Iwai 			if (snd_hda_get_input_pin_attr(def) != attr)
19240f98c24bSTakashi Iwai 				continue;
19250f98c24bSTakashi Iwai 			for (j = 0; j < nums; j++)
19260f98c24bSTakashi Iwai 				if (ins[pins[j]].type < ins[i].type) {
19270f98c24bSTakashi Iwai 					memmove(pins + j + 1, pins + j,
192821d45d2bSTakashi Iwai 						(nums - j) * sizeof(int));
19290f98c24bSTakashi Iwai 					break;
19300f98c24bSTakashi Iwai 				}
19310f98c24bSTakashi Iwai 			pins[j] = i;
1932e3d7a143STakashi Iwai 			nums++;
1933e3d7a143STakashi Iwai 		}
1934e3d7a143STakashi Iwai 		if (cfg->line_outs + nums < 3)
1935f4a7828bSTakashi Iwai 			continue;
19360f98c24bSTakashi Iwai 		for (i = 0; i < nums; i++) {
19370f98c24bSTakashi Iwai 			hda_nid_t pin = ins[pins[i]].pin;
19380f98c24bSTakashi Iwai 			spec->smart51_pins[spec->smart51_nums++] = pin;
19390f98c24bSTakashi Iwai 			cfg->line_out_pins[cfg->line_outs++] = pin;
1940f4a7828bSTakashi Iwai 			if (cfg->line_outs == 3)
1941f4a7828bSTakashi Iwai 				break;
1942f4a7828bSTakashi Iwai 		}
19430f98c24bSTakashi Iwai 		return;
19440f98c24bSTakashi Iwai 	}
1945f4a7828bSTakashi Iwai }
1946f4a7828bSTakashi Iwai 
1947020066d1STakashi Iwai static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1948020066d1STakashi Iwai {
1949020066d1STakashi Iwai 	dst->vol_ctl = src->vol_ctl;
1950020066d1STakashi Iwai 	dst->mute_ctl = src->mute_ctl;
1951020066d1STakashi Iwai }
1952020066d1STakashi Iwai 
19534a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
19544a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
19554a79616dSTakashi Iwai {
19564a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1957f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
19583214b966STakashi Iwai 	struct nid_path *path;
19594a79616dSTakashi Iwai 	static const char * const chname[4] = {
196034ca8d33SDavid Henningsson 		"Front", "Surround", NULL /* "CLFE" */, "Side"
19614a79616dSTakashi Iwai 	};
19624a79616dSTakashi Iwai 	int i, idx, err;
1963f4a7828bSTakashi Iwai 	int old_line_outs;
1964f4a7828bSTakashi Iwai 
1965f4a7828bSTakashi Iwai 	/* check smart51 */
1966f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
1967f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
1968f4a7828bSTakashi Iwai 		mangle_smart51(codec);
19694a79616dSTakashi Iwai 
1970e3d7a143STakashi Iwai 	err = via_auto_fill_dac_nids(codec);
1971e3d7a143STakashi Iwai 	if (err < 0)
1972e3d7a143STakashi Iwai 		return err;
1973e3d7a143STakashi Iwai 
19745c9a5615SLydia Wang 	if (spec->multiout.num_dacs < 3) {
19755c9a5615SLydia Wang 		spec->smart51_nums = 0;
19765c9a5615SLydia Wang 		cfg->line_outs = old_line_outs;
19775c9a5615SLydia Wang 	}
19784a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
19794a79616dSTakashi Iwai 		hda_nid_t pin, dac;
19804a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
19814a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
19824a79616dSTakashi Iwai 		if (!pin || !dac)
19834a79616dSTakashi Iwai 			continue;
19843214b966STakashi Iwai 		path = spec->out_path + i;
19850fe0adf8STakashi Iwai 		if (i == HDA_CLFE) {
19863214b966STakashi Iwai 			err = create_ch_ctls(codec, "Center", 1, true, path);
19874a79616dSTakashi Iwai 			if (err < 0)
19884a79616dSTakashi Iwai 				return err;
19893214b966STakashi Iwai 			err = create_ch_ctls(codec, "LFE", 2, true, path);
19904a79616dSTakashi Iwai 			if (err < 0)
19914a79616dSTakashi Iwai 				return err;
19924a79616dSTakashi Iwai 		} else {
19936aadf41dSTakashi Iwai 			const char *pfx = chname[i];
19946aadf41dSTakashi Iwai 			if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1995f37bc7a8STakashi Iwai 			    cfg->line_outs <= 2)
1996f37bc7a8STakashi Iwai 				pfx = i ? "Bass Speaker" : "Speaker";
19973214b966STakashi Iwai 			err = create_ch_ctls(codec, pfx, 3, true, path);
19984a79616dSTakashi Iwai 			if (err < 0)
19994a79616dSTakashi Iwai 				return err;
20004a79616dSTakashi Iwai 		}
2001020066d1STakashi Iwai 		if (path != spec->out_path + i)
2002020066d1STakashi Iwai 			copy_path_mixer_ctls(&spec->out_path[i], path);
2003020066d1STakashi Iwai 		if (path == spec->out_path && spec->out_mix_path.depth)
2004020066d1STakashi Iwai 			copy_path_mixer_ctls(&spec->out_mix_path, path);
20054a79616dSTakashi Iwai 	}
20064a79616dSTakashi Iwai 
20074a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
20084a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
20094a79616dSTakashi Iwai 	if (idx >= 0) {
20104a79616dSTakashi Iwai 		/* add control to mixer */
20113214b966STakashi Iwai 		const char *name;
20123214b966STakashi Iwai 		name = spec->out_mix_path.depth ?
20133214b966STakashi Iwai 			"PCM Loopback Playback Volume" : "PCM Playback Volume";
20143214b966STakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
20154a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20164a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20174a79616dSTakashi Iwai 		if (err < 0)
20184a79616dSTakashi Iwai 			return err;
20193214b966STakashi Iwai 		name = spec->out_mix_path.depth ?
20203214b966STakashi Iwai 			"PCM Loopback Playback Switch" : "PCM Playback Switch";
20213214b966STakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
20224a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
20234a79616dSTakashi Iwai 							  idx, HDA_INPUT));
20244a79616dSTakashi Iwai 		if (err < 0)
20254a79616dSTakashi Iwai 			return err;
2026c577b8a1SJoseph Chan 	}
2027c577b8a1SJoseph Chan 
2028f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
2029f4a7828bSTakashi Iwai 
2030c577b8a1SJoseph Chan 	return 0;
2031c577b8a1SJoseph Chan }
2032c577b8a1SJoseph Chan 
20334a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
2034c577b8a1SJoseph Chan {
20354a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
203609a9ad69STakashi Iwai 	struct nid_path *path;
203718bd2c44STakashi Iwai 	bool check_dac;
20383214b966STakashi Iwai 	int i, err;
2039c577b8a1SJoseph Chan 
2040c577b8a1SJoseph Chan 	if (!pin)
2041c577b8a1SJoseph Chan 		return 0;
2042c577b8a1SJoseph Chan 
20433214b966STakashi Iwai 	if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
20443214b966STakashi Iwai 		for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
20453214b966STakashi Iwai 			if (i < spec->multiout.num_dacs &&
204625250505STakashi Iwai 			    parse_output_path(codec, pin,
20473214b966STakashi Iwai 					      spec->multiout.dac_nids[i], 0,
20483214b966STakashi Iwai 					      &spec->hp_indep_path)) {
20493214b966STakashi Iwai 				spec->hp_indep_shared = i;
20503214b966STakashi Iwai 				break;
205125250505STakashi Iwai 			}
20523214b966STakashi Iwai 		}
20533214b966STakashi Iwai 	}
20543214b966STakashi Iwai 	if (spec->hp_indep_path.depth) {
20553214b966STakashi Iwai 		spec->hp_dac_nid = spec->hp_indep_path.path[0];
20563214b966STakashi Iwai 		if (!spec->hp_indep_shared)
20573214b966STakashi Iwai 			spec->hp_path = spec->hp_indep_path;
20583214b966STakashi Iwai 	}
20593214b966STakashi Iwai 	/* optionally check front-path w/o AA-mix */
20603214b966STakashi Iwai 	if (!spec->hp_path.depth)
20613214b966STakashi Iwai 		parse_output_path(codec, pin,
20623214b966STakashi Iwai 				  spec->multiout.dac_nids[HDA_FRONT], 0,
20633214b966STakashi Iwai 				  &spec->hp_path);
20644a79616dSTakashi Iwai 
2065ece8d043STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
20663214b966STakashi Iwai 			       1, &spec->hp_mix_path) && !spec->hp_path.depth)
2067ece8d043STakashi Iwai 		return 0;
2068ece8d043STakashi Iwai 
20693214b966STakashi Iwai 	if (spec->hp_path.depth) {
207009a9ad69STakashi Iwai 		path = &spec->hp_path;
207118bd2c44STakashi Iwai 		check_dac = true;
207218bd2c44STakashi Iwai 	} else {
20733214b966STakashi Iwai 		path = &spec->hp_mix_path;
207418bd2c44STakashi Iwai 		check_dac = false;
207518bd2c44STakashi Iwai 	}
207618bd2c44STakashi Iwai 	err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
20774a79616dSTakashi Iwai 	if (err < 0)
20784a79616dSTakashi Iwai 		return err;
2079020066d1STakashi Iwai 	if (check_dac)
2080020066d1STakashi Iwai 		copy_path_mixer_ctls(&spec->hp_mix_path, path);
2081020066d1STakashi Iwai 	else
2082020066d1STakashi Iwai 		copy_path_mixer_ctls(&spec->hp_path, path);
2083020066d1STakashi Iwai 	if (spec->hp_indep_path.depth)
2084020066d1STakashi Iwai 		copy_path_mixer_ctls(&spec->hp_indep_path, path);
2085c577b8a1SJoseph Chan 	return 0;
2086c577b8a1SJoseph Chan }
2087c577b8a1SJoseph Chan 
20884a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec)
20894a918ffeSTakashi Iwai {
20904a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
20913214b966STakashi Iwai 	struct nid_path *path;
20923214b966STakashi Iwai 	bool check_dac;
209381c0a78bSWang Shaoyan 	hda_nid_t pin, dac = 0;
20943214b966STakashi Iwai 	int err;
20954a918ffeSTakashi Iwai 
20964a918ffeSTakashi Iwai 	pin = spec->autocfg.speaker_pins[0];
20974a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs || !pin)
20984a918ffeSTakashi Iwai 		return 0;
20994a918ffeSTakashi Iwai 
21003214b966STakashi Iwai 	if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
21018e3679dcSTakashi Iwai 		dac = spec->speaker_path.path[0];
21023214b966STakashi Iwai 	if (!dac)
21033214b966STakashi Iwai 		parse_output_path(codec, pin,
21043214b966STakashi Iwai 				  spec->multiout.dac_nids[HDA_FRONT], 0,
210509a9ad69STakashi Iwai 				  &spec->speaker_path);
21063214b966STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
21073214b966STakashi Iwai 			       1, &spec->speaker_mix_path) && !dac)
21083214b966STakashi Iwai 		return 0;
21094a918ffeSTakashi Iwai 
21103214b966STakashi Iwai 	/* no AA-path for front? */
21113214b966STakashi Iwai 	if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
21123214b966STakashi Iwai 		dac = 0;
21133214b966STakashi Iwai 
21143214b966STakashi Iwai 	spec->speaker_dac_nid = dac;
21153214b966STakashi Iwai 	spec->multiout.extra_out_nid[0] = dac;
21163214b966STakashi Iwai 	if (dac) {
21173214b966STakashi Iwai 		path = &spec->speaker_path;
21183214b966STakashi Iwai 		check_dac = true;
21193214b966STakashi Iwai 	} else {
21203214b966STakashi Iwai 		path = &spec->speaker_mix_path;
21213214b966STakashi Iwai 		check_dac = false;
21223214b966STakashi Iwai 	}
21233214b966STakashi Iwai 	err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
21243214b966STakashi Iwai 	if (err < 0)
21253214b966STakashi Iwai 		return err;
2126020066d1STakashi Iwai 	if (check_dac)
2127020066d1STakashi Iwai 		copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2128020066d1STakashi Iwai 	else
2129020066d1STakashi Iwai 		copy_path_mixer_ctls(&spec->speaker_path, path);
21303214b966STakashi Iwai 	return 0;
21313214b966STakashi Iwai }
21323214b966STakashi Iwai 
21333214b966STakashi Iwai #define via_aamix_ctl_info	via_pin_power_ctl_info
21343214b966STakashi Iwai 
21353214b966STakashi Iwai static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
21363214b966STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
21373214b966STakashi Iwai {
21383214b966STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21393214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
21403214b966STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->aamix_mode;
21413214b966STakashi Iwai 	return 0;
21423214b966STakashi Iwai }
21433214b966STakashi Iwai 
21443214b966STakashi Iwai static void update_aamix_paths(struct hda_codec *codec, int do_mix,
21453214b966STakashi Iwai 			       struct nid_path *nomix, struct nid_path *mix)
21463214b966STakashi Iwai {
21473214b966STakashi Iwai 	if (do_mix) {
21483214b966STakashi Iwai 		activate_output_path(codec, nomix, false, false);
21493214b966STakashi Iwai 		activate_output_path(codec, mix, true, false);
21503214b966STakashi Iwai 	} else {
21513214b966STakashi Iwai 		activate_output_path(codec, mix, false, false);
21523214b966STakashi Iwai 		activate_output_path(codec, nomix, true, false);
21533214b966STakashi Iwai 	}
21543214b966STakashi Iwai }
21553214b966STakashi Iwai 
21563214b966STakashi Iwai static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
21573214b966STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
21583214b966STakashi Iwai {
21593214b966STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
21603214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
21613214b966STakashi Iwai 	unsigned int val = ucontrol->value.enumerated.item[0];
21623214b966STakashi Iwai 
21633214b966STakashi Iwai 	if (val == spec->aamix_mode)
21643214b966STakashi Iwai 		return 0;
21653214b966STakashi Iwai 	spec->aamix_mode = val;
21663214b966STakashi Iwai 	/* update front path */
21673214b966STakashi Iwai 	update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
21683214b966STakashi Iwai 	/* update HP path */
21693214b966STakashi Iwai 	if (!spec->hp_independent_mode) {
21703214b966STakashi Iwai 		update_aamix_paths(codec, val, &spec->hp_path,
21713214b966STakashi Iwai 				   &spec->hp_mix_path);
21723214b966STakashi Iwai 	}
21733214b966STakashi Iwai 	/* update speaker path */
21743214b966STakashi Iwai 	update_aamix_paths(codec, val, &spec->speaker_path,
21753214b966STakashi Iwai 			   &spec->speaker_mix_path);
21763214b966STakashi Iwai 	return 1;
21773214b966STakashi Iwai }
21783214b966STakashi Iwai 
21793214b966STakashi Iwai static const struct snd_kcontrol_new via_aamix_ctl_enum = {
21803214b966STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
21813214b966STakashi Iwai 	.name = "Loopback Mixing",
21823214b966STakashi Iwai 	.info = via_aamix_ctl_info,
21833214b966STakashi Iwai 	.get = via_aamix_ctl_get,
21843214b966STakashi Iwai 	.put = via_aamix_ctl_put,
21853214b966STakashi Iwai };
21863214b966STakashi Iwai 
21873214b966STakashi Iwai static int via_auto_create_loopback_switch(struct hda_codec *codec)
21883214b966STakashi Iwai {
21893214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
21903214b966STakashi Iwai 
21914808d12dSTakashi Iwai 	if (!spec->aa_mix_nid)
21924808d12dSTakashi Iwai 		return 0; /* no loopback switching available */
21934808d12dSTakashi Iwai 	if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
21944808d12dSTakashi Iwai 	      spec->speaker_path.depth))
21953214b966STakashi Iwai 		return 0; /* no loopback switching available */
21963214b966STakashi Iwai 	if (!via_clone_control(spec, &via_aamix_ctl_enum))
21973214b966STakashi Iwai 		return -ENOMEM;
21984a918ffeSTakashi Iwai 	return 0;
21994a918ffeSTakashi Iwai }
22004a918ffeSTakashi Iwai 
2201a766d0d7STakashi Iwai /* look for ADCs */
2202a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2203a766d0d7STakashi Iwai {
2204a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2205a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2206a766d0d7STakashi Iwai 	int i;
2207a766d0d7STakashi Iwai 
2208a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2209a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2210a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2211a766d0d7STakashi Iwai 			continue;
2212a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2213a766d0d7STakashi Iwai 			continue;
2214a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2215a766d0d7STakashi Iwai 			continue;
2216a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2217a766d0d7STakashi Iwai 			return -ENOMEM;
2218a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2219a766d0d7STakashi Iwai 	}
2220a766d0d7STakashi Iwai 	return 0;
2221a766d0d7STakashi Iwai }
2222a766d0d7STakashi Iwai 
2223a86a88eaSTakashi Iwai /* input-src control */
2224a86a88eaSTakashi Iwai static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2225a86a88eaSTakashi Iwai 			     struct snd_ctl_elem_info *uinfo)
2226a86a88eaSTakashi Iwai {
2227a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2228a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2229a86a88eaSTakashi Iwai 
2230a86a88eaSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2231a86a88eaSTakashi Iwai 	uinfo->count = 1;
2232a86a88eaSTakashi Iwai 	uinfo->value.enumerated.items = spec->num_inputs;
2233a86a88eaSTakashi Iwai 	if (uinfo->value.enumerated.item >= spec->num_inputs)
2234a86a88eaSTakashi Iwai 		uinfo->value.enumerated.item = spec->num_inputs - 1;
2235a86a88eaSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
2236a86a88eaSTakashi Iwai 	       spec->inputs[uinfo->value.enumerated.item].label);
2237a86a88eaSTakashi Iwai 	return 0;
2238a86a88eaSTakashi Iwai }
2239a86a88eaSTakashi Iwai 
2240a86a88eaSTakashi Iwai static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2241a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
2242a86a88eaSTakashi Iwai {
2243a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2244a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2245a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2246a86a88eaSTakashi Iwai 
2247a86a88eaSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2248a86a88eaSTakashi Iwai 	return 0;
2249a86a88eaSTakashi Iwai }
2250a86a88eaSTakashi Iwai 
2251a86a88eaSTakashi Iwai static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2252a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
2253a86a88eaSTakashi Iwai {
2254a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2255a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2256a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2257a86a88eaSTakashi Iwai 	hda_nid_t mux;
2258a86a88eaSTakashi Iwai 	int cur;
2259a86a88eaSTakashi Iwai 
2260a86a88eaSTakashi Iwai 	cur = ucontrol->value.enumerated.item[0];
2261a86a88eaSTakashi Iwai 	if (cur < 0 || cur >= spec->num_inputs)
2262a86a88eaSTakashi Iwai 		return -EINVAL;
2263a86a88eaSTakashi Iwai 	if (spec->cur_mux[idx] == cur)
2264a86a88eaSTakashi Iwai 		return 0;
2265a86a88eaSTakashi Iwai 	spec->cur_mux[idx] = cur;
2266a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch) {
2267a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[cur].adc_idx;
2268a86a88eaSTakashi Iwai 		mux = spec->mux_nids[adc_idx];
2269a86a88eaSTakashi Iwai 		via_dyn_adc_pcm_resetup(codec, cur);
2270a86a88eaSTakashi Iwai 	} else {
2271a86a88eaSTakashi Iwai 		mux = spec->mux_nids[idx];
2272a86a88eaSTakashi Iwai 		if (snd_BUG_ON(!mux))
2273a86a88eaSTakashi Iwai 			return -EINVAL;
2274a86a88eaSTakashi Iwai 	}
2275a86a88eaSTakashi Iwai 
2276a86a88eaSTakashi Iwai 	if (mux) {
2277a86a88eaSTakashi Iwai 		/* switch to D0 beofre change index */
2278054d867eSTakashi Iwai 		update_power_state(codec, mux, AC_PWRST_D0);
2279a86a88eaSTakashi Iwai 		snd_hda_codec_write(codec, mux, 0,
2280a86a88eaSTakashi Iwai 				    AC_VERB_SET_CONNECT_SEL,
2281a86a88eaSTakashi Iwai 				    spec->inputs[cur].mux_idx);
2282a86a88eaSTakashi Iwai 	}
2283a86a88eaSTakashi Iwai 
2284a86a88eaSTakashi Iwai 	/* update jack power state */
2285a86a88eaSTakashi Iwai 	set_widgets_power_state(codec);
2286a86a88eaSTakashi Iwai 	return 0;
2287a86a88eaSTakashi Iwai }
2288a766d0d7STakashi Iwai 
2289d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
2290d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2291d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
2292d7a99cceSTakashi Iwai 	 * So call somewhat different..
2293d7a99cceSTakashi Iwai 	 */
2294d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
2295d7a99cceSTakashi Iwai 	.name = "Input Source",
2296d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
2297d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
2298d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
2299d7a99cceSTakashi Iwai };
2300d7a99cceSTakashi Iwai 
2301a86a88eaSTakashi Iwai static int create_input_src_ctls(struct hda_codec *codec, int count)
2302a86a88eaSTakashi Iwai {
2303a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2304a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
2305a86a88eaSTakashi Iwai 
2306a86a88eaSTakashi Iwai 	if (spec->num_inputs <= 1 || !count)
2307a86a88eaSTakashi Iwai 		return 0; /* no need for single src */
2308a86a88eaSTakashi Iwai 
2309a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_input_src_ctl);
2310a86a88eaSTakashi Iwai 	if (!knew)
2311a86a88eaSTakashi Iwai 		return -ENOMEM;
2312a86a88eaSTakashi Iwai 	knew->count = count;
2313a86a88eaSTakashi Iwai 	return 0;
2314a86a88eaSTakashi Iwai }
2315a86a88eaSTakashi Iwai 
2316a86a88eaSTakashi Iwai /* add the powersave loopback-list entry */
231713af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
231813af8e77STakashi Iwai {
231913af8e77STakashi Iwai 	struct hda_amp_list *list;
232013af8e77STakashi Iwai 
232113af8e77STakashi Iwai 	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
232213af8e77STakashi Iwai 		return;
232313af8e77STakashi Iwai 	list = spec->loopback_list + spec->num_loopbacks;
232413af8e77STakashi Iwai 	list->nid = mix;
232513af8e77STakashi Iwai 	list->dir = HDA_INPUT;
232613af8e77STakashi Iwai 	list->idx = idx;
232713af8e77STakashi Iwai 	spec->num_loopbacks++;
232813af8e77STakashi Iwai 	spec->loopback.amplist = spec->loopback_list;
232913af8e77STakashi Iwai }
233013af8e77STakashi Iwai 
2331a86a88eaSTakashi Iwai static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
23328d087c76STakashi Iwai 			     hda_nid_t dst)
2333a86a88eaSTakashi Iwai {
23348d087c76STakashi Iwai 	return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
2335a86a88eaSTakashi Iwai }
2336a86a88eaSTakashi Iwai 
2337a86a88eaSTakashi Iwai /* add the input-route to the given pin */
2338a86a88eaSTakashi Iwai static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
2339c577b8a1SJoseph Chan {
234010a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
2341a86a88eaSTakashi Iwai 	int c, idx;
2342a86a88eaSTakashi Iwai 
2343a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].adc_idx = -1;
2344a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].pin = pin;
2345a86a88eaSTakashi Iwai 	for (c = 0; c < spec->num_adc_nids; c++) {
2346a86a88eaSTakashi Iwai 		if (spec->mux_nids[c]) {
2347a86a88eaSTakashi Iwai 			idx = get_connection_index(codec, spec->mux_nids[c],
2348a86a88eaSTakashi Iwai 						   pin);
2349a86a88eaSTakashi Iwai 			if (idx < 0)
2350a86a88eaSTakashi Iwai 				continue;
2351a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs].mux_idx = idx;
2352a86a88eaSTakashi Iwai 		} else {
23538d087c76STakashi Iwai 			if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
2354a86a88eaSTakashi Iwai 				continue;
2355a86a88eaSTakashi Iwai 		}
2356a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs].adc_idx = c;
2357a86a88eaSTakashi Iwai 		/* Can primary ADC satisfy all inputs? */
2358a86a88eaSTakashi Iwai 		if (!spec->dyn_adc_switch &&
2359a86a88eaSTakashi Iwai 		    spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2360a86a88eaSTakashi Iwai 			snd_printd(KERN_INFO
2361a86a88eaSTakashi Iwai 				   "via: dynamic ADC switching enabled\n");
2362a86a88eaSTakashi Iwai 			spec->dyn_adc_switch = 1;
2363a86a88eaSTakashi Iwai 		}
2364a86a88eaSTakashi Iwai 		return true;
2365a86a88eaSTakashi Iwai 	}
2366a86a88eaSTakashi Iwai 	return false;
2367a86a88eaSTakashi Iwai }
2368a86a88eaSTakashi Iwai 
2369a86a88eaSTakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2370a86a88eaSTakashi Iwai 
2371a86a88eaSTakashi Iwai /* parse input-routes; fill ADCs, MUXs and input-src entries */
2372a86a88eaSTakashi Iwai static int parse_analog_inputs(struct hda_codec *codec)
2373a86a88eaSTakashi Iwai {
2374a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2375a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2376a86a88eaSTakashi Iwai 	int i, err;
2377a766d0d7STakashi Iwai 
2378a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2379a766d0d7STakashi Iwai 	if (err < 0)
2380a766d0d7STakashi Iwai 		return err;
2381a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2382a766d0d7STakashi Iwai 	if (err < 0)
2383a766d0d7STakashi Iwai 		return err;
2384a766d0d7STakashi Iwai 
2385a86a88eaSTakashi Iwai 	/* fill all input-routes */
2386a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2387a86a88eaSTakashi Iwai 		if (add_input_route(codec, cfg->inputs[i].pin))
2388a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs++].label =
2389a86a88eaSTakashi Iwai 				hda_get_autocfg_input_label(codec, cfg, i);
2390a86a88eaSTakashi Iwai 	}
2391a86a88eaSTakashi Iwai 
2392a86a88eaSTakashi Iwai 	/* check for internal loopback recording */
2393a86a88eaSTakashi Iwai 	if (spec->aa_mix_nid &&
2394a86a88eaSTakashi Iwai 	    add_input_route(codec, spec->aa_mix_nid))
2395a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2396a86a88eaSTakashi Iwai 
2397a86a88eaSTakashi Iwai 	return 0;
2398a86a88eaSTakashi Iwai }
2399a86a88eaSTakashi Iwai 
2400a86a88eaSTakashi Iwai /* create analog-loopback volume/switch controls */
2401a86a88eaSTakashi Iwai static int create_loopback_ctls(struct hda_codec *codec)
2402a86a88eaSTakashi Iwai {
2403a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2404a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2405a86a88eaSTakashi Iwai 	const char *prev_label = NULL;
2406a86a88eaSTakashi Iwai 	int type_idx = 0;
2407a86a88eaSTakashi Iwai 	int i, j, err, idx;
2408a86a88eaSTakashi Iwai 
2409a86a88eaSTakashi Iwai 	if (!spec->aa_mix_nid)
2410a766d0d7STakashi Iwai 		return 0;
2411c577b8a1SJoseph Chan 
24127b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2413a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2414a86a88eaSTakashi Iwai 		const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2415a86a88eaSTakashi Iwai 
24161e11cae1STakashi Iwai 		if (prev_label && !strcmp(label, prev_label))
24177b315bb4STakashi Iwai 			type_idx++;
24187b315bb4STakashi Iwai 		else
24197b315bb4STakashi Iwai 			type_idx = 0;
24201e11cae1STakashi Iwai 		prev_label = label;
2421a86a88eaSTakashi Iwai 		idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2422a86a88eaSTakashi Iwai 		if (idx >= 0) {
242316922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2424a86a88eaSTakashi Iwai 						   idx, spec->aa_mix_nid);
2425c577b8a1SJoseph Chan 			if (err < 0)
2426c577b8a1SJoseph Chan 				return err;
2427a86a88eaSTakashi Iwai 			add_loopback_list(spec, spec->aa_mix_nid, idx);
242813af8e77STakashi Iwai 		}
2429e3d7a143STakashi Iwai 
2430e3d7a143STakashi Iwai 		/* remember the label for smart51 control */
2431e3d7a143STakashi Iwai 		for (j = 0; j < spec->smart51_nums; j++) {
2432a86a88eaSTakashi Iwai 			if (spec->smart51_pins[j] == pin) {
2433e3d7a143STakashi Iwai 				spec->smart51_idxs[j] = idx;
2434e3d7a143STakashi Iwai 				spec->smart51_labels[j] = label;
2435e3d7a143STakashi Iwai 				break;
2436e3d7a143STakashi Iwai 			}
2437e3d7a143STakashi Iwai 		}
2438c577b8a1SJoseph Chan 	}
2439a86a88eaSTakashi Iwai 	return 0;
2440a86a88eaSTakashi Iwai }
2441a86a88eaSTakashi Iwai 
2442a86a88eaSTakashi Iwai /* create mic-boost controls (if present) */
2443a86a88eaSTakashi Iwai static int create_mic_boost_ctls(struct hda_codec *codec)
2444a86a88eaSTakashi Iwai {
2445a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2446a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
24478d8bbc6fSTakashi Iwai 	const char *prev_label = NULL;
24488d8bbc6fSTakashi Iwai 	int type_idx = 0;
2449a86a88eaSTakashi Iwai 	int i, err;
2450a86a88eaSTakashi Iwai 
2451a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2452a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2453a86a88eaSTakashi Iwai 		unsigned int caps;
2454a86a88eaSTakashi Iwai 		const char *label;
2455a86a88eaSTakashi Iwai 		char name[32];
2456a86a88eaSTakashi Iwai 
2457a86a88eaSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
2458a86a88eaSTakashi Iwai 			continue;
2459a86a88eaSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
2460a86a88eaSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2461a86a88eaSTakashi Iwai 			continue;
2462a86a88eaSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
24638d8bbc6fSTakashi Iwai 		if (prev_label && !strcmp(label, prev_label))
24648d8bbc6fSTakashi Iwai 			type_idx++;
24658d8bbc6fSTakashi Iwai 		else
24668d8bbc6fSTakashi Iwai 			type_idx = 0;
24678d8bbc6fSTakashi Iwai 		prev_label = label;
2468a86a88eaSTakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Volume", label);
24698d8bbc6fSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
2470a86a88eaSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2471a86a88eaSTakashi Iwai 		if (err < 0)
2472a86a88eaSTakashi Iwai 			return err;
2473a86a88eaSTakashi Iwai 	}
2474a86a88eaSTakashi Iwai 	return 0;
2475a86a88eaSTakashi Iwai }
2476a86a88eaSTakashi Iwai 
2477a86a88eaSTakashi Iwai /* create capture and input-src controls for multiple streams */
2478a86a88eaSTakashi Iwai static int create_multi_adc_ctls(struct hda_codec *codec)
2479a86a88eaSTakashi Iwai {
2480a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2481a86a88eaSTakashi Iwai 	int i, err;
2482d7a99cceSTakashi Iwai 
2483d7a99cceSTakashi Iwai 	/* create capture mixer elements */
2484d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2485d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
2486d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2487d7a99cceSTakashi Iwai 					"Capture Volume", i,
2488d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2489d7a99cceSTakashi Iwai 							    HDA_INPUT));
2490d7a99cceSTakashi Iwai 		if (err < 0)
2491d7a99cceSTakashi Iwai 			return err;
2492d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2493d7a99cceSTakashi Iwai 					"Capture Switch", i,
2494d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2495d7a99cceSTakashi Iwai 							    HDA_INPUT));
2496d7a99cceSTakashi Iwai 		if (err < 0)
2497d7a99cceSTakashi Iwai 			return err;
2498d7a99cceSTakashi Iwai 	}
2499d7a99cceSTakashi Iwai 
2500d7a99cceSTakashi Iwai 	/* input-source control */
2501d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2502d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
2503d7a99cceSTakashi Iwai 			break;
2504a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, i);
2505d7a99cceSTakashi Iwai 	if (err < 0)
2506d7a99cceSTakashi Iwai 		return err;
2507a86a88eaSTakashi Iwai 	return 0;
2508d7a99cceSTakashi Iwai }
2509d7a99cceSTakashi Iwai 
2510a86a88eaSTakashi Iwai /* bind capture volume/switch */
2511a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2512a86a88eaSTakashi Iwai 	HDA_BIND_VOL("Capture Volume", 0);
2513a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2514a86a88eaSTakashi Iwai 	HDA_BIND_SW("Capture Switch", 0);
2515a86a88eaSTakashi Iwai 
2516a86a88eaSTakashi Iwai static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2517a86a88eaSTakashi Iwai 			 struct hda_ctl_ops *ops)
2518a86a88eaSTakashi Iwai {
2519a86a88eaSTakashi Iwai 	struct hda_bind_ctls *ctl;
2520a86a88eaSTakashi Iwai 	int i;
2521a86a88eaSTakashi Iwai 
2522a86a88eaSTakashi Iwai 	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2523a86a88eaSTakashi Iwai 	if (!ctl)
2524a86a88eaSTakashi Iwai 		return -ENOMEM;
2525a86a88eaSTakashi Iwai 	ctl->ops = ops;
2526a86a88eaSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2527a86a88eaSTakashi Iwai 		ctl->values[i] =
2528a86a88eaSTakashi Iwai 			HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2529a86a88eaSTakashi Iwai 	*ctl_ret = ctl;
2530a86a88eaSTakashi Iwai 	return 0;
2531a86a88eaSTakashi Iwai }
2532a86a88eaSTakashi Iwai 
2533a86a88eaSTakashi Iwai /* create capture and input-src controls for dynamic ADC-switch case */
2534a86a88eaSTakashi Iwai static int create_dyn_adc_ctls(struct hda_codec *codec)
2535a86a88eaSTakashi Iwai {
2536a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2537a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
2538a86a88eaSTakashi Iwai 	int err;
2539a86a88eaSTakashi Iwai 
2540a86a88eaSTakashi Iwai 	/* set up the bind capture ctls */
2541a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2542a86a88eaSTakashi Iwai 	if (err < 0)
2543a86a88eaSTakashi Iwai 		return err;
2544a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2545a86a88eaSTakashi Iwai 	if (err < 0)
2546a86a88eaSTakashi Iwai 		return err;
2547a86a88eaSTakashi Iwai 
2548a86a88eaSTakashi Iwai 	/* create capture mixer elements */
2549a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2550a86a88eaSTakashi Iwai 	if (!knew)
2551a86a88eaSTakashi Iwai 		return -ENOMEM;
2552a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_vol;
2553a86a88eaSTakashi Iwai 
2554a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2555a86a88eaSTakashi Iwai 	if (!knew)
2556a86a88eaSTakashi Iwai 		return -ENOMEM;
2557a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_sw;
2558a86a88eaSTakashi Iwai 
2559a86a88eaSTakashi Iwai 	/* input-source control */
2560a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, 1);
2561a86a88eaSTakashi Iwai 	if (err < 0)
2562a86a88eaSTakashi Iwai 		return err;
2563a86a88eaSTakashi Iwai 	return 0;
2564a86a88eaSTakashi Iwai }
2565a86a88eaSTakashi Iwai 
2566a86a88eaSTakashi Iwai /* parse and create capture-related stuff */
2567a86a88eaSTakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2568a86a88eaSTakashi Iwai {
2569a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2570a86a88eaSTakashi Iwai 	int err;
2571a86a88eaSTakashi Iwai 
2572a86a88eaSTakashi Iwai 	err = parse_analog_inputs(codec);
2573a86a88eaSTakashi Iwai 	if (err < 0)
2574a86a88eaSTakashi Iwai 		return err;
2575a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch)
2576a86a88eaSTakashi Iwai 		err = create_dyn_adc_ctls(codec);
2577a86a88eaSTakashi Iwai 	else
2578a86a88eaSTakashi Iwai 		err = create_multi_adc_ctls(codec);
2579a86a88eaSTakashi Iwai 	if (err < 0)
2580a86a88eaSTakashi Iwai 		return err;
2581a86a88eaSTakashi Iwai 	err = create_loopback_ctls(codec);
2582a86a88eaSTakashi Iwai 	if (err < 0)
2583a86a88eaSTakashi Iwai 		return err;
2584a86a88eaSTakashi Iwai 	err = create_mic_boost_ctls(codec);
2585a86a88eaSTakashi Iwai 	if (err < 0)
2586a86a88eaSTakashi Iwai 		return err;
2587c577b8a1SJoseph Chan 	return 0;
2588c577b8a1SJoseph Chan }
2589c577b8a1SJoseph Chan 
259076d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
259176d9b0ddSHarald Welte {
259276d9b0ddSHarald Welte 	unsigned int def_conf;
259376d9b0ddSHarald Welte 	unsigned char seqassoc;
259476d9b0ddSHarald Welte 
25952f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
259676d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
259776d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
259882ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
259982ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
260076d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
26012f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
260276d9b0ddSHarald Welte 	}
260376d9b0ddSHarald Welte 
260476d9b0ddSHarald Welte 	return;
260576d9b0ddSHarald Welte }
260676d9b0ddSHarald Welte 
2607e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
26081f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
26091f2e99feSLydia Wang {
26101f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
26111f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
26121f2e99feSLydia Wang 
26131f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
26141f2e99feSLydia Wang 		return 0;
2615e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
26161f2e99feSLydia Wang 	return 0;
26171f2e99feSLydia Wang }
26181f2e99feSLydia Wang 
2619e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
26201f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
26211f2e99feSLydia Wang {
26221f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
26231f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
2624187d333eSTakashi Iwai 	int val;
26251f2e99feSLydia Wang 
26261f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
26271f2e99feSLydia Wang 		return 0;
2628187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
2629187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
2630187d333eSTakashi Iwai 		return 0;
2631187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
2632187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect &&
2633187d333eSTakashi Iwai 	    snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
26341f2e99feSLydia Wang 		mute_aa_path(codec, 1);
26351f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
26361f2e99feSLydia Wang 	}
2637187d333eSTakashi Iwai 	via_hp_automute(codec);
2638187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
2639187d333eSTakashi Iwai 	return 1;
26401f2e99feSLydia Wang }
26411f2e99feSLydia Wang 
2642e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
26431f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26441f2e99feSLydia Wang 	.name = "Jack Detect",
26451f2e99feSLydia Wang 	.count = 1,
26461f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2647e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2648e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
26491f2e99feSLydia Wang };
26501f2e99feSLydia Wang 
265112daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec);
265212daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec);
265312daef65STakashi Iwai 
265412daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
2655c577b8a1SJoseph Chan {
2656c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2657c577b8a1SJoseph Chan 	int err;
2658c577b8a1SJoseph Chan 
2659c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2660c577b8a1SJoseph Chan 	if (err < 0)
2661c577b8a1SJoseph Chan 		return err;
2662c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
26637f0df88cSTakashi Iwai 		return -EINVAL;
2664c577b8a1SJoseph Chan 
26654a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2666c577b8a1SJoseph Chan 	if (err < 0)
2667c577b8a1SJoseph Chan 		return err;
26684a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2669c577b8a1SJoseph Chan 	if (err < 0)
2670c577b8a1SJoseph Chan 		return err;
26714a918ffeSTakashi Iwai 	err = via_auto_create_speaker_ctls(codec);
26724a918ffeSTakashi Iwai 	if (err < 0)
26734a918ffeSTakashi Iwai 		return err;
26743214b966STakashi Iwai 	err = via_auto_create_loopback_switch(codec);
26753214b966STakashi Iwai 	if (err < 0)
26763214b966STakashi Iwai 		return err;
2677a86a88eaSTakashi Iwai 	err = via_auto_create_analog_input_ctls(codec);
2678c577b8a1SJoseph Chan 	if (err < 0)
2679c577b8a1SJoseph Chan 		return err;
2680c577b8a1SJoseph Chan 
2681c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2682c577b8a1SJoseph Chan 
268312daef65STakashi Iwai 	fill_dig_outs(codec);
268412daef65STakashi Iwai 	fill_dig_in(codec);
2685c577b8a1SJoseph Chan 
2686603c4019STakashi Iwai 	if (spec->kctls.list)
2687603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2688c577b8a1SJoseph Chan 
2689c577b8a1SJoseph Chan 
26903214b966STakashi Iwai 	if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
2691ece8d043STakashi Iwai 		err = via_hp_build(codec);
2692ece8d043STakashi Iwai 		if (err < 0)
2693ece8d043STakashi Iwai 			return err;
2694ece8d043STakashi Iwai 	}
2695c577b8a1SJoseph Chan 
2696f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2697f4a7828bSTakashi Iwai 	if (err < 0)
2698f4a7828bSTakashi Iwai 		return err;
2699f4a7828bSTakashi Iwai 
27005d41762aSTakashi Iwai 	/* assign slave outs */
27015d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
27025d41762aSTakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
27035d41762aSTakashi Iwai 
2704c577b8a1SJoseph Chan 	return 1;
2705c577b8a1SJoseph Chan }
2706c577b8a1SJoseph Chan 
27075d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec)
2708c577b8a1SJoseph Chan {
270925eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
27105d41762aSTakashi Iwai 	if (spec->multiout.dig_out_nid)
27115d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
27125d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
27135d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
27145d41762aSTakashi Iwai }
271525eaba2fSLydia Wang 
27165d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec)
27175d41762aSTakashi Iwai {
27185d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
27195d41762aSTakashi Iwai 	if (!spec->dig_in_nid)
27205d41762aSTakashi Iwai 		return;
2721cdd03cedSTakashi Iwai 	snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
27225d41762aSTakashi Iwai }
27235d41762aSTakashi Iwai 
27244e2d16d3SDavid Henningsson static void via_jack_output_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
27254e2d16d3SDavid Henningsson {
27264e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
27274e2d16d3SDavid Henningsson 	via_hp_automute(codec);
27284e2d16d3SDavid Henningsson }
27294e2d16d3SDavid Henningsson 
27304e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
27314e2d16d3SDavid Henningsson {
27324e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
27334e2d16d3SDavid Henningsson }
27344e2d16d3SDavid Henningsson 
27354a918ffeSTakashi Iwai /* initialize the unsolicited events */
27364a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec)
27374a918ffeSTakashi Iwai {
27384a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
27394a918ffeSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
27404a918ffeSTakashi Iwai 	unsigned int ev;
27414a918ffeSTakashi Iwai 	int i;
27424e2d16d3SDavid Henningsson 	hda_jack_callback cb;
27434a918ffeSTakashi Iwai 
27444a918ffeSTakashi Iwai 	if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
27454e2d16d3SDavid Henningsson 		snd_hda_jack_detect_enable_callback(codec, cfg->hp_pins[0],
27464e2d16d3SDavid Henningsson 						    VIA_HP_EVENT | VIA_JACK_EVENT,
27474e2d16d3SDavid Henningsson 						    via_jack_output_event);
27484a918ffeSTakashi Iwai 
27494a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
27504a918ffeSTakashi Iwai 		ev = VIA_LINE_EVENT;
27514a918ffeSTakashi Iwai 	else
27524a918ffeSTakashi Iwai 		ev = 0;
27534e2d16d3SDavid Henningsson 	cb = ev ? via_jack_output_event : via_jack_powerstate_event;
27544e2d16d3SDavid Henningsson 
27554a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
27564a918ffeSTakashi Iwai 		if (cfg->line_out_pins[i] &&
27574a918ffeSTakashi Iwai 		    is_jack_detectable(codec, cfg->line_out_pins[i]))
27584e2d16d3SDavid Henningsson 			snd_hda_jack_detect_enable_callback(codec, cfg->line_out_pins[i],
27594e2d16d3SDavid Henningsson 							    ev | VIA_JACK_EVENT, cb);
27604a918ffeSTakashi Iwai 	}
27614a918ffeSTakashi Iwai 
27624a918ffeSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
27634a918ffeSTakashi Iwai 		if (is_jack_detectable(codec, cfg->inputs[i].pin))
27644e2d16d3SDavid Henningsson 			snd_hda_jack_detect_enable_callback(codec, cfg->inputs[i].pin,
27654e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
27664e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
27674a918ffeSTakashi Iwai 	}
27684a918ffeSTakashi Iwai }
27694a918ffeSTakashi Iwai 
27705d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
27715d41762aSTakashi Iwai {
27725d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
27735d41762aSTakashi Iwai 	int i;
27745d41762aSTakashi Iwai 
27755d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
27765d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
27775d41762aSTakashi Iwai 
2778e9d010c2STakashi Iwai 	/* init power states */
2779e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
2780e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
2781e9d010c2STakashi Iwai 
2782c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2783c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
27844a918ffeSTakashi Iwai 	via_auto_init_speaker_out(codec);
2785c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
27865d41762aSTakashi Iwai 	via_auto_init_dig_outs(codec);
27875d41762aSTakashi Iwai 	via_auto_init_dig_in(codec);
278811890956SLydia Wang 
27894a918ffeSTakashi Iwai 	via_auto_init_unsol_event(codec);
27904a918ffeSTakashi Iwai 
279125eaba2fSLydia Wang 	via_hp_automute(codec);
2792187d333eSTakashi Iwai 	vt1708_update_hp_work(spec);
279325eaba2fSLydia Wang 
2794c577b8a1SJoseph Chan 	return 0;
2795c577b8a1SJoseph Chan }
2796c577b8a1SJoseph Chan 
27971f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
27981f2e99feSLydia Wang {
27991f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
28001f2e99feSLydia Wang 					     vt1708_hp_work.work);
28011f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
28021f2e99feSLydia Wang 		return;
28031835a0f9STakashi Iwai 	snd_hda_jack_set_dirty_all(spec->codec);
28041f2e99feSLydia Wang 	/* if jack state toggled */
28051f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2806d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
28071f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
28081f2e99feSLydia Wang 		via_hp_automute(spec->codec);
28091f2e99feSLydia Wang 	}
2810187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect)
2811187d333eSTakashi Iwai 		schedule_delayed_work(&spec->vt1708_hp_work,
2812187d333eSTakashi Iwai 				      msecs_to_jiffies(100));
28131f2e99feSLydia Wang }
28141f2e99feSLydia Wang 
2815337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2816337b9d02STakashi Iwai {
2817337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2818337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2819337b9d02STakashi Iwai 	unsigned int type;
2820337b9d02STakashi Iwai 	int i, n;
2821337b9d02STakashi Iwai 
2822337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2823337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2824337b9d02STakashi Iwai 		while (nid) {
2825a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
28261c55d521STakashi Iwai 			if (type == AC_WID_PIN)
28271c55d521STakashi Iwai 				break;
2828337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2829337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2830337b9d02STakashi Iwai 			if (n <= 0)
2831337b9d02STakashi Iwai 				break;
2832337b9d02STakashi Iwai 			if (n > 1) {
2833337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2834337b9d02STakashi Iwai 				break;
2835337b9d02STakashi Iwai 			}
2836337b9d02STakashi Iwai 			nid = conn[0];
2837337b9d02STakashi Iwai 		}
2838337b9d02STakashi Iwai 	}
28391c55d521STakashi Iwai 	return 0;
2840337b9d02STakashi Iwai }
2841337b9d02STakashi Iwai 
2842c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2843c577b8a1SJoseph Chan {
2844c577b8a1SJoseph Chan 	struct via_spec *spec;
2845c577b8a1SJoseph Chan 	int err;
2846c577b8a1SJoseph Chan 
2847c577b8a1SJoseph Chan 	/* create a codec specific record */
28485b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2849c577b8a1SJoseph Chan 	if (spec == NULL)
2850c577b8a1SJoseph Chan 		return -ENOMEM;
2851c577b8a1SJoseph Chan 
2852620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2853620e2b28STakashi Iwai 
285412daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
285512daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
285612daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
285712daef65STakashi Iwai 
2858c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
285912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2860c577b8a1SJoseph Chan 	if (err < 0) {
2861c577b8a1SJoseph Chan 		via_free(codec);
2862c577b8a1SJoseph Chan 		return err;
2863c577b8a1SJoseph Chan 	}
2864c577b8a1SJoseph Chan 
286512daef65STakashi Iwai 	/* add jack detect on/off control */
286612daef65STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
286712daef65STakashi Iwai 		return -ENOMEM;
286812daef65STakashi Iwai 
2869bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2870bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2871bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2872c577b8a1SJoseph Chan 
2873e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2874e322a36dSLydia Wang 
2875c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2876c577b8a1SJoseph Chan 
28771f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2878c577b8a1SJoseph Chan 	return 0;
2879c577b8a1SJoseph Chan }
2880c577b8a1SJoseph Chan 
2881ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
2882c577b8a1SJoseph Chan {
2883c577b8a1SJoseph Chan 	struct via_spec *spec;
2884c577b8a1SJoseph Chan 	int err;
2885c577b8a1SJoseph Chan 
2886c577b8a1SJoseph Chan 	/* create a codec specific record */
28875b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2888c577b8a1SJoseph Chan 	if (spec == NULL)
2889c577b8a1SJoseph Chan 		return -ENOMEM;
2890c577b8a1SJoseph Chan 
2891620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2892620e2b28STakashi Iwai 
289312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2894c577b8a1SJoseph Chan 	if (err < 0) {
2895c577b8a1SJoseph Chan 		via_free(codec);
2896c577b8a1SJoseph Chan 		return err;
2897c577b8a1SJoseph Chan 	}
2898c577b8a1SJoseph Chan 
2899c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2900c577b8a1SJoseph Chan 
2901f7278fd0SJosepch Chan 	return 0;
2902f7278fd0SJosepch Chan }
2903f7278fd0SJosepch Chan 
29043e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
29053e95b9abSLydia Wang {
29063e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
29073e95b9abSLydia Wang 	int imux_is_smixer;
29083e95b9abSLydia Wang 	unsigned int parm;
29093e95b9abSLydia Wang 	int is_8ch = 0;
2910bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2911bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
29123e95b9abSLydia Wang 		is_8ch = 1;
29133e95b9abSLydia Wang 
29143e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
29153e95b9abSLydia Wang 	imux_is_smixer =
29163e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
29173e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
29183e95b9abSLydia Wang 	/* inputs */
29193e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
29203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
29223e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
29233e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
29243e95b9abSLydia Wang 	if (imux_is_smixer)
29253e95b9abSLydia Wang 		parm = AC_PWRST_D0;
29263e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
2927054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
2928054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
2929054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
29303e95b9abSLydia Wang 
29313e95b9abSLydia Wang 	/* outputs */
29323e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
29333e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
29353e95b9abSLydia Wang 	if (spec->smart51_enabled)
29363e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
2937054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
2938054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
29393e95b9abSLydia Wang 
29403e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
29413e95b9abSLydia Wang 	if (is_8ch) {
29423e95b9abSLydia Wang 		parm = AC_PWRST_D3;
29433e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
29443e95b9abSLydia Wang 		if (spec->smart51_enabled)
29453e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2946054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
2947054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
2948bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2949bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2950bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2951bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2952bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2953bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2954054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
2955054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
29563e95b9abSLydia Wang 	}
29573e95b9abSLydia Wang 
29583e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
29593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29603e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
29613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
29623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
29633e95b9abSLydia Wang 	if (is_8ch)
29643e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
29653e95b9abSLydia Wang 
29663e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2967054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2968054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
29693e95b9abSLydia Wang 	if (is_8ch) {
2970054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
2971054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
2972bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2973054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
29743e95b9abSLydia Wang }
29753e95b9abSLydia Wang 
2976518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2977ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
2978f7278fd0SJosepch Chan {
2979f7278fd0SJosepch Chan 	struct via_spec *spec;
2980f7278fd0SJosepch Chan 	int err;
2981f7278fd0SJosepch Chan 
2982518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2983518bf3baSLydia Wang 		return patch_vt1708S(codec);
2984ddd304d8STakashi Iwai 
2985f7278fd0SJosepch Chan 	/* create a codec specific record */
29865b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2987f7278fd0SJosepch Chan 	if (spec == NULL)
2988f7278fd0SJosepch Chan 		return -ENOMEM;
2989f7278fd0SJosepch Chan 
2990620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2991620e2b28STakashi Iwai 
2992f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
299312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2994f7278fd0SJosepch Chan 	if (err < 0) {
2995f7278fd0SJosepch Chan 		via_free(codec);
2996f7278fd0SJosepch Chan 		return err;
2997f7278fd0SJosepch Chan 	}
2998f7278fd0SJosepch Chan 
2999f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
3000f7278fd0SJosepch Chan 
30013e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
30023e95b9abSLydia Wang 
3003f7278fd0SJosepch Chan 	return 0;
3004f7278fd0SJosepch Chan }
3005f7278fd0SJosepch Chan 
3006d949cac1SHarald Welte /* Patch for VT1708S */
3007096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
3008d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
3009d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
3010bc7e7e5cSLydia Wang 	/* don't bybass mixer */
3011bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
3012d949cac1SHarald Welte 	{ }
3013d949cac1SHarald Welte };
3014d949cac1SHarald Welte 
30159da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
30169da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
30179da29271STakashi Iwai {
30189da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
30199da29271STakashi Iwai 	int i;
30209da29271STakashi Iwai 
30219da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
30229da29271STakashi Iwai 		hda_nid_t nid;
30239da29271STakashi Iwai 		int conn;
30249da29271STakashi Iwai 
30259da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
30269da29271STakashi Iwai 		if (!nid)
30279da29271STakashi Iwai 			continue;
30289da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
30299da29271STakashi Iwai 		if (conn < 1)
30309da29271STakashi Iwai 			continue;
30319da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
30329da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
30339da29271STakashi Iwai 		else {
30349da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
30359da29271STakashi Iwai 			break; /* at most two dig outs */
30369da29271STakashi Iwai 		}
30379da29271STakashi Iwai 	}
30389da29271STakashi Iwai }
30399da29271STakashi Iwai 
304012daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec)
3041d949cac1SHarald Welte {
3042d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
304312daef65STakashi Iwai 	hda_nid_t dig_nid;
304412daef65STakashi Iwai 	int i, err;
3045d949cac1SHarald Welte 
304612daef65STakashi Iwai 	if (!spec->autocfg.dig_in_pin)
304712daef65STakashi Iwai 		return;
3048d949cac1SHarald Welte 
304912daef65STakashi Iwai 	dig_nid = codec->start_nid;
305012daef65STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
305112daef65STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, dig_nid);
305212daef65STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
305312daef65STakashi Iwai 			continue;
305412daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_DIGITAL))
305512daef65STakashi Iwai 			continue;
305612daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
305712daef65STakashi Iwai 			continue;
305812daef65STakashi Iwai 		err = get_connection_index(codec, dig_nid,
305912daef65STakashi Iwai 					   spec->autocfg.dig_in_pin);
306012daef65STakashi Iwai 		if (err >= 0) {
306112daef65STakashi Iwai 			spec->dig_in_nid = dig_nid;
306212daef65STakashi Iwai 			break;
306312daef65STakashi Iwai 		}
306412daef65STakashi Iwai 	}
3065d949cac1SHarald Welte }
3066d949cac1SHarald Welte 
30676369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
30686369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
30696369bcfcSLydia Wang {
30706369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
30716369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
30726369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
30736369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
30746369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
30756369bcfcSLydia Wang }
30766369bcfcSLydia Wang 
3077d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
3078d949cac1SHarald Welte {
3079d949cac1SHarald Welte 	struct via_spec *spec;
3080d949cac1SHarald Welte 	int err;
3081d949cac1SHarald Welte 
3082d949cac1SHarald Welte 	/* create a codec specific record */
30835b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3084d949cac1SHarald Welte 	if (spec == NULL)
3085d949cac1SHarald Welte 		return -ENOMEM;
3086d949cac1SHarald Welte 
3087620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3088d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3089d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3090620e2b28STakashi Iwai 
3091d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
309212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3093d949cac1SHarald Welte 	if (err < 0) {
3094d949cac1SHarald Welte 		via_free(codec);
3095d949cac1SHarald Welte 		return err;
3096d949cac1SHarald Welte 	}
3097d949cac1SHarald Welte 
3098096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
3099d949cac1SHarald Welte 
3100d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3101d949cac1SHarald Welte 
3102518bf3baSLydia Wang 	/* correct names for VT1708BCE */
3103518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
3104518bf3baSLydia Wang 		kfree(codec->chip_name);
3105518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3106518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
3107518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
3108518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3109970f630fSLydia Wang 	}
3110bc92df7fSLydia Wang 	/* correct names for VT1705 */
3111bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
3112bc92df7fSLydia Wang 		kfree(codec->chip_name);
3113bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3114bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
3115bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
3116bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
3117bc92df7fSLydia Wang 	}
31183e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
3119d949cac1SHarald Welte 	return 0;
3120d949cac1SHarald Welte }
3121d949cac1SHarald Welte 
3122d949cac1SHarald Welte /* Patch for VT1702 */
3123d949cac1SHarald Welte 
3124096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
3125bc7e7e5cSLydia Wang 	/* mixer enable */
3126bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3127bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3128bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3129d949cac1SHarald Welte 	{ }
3130d949cac1SHarald Welte };
3131d949cac1SHarald Welte 
31323e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
31333e95b9abSLydia Wang {
31343e95b9abSLydia Wang 	int imux_is_smixer =
31353e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
31363e95b9abSLydia Wang 	unsigned int parm;
31373e95b9abSLydia Wang 	/* inputs */
31383e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
31393e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
31413e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
31423e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
31433e95b9abSLydia Wang 	if (imux_is_smixer)
31443e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
31453e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3146054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
3147054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
3148054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
3149054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
31503e95b9abSLydia Wang 
31513e95b9abSLydia Wang 	/* outputs */
31523e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
31533e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
31553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
31563e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
3157054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3158054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
3159054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
31603e95b9abSLydia Wang }
31613e95b9abSLydia Wang 
3162d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
3163d949cac1SHarald Welte {
3164d949cac1SHarald Welte 	struct via_spec *spec;
3165d949cac1SHarald Welte 	int err;
3166d949cac1SHarald Welte 
3167d949cac1SHarald Welte 	/* create a codec specific record */
31685b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3169d949cac1SHarald Welte 	if (spec == NULL)
3170d949cac1SHarald Welte 		return -ENOMEM;
3171d949cac1SHarald Welte 
3172620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
3173620e2b28STakashi Iwai 
317412daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
317512daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
317612daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
317712daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
317812daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
317912daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
318012daef65STakashi Iwai 
3181d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
318212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3183d949cac1SHarald Welte 	if (err < 0) {
3184d949cac1SHarald Welte 		via_free(codec);
3185d949cac1SHarald Welte 		return err;
3186d949cac1SHarald Welte 	}
3187d949cac1SHarald Welte 
3188096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
3189d949cac1SHarald Welte 
3190d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3191d949cac1SHarald Welte 
31923e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
3193d949cac1SHarald Welte 	return 0;
3194d949cac1SHarald Welte }
3195d949cac1SHarald Welte 
3196eb7188caSLydia Wang /* Patch for VT1718S */
3197eb7188caSLydia Wang 
3198096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
31994ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
32004ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
3201eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
3202eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
32035d41762aSTakashi Iwai 
3204eb7188caSLydia Wang 	{ }
3205eb7188caSLydia Wang };
3206eb7188caSLydia Wang 
32073e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
32083e95b9abSLydia Wang {
32093e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
32103e95b9abSLydia Wang 	int imux_is_smixer;
32116162552bSTakashi Iwai 	unsigned int parm, parm2;
32123e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
32133e95b9abSLydia Wang 	imux_is_smixer =
32143e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
32153e95b9abSLydia Wang 	/* inputs */
32163e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
32173e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32183e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
32193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
32203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
32213e95b9abSLydia Wang 	if (imux_is_smixer)
32223e95b9abSLydia Wang 		parm = AC_PWRST_D0;
32233e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3224054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
3225054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
3226054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
3227054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
32283e95b9abSLydia Wang 
32293e95b9abSLydia Wang 	/* outputs */
32303e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
32313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
3233054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
32346162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
32353e95b9abSLydia Wang 
32363e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
32373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
32393e95b9abSLydia Wang 	if (spec->smart51_enabled)
32403e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
3241054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
32423e95b9abSLydia Wang 
32433e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
32443e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32453e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
32463e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
32473e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
3248054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
32496162552bSTakashi Iwai 	if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
32506162552bSTakashi Iwai 		parm = parm2;
32516162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
32523e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3253054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
32543e95b9abSLydia Wang 
32553e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
32563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
32583e95b9abSLydia Wang 	if (spec->smart51_enabled)
32593e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
3260054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
32613e95b9abSLydia Wang 
32623e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
32633e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
32643e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32653e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
3266054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
3267054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
3268054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
32693e95b9abSLydia Wang 	}
32703e95b9abSLydia Wang }
32713e95b9abSLydia Wang 
327230b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
327330b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
327430b45033STakashi Iwai  */
327530b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
327630b45033STakashi Iwai {
327730b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
327830b45033STakashi Iwai 	int i, nums;
327930b45033STakashi Iwai 	hda_nid_t conn[8];
328030b45033STakashi Iwai 	hda_nid_t nid;
328130b45033STakashi Iwai 
328230b45033STakashi Iwai 	if (!spec->aa_mix_nid)
328330b45033STakashi Iwai 		return 0;
328430b45033STakashi Iwai 	nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
328530b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
328630b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
328730b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
328830b45033STakashi Iwai 			return 0;
328930b45033STakashi Iwai 	}
329030b45033STakashi Iwai 
329130b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
329230b45033STakashi Iwai 	nid = codec->start_nid;
329330b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
329430b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
329530b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
329630b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
329730b45033STakashi Iwai 			conn[nums++] = nid;
329830b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
329930b45033STakashi Iwai 							  spec->aa_mix_nid,
330030b45033STakashi Iwai 							  nums, conn);
330130b45033STakashi Iwai 		}
330230b45033STakashi Iwai 	}
330330b45033STakashi Iwai 	return 0;
330430b45033STakashi Iwai }
330530b45033STakashi Iwai 
330630b45033STakashi Iwai 
3307eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
3308eb7188caSLydia Wang {
3309eb7188caSLydia Wang 	struct via_spec *spec;
3310eb7188caSLydia Wang 	int err;
3311eb7188caSLydia Wang 
3312eb7188caSLydia Wang 	/* create a codec specific record */
33135b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3314eb7188caSLydia Wang 	if (spec == NULL)
3315eb7188caSLydia Wang 		return -ENOMEM;
3316eb7188caSLydia Wang 
3317620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3318d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3319d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
332030b45033STakashi Iwai 	add_secret_dac_path(codec);
3321620e2b28STakashi Iwai 
3322eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
332312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3324eb7188caSLydia Wang 	if (err < 0) {
3325eb7188caSLydia Wang 		via_free(codec);
3326eb7188caSLydia Wang 		return err;
3327eb7188caSLydia Wang 	}
3328eb7188caSLydia Wang 
3329096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
3330eb7188caSLydia Wang 
3331eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
3332eb7188caSLydia Wang 
33333e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
33343e95b9abSLydia Wang 
3335eb7188caSLydia Wang 	return 0;
3336eb7188caSLydia Wang }
3337f3db423dSLydia Wang 
3338f3db423dSLydia Wang /* Patch for VT1716S */
3339f3db423dSLydia Wang 
3340f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3341f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
3342f3db423dSLydia Wang {
3343f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3344f3db423dSLydia Wang 	uinfo->count = 1;
3345f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
3346f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
3347f3db423dSLydia Wang 	return 0;
3348f3db423dSLydia Wang }
3349f3db423dSLydia Wang 
3350f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3351f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3352f3db423dSLydia Wang {
3353f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3354f3db423dSLydia Wang 	int index = 0;
3355f3db423dSLydia Wang 
3356f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
3357f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
3358f3db423dSLydia Wang 	if (index != -1)
3359f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
3360f3db423dSLydia Wang 
3361f3db423dSLydia Wang 	return 0;
3362f3db423dSLydia Wang }
3363f3db423dSLydia Wang 
3364f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3365f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3366f3db423dSLydia Wang {
3367f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3368f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3369f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
3370f3db423dSLydia Wang 
3371f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
3372f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
3373f3db423dSLydia Wang 	spec->dmic_enabled = index;
33743e95b9abSLydia Wang 	set_widgets_power_state(codec);
3375f3db423dSLydia Wang 	return 1;
3376f3db423dSLydia Wang }
3377f3db423dSLydia Wang 
337890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
3379f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3380f3db423dSLydia Wang 	{
3381f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3382f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
33835b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
3384f3db423dSLydia Wang 	 .count = 1,
3385f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
3386f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
3387f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
3388f3db423dSLydia Wang 	 },
3389f3db423dSLydia Wang 	{}			/* end */
3390f3db423dSLydia Wang };
3391f3db423dSLydia Wang 
3392f3db423dSLydia Wang 
3393f3db423dSLydia Wang /* mono-out mixer elements */
339490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3395f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3396f3db423dSLydia Wang 	{ } /* end */
3397f3db423dSLydia Wang };
3398f3db423dSLydia Wang 
3399096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
3400f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
3401f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
3402f3db423dSLydia Wang 	/* don't bybass mixer */
3403f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
3404f3db423dSLydia Wang 	/* Enable mono output */
3405f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
3406f3db423dSLydia Wang 	{ }
3407f3db423dSLydia Wang };
3408f3db423dSLydia Wang 
34093e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
34103e95b9abSLydia Wang {
34113e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
34123e95b9abSLydia Wang 	int imux_is_smixer;
34133e95b9abSLydia Wang 	unsigned int parm;
34143e95b9abSLydia Wang 	unsigned int mono_out, present;
34153e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
34163e95b9abSLydia Wang 	imux_is_smixer =
34173e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
34183e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
34193e95b9abSLydia Wang 	/* inputs */
34203e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
34213e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34223e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
34233e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
34243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
34253e95b9abSLydia Wang 	if (imux_is_smixer)
34263e95b9abSLydia Wang 		parm = AC_PWRST_D0;
34273e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
3428054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
3429054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
34303e95b9abSLydia Wang 
34313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
34333e95b9abSLydia Wang 	/* PW11 (22h) */
34343e95b9abSLydia Wang 	if (spec->dmic_enabled)
34353e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
34363e95b9abSLydia Wang 	else
3437054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
34383e95b9abSLydia Wang 
34393e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
3440054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
3441054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
34423e95b9abSLydia Wang 
34433e95b9abSLydia Wang 	/* outputs */
34443e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
34453e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34463e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
34473e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
34483e95b9abSLydia Wang 	if (spec->smart51_enabled)
34493e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
3450054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
3451054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
34523e95b9abSLydia Wang 
34533e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
34543e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
34563e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
34573e95b9abSLydia Wang 	if (spec->smart51_enabled)
34583e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
3459054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
34603e95b9abSLydia Wang 
34613e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
34623e95b9abSLydia Wang 	if (spec->smart51_enabled)
34633e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
3464054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
34653e95b9abSLydia Wang 
34663e95b9abSLydia Wang 	/* Mono out */
34673e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
34683e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
34693e95b9abSLydia Wang 
34703e95b9abSLydia Wang 	if (present)
34713e95b9abSLydia Wang 		mono_out = 0;
34723e95b9abSLydia Wang 	else {
34733e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
34743e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
34753e95b9abSLydia Wang 			mono_out = 0;
34763e95b9abSLydia Wang 		else
34773e95b9abSLydia Wang 			mono_out = 1;
34783e95b9abSLydia Wang 	}
34793e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3480054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
3481054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
3482054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
34833e95b9abSLydia Wang 
34843e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
34853e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
34873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
34883e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
34893e95b9abSLydia Wang 	if (spec->hp_independent_mode)
3490054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
34913e95b9abSLydia Wang 
34923e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
34933e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
3494054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3495054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
34963e95b9abSLydia Wang }
34973e95b9abSLydia Wang 
3498f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3499f3db423dSLydia Wang {
3500f3db423dSLydia Wang 	struct via_spec *spec;
3501f3db423dSLydia Wang 	int err;
3502f3db423dSLydia Wang 
3503f3db423dSLydia Wang 	/* create a codec specific record */
35045b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3505f3db423dSLydia Wang 	if (spec == NULL)
3506f3db423dSLydia Wang 		return -ENOMEM;
3507f3db423dSLydia Wang 
3508620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3509d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3510d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3511620e2b28STakashi Iwai 
3512f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
351312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3514f3db423dSLydia Wang 	if (err < 0) {
3515f3db423dSLydia Wang 		via_free(codec);
3516f3db423dSLydia Wang 		return err;
3517f3db423dSLydia Wang 	}
3518f3db423dSLydia Wang 
3519096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
3520f3db423dSLydia Wang 
3521f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3522f3db423dSLydia Wang 	spec->num_mixers++;
3523f3db423dSLydia Wang 
3524f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3525f3db423dSLydia Wang 
3526f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3527f3db423dSLydia Wang 
35283e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
3529f3db423dSLydia Wang 	return 0;
3530f3db423dSLydia Wang }
353125eaba2fSLydia Wang 
353225eaba2fSLydia Wang /* for vt2002P */
353325eaba2fSLydia Wang 
3534096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
3535eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
3536eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
3537eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
3538eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
353925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
354025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
354125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
354225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
354325eaba2fSLydia Wang 	{ }
354425eaba2fSLydia Wang };
35454a918ffeSTakashi Iwai 
3546096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
354711890956SLydia Wang 	/* Enable Boost Volume backdoor */
354811890956SLydia Wang 	{0x1, 0xfb9, 0x24},
354911890956SLydia Wang 	/* Enable AOW0 to MW9 */
355011890956SLydia Wang 	{0x1, 0xfb8, 0x88},
355111890956SLydia Wang 	{ }
355211890956SLydia Wang };
355325eaba2fSLydia Wang 
35543e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
35553e95b9abSLydia Wang {
35563e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
35573e95b9abSLydia Wang 	int imux_is_smixer;
35583e95b9abSLydia Wang 	unsigned int parm;
35593e95b9abSLydia Wang 	unsigned int present;
35603e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
35613e95b9abSLydia Wang 	imux_is_smixer =
35623e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
35633e95b9abSLydia Wang 	/* inputs */
35643e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
35653e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
35673e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
35683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
35693e95b9abSLydia Wang 	parm = AC_PWRST_D0;
35703e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3571054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
3572054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
3573054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
3574054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
35753e95b9abSLydia Wang 
35763e95b9abSLydia Wang 	/* outputs */
35773e95b9abSLydia Wang 	/* AOW0 (8h)*/
3578054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
35793e95b9abSLydia Wang 
358011890956SLydia Wang 	if (spec->codec_type == VT1802) {
358111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
358211890956SLydia Wang 		parm = AC_PWRST_D3;
358311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
3584054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
3585054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
358611890956SLydia Wang 	} else {
35873e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
35883e95b9abSLydia Wang 		parm = AC_PWRST_D3;
35893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
3590054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
3591054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
359211890956SLydia Wang 	}
35933e95b9abSLydia Wang 
359411890956SLydia Wang 	if (spec->codec_type == VT1802) {
359511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
359611890956SLydia Wang 		parm = AC_PWRST_D3;
359711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
3598054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
3599054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
360011890956SLydia Wang 	} else {
36013e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
36023e95b9abSLydia Wang 		parm = AC_PWRST_D3;
36033e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
3604054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
3605054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
360611890956SLydia Wang 	}
36073e95b9abSLydia Wang 
36083e95b9abSLydia Wang 	if (spec->hp_independent_mode)
3609054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
36103e95b9abSLydia Wang 
36113e95b9abSLydia Wang 	/* Class-D */
36123e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
36133e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
36143e95b9abSLydia Wang 
36153e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
36173e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
361811890956SLydia Wang 	if (spec->codec_type == VT1802)
3619054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
362011890956SLydia Wang 	else
3621054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
3622054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
36233e95b9abSLydia Wang 
36243e95b9abSLydia Wang 	/* Mono Out */
36253e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
36263e95b9abSLydia Wang 
36273e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
362811890956SLydia Wang 	if (spec->codec_type == VT1802) {
362911890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
3630054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
3631054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
3632054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
363311890956SLydia Wang 	} else {
36343e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
3635054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
3636054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
3637054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
363811890956SLydia Wang 	}
36393e95b9abSLydia Wang 	/* MW9 (21h) */
36403e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
3641054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
36423e95b9abSLydia Wang 	else
3643054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
36443e95b9abSLydia Wang }
364525eaba2fSLydia Wang 
36464b527b65SDavid Henningsson /*
36474b527b65SDavid Henningsson  * pin fix-up
36484b527b65SDavid Henningsson  */
36494b527b65SDavid Henningsson enum {
36504b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
3651d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
36524b527b65SDavid Henningsson };
36534b527b65SDavid Henningsson 
36544b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
36554b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
36564b527b65SDavid Henningsson {
36574b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
36584b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
36594b527b65SDavid Henningsson }
36604b527b65SDavid Henningsson 
36614b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
36624b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
36634b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
36644b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
36654b527b65SDavid Henningsson 	},
3666d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
3667d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
3668d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
3669d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
3670d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
3671d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
3672d5266125STakashi Iwai 			{ }
3673d5266125STakashi Iwai 		}
3674d5266125STakashi Iwai 	},
36754b527b65SDavid Henningsson };
36764b527b65SDavid Henningsson 
36774b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
3678d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
36794b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
36804b527b65SDavid Henningsson 	{}
36814b527b65SDavid Henningsson };
36824b527b65SDavid Henningsson 
3683ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
3684ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
3685ef4da458STakashi Iwai  */
3686ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
3687ef4da458STakashi Iwai {
3688ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
3689ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
3690ef4da458STakashi Iwai 
3691ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
3692ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
3693ef4da458STakashi Iwai }
3694ef4da458STakashi Iwai 
369525eaba2fSLydia Wang /* patch for vt2002P */
369625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
369725eaba2fSLydia Wang {
369825eaba2fSLydia Wang 	struct via_spec *spec;
369925eaba2fSLydia Wang 	int err;
370025eaba2fSLydia Wang 
370125eaba2fSLydia Wang 	/* create a codec specific record */
37025b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
370325eaba2fSLydia Wang 	if (spec == NULL)
370425eaba2fSLydia Wang 		return -ENOMEM;
370525eaba2fSLydia Wang 
3706620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3707d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3708d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3709ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
3710ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
371130b45033STakashi Iwai 	add_secret_dac_path(codec);
3712620e2b28STakashi Iwai 
37134b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
37144b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
37154b527b65SDavid Henningsson 
371625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
371712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
371825eaba2fSLydia Wang 	if (err < 0) {
371925eaba2fSLydia Wang 		via_free(codec);
372025eaba2fSLydia Wang 		return err;
372125eaba2fSLydia Wang 	}
372225eaba2fSLydia Wang 
372311890956SLydia Wang 	if (spec->codec_type == VT1802)
37244a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
372511890956SLydia Wang 	else
37264a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
372711890956SLydia Wang 
372825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
372925eaba2fSLydia Wang 
37303e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
373125eaba2fSLydia Wang 	return 0;
373225eaba2fSLydia Wang }
3733ab6734e7SLydia Wang 
3734ab6734e7SLydia Wang /* for vt1812 */
3735ab6734e7SLydia Wang 
3736096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
3737ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
3738ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
3739ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
3740ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
3741ab6734e7SLydia Wang 	{ }
3742ab6734e7SLydia Wang };
3743ab6734e7SLydia Wang 
37443e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
37453e95b9abSLydia Wang {
37463e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
37473e95b9abSLydia Wang 	unsigned int parm;
37483e95b9abSLydia Wang 	unsigned int present;
37493e95b9abSLydia Wang 	/* inputs */
37503e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
37513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
37533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
37543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
37553e95b9abSLydia Wang 	parm = AC_PWRST_D0;
37563e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3757054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
3758054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
3759054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
3760054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
37613e95b9abSLydia Wang 
37623e95b9abSLydia Wang 	/* outputs */
37633e95b9abSLydia Wang 	/* AOW0 (8h)*/
3764054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
37653e95b9abSLydia Wang 
37663e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
37673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
3769054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
3770054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
37713e95b9abSLydia Wang 
37723e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
37733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
3775054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
3776054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
37773e95b9abSLydia Wang 	if (spec->hp_independent_mode)
3778054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
37793e95b9abSLydia Wang 
37803e95b9abSLydia Wang 	/* Internal Speaker */
37813e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
37823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
37833e95b9abSLydia Wang 
37843e95b9abSLydia Wang 	parm = AC_PWRST_D3;
37853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
37863e95b9abSLydia Wang 	if (present) {
3787054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
3788054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
37893e95b9abSLydia Wang 	} else {
3790054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
3791054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
37923e95b9abSLydia Wang 	}
37933e95b9abSLydia Wang 
37943e95b9abSLydia Wang 
37953e95b9abSLydia Wang 	/* Mono Out */
37963e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
37973e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
37983e95b9abSLydia Wang 
37993e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
38013e95b9abSLydia Wang 	if (present) {
3802054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
3803054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
3804054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
38053e95b9abSLydia Wang 	} else {
3806054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
3807054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
3808054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
38093e95b9abSLydia Wang 	}
38103e95b9abSLydia Wang 
38113e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
38123e95b9abSLydia Wang 	parm = AC_PWRST_D3;
38133e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
3814054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
3815054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
38163e95b9abSLydia Wang 
38173e95b9abSLydia Wang }
3818ab6734e7SLydia Wang 
3819ab6734e7SLydia Wang /* patch for vt1812 */
3820ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
3821ab6734e7SLydia Wang {
3822ab6734e7SLydia Wang 	struct via_spec *spec;
3823ab6734e7SLydia Wang 	int err;
3824ab6734e7SLydia Wang 
3825ab6734e7SLydia Wang 	/* create a codec specific record */
38265b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3827ab6734e7SLydia Wang 	if (spec == NULL)
3828ab6734e7SLydia Wang 		return -ENOMEM;
3829ab6734e7SLydia Wang 
3830620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3831d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3832d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
383330b45033STakashi Iwai 	add_secret_dac_path(codec);
3834620e2b28STakashi Iwai 
3835ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
383612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3837ab6734e7SLydia Wang 	if (err < 0) {
3838ab6734e7SLydia Wang 		via_free(codec);
3839ab6734e7SLydia Wang 		return err;
3840ab6734e7SLydia Wang 	}
3841ab6734e7SLydia Wang 
3842096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
3843ab6734e7SLydia Wang 
3844ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
3845ab6734e7SLydia Wang 
38463e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
3847ab6734e7SLydia Wang 	return 0;
3848ab6734e7SLydia Wang }
3849ab6734e7SLydia Wang 
3850*43737e0aSLydia Wang /* patch for vt3476 */
3851*43737e0aSLydia Wang 
3852*43737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
3853*43737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
3854*43737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
3855*43737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
3856*43737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
3857*43737e0aSLydia Wang 	/* Enable AOW-MW9 path */
3858*43737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
3859*43737e0aSLydia Wang 	{ }
3860*43737e0aSLydia Wang };
3861*43737e0aSLydia Wang 
3862*43737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
3863*43737e0aSLydia Wang {
3864*43737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
3865*43737e0aSLydia Wang 	int imux_is_smixer;
3866*43737e0aSLydia Wang 	unsigned int parm, parm2;
3867*43737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
3868*43737e0aSLydia Wang 	imux_is_smixer =
3869*43737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
3870*43737e0aSLydia Wang 	/* inputs */
3871*43737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
3872*43737e0aSLydia Wang 	parm = AC_PWRST_D3;
3873*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
3874*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
3875*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
3876*43737e0aSLydia Wang 	if (imux_is_smixer)
3877*43737e0aSLydia Wang 		parm = AC_PWRST_D0;
3878*43737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3879*43737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
3880*43737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
3881*43737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
3882*43737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
3883*43737e0aSLydia Wang 
3884*43737e0aSLydia Wang 	/* outputs */
3885*43737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
3886*43737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
3887*43737e0aSLydia Wang 		parm = AC_PWRST_D3;
3888*43737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
3889*43737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
3890*43737e0aSLydia Wang 	}	else {
3891*43737e0aSLydia Wang 		parm = AC_PWRST_D3;
3892*43737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
3893*43737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
3894*43737e0aSLydia Wang 	}
3895*43737e0aSLydia Wang 
3896*43737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
3897*43737e0aSLydia Wang 	parm = AC_PWRST_D3;
3898*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
3899*43737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
3900*43737e0aSLydia Wang 	if (spec->smart51_enabled) {
3901*43737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
3902*43737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
3903*43737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
3904*43737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
3905*43737e0aSLydia Wang 	}
3906*43737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
3907*43737e0aSLydia Wang 
3908*43737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
3909*43737e0aSLydia Wang 	parm = AC_PWRST_D3;
3910*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
3911*43737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
3912*43737e0aSLydia Wang 	if (spec->smart51_enabled) {
3913*43737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
3914*43737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
3915*43737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
3916*43737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
3917*43737e0aSLydia Wang 	}
3918*43737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
3919*43737e0aSLydia Wang 
3920*43737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
3921*43737e0aSLydia Wang 	parm = AC_PWRST_D3;
3922*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
3923*43737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
3924*43737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
3925*43737e0aSLydia Wang 	if (spec->hp_independent_mode)
3926*43737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
3927*43737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
3928*43737e0aSLydia Wang 
3929*43737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
3930*43737e0aSLydia Wang 	parm = AC_PWRST_D3;
3931*43737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
3932*43737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
3933*43737e0aSLydia Wang 	if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
3934*43737e0aSLydia Wang 		parm = parm2;
3935*43737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
3936*43737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3937*43737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
3938*43737e0aSLydia Wang }
3939*43737e0aSLydia Wang 
3940*43737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
3941*43737e0aSLydia Wang {
3942*43737e0aSLydia Wang 	struct via_spec *spec;
3943*43737e0aSLydia Wang 	int err;
3944*43737e0aSLydia Wang 
3945*43737e0aSLydia Wang 	/* create a codec specific record */
3946*43737e0aSLydia Wang 	spec = via_new_spec(codec);
3947*43737e0aSLydia Wang 	if (spec == NULL)
3948*43737e0aSLydia Wang 		return -ENOMEM;
3949*43737e0aSLydia Wang 
3950*43737e0aSLydia Wang 	spec->aa_mix_nid = 0x3f;
3951*43737e0aSLydia Wang 	add_secret_dac_path(codec);
3952*43737e0aSLydia Wang 
3953*43737e0aSLydia Wang 	/* automatic parse from the BIOS config */
3954*43737e0aSLydia Wang 	err = via_parse_auto_config(codec);
3955*43737e0aSLydia Wang 	if (err < 0) {
3956*43737e0aSLydia Wang 		via_free(codec);
3957*43737e0aSLydia Wang 		return err;
3958*43737e0aSLydia Wang 	}
3959*43737e0aSLydia Wang 
3960*43737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
3961*43737e0aSLydia Wang 
3962*43737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
3963*43737e0aSLydia Wang 
3964*43737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
3965*43737e0aSLydia Wang 
3966*43737e0aSLydia Wang 	return 0;
3967*43737e0aSLydia Wang }
3968*43737e0aSLydia Wang 
3969c577b8a1SJoseph Chan /*
3970c577b8a1SJoseph Chan  * patch entries
3971c577b8a1SJoseph Chan  */
397290dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
39733218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
39743218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
39753218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
39763218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
39773218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
3978ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39793218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
3980ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39813218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
3982ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39833218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
3984ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39853218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
3986ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39873218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
3988ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39893218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
3990ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39913218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
3992ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
39933218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
3994ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
39953218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
3996ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
39973218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
3998ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
39993218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
4000ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
40013218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
4002ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
40033218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
4004ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
40053218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
4006ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
40073218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
4008ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
40093218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
4010d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40113218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
4012d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40133218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
4014d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40153218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
4016d949cac1SHarald Welte 	  .patch = patch_vt1708S},
4017bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
4018d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40193218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
4020d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40213218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
4022d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40233218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
4024d949cac1SHarald Welte 	  .patch = patch_vt1708S},
40253218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
4026d949cac1SHarald Welte 	  .patch = patch_vt1702},
40273218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
4028d949cac1SHarald Welte 	  .patch = patch_vt1702},
40293218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
4030d949cac1SHarald Welte 	  .patch = patch_vt1702},
40313218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
4032d949cac1SHarald Welte 	  .patch = patch_vt1702},
40333218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
4034d949cac1SHarald Welte 	  .patch = patch_vt1702},
40353218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
4036d949cac1SHarald Welte 	  .patch = patch_vt1702},
40373218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
4038d949cac1SHarald Welte 	  .patch = patch_vt1702},
40393218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
4040d949cac1SHarald Welte 	  .patch = patch_vt1702},
4041eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
4042eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4043eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
4044eb7188caSLydia Wang 	  .patch = patch_vt1718S},
4045bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
4046bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4047bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
4048bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
4049f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
4050f3db423dSLydia Wang 	  .patch = patch_vt1716S},
4051f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
4052f3db423dSLydia Wang 	  .patch = patch_vt1716S},
405325eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
405425eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
4055ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
405636dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
405736dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
405811890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
405911890956SLydia Wang 		.patch = patch_vt2002P},
406011890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
406111890956SLydia Wang 		.patch = patch_vt2002P},
4062*43737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
4063*43737e0aSLydia Wang 		.patch = patch_vt3476},
4064c577b8a1SJoseph Chan 	{} /* terminator */
4065c577b8a1SJoseph Chan };
40661289e9e8STakashi Iwai 
40671289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
40681289e9e8STakashi Iwai 
40691289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
40701289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
40711289e9e8STakashi Iwai 	.owner = THIS_MODULE,
40721289e9e8STakashi Iwai };
40731289e9e8STakashi Iwai 
40741289e9e8STakashi Iwai MODULE_LICENSE("GPL");
40751289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
40761289e9e8STakashi Iwai 
40771289e9e8STakashi Iwai static int __init patch_via_init(void)
40781289e9e8STakashi Iwai {
40791289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
40801289e9e8STakashi Iwai }
40811289e9e8STakashi Iwai 
40821289e9e8STakashi Iwai static void __exit patch_via_exit(void)
40831289e9e8STakashi Iwai {
40841289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
40851289e9e8STakashi Iwai }
40861289e9e8STakashi Iwai 
40871289e9e8STakashi Iwai module_init(patch_via_init)
40881289e9e8STakashi Iwai module_exit(patch_via_exit)
4089