xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 3214b9665c06f684011f169428963b20f8ac554b)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
57c577b8a1SJoseph Chan /* Pin Widget NID */
5876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
5976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
60c577b8a1SJoseph Chan 
61d7426329SHarald Welte enum VIA_HDA_CODEC {
62d7426329SHarald Welte 	UNKNOWN = -1,
63d7426329SHarald Welte 	VT1708,
64d7426329SHarald Welte 	VT1709_10CH,
65d7426329SHarald Welte 	VT1709_6CH,
66d7426329SHarald Welte 	VT1708B_8CH,
67d7426329SHarald Welte 	VT1708B_4CH,
68d7426329SHarald Welte 	VT1708S,
69518bf3baSLydia Wang 	VT1708BCE,
70d7426329SHarald Welte 	VT1702,
71eb7188caSLydia Wang 	VT1718S,
72f3db423dSLydia Wang 	VT1716S,
7325eaba2fSLydia Wang 	VT2002P,
74ab6734e7SLydia Wang 	VT1812,
7511890956SLydia Wang 	VT1802,
76d7426329SHarald Welte 	CODEC_TYPES,
77d7426329SHarald Welte };
78d7426329SHarald Welte 
7911890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8011890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8111890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8211890956SLydia Wang 	 (spec)->codec_type == VT1802)
8311890956SLydia Wang 
848e3679dcSTakashi Iwai #define MAX_NID_PATH_DEPTH	5
858e3679dcSTakashi Iwai 
8609a9ad69STakashi Iwai /* output-path: DAC -> ... -> pin
8709a9ad69STakashi Iwai  * idx[] contains the source index number of the next widget;
8809a9ad69STakashi Iwai  * e.g. idx[0] is the index of the DAC selected by path[1] widget
8909a9ad69STakashi Iwai  * multi[] indicates whether it's a selector widget with multi-connectors
9009a9ad69STakashi Iwai  * (i.e. the connection selection is mandatory)
9109a9ad69STakashi Iwai  * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
9209a9ad69STakashi Iwai  */
934a79616dSTakashi Iwai struct nid_path {
944a79616dSTakashi Iwai 	int depth;
958e3679dcSTakashi Iwai 	hda_nid_t path[MAX_NID_PATH_DEPTH];
9609a9ad69STakashi Iwai 	unsigned char idx[MAX_NID_PATH_DEPTH];
9709a9ad69STakashi Iwai 	unsigned char multi[MAX_NID_PATH_DEPTH];
9809a9ad69STakashi Iwai 	unsigned int vol_ctl;
9909a9ad69STakashi Iwai 	unsigned int mute_ctl;
1004a79616dSTakashi Iwai };
1014a79616dSTakashi Iwai 
102a86a88eaSTakashi Iwai /* input-path */
103a86a88eaSTakashi Iwai struct via_input {
104a86a88eaSTakashi Iwai 	hda_nid_t pin;	/* input-pin or aa-mix */
105a86a88eaSTakashi Iwai 	int adc_idx;	/* ADC index to be used */
106a86a88eaSTakashi Iwai 	int mux_idx;	/* MUX index (if any) */
107a86a88eaSTakashi Iwai 	const char *label;	/* input-source label */
108a86a88eaSTakashi Iwai };
109a86a88eaSTakashi Iwai 
110de6c74f3STakashi Iwai #define VIA_MAX_ADCS	3
111de6c74f3STakashi Iwai 
1121f2e99feSLydia Wang struct via_spec {
1131f2e99feSLydia Wang 	/* codec parameterization */
11490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1151f2e99feSLydia Wang 	unsigned int num_mixers;
1161f2e99feSLydia Wang 
11790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1181f2e99feSLydia Wang 	unsigned int num_iverbs;
1191f2e99feSLydia Wang 
12082673bc8STakashi Iwai 	char stream_name_analog[32];
1217eb56e84STakashi Iwai 	char stream_name_hp[32];
12290dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12390dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1241f2e99feSLydia Wang 
12582673bc8STakashi Iwai 	char stream_name_digital[32];
12690dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
12790dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1281f2e99feSLydia Wang 
1291f2e99feSLydia Wang 	/* playback */
1301f2e99feSLydia Wang 	struct hda_multi_out multiout;
1311f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
132ece8d043STakashi Iwai 	hda_nid_t hp_dac_nid;
133*3214b966STakashi Iwai 	hda_nid_t speaker_dac_nid;
134*3214b966STakashi Iwai 	int hp_indep_shared;	/* indep HP-DAC is shared with side ch */
135ada509ecSTakashi Iwai 	int num_active_streams;
136*3214b966STakashi Iwai 	int aamix_mode;		/* loopback is enabled for output-path? */
1371f2e99feSLydia Wang 
138*3214b966STakashi Iwai 	/* Output-paths:
139*3214b966STakashi Iwai 	 * There are different output-paths depending on the setup.
140*3214b966STakashi Iwai 	 * out_path, hp_path and speaker_path are primary paths.  If both
141*3214b966STakashi Iwai 	 * direct DAC and aa-loopback routes are available, these contain
142*3214b966STakashi Iwai 	 * the former paths.  Meanwhile *_mix_path contain the paths with
143*3214b966STakashi Iwai 	 * loopback mixer.  (Since the loopback is only for front channel,
144*3214b966STakashi Iwai 	 * no out_mix_path for surround channels.)
145*3214b966STakashi Iwai 	 * The HP output has another path, hp_indep_path, which is used in
146*3214b966STakashi Iwai 	 * the independent-HP mode.
147*3214b966STakashi Iwai 	 */
148de6c74f3STakashi Iwai 	struct nid_path out_path[HDA_SIDE + 1];
149*3214b966STakashi Iwai 	struct nid_path out_mix_path;
1504a79616dSTakashi Iwai 	struct nid_path hp_path;
151*3214b966STakashi Iwai 	struct nid_path hp_mix_path;
152*3214b966STakashi Iwai 	struct nid_path hp_indep_path;
1534a918ffeSTakashi Iwai 	struct nid_path speaker_path;
154*3214b966STakashi Iwai 	struct nid_path speaker_mix_path;
1554a79616dSTakashi Iwai 
1561f2e99feSLydia Wang 	/* capture */
1571f2e99feSLydia Wang 	unsigned int num_adc_nids;
158de6c74f3STakashi Iwai 	hda_nid_t adc_nids[VIA_MAX_ADCS];
159de6c74f3STakashi Iwai 	hda_nid_t mux_nids[VIA_MAX_ADCS];
160620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1611f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1621f2e99feSLydia Wang 
1631f2e99feSLydia Wang 	/* capture source */
164a86a88eaSTakashi Iwai 	bool dyn_adc_switch;
165a86a88eaSTakashi Iwai 	int num_inputs;
166a86a88eaSTakashi Iwai 	struct via_input inputs[AUTO_CFG_MAX_INS + 1];
167de6c74f3STakashi Iwai 	unsigned int cur_mux[VIA_MAX_ADCS];
1681f2e99feSLydia Wang 
169a86a88eaSTakashi Iwai 	/* dynamic ADC switching */
170a86a88eaSTakashi Iwai 	hda_nid_t cur_adc;
171a86a88eaSTakashi Iwai 	unsigned int cur_adc_stream_tag;
172a86a88eaSTakashi Iwai 	unsigned int cur_adc_format;
173a86a88eaSTakashi Iwai 
1741f2e99feSLydia Wang 	/* PCM information */
1751f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1761f2e99feSLydia Wang 
1771f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1781f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1791f2e99feSLydia Wang 	struct snd_array kctls;
1801f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1811f2e99feSLydia Wang 
1821f2e99feSLydia Wang 	/* HP mode source */
1831f2e99feSLydia Wang 	unsigned int hp_independent_mode;
184f3db423dSLydia Wang 	unsigned int dmic_enabled;
18524088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1861f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1871f2e99feSLydia Wang 
188e3d7a143STakashi Iwai 	/* smart51 setup */
189e3d7a143STakashi Iwai 	unsigned int smart51_nums;
190e3d7a143STakashi Iwai 	hda_nid_t smart51_pins[2];
191e3d7a143STakashi Iwai 	int smart51_idxs[2];
192e3d7a143STakashi Iwai 	const char *smart51_labels[2];
193e3d7a143STakashi Iwai 	unsigned int smart51_enabled;
194e3d7a143STakashi Iwai 
1951f2e99feSLydia Wang 	/* work to check hp jack state */
1961f2e99feSLydia Wang 	struct hda_codec *codec;
1971f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
198e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1991f2e99feSLydia Wang 	int vt1708_hp_present;
2003e95b9abSLydia Wang 
2013e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
2023e95b9abSLydia Wang 
2031f2e99feSLydia Wang 	struct hda_loopback_check loopback;
20413af8e77STakashi Iwai 	int num_loopbacks;
20513af8e77STakashi Iwai 	struct hda_amp_list loopback_list[8];
206a86a88eaSTakashi Iwai 
207a86a88eaSTakashi Iwai 	/* bind capture-volume */
208a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_vol;
209a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_sw;
2101f2e99feSLydia Wang };
2111f2e99feSLydia Wang 
2120341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
2135b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
2145b0cb1d8SJaroslav Kysela {
2155b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
2165b0cb1d8SJaroslav Kysela 
2175b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2185b0cb1d8SJaroslav Kysela 	if (spec == NULL)
2195b0cb1d8SJaroslav Kysela 		return NULL;
2205b0cb1d8SJaroslav Kysela 
2215b0cb1d8SJaroslav Kysela 	codec->spec = spec;
2225b0cb1d8SJaroslav Kysela 	spec->codec = codec;
2230341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
2240341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
2250341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
2260341ccd7SLydia Wang 		spec->codec_type = VT1708S;
2275b0cb1d8SJaroslav Kysela 	return spec;
2285b0cb1d8SJaroslav Kysela }
2295b0cb1d8SJaroslav Kysela 
230744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
231d7426329SHarald Welte {
232744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
233d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
234d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
235d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
236d7426329SHarald Welte 
237d7426329SHarald Welte 	/* get codec type */
238d7426329SHarald Welte 	if (ven_id != 0x1106)
239d7426329SHarald Welte 		codec_type = UNKNOWN;
240d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
241d7426329SHarald Welte 		codec_type = VT1708;
242d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
243d7426329SHarald Welte 		codec_type = VT1709_10CH;
244d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
245d7426329SHarald Welte 		codec_type = VT1709_6CH;
246518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
247d7426329SHarald Welte 		codec_type = VT1708B_8CH;
248518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
249518bf3baSLydia Wang 			codec_type = VT1708BCE;
250518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
251d7426329SHarald Welte 		codec_type = VT1708B_4CH;
252d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
253d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
254d7426329SHarald Welte 		codec_type = VT1708S;
255d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
256d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
257d7426329SHarald Welte 		codec_type = VT1702;
258eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
259eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
260eb7188caSLydia Wang 		codec_type = VT1718S;
261f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
262f3db423dSLydia Wang 		codec_type = VT1716S;
263bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
264bb3c6bfcSLydia Wang 		codec_type = VT1718S;
26525eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
26625eaba2fSLydia Wang 		codec_type = VT2002P;
267ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
268ab6734e7SLydia Wang 		codec_type = VT1812;
26936dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
27036dd5c4aSLydia Wang 		codec_type = VT1708S;
27111890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
27211890956SLydia Wang 		codec_type = VT1802;
273d7426329SHarald Welte 	else
274d7426329SHarald Welte 		codec_type = UNKNOWN;
275d7426329SHarald Welte 	return codec_type;
276d7426329SHarald Welte };
277d7426329SHarald Welte 
278ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
27969e52a80SHarald Welte #define VIA_HP_EVENT		0x01
28069e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
2814a918ffeSTakashi Iwai #define VIA_LINE_EVENT		0x03
28269e52a80SHarald Welte 
283c577b8a1SJoseph Chan enum {
284c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
285c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
286f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
287c577b8a1SJoseph Chan };
288c577b8a1SJoseph Chan 
289ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
290ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
2911f2e99feSLydia Wang 
2921f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2931f2e99feSLydia Wang {
2941f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2951f2e99feSLydia Wang 		return;
2961f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
297e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2981f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2991f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
3001f2e99feSLydia Wang 				      msecs_to_jiffies(100));
3011f2e99feSLydia Wang }
3021f2e99feSLydia Wang 
3031f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
3041f2e99feSLydia Wang {
3051f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
3061f2e99feSLydia Wang 		return;
3071f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
3081f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
3091f2e99feSLydia Wang 		return;
3101f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
311e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
3125b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
3131f2e99feSLydia Wang }
314f5271101SLydia Wang 
3153e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
3163e95b9abSLydia Wang {
3173e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
3183e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3193e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3203e95b9abSLydia Wang }
32125eaba2fSLydia Wang 
322f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
323f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
324f5271101SLydia Wang {
325f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
326f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
327f5271101SLydia Wang 
3283e95b9abSLydia Wang 	set_widgets_power_state(codec);
329ada509ecSTakashi Iwai 	analog_low_current_mode(snd_kcontrol_chip(kcontrol));
3301f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3311f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3321f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3331f2e99feSLydia Wang 		else
3341f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3351f2e99feSLydia Wang 	}
336f5271101SLydia Wang 	return change;
337f5271101SLydia Wang }
338f5271101SLydia Wang 
339f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
340f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
341f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
342f5271101SLydia Wang 			.name = NULL,					\
343f5271101SLydia Wang 			.index = 0,					\
344f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
345f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
346f5271101SLydia Wang 			.put = analog_input_switch_put,			\
347f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
348f5271101SLydia Wang 
34990dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
350c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
351c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
352f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
353c577b8a1SJoseph Chan };
354c577b8a1SJoseph Chan 
355ab6734e7SLydia Wang 
356c577b8a1SJoseph Chan /* add dynamic controls */
357291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
358291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
359291c9e33STakashi Iwai 				const char *name)
360c577b8a1SJoseph Chan {
361c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
362c577b8a1SJoseph Chan 
363603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
364603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
365c577b8a1SJoseph Chan 	if (!knew)
366291c9e33STakashi Iwai 		return NULL;
367291c9e33STakashi Iwai 	*knew = *tmpl;
368291c9e33STakashi Iwai 	if (!name)
369291c9e33STakashi Iwai 		name = tmpl->name;
370291c9e33STakashi Iwai 	if (name) {
371c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
372c577b8a1SJoseph Chan 		if (!knew->name)
373291c9e33STakashi Iwai 			return NULL;
374291c9e33STakashi Iwai 	}
375291c9e33STakashi Iwai 	return knew;
376291c9e33STakashi Iwai }
377291c9e33STakashi Iwai 
378291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
379291c9e33STakashi Iwai 			     int idx, unsigned long val)
380291c9e33STakashi Iwai {
381291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
382291c9e33STakashi Iwai 
383291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
384291c9e33STakashi Iwai 	if (!knew)
385c577b8a1SJoseph Chan 		return -ENOMEM;
386d7a99cceSTakashi Iwai 	knew->index = idx;
3874d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
3885e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
389c577b8a1SJoseph Chan 	knew->private_value = val;
390c577b8a1SJoseph Chan 	return 0;
391c577b8a1SJoseph Chan }
392c577b8a1SJoseph Chan 
3937b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
3947b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
3957b315bb4STakashi Iwai 
396291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
3975b0cb1d8SJaroslav Kysela 
398603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
399603c4019STakashi Iwai {
400603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
401603c4019STakashi Iwai 
402603c4019STakashi Iwai 	if (spec->kctls.list) {
403603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
404603c4019STakashi Iwai 		int i;
405603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
406603c4019STakashi Iwai 			kfree(kctl[i].name);
407603c4019STakashi Iwai 	}
408603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
409603c4019STakashi Iwai }
410603c4019STakashi Iwai 
411c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
4129510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
4137b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
414c577b8a1SJoseph Chan {
415c577b8a1SJoseph Chan 	char name[32];
416c577b8a1SJoseph Chan 	int err;
417c577b8a1SJoseph Chan 
418c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4197b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
420c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
421c577b8a1SJoseph Chan 	if (err < 0)
422c577b8a1SJoseph Chan 		return err;
423c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
4247b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
425c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
426c577b8a1SJoseph Chan 	if (err < 0)
427c577b8a1SJoseph Chan 		return err;
428c577b8a1SJoseph Chan 	return 0;
429c577b8a1SJoseph Chan }
430c577b8a1SJoseph Chan 
4315d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \
4328d087c76STakashi Iwai 	snd_hda_get_conn_index(codec, mux, nid, 0)
4335d41762aSTakashi Iwai 
4348df2a312STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
4358df2a312STakashi Iwai 			   unsigned int mask)
4368df2a312STakashi Iwai {
437a934d5a9STakashi Iwai 	unsigned int caps;
438a934d5a9STakashi Iwai 	if (!nid)
439a934d5a9STakashi Iwai 		return false;
440a934d5a9STakashi Iwai 	caps = get_wcaps(codec, nid);
4418df2a312STakashi Iwai 	if (dir == HDA_INPUT)
4428df2a312STakashi Iwai 		caps &= AC_WCAP_IN_AMP;
4438df2a312STakashi Iwai 	else
4448df2a312STakashi Iwai 		caps &= AC_WCAP_OUT_AMP;
4458df2a312STakashi Iwai 	if (!caps)
4468df2a312STakashi Iwai 		return false;
4478df2a312STakashi Iwai 	if (query_amp_caps(codec, nid, dir) & mask)
4488df2a312STakashi Iwai 		return true;
4498df2a312STakashi Iwai 	return false;
4508df2a312STakashi Iwai }
4518df2a312STakashi Iwai 
45209a9ad69STakashi Iwai #define have_mute(codec, nid, dir) \
45309a9ad69STakashi Iwai 	check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
4548df2a312STakashi Iwai 
455d69607b3SLydia Wang /* enable/disable the output-route mixers */
456d69607b3SLydia Wang static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
457*3214b966STakashi Iwai 				hda_nid_t mix_nid, int idx, bool enable)
458d69607b3SLydia Wang {
459d69607b3SLydia Wang 	int i, num, val;
460d69607b3SLydia Wang 
461d69607b3SLydia Wang 	if (!path)
462d69607b3SLydia Wang 		return;
463d69607b3SLydia Wang 	num = snd_hda_get_conn_list(codec, mix_nid, NULL);
464d69607b3SLydia Wang 	for (i = 0; i < num; i++) {
465*3214b966STakashi Iwai 		if (i == idx)
466d69607b3SLydia Wang 			val = AMP_IN_UNMUTE(i);
467d69607b3SLydia Wang 		else
468d69607b3SLydia Wang 			val = AMP_IN_MUTE(i);
469d69607b3SLydia Wang 		snd_hda_codec_write(codec, mix_nid, 0,
470d69607b3SLydia Wang 				    AC_VERB_SET_AMP_GAIN_MUTE, val);
471d69607b3SLydia Wang 	}
472d69607b3SLydia Wang }
473d69607b3SLydia Wang 
47409a9ad69STakashi Iwai /* enable/disable the output-route */
47509a9ad69STakashi Iwai static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
47609a9ad69STakashi Iwai 				 bool enable, bool force)
4775d41762aSTakashi Iwai {
478d69607b3SLydia Wang 	struct via_spec *spec = codec->spec;
479*3214b966STakashi Iwai 	int i;
48009a9ad69STakashi Iwai 	for (i = 0; i < path->depth; i++) {
48109a9ad69STakashi Iwai 		hda_nid_t src, dst;
48209a9ad69STakashi Iwai 		int idx = path->idx[i];
48309a9ad69STakashi Iwai 		src = path->path[i];
48409a9ad69STakashi Iwai 		if (i < path->depth - 1)
48509a9ad69STakashi Iwai 			dst = path->path[i + 1];
48609a9ad69STakashi Iwai 		else
48709a9ad69STakashi Iwai 			dst = 0;
48809a9ad69STakashi Iwai 		if (enable && path->multi[i])
48909a9ad69STakashi Iwai 			snd_hda_codec_write(codec, dst, 0,
4905d41762aSTakashi Iwai 					    AC_VERB_SET_CONNECT_SEL, idx);
491*3214b966STakashi Iwai 		if (!force && (dst == spec->aa_mix_nid))
492e5e14681SLydia Wang 			continue;
493*3214b966STakashi Iwai 		if (have_mute(codec, dst, HDA_INPUT))
494*3214b966STakashi Iwai 			activate_output_mix(codec, path, dst, idx, enable);
49509a9ad69STakashi Iwai 		if (!force && (src == path->vol_ctl || src == path->mute_ctl))
49609a9ad69STakashi Iwai 			continue;
49709a9ad69STakashi Iwai 		if (have_mute(codec, src, HDA_OUTPUT)) {
49809a9ad69STakashi Iwai 			int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
49909a9ad69STakashi Iwai 			snd_hda_codec_write(codec, src, 0,
50009a9ad69STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE, val);
50109a9ad69STakashi Iwai 		}
50209a9ad69STakashi Iwai 	}
5035d41762aSTakashi Iwai }
5045d41762aSTakashi Iwai 
5055d41762aSTakashi Iwai /* set the given pin as output */
5065d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
5075d41762aSTakashi Iwai 			    int pin_type)
5085d41762aSTakashi Iwai {
5095d41762aSTakashi Iwai 	if (!pin)
5105d41762aSTakashi Iwai 		return;
5115d41762aSTakashi Iwai 	snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
5125d41762aSTakashi Iwai 			    pin_type);
5135d41762aSTakashi Iwai 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
5145d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0,
515d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
516c577b8a1SJoseph Chan }
517c577b8a1SJoseph Chan 
51809a9ad69STakashi Iwai static void via_auto_init_output(struct hda_codec *codec,
51909a9ad69STakashi Iwai 				 struct nid_path *path, int pin_type,
520*3214b966STakashi Iwai 				 bool force)
5215d41762aSTakashi Iwai {
5225d41762aSTakashi Iwai 	unsigned int caps;
523d69607b3SLydia Wang 	hda_nid_t pin;
5245d41762aSTakashi Iwai 
52509a9ad69STakashi Iwai 	if (!path->depth)
5265d41762aSTakashi Iwai 		return;
52709a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
5285d41762aSTakashi Iwai 
5295d41762aSTakashi Iwai 	init_output_pin(codec, pin, pin_type);
5305d41762aSTakashi Iwai 	caps = query_amp_caps(codec, pin, HDA_OUTPUT);
5315d41762aSTakashi Iwai 	if (caps & AC_AMPCAP_MUTE) {
5325d41762aSTakashi Iwai 		unsigned int val;
5335d41762aSTakashi Iwai 		val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
5345d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
5355d41762aSTakashi Iwai 				    AMP_OUT_MUTE | val);
5365d41762aSTakashi Iwai 	}
537d69607b3SLydia Wang 	activate_output_path(codec, path, true, force);
53809a9ad69STakashi Iwai }
539c577b8a1SJoseph Chan 
540c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
541c577b8a1SJoseph Chan {
542c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
543*3214b966STakashi Iwai 	struct nid_path *path;
544c577b8a1SJoseph Chan 	int i;
545c577b8a1SJoseph Chan 
546*3214b966STakashi Iwai 	for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
547*3214b966STakashi Iwai 		path = &spec->out_path[i];
548*3214b966STakashi Iwai 		if (!i && spec->aamix_mode && spec->out_mix_path.depth)
549*3214b966STakashi Iwai 			path = &spec->out_mix_path;
550*3214b966STakashi Iwai 		via_auto_init_output(codec, path, PIN_OUT, true);
551*3214b966STakashi Iwai 	}
552c577b8a1SJoseph Chan }
553c577b8a1SJoseph Chan 
554c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
555c577b8a1SJoseph Chan {
556c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
557*3214b966STakashi Iwai 	int shared = spec->hp_indep_shared;
558c577b8a1SJoseph Chan 
559*3214b966STakashi Iwai 	if (!spec->hp_path.depth) {
560*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
56109a9ad69STakashi Iwai 		return;
56209a9ad69STakashi Iwai 	}
56309a9ad69STakashi Iwai 	if (spec->hp_independent_mode) {
56409a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
565*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, false, false);
566*3214b966STakashi Iwai 		if (shared)
567*3214b966STakashi Iwai 			activate_output_path(codec, &spec->out_path[shared],
568*3214b966STakashi Iwai 					     false, false);
569*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true);
570*3214b966STakashi Iwai 	} else if (spec->aamix_mode) {
571*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
572*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
573*3214b966STakashi Iwai 	} else {
574*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, false, false);
575*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
57609a9ad69STakashi Iwai 	}
57725eaba2fSLydia Wang }
578c577b8a1SJoseph Chan 
5794a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec)
5804a918ffeSTakashi Iwai {
5814a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
5824a918ffeSTakashi Iwai 
583*3214b966STakashi Iwai 	if (!spec->autocfg.speaker_outs)
584*3214b966STakashi Iwai 		return;
585*3214b966STakashi Iwai 	if (!spec->speaker_path.depth) {
586*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
587*3214b966STakashi Iwai 				     true);
588*3214b966STakashi Iwai 		return;
589*3214b966STakashi Iwai 	}
590*3214b966STakashi Iwai 	if (!spec->aamix_mode) {
591*3214b966STakashi Iwai 		activate_output_path(codec, &spec->speaker_mix_path,
592*3214b966STakashi Iwai 				     false, false);
593bac4b92cSTakashi Iwai 		via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
594*3214b966STakashi Iwai 				     true);
595*3214b966STakashi Iwai 	} else {
596*3214b966STakashi Iwai 		activate_output_path(codec, &spec->speaker_path, false, false);
597*3214b966STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
598*3214b966STakashi Iwai 				     true);
599*3214b966STakashi Iwai 	}
6004a918ffeSTakashi Iwai }
6014a918ffeSTakashi Iwai 
602f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
6036e969d91STakashi Iwai static void via_hp_automute(struct hda_codec *codec);
60432e0191dSClemens Ladisch 
605c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
606c577b8a1SJoseph Chan {
607c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
6087b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
609096a8854STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
61032e0191dSClemens Ladisch 	unsigned int ctl;
611096a8854STakashi Iwai 	int i, num_conns;
612c577b8a1SJoseph Chan 
613096a8854STakashi Iwai 	/* init ADCs */
614096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
615096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
616096a8854STakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
617096a8854STakashi Iwai 				    AMP_IN_UNMUTE(0));
618096a8854STakashi Iwai 	}
619096a8854STakashi Iwai 
620096a8854STakashi Iwai 	/* init pins */
6217b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
6227b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
623f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
62432e0191dSClemens Ladisch 			ctl = PIN_OUT;
62530649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
62632e0191dSClemens Ladisch 			ctl = PIN_VREF50;
62732e0191dSClemens Ladisch 		else
62832e0191dSClemens Ladisch 			ctl = PIN_IN;
629c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
63032e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
631c577b8a1SJoseph Chan 	}
632096a8854STakashi Iwai 
633096a8854STakashi Iwai 	/* init input-src */
634096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
635a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
636a86a88eaSTakashi Iwai 		if (spec->mux_nids[adc_idx]) {
637a86a88eaSTakashi Iwai 			int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
638a86a88eaSTakashi Iwai 			snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
639096a8854STakashi Iwai 					    AC_VERB_SET_CONNECT_SEL,
640a86a88eaSTakashi Iwai 					    mux_idx);
641a86a88eaSTakashi Iwai 		}
642a86a88eaSTakashi Iwai 		if (spec->dyn_adc_switch)
643a86a88eaSTakashi Iwai 			break; /* only one input-src */
644096a8854STakashi Iwai 	}
645096a8854STakashi Iwai 
646096a8854STakashi Iwai 	/* init aa-mixer */
647096a8854STakashi Iwai 	if (!spec->aa_mix_nid)
648096a8854STakashi Iwai 		return;
649096a8854STakashi Iwai 	num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
650096a8854STakashi Iwai 					    ARRAY_SIZE(conn));
651096a8854STakashi Iwai 	for (i = 0; i < num_conns; i++) {
652096a8854STakashi Iwai 		unsigned int caps = get_wcaps(codec, conn[i]);
653096a8854STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_PIN)
654096a8854STakashi Iwai 			snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
655096a8854STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
656096a8854STakashi Iwai 					    AMP_IN_MUTE(i));
657096a8854STakashi Iwai 	}
658c577b8a1SJoseph Chan }
659f5271101SLydia Wang 
660f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
661f5271101SLydia Wang 				unsigned int *affected_parm)
662f5271101SLydia Wang {
663f5271101SLydia Wang 	unsigned parm;
664f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
665f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
666f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
667f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
6681564b287SLydia Wang 	struct via_spec *spec = codec->spec;
66924088a58STakashi Iwai 	unsigned present = 0;
67024088a58STakashi Iwai 
67124088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
67224088a58STakashi Iwai 	if (!no_presence)
67324088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
674f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
6751564b287SLydia Wang 	    || ((no_presence || present)
6761564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
677f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
678f5271101SLydia Wang 		parm = AC_PWRST_D0;
679f5271101SLydia Wang 	} else
680f5271101SLydia Wang 		parm = AC_PWRST_D3;
681f5271101SLydia Wang 
682f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
683f5271101SLydia Wang }
684f5271101SLydia Wang 
68524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
68624088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
68724088a58STakashi Iwai {
68824088a58STakashi Iwai 	static const char * const texts[] = {
68924088a58STakashi Iwai 		"Disabled", "Enabled"
69024088a58STakashi Iwai 	};
69124088a58STakashi Iwai 
69224088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
69324088a58STakashi Iwai 	uinfo->count = 1;
69424088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
69524088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
69624088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
69724088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
69824088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
69924088a58STakashi Iwai 	return 0;
70024088a58STakashi Iwai }
70124088a58STakashi Iwai 
70224088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
70324088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
70424088a58STakashi Iwai {
70524088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
70624088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
70724088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
70824088a58STakashi Iwai 	return 0;
70924088a58STakashi Iwai }
71024088a58STakashi Iwai 
71124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
71224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
71324088a58STakashi Iwai {
71424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
71524088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
71624088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
71724088a58STakashi Iwai 
71824088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
71924088a58STakashi Iwai 		return 0;
72024088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
72124088a58STakashi Iwai 	set_widgets_power_state(codec);
72224088a58STakashi Iwai 	return 1;
72324088a58STakashi Iwai }
72424088a58STakashi Iwai 
72524088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
72624088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
72724088a58STakashi Iwai 	.name = "Dynamic Power-Control",
72824088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
72924088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
73024088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
73124088a58STakashi Iwai };
73224088a58STakashi Iwai 
73324088a58STakashi Iwai 
7340aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
7350aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
7360aa62aefSHarald Welte {
7378df2a312STakashi Iwai 	static const char * const texts[] = { "OFF", "ON" };
7388df2a312STakashi Iwai 
7398df2a312STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
7408df2a312STakashi Iwai 	uinfo->count = 1;
7418df2a312STakashi Iwai 	uinfo->value.enumerated.items = 2;
7428df2a312STakashi Iwai 	if (uinfo->value.enumerated.item >= 2)
7438df2a312STakashi Iwai 		uinfo->value.enumerated.item = 1;
7448df2a312STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
7458df2a312STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
7468df2a312STakashi Iwai 	return 0;
7470aa62aefSHarald Welte }
7480aa62aefSHarald Welte 
7490aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7500aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7510aa62aefSHarald Welte {
7520aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
753cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
754cdc1784dSLydia Wang 
755ece8d043STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
756cdc1784dSLydia Wang 	return 0;
757cdc1784dSLydia Wang }
758cdc1784dSLydia Wang 
7590aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7600aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7610aa62aefSHarald Welte {
7620aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7630aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
764*3214b966STakashi Iwai 	int cur, shared;
7658df2a312STakashi Iwai 
76625250505STakashi Iwai 	/* no independent-hp status change during PCM playback is running */
76725250505STakashi Iwai 	if (spec->num_active_streams)
76825250505STakashi Iwai 		return -EBUSY;
76925250505STakashi Iwai 
77025250505STakashi Iwai 	cur = !!ucontrol->value.enumerated.item[0];
77125250505STakashi Iwai 	if (spec->hp_independent_mode == cur)
77225250505STakashi Iwai 		return 0;
77325250505STakashi Iwai 	spec->hp_independent_mode = cur;
774*3214b966STakashi Iwai 	shared = spec->hp_indep_shared;
77525250505STakashi Iwai 	if (cur) {
776*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, false, false);
777*3214b966STakashi Iwai 		if (shared)
778*3214b966STakashi Iwai 			activate_output_path(codec, &spec->out_path[shared],
77925250505STakashi Iwai 					     false, false);
780*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_path, true, false);
78109a9ad69STakashi Iwai 	} else {
78209a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
783*3214b966STakashi Iwai 		if (shared)
784*3214b966STakashi Iwai 			activate_output_path(codec, &spec->out_path[shared],
78525250505STakashi Iwai 					     true, false);
786*3214b966STakashi Iwai 		activate_output_path(codec, &spec->hp_mix_path, true, false);
7878df2a312STakashi Iwai 	}
7880aa62aefSHarald Welte 
789ce0e5a9eSLydia Wang 	/* update jack power state */
7903e95b9abSLydia Wang 	set_widgets_power_state(codec);
7916e969d91STakashi Iwai 	via_hp_automute(codec);
79225250505STakashi Iwai 	return 1;
7930aa62aefSHarald Welte }
7940aa62aefSHarald Welte 
795ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = {
7960aa62aefSHarald Welte 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7970aa62aefSHarald Welte 	.name = "Independent HP",
7980aa62aefSHarald Welte 	.info = via_independent_hp_info,
7990aa62aefSHarald Welte 	.get = via_independent_hp_get,
8000aa62aefSHarald Welte 	.put = via_independent_hp_put,
8010aa62aefSHarald Welte };
8020aa62aefSHarald Welte 
8033d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
8045b0cb1d8SJaroslav Kysela {
8053d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
8065b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
8075b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
8085b0cb1d8SJaroslav Kysela 
8095b0cb1d8SJaroslav Kysela 	nid = spec->autocfg.hp_pins[0];
810ece8d043STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer);
8113d83e577STakashi Iwai 	if (knew == NULL)
8123d83e577STakashi Iwai 		return -ENOMEM;
8133d83e577STakashi Iwai 
8145b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
8155b0cb1d8SJaroslav Kysela 
8165b0cb1d8SJaroslav Kysela 	return 0;
8175b0cb1d8SJaroslav Kysela }
8185b0cb1d8SJaroslav Kysela 
8191564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
8201564b287SLydia Wang {
821e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
8221564b287SLydia Wang 	int i;
8231564b287SLydia Wang 
824e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
825e3d7a143STakashi Iwai 		struct snd_kcontrol *ctl;
826e3d7a143STakashi Iwai 		struct snd_ctl_elem_id id;
8271564b287SLydia Wang 		memset(&id, 0, sizeof(id));
8281564b287SLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
829e3d7a143STakashi Iwai 		sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
830525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
831525566cbSLydia Wang 		if (ctl)
832525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
833525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
834525566cbSLydia Wang 					&ctl->id);
8351564b287SLydia Wang 	}
8361564b287SLydia Wang }
8371564b287SLydia Wang 
8381564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
8391564b287SLydia Wang {
8401564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8411564b287SLydia Wang 	int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
842e3d7a143STakashi Iwai 	int i;
843e3d7a143STakashi Iwai 
844e3d7a143STakashi Iwai 	/* check AA path's mute status */
845e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
846e3d7a143STakashi Iwai 		if (spec->smart51_idxs[i] < 0)
847e3d7a143STakashi Iwai 			continue;
848e3d7a143STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
849e3d7a143STakashi Iwai 					 HDA_INPUT, spec->smart51_idxs[i],
8501564b287SLydia Wang 					 HDA_AMP_MUTE, val);
8511564b287SLydia Wang 	}
8521564b287SLydia Wang }
853f4a7828bSTakashi Iwai 
854e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
855e3d7a143STakashi Iwai {
856e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
857e3d7a143STakashi Iwai 	int i;
858e3d7a143STakashi Iwai 
859e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++)
860e3d7a143STakashi Iwai 		if (spec->smart51_pins[i] == pin)
861e3d7a143STakashi Iwai 			return true;
862e3d7a143STakashi Iwai 	return false;
863e3d7a143STakashi Iwai }
864e3d7a143STakashi Iwai 
8651564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
8661564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8671564b287SLydia Wang {
8681564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8691564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8701564b287SLydia Wang 
871f2b1c9f0STakashi Iwai 	*ucontrol->value.integer.value = spec->smart51_enabled;
8721564b287SLydia Wang 	return 0;
8731564b287SLydia Wang }
8741564b287SLydia Wang 
8751564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
8761564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8771564b287SLydia Wang {
8781564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8791564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8801564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
8811564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
8821564b287SLydia Wang 	int i;
8831564b287SLydia Wang 
884e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
885e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
8867b315bb4STakashi Iwai 		unsigned int parm;
8877b315bb4STakashi Iwai 
8887b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
8891564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8901564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
8911564b287SLydia Wang 		parm |= out_in;
8921564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
8931564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
8941564b287SLydia Wang 				    parm);
8951564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
8961564b287SLydia Wang 			mute_aa_path(codec, 1);
8971564b287SLydia Wang 			notify_aa_path_ctls(codec);
8981564b287SLydia Wang 		}
8991564b287SLydia Wang 	}
9001564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
9013e95b9abSLydia Wang 	set_widgets_power_state(codec);
9021564b287SLydia Wang 	return 1;
9031564b287SLydia Wang }
9041564b287SLydia Wang 
9055f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
9061564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9071564b287SLydia Wang 	.name = "Smart 5.1",
9081564b287SLydia Wang 	.count = 1,
909f2b1c9f0STakashi Iwai 	.info = snd_ctl_boolean_mono_info,
9101564b287SLydia Wang 	.get = via_smart51_get,
9111564b287SLydia Wang 	.put = via_smart51_put,
9121564b287SLydia Wang };
9131564b287SLydia Wang 
914f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
9155b0cb1d8SJaroslav Kysela {
916f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
9175b0cb1d8SJaroslav Kysela 
918e3d7a143STakashi Iwai 	if (!spec->smart51_nums)
919cb34c207SLydia Wang 		return 0;
920e3d7a143STakashi Iwai 	if (!via_clone_control(spec, &via_smart51_mixer))
9215b0cb1d8SJaroslav Kysela 		return -ENOMEM;
9225b0cb1d8SJaroslav Kysela 	return 0;
9235b0cb1d8SJaroslav Kysela }
9245b0cb1d8SJaroslav Kysela 
925f5271101SLydia Wang /* check AA path's mute status */
926ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
927ada509ecSTakashi Iwai {
928ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
929ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
930ada509ecSTakashi Iwai 	int i, ch, v;
931ada509ecSTakashi Iwai 
932ada509ecSTakashi Iwai 	for (i = 0; i < spec->num_loopbacks; i++) {
933ada509ecSTakashi Iwai 		p = &spec->loopback_list[i];
934ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
935ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
936ada509ecSTakashi Iwai 						   p->idx);
937ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
938ada509ecSTakashi Iwai 				return false;
939f5271101SLydia Wang 		}
940f5271101SLydia Wang 	}
941ada509ecSTakashi Iwai 	return true;
942f5271101SLydia Wang }
943f5271101SLydia Wang 
944f5271101SLydia Wang /* enter/exit analog low-current mode */
945ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
946f5271101SLydia Wang {
947f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
948ada509ecSTakashi Iwai 	bool enable;
949ada509ecSTakashi Iwai 	unsigned int verb, parm;
950f5271101SLydia Wang 
951ada509ecSTakashi Iwai 	enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
952f5271101SLydia Wang 
953f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
954f5271101SLydia Wang 	switch (spec->codec_type) {
955f5271101SLydia Wang 	case VT1708B_8CH:
956f5271101SLydia Wang 	case VT1708B_4CH:
957f5271101SLydia Wang 		verb = 0xf70;
958f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
959f5271101SLydia Wang 		break;
960f5271101SLydia Wang 	case VT1708S:
961eb7188caSLydia Wang 	case VT1718S:
962f3db423dSLydia Wang 	case VT1716S:
963f5271101SLydia Wang 		verb = 0xf73;
964f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
965f5271101SLydia Wang 		break;
966f5271101SLydia Wang 	case VT1702:
967f5271101SLydia Wang 		verb = 0xf73;
968f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
969f5271101SLydia Wang 		break;
97025eaba2fSLydia Wang 	case VT2002P:
971ab6734e7SLydia Wang 	case VT1812:
97211890956SLydia Wang 	case VT1802:
97325eaba2fSLydia Wang 		verb = 0xf93;
97425eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
97525eaba2fSLydia Wang 		break;
976f5271101SLydia Wang 	default:
977f5271101SLydia Wang 		return;		/* other codecs are not supported */
978f5271101SLydia Wang 	}
979f5271101SLydia Wang 	/* send verb */
980f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
981f5271101SLydia Wang }
982f5271101SLydia Wang 
983c577b8a1SJoseph Chan /*
984c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
985c577b8a1SJoseph Chan  */
986096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
987aa266fccSLydia Wang 	/* power down jack detect function */
988aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
989f7278fd0SJosepch Chan 	{ }
990c577b8a1SJoseph Chan };
991c577b8a1SJoseph Chan 
992ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active)
9937eb56e84STakashi Iwai {
994ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
995ada509ecSTakashi Iwai 
996ada509ecSTakashi Iwai 	if (active)
997ada509ecSTakashi Iwai 		spec->num_active_streams++;
998ada509ecSTakashi Iwai 	else
999ada509ecSTakashi Iwai 		spec->num_active_streams--;
1000ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
10017eb56e84STakashi Iwai }
10027eb56e84STakashi Iwai 
1003ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
1004c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1005c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1006c577b8a1SJoseph Chan {
1007c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
100825250505STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1009ada509ecSTakashi Iwai 	int err;
1010ece8d043STakashi Iwai 
101125250505STakashi Iwai 	spec->multiout.hp_nid = 0;
101225250505STakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
101325250505STakashi Iwai 	if (!spec->hp_independent_mode) {
101425250505STakashi Iwai 		if (!spec->hp_indep_shared)
1015ece8d043STakashi Iwai 			spec->multiout.hp_nid = spec->hp_dac_nid;
101625250505STakashi Iwai 	} else {
101725250505STakashi Iwai 		if (spec->hp_indep_shared)
101825250505STakashi Iwai 			spec->multiout.num_dacs = cfg->line_outs - 1;
101925250505STakashi Iwai 	}
102025250505STakashi Iwai 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
1021ada509ecSTakashi Iwai 	set_stream_active(codec, true);
1022ada509ecSTakashi Iwai 	err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
10239a08160bSTakashi Iwai 					    hinfo);
1024ada509ecSTakashi Iwai 	if (err < 0) {
1025ada509ecSTakashi Iwai 		spec->multiout.hp_nid = 0;
1026ada509ecSTakashi Iwai 		set_stream_active(codec, false);
1027ada509ecSTakashi Iwai 		return err;
1028ada509ecSTakashi Iwai 	}
1029ada509ecSTakashi Iwai 	return 0;
1030c577b8a1SJoseph Chan }
1031c577b8a1SJoseph Chan 
1032ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
10339af74210STakashi Iwai 				  struct hda_codec *codec,
10349af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
10359af74210STakashi Iwai {
1036ece8d043STakashi Iwai 	struct via_spec *spec = codec->spec;
1037ece8d043STakashi Iwai 
1038ece8d043STakashi Iwai 	spec->multiout.hp_nid = 0;
1039ada509ecSTakashi Iwai 	set_stream_active(codec, false);
10409af74210STakashi Iwai 	return 0;
10419af74210STakashi Iwai }
10429af74210STakashi Iwai 
10437eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
10447eb56e84STakashi Iwai 				    struct hda_codec *codec,
10457eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
10467eb56e84STakashi Iwai {
10477eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
10487eb56e84STakashi Iwai 
1049ece8d043STakashi Iwai 	if (snd_BUG_ON(!spec->hp_dac_nid))
10507eb56e84STakashi Iwai 		return -EINVAL;
1051ece8d043STakashi Iwai 	if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1052ece8d043STakashi Iwai 		return -EBUSY;
1053ada509ecSTakashi Iwai 	set_stream_active(codec, true);
1054ece8d043STakashi Iwai 	return 0;
1055ece8d043STakashi Iwai }
1056ece8d043STakashi Iwai 
1057ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1058ece8d043STakashi Iwai 				     struct hda_codec *codec,
1059ece8d043STakashi Iwai 				     struct snd_pcm_substream *substream)
1060ece8d043STakashi Iwai {
1061ada509ecSTakashi Iwai 	set_stream_active(codec, false);
10627eb56e84STakashi Iwai 	return 0;
10637eb56e84STakashi Iwai }
10647eb56e84STakashi Iwai 
10657eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
10667eb56e84STakashi Iwai 					  struct hda_codec *codec,
10670aa62aefSHarald Welte 					  unsigned int stream_tag,
10680aa62aefSHarald Welte 					  unsigned int format,
10690aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
10700aa62aefSHarald Welte {
10710aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10720aa62aefSHarald Welte 
1073ece8d043STakashi Iwai 	snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1074ece8d043STakashi Iwai 					 format, substream);
10757eb56e84STakashi Iwai 	vt1708_start_hp_work(spec);
10767eb56e84STakashi Iwai 	return 0;
10770aa62aefSHarald Welte }
10780aa62aefSHarald Welte 
10797eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
10800aa62aefSHarald Welte 				       struct hda_codec *codec,
10810aa62aefSHarald Welte 				       unsigned int stream_tag,
10820aa62aefSHarald Welte 				       unsigned int format,
10830aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
10840aa62aefSHarald Welte {
10850aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10860aa62aefSHarald Welte 
1087ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1088ece8d043STakashi Iwai 				   stream_tag, 0, format);
10891f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
10900aa62aefSHarald Welte 	return 0;
10910aa62aefSHarald Welte }
10920aa62aefSHarald Welte 
10930aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
10940aa62aefSHarald Welte 				    struct hda_codec *codec,
10950aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
10960aa62aefSHarald Welte {
10970aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10980aa62aefSHarald Welte 
1099ece8d043STakashi Iwai 	snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
11007eb56e84STakashi Iwai 	vt1708_stop_hp_work(spec);
11017eb56e84STakashi Iwai 	return 0;
11020aa62aefSHarald Welte }
11037eb56e84STakashi Iwai 
11047eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
11057eb56e84STakashi Iwai 				       struct hda_codec *codec,
11067eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
11077eb56e84STakashi Iwai {
11087eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
11097eb56e84STakashi Iwai 
1110ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
11111f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
11120aa62aefSHarald Welte 	return 0;
11130aa62aefSHarald Welte }
11140aa62aefSHarald Welte 
1115c577b8a1SJoseph Chan /*
1116c577b8a1SJoseph Chan  * Digital out
1117c577b8a1SJoseph Chan  */
1118c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1119c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1120c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1121c577b8a1SJoseph Chan {
1122c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1123c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1124c577b8a1SJoseph Chan }
1125c577b8a1SJoseph Chan 
1126c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1127c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1128c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1129c577b8a1SJoseph Chan {
1130c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1131c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1132c577b8a1SJoseph Chan }
1133c577b8a1SJoseph Chan 
11345691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
113598aa34c0SHarald Welte 					struct hda_codec *codec,
113698aa34c0SHarald Welte 					unsigned int stream_tag,
113798aa34c0SHarald Welte 					unsigned int format,
113898aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
113998aa34c0SHarald Welte {
114098aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
11419da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
11429da29271STakashi Iwai 					     stream_tag, format, substream);
11439da29271STakashi Iwai }
11445691ec7fSHarald Welte 
11459da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
11469da29271STakashi Iwai 					struct hda_codec *codec,
11479da29271STakashi Iwai 					struct snd_pcm_substream *substream)
11489da29271STakashi Iwai {
11499da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
11509da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
115198aa34c0SHarald Welte 	return 0;
115298aa34c0SHarald Welte }
115398aa34c0SHarald Welte 
1154c577b8a1SJoseph Chan /*
1155c577b8a1SJoseph Chan  * Analog capture
1156c577b8a1SJoseph Chan  */
1157c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1158c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1159c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1160c577b8a1SJoseph Chan 				   unsigned int format,
1161c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1162c577b8a1SJoseph Chan {
1163c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1164c577b8a1SJoseph Chan 
1165c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1166c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1167c577b8a1SJoseph Chan 	return 0;
1168c577b8a1SJoseph Chan }
1169c577b8a1SJoseph Chan 
1170c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1171c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1172c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1173c577b8a1SJoseph Chan {
1174c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1175888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1176c577b8a1SJoseph Chan 	return 0;
1177c577b8a1SJoseph Chan }
1178c577b8a1SJoseph Chan 
1179a86a88eaSTakashi Iwai /* analog capture with dynamic ADC switching */
1180a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1181a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1182a86a88eaSTakashi Iwai 					   unsigned int stream_tag,
1183a86a88eaSTakashi Iwai 					   unsigned int format,
1184a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1185a86a88eaSTakashi Iwai {
1186a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1187a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1188a86a88eaSTakashi Iwai 
1189a86a88eaSTakashi Iwai 	spec->cur_adc = spec->adc_nids[adc_idx];
1190a86a88eaSTakashi Iwai 	spec->cur_adc_stream_tag = stream_tag;
1191a86a88eaSTakashi Iwai 	spec->cur_adc_format = format;
1192a86a88eaSTakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1193a86a88eaSTakashi Iwai 	return 0;
1194a86a88eaSTakashi Iwai }
1195a86a88eaSTakashi Iwai 
1196a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1197a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1198a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1199a86a88eaSTakashi Iwai {
1200a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1201a86a88eaSTakashi Iwai 
1202a86a88eaSTakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1203a86a88eaSTakashi Iwai 	spec->cur_adc = 0;
1204a86a88eaSTakashi Iwai 	return 0;
1205a86a88eaSTakashi Iwai }
1206a86a88eaSTakashi Iwai 
1207a86a88eaSTakashi Iwai /* re-setup the stream if running; called from input-src put */
1208a86a88eaSTakashi Iwai static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1209a86a88eaSTakashi Iwai {
1210a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1211a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[cur].adc_idx;
1212a86a88eaSTakashi Iwai 	hda_nid_t adc = spec->adc_nids[adc_idx];
1213a86a88eaSTakashi Iwai 
1214a86a88eaSTakashi Iwai 	if (spec->cur_adc && spec->cur_adc != adc) {
1215a86a88eaSTakashi Iwai 		/* stream is running, let's swap the current ADC */
1216a86a88eaSTakashi Iwai 		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1217a86a88eaSTakashi Iwai 		spec->cur_adc = adc;
1218a86a88eaSTakashi Iwai 		snd_hda_codec_setup_stream(codec, adc,
1219a86a88eaSTakashi Iwai 					   spec->cur_adc_stream_tag, 0,
1220a86a88eaSTakashi Iwai 					   spec->cur_adc_format);
1221a86a88eaSTakashi Iwai 		return true;
1222a86a88eaSTakashi Iwai 	}
1223a86a88eaSTakashi Iwai 	return false;
1224a86a88eaSTakashi Iwai }
1225a86a88eaSTakashi Iwai 
12269af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
12277eb56e84STakashi Iwai 	.substreams = 1,
1228c577b8a1SJoseph Chan 	.channels_min = 2,
1229c577b8a1SJoseph Chan 	.channels_max = 8,
12309af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1231c577b8a1SJoseph Chan 	.ops = {
1232ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1233ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
12340aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
12350aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1236c577b8a1SJoseph Chan 	},
1237c577b8a1SJoseph Chan };
1238c577b8a1SJoseph Chan 
12397eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
12407eb56e84STakashi Iwai 	.substreams = 1,
12417eb56e84STakashi Iwai 	.channels_min = 2,
12427eb56e84STakashi Iwai 	.channels_max = 2,
12437eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
12447eb56e84STakashi Iwai 	.ops = {
12457eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
1246ece8d043STakashi Iwai 		.close = via_playback_hp_pcm_close,
12477eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
12487eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
12497eb56e84STakashi Iwai 	},
12507eb56e84STakashi Iwai };
12517eb56e84STakashi Iwai 
125290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
12537eb56e84STakashi Iwai 	.substreams = 1,
1254bc9b5623STakashi Iwai 	.channels_min = 2,
1255bc9b5623STakashi Iwai 	.channels_max = 8,
12569af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1257bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1258bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1259bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1260bc9b5623STakashi Iwai 	 */
1261bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1262bc9b5623STakashi Iwai 	.ops = {
1263ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1264ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
1265c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1266c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1267bc9b5623STakashi Iwai 	},
1268bc9b5623STakashi Iwai };
1269bc9b5623STakashi Iwai 
12709af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
12717eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1272c577b8a1SJoseph Chan 	.channels_min = 2,
1273c577b8a1SJoseph Chan 	.channels_max = 2,
12749af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1275c577b8a1SJoseph Chan 	.ops = {
1276c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1277c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1278c577b8a1SJoseph Chan 	},
1279c577b8a1SJoseph Chan };
1280c577b8a1SJoseph Chan 
1281a86a88eaSTakashi Iwai static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1282a86a88eaSTakashi Iwai 	.substreams = 1,
1283a86a88eaSTakashi Iwai 	.channels_min = 2,
1284a86a88eaSTakashi Iwai 	.channels_max = 2,
1285a86a88eaSTakashi Iwai 	/* NID is set in via_build_pcms */
1286a86a88eaSTakashi Iwai 	.ops = {
1287a86a88eaSTakashi Iwai 		.prepare = via_dyn_adc_capture_pcm_prepare,
1288a86a88eaSTakashi Iwai 		.cleanup = via_dyn_adc_capture_pcm_cleanup,
1289a86a88eaSTakashi Iwai 	},
1290a86a88eaSTakashi Iwai };
1291a86a88eaSTakashi Iwai 
12929af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1293c577b8a1SJoseph Chan 	.substreams = 1,
1294c577b8a1SJoseph Chan 	.channels_min = 2,
1295c577b8a1SJoseph Chan 	.channels_max = 2,
1296c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1297c577b8a1SJoseph Chan 	.ops = {
1298c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
12996b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
13009da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
13019da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1302c577b8a1SJoseph Chan 	},
1303c577b8a1SJoseph Chan };
1304c577b8a1SJoseph Chan 
13059af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1306c577b8a1SJoseph Chan 	.substreams = 1,
1307c577b8a1SJoseph Chan 	.channels_min = 2,
1308c577b8a1SJoseph Chan 	.channels_max = 2,
1309c577b8a1SJoseph Chan };
1310c577b8a1SJoseph Chan 
1311370bafbdSTakashi Iwai /*
1312370bafbdSTakashi Iwai  * slave controls for virtual master
1313370bafbdSTakashi Iwai  */
1314370bafbdSTakashi Iwai static const char * const via_slave_vols[] = {
1315370bafbdSTakashi Iwai 	"Front Playback Volume",
1316370bafbdSTakashi Iwai 	"Surround Playback Volume",
1317370bafbdSTakashi Iwai 	"Center Playback Volume",
1318370bafbdSTakashi Iwai 	"LFE Playback Volume",
1319370bafbdSTakashi Iwai 	"Side Playback Volume",
1320370bafbdSTakashi Iwai 	"Headphone Playback Volume",
1321370bafbdSTakashi Iwai 	"Speaker Playback Volume",
1322370bafbdSTakashi Iwai 	NULL,
1323370bafbdSTakashi Iwai };
1324370bafbdSTakashi Iwai 
1325370bafbdSTakashi Iwai static const char * const via_slave_sws[] = {
1326370bafbdSTakashi Iwai 	"Front Playback Switch",
1327370bafbdSTakashi Iwai 	"Surround Playback Switch",
1328370bafbdSTakashi Iwai 	"Center Playback Switch",
1329370bafbdSTakashi Iwai 	"LFE Playback Switch",
1330370bafbdSTakashi Iwai 	"Side Playback Switch",
1331370bafbdSTakashi Iwai 	"Headphone Playback Switch",
1332370bafbdSTakashi Iwai 	"Speaker Playback Switch",
1333370bafbdSTakashi Iwai 	NULL,
1334370bafbdSTakashi Iwai };
1335370bafbdSTakashi Iwai 
1336c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1337c577b8a1SJoseph Chan {
1338c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
13395b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
13405b0cb1d8SJaroslav Kysela 	int err, i;
1341c577b8a1SJoseph Chan 
134224088a58STakashi Iwai 	if (spec->set_widgets_power_state)
134324088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
134424088a58STakashi Iwai 			return -ENOMEM;
134524088a58STakashi Iwai 
1346c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1347c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1348c577b8a1SJoseph Chan 		if (err < 0)
1349c577b8a1SJoseph Chan 			return err;
1350c577b8a1SJoseph Chan 	}
1351c577b8a1SJoseph Chan 
1352c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1353c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
135474b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1355c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1356c577b8a1SJoseph Chan 		if (err < 0)
1357c577b8a1SJoseph Chan 			return err;
13589a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
13599a08160bSTakashi Iwai 						    &spec->multiout);
13609a08160bSTakashi Iwai 		if (err < 0)
13619a08160bSTakashi Iwai 			return err;
13629a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1363c577b8a1SJoseph Chan 	}
1364c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1365c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1366c577b8a1SJoseph Chan 		if (err < 0)
1367c577b8a1SJoseph Chan 			return err;
1368c577b8a1SJoseph Chan 	}
136917314379SLydia Wang 
1370370bafbdSTakashi Iwai 	/* if we have no master control, let's create it */
1371370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1372370bafbdSTakashi Iwai 		unsigned int vmaster_tlv[4];
1373370bafbdSTakashi Iwai 		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1374370bafbdSTakashi Iwai 					HDA_OUTPUT, vmaster_tlv);
1375370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1376370bafbdSTakashi Iwai 					  vmaster_tlv, via_slave_vols);
1377370bafbdSTakashi Iwai 		if (err < 0)
1378370bafbdSTakashi Iwai 			return err;
1379370bafbdSTakashi Iwai 	}
1380370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1381370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1382370bafbdSTakashi Iwai 					  NULL, via_slave_sws);
1383370bafbdSTakashi Iwai 		if (err < 0)
1384370bafbdSTakashi Iwai 			return err;
1385370bafbdSTakashi Iwai 	}
1386370bafbdSTakashi Iwai 
13875b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
13885b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
13895b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
139021949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
13915b0cb1d8SJaroslav Kysela 		if (err < 0)
13925b0cb1d8SJaroslav Kysela 			return err;
13935b0cb1d8SJaroslav Kysela 	}
13945b0cb1d8SJaroslav Kysela 
139517314379SLydia Wang 	/* init power states */
13963e95b9abSLydia Wang 	set_widgets_power_state(codec);
1397ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
139817314379SLydia Wang 
1399603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1400c577b8a1SJoseph Chan 	return 0;
1401c577b8a1SJoseph Chan }
1402c577b8a1SJoseph Chan 
1403c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1404c577b8a1SJoseph Chan {
1405c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1406c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1407c577b8a1SJoseph Chan 
1408c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1409c577b8a1SJoseph Chan 	codec->pcm_info = info;
1410c577b8a1SJoseph Chan 
141182673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
141282673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1413c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
14149af74210STakashi Iwai 
14159af74210STakashi Iwai 	if (!spec->stream_analog_playback)
14169af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1417377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
14189af74210STakashi Iwai 		*spec->stream_analog_playback;
1419377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1420377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1421c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1422c577b8a1SJoseph Chan 		spec->multiout.max_channels;
14239af74210STakashi Iwai 
1424a86a88eaSTakashi Iwai 	if (!spec->stream_analog_capture) {
1425a86a88eaSTakashi Iwai 		if (spec->dyn_adc_switch)
1426a86a88eaSTakashi Iwai 			spec->stream_analog_capture =
1427a86a88eaSTakashi Iwai 				&via_pcm_dyn_adc_analog_capture;
1428a86a88eaSTakashi Iwai 		else
14299af74210STakashi Iwai 			spec->stream_analog_capture = &via_pcm_analog_capture;
1430a86a88eaSTakashi Iwai 	}
14319af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
14329af74210STakashi Iwai 		*spec->stream_analog_capture;
14339af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1434a86a88eaSTakashi Iwai 	if (!spec->dyn_adc_switch)
14359af74210STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
14369af74210STakashi Iwai 			spec->num_adc_nids;
1437c577b8a1SJoseph Chan 
1438c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1439c577b8a1SJoseph Chan 		codec->num_pcms++;
1440c577b8a1SJoseph Chan 		info++;
144182673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
144282673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
144382673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1444c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
14457ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1446c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
14479af74210STakashi Iwai 			if (!spec->stream_digital_playback)
14489af74210STakashi Iwai 				spec->stream_digital_playback =
14499af74210STakashi Iwai 					&via_pcm_digital_playback;
1450c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
14519af74210STakashi Iwai 				*spec->stream_digital_playback;
1452c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1453c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1454c577b8a1SJoseph Chan 		}
1455c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
14569af74210STakashi Iwai 			if (!spec->stream_digital_capture)
14579af74210STakashi Iwai 				spec->stream_digital_capture =
14589af74210STakashi Iwai 					&via_pcm_digital_capture;
1459c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
14609af74210STakashi Iwai 				*spec->stream_digital_capture;
1461c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1462c577b8a1SJoseph Chan 				spec->dig_in_nid;
1463c577b8a1SJoseph Chan 		}
1464c577b8a1SJoseph Chan 	}
1465c577b8a1SJoseph Chan 
1466ece8d043STakashi Iwai 	if (spec->hp_dac_nid) {
14677eb56e84STakashi Iwai 		codec->num_pcms++;
14687eb56e84STakashi Iwai 		info++;
14697eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
14707eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
14717eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
14727eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
14737eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1474ece8d043STakashi Iwai 			spec->hp_dac_nid;
14757eb56e84STakashi Iwai 	}
1476c577b8a1SJoseph Chan 	return 0;
1477c577b8a1SJoseph Chan }
1478c577b8a1SJoseph Chan 
1479c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1480c577b8a1SJoseph Chan {
1481c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1482c577b8a1SJoseph Chan 
1483c577b8a1SJoseph Chan 	if (!spec)
1484c577b8a1SJoseph Chan 		return;
1485c577b8a1SJoseph Chan 
1486603c4019STakashi Iwai 	via_free_kctls(codec);
14871f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1488a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_vol);
1489a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_sw);
1490a86a88eaSTakashi Iwai 	kfree(spec);
1491c577b8a1SJoseph Chan }
1492c577b8a1SJoseph Chan 
149364be285bSTakashi Iwai /* mute/unmute outputs */
149464be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
149564be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
149664be285bSTakashi Iwai {
149764be285bSTakashi Iwai 	int i;
149894994734STakashi Iwai 	for (i = 0; i < num_pins; i++) {
149994994734STakashi Iwai 		unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
150094994734STakashi Iwai 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
150194994734STakashi Iwai 		if (parm & AC_PINCTL_IN_EN)
150294994734STakashi Iwai 			continue;
150394994734STakashi Iwai 		if (mute)
150494994734STakashi Iwai 			parm &= ~AC_PINCTL_OUT_EN;
150594994734STakashi Iwai 		else
150694994734STakashi Iwai 			parm |= AC_PINCTL_OUT_EN;
150764be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
150894994734STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
150994994734STakashi Iwai 	}
151064be285bSTakashi Iwai }
151164be285bSTakashi Iwai 
15124a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */
15134a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present)
15144a918ffeSTakashi Iwai {
15154a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
15164a918ffeSTakashi Iwai 
15174a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs)
15184a918ffeSTakashi Iwai 		return;
15194a918ffeSTakashi Iwai 	if (!present)
15204a918ffeSTakashi Iwai 		present = snd_hda_jack_detect(codec,
15214a918ffeSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
15224a918ffeSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
15234a918ffeSTakashi Iwai 			    spec->autocfg.speaker_pins,
15244a918ffeSTakashi Iwai 			    present);
15254a918ffeSTakashi Iwai }
15264a918ffeSTakashi Iwai 
152769e52a80SHarald Welte /* mute internal speaker if HP is plugged */
152869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
152969e52a80SHarald Welte {
15304a918ffeSTakashi Iwai 	int present = 0;
15316e969d91STakashi Iwai 	int nums;
153269e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
153369e52a80SHarald Welte 
15346e969d91STakashi Iwai 	if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
1535d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
15366e969d91STakashi Iwai 
1537f2b1c9f0STakashi Iwai 	if (spec->smart51_enabled)
1538f2b1c9f0STakashi Iwai 		nums = spec->autocfg.line_outs + spec->smart51_nums;
1539f2b1c9f0STakashi Iwai 	else
1540f2b1c9f0STakashi Iwai 		nums = spec->autocfg.line_outs;
15416e969d91STakashi Iwai 	toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
15426e969d91STakashi Iwai 
15434a918ffeSTakashi Iwai 	via_line_automute(codec, present);
1544f3db423dSLydia Wang }
1545f3db423dSLydia Wang 
154669e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
154769e52a80SHarald Welte {
154869e52a80SHarald Welte 	unsigned int gpio_data;
154969e52a80SHarald Welte 	unsigned int vol_counter;
155069e52a80SHarald Welte 	unsigned int vol;
155169e52a80SHarald Welte 	unsigned int master_vol;
155269e52a80SHarald Welte 
155369e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
155469e52a80SHarald Welte 
155569e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
155669e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
155769e52a80SHarald Welte 
155869e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
155969e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
156069e52a80SHarald Welte 
156169e52a80SHarald Welte 	vol = vol_counter & 0x1F;
156269e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
156369e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
156469e52a80SHarald Welte 					AC_AMP_GET_INPUT);
156569e52a80SHarald Welte 
156669e52a80SHarald Welte 	if (gpio_data == 0x02) {
156769e52a80SHarald Welte 		/* unmute line out */
15683e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
15693e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
15703e0693e2STakashi Iwai 				    PIN_OUT);
157169e52a80SHarald Welte 		if (vol_counter & 0x20) {
157269e52a80SHarald Welte 			/* decrease volume */
157369e52a80SHarald Welte 			if (vol > master_vol)
157469e52a80SHarald Welte 				vol = master_vol;
157569e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
157669e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
157769e52a80SHarald Welte 						 master_vol-vol);
157869e52a80SHarald Welte 		} else {
157969e52a80SHarald Welte 			/* increase volume */
158069e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
158169e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
158269e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
158369e52a80SHarald Welte 					  (master_vol+vol));
158469e52a80SHarald Welte 		}
158569e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
158669e52a80SHarald Welte 		/* mute line out */
15873e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
15883e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
15893e0693e2STakashi Iwai 				    0);
159069e52a80SHarald Welte 	}
159169e52a80SHarald Welte }
159269e52a80SHarald Welte 
159369e52a80SHarald Welte /* unsolicited event for jack sensing */
159469e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
159569e52a80SHarald Welte 				  unsigned int res)
159669e52a80SHarald Welte {
159769e52a80SHarald Welte 	res >>= 26;
1598ec7e7e42SLydia Wang 
1599a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
16003e95b9abSLydia Wang 		set_widgets_power_state(codec);
1601ec7e7e42SLydia Wang 
1602ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1603ec7e7e42SLydia Wang 
160421ce0b65STakashi Iwai 	if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
1605ec7e7e42SLydia Wang 		via_hp_automute(codec);
1606ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1607ec7e7e42SLydia Wang 		via_gpio_control(codec);
160869e52a80SHarald Welte }
160969e52a80SHarald Welte 
16101f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
16111f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
16121f2e99feSLydia Wang {
16131f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
16141f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
16151f2e99feSLydia Wang 	return 0;
16161f2e99feSLydia Wang }
16171f2e99feSLydia Wang #endif
16181f2e99feSLydia Wang 
1619cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1620cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1621cb53c626STakashi Iwai {
1622cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1623cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1624cb53c626STakashi Iwai }
1625cb53c626STakashi Iwai #endif
1626cb53c626STakashi Iwai 
1627c577b8a1SJoseph Chan /*
1628c577b8a1SJoseph Chan  */
16295d41762aSTakashi Iwai 
16305d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
16315d41762aSTakashi Iwai 
163290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1633c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1634c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1635c577b8a1SJoseph Chan 	.init = via_init,
1636c577b8a1SJoseph Chan 	.free = via_free,
16374a918ffeSTakashi Iwai 	.unsol_event = via_unsol_event,
16381f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
16391f2e99feSLydia Wang 	.suspend = via_suspend,
16401f2e99feSLydia Wang #endif
1641cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1642cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1643cb53c626STakashi Iwai #endif
1644c577b8a1SJoseph Chan };
1645c577b8a1SJoseph Chan 
16464a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1647c577b8a1SJoseph Chan {
16484a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
16494a79616dSTakashi Iwai 	int i;
16504a79616dSTakashi Iwai 
16514a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
16524a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
16534a79616dSTakashi Iwai 			return false;
16544a79616dSTakashi Iwai 	}
1655ece8d043STakashi Iwai 	if (spec->hp_dac_nid == dac)
16564a79616dSTakashi Iwai 		return false;
16574a79616dSTakashi Iwai 	return true;
16584a79616dSTakashi Iwai }
16594a79616dSTakashi Iwai 
16608e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1661*3214b966STakashi Iwai 				hda_nid_t target_dac, int with_aa_mix,
1662*3214b966STakashi Iwai 				struct nid_path *path, int depth)
16634a79616dSTakashi Iwai {
1664*3214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
16654a79616dSTakashi Iwai 	hda_nid_t conn[8];
16664a79616dSTakashi Iwai 	int i, nums;
16674a79616dSTakashi Iwai 
1668*3214b966STakashi Iwai 	if (nid == spec->aa_mix_nid) {
1669*3214b966STakashi Iwai 		if (!with_aa_mix)
1670*3214b966STakashi Iwai 			return false;
1671*3214b966STakashi Iwai 		with_aa_mix = 2; /* mark aa-mix is included */
1672*3214b966STakashi Iwai 	}
1673*3214b966STakashi Iwai 
16744a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
16754a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
16764a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
16774a79616dSTakashi Iwai 			continue;
1678*3214b966STakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1679*3214b966STakashi Iwai 			/* aa-mix is requested but not included? */
1680*3214b966STakashi Iwai 			if (!(spec->aa_mix_nid && with_aa_mix == 1))
168109a9ad69STakashi Iwai 				goto found;
16824a79616dSTakashi Iwai 		}
1683*3214b966STakashi Iwai 	}
16848e3679dcSTakashi Iwai 	if (depth >= MAX_NID_PATH_DEPTH)
16854a79616dSTakashi Iwai 		return false;
16864a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
16874a79616dSTakashi Iwai 		unsigned int type;
16884a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
1689*3214b966STakashi Iwai 		if (type == AC_WID_AUD_OUT)
16904a79616dSTakashi Iwai 			continue;
16918e3679dcSTakashi Iwai 		if (__parse_output_path(codec, conn[i], target_dac,
1692*3214b966STakashi Iwai 					with_aa_mix, path, depth + 1))
169309a9ad69STakashi Iwai 			goto found;
16944a79616dSTakashi Iwai 	}
16954a79616dSTakashi Iwai 	return false;
169609a9ad69STakashi Iwai 
169709a9ad69STakashi Iwai  found:
169809a9ad69STakashi Iwai 	path->path[path->depth] = conn[i];
169909a9ad69STakashi Iwai 	path->idx[path->depth] = i;
170009a9ad69STakashi Iwai 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
170109a9ad69STakashi Iwai 		path->multi[path->depth] = 1;
170209a9ad69STakashi Iwai 	path->depth++;
170309a9ad69STakashi Iwai 	return true;
17044a79616dSTakashi Iwai }
17054a79616dSTakashi Iwai 
17068e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1707*3214b966STakashi Iwai 			      hda_nid_t target_dac, int with_aa_mix,
1708*3214b966STakashi Iwai 			      struct nid_path *path)
17098e3679dcSTakashi Iwai {
1710*3214b966STakashi Iwai 	if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
17118e3679dcSTakashi Iwai 		path->path[path->depth] = nid;
17128e3679dcSTakashi Iwai 		path->depth++;
1713*3214b966STakashi Iwai 		snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1714*3214b966STakashi Iwai 			    path->depth, path->path[0], path->path[1],
1715*3214b966STakashi Iwai 			    path->path[2], path->path[3], path->path[4]);
17168e3679dcSTakashi Iwai 		return true;
17178e3679dcSTakashi Iwai 	}
17188e3679dcSTakashi Iwai 	return false;
17198e3679dcSTakashi Iwai }
17208e3679dcSTakashi Iwai 
17214a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
17224a79616dSTakashi Iwai {
17234a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
17244a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
17255c9a5615SLydia Wang 	int i, dac_num;
1726c577b8a1SJoseph Chan 	hda_nid_t nid;
1727c577b8a1SJoseph Chan 
1728c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
17295c9a5615SLydia Wang 	dac_num = 0;
17304a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1731*3214b966STakashi Iwai 		hda_nid_t dac = 0;
1732c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
17334a79616dSTakashi Iwai 		if (!nid)
17344a79616dSTakashi Iwai 			continue;
1735*3214b966STakashi Iwai 		if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1736*3214b966STakashi Iwai 			dac = spec->out_path[i].path[0];
1737*3214b966STakashi Iwai 		if (!i && parse_output_path(codec, nid, dac, 1,
1738*3214b966STakashi Iwai 					    &spec->out_mix_path))
1739*3214b966STakashi Iwai 			dac = spec->out_mix_path.path[0];
1740*3214b966STakashi Iwai 		if (dac) {
1741*3214b966STakashi Iwai 			spec->private_dac_nids[i] = dac;
17425c9a5615SLydia Wang 			dac_num++;
1743c577b8a1SJoseph Chan 		}
17445c9a5615SLydia Wang 	}
1745*3214b966STakashi Iwai 	if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1746*3214b966STakashi Iwai 		spec->out_path[0] = spec->out_mix_path;
1747*3214b966STakashi Iwai 		spec->out_mix_path.depth = 0;
1748*3214b966STakashi Iwai 	}
17495c9a5615SLydia Wang 	spec->multiout.num_dacs = dac_num;
1750c577b8a1SJoseph Chan 	return 0;
1751c577b8a1SJoseph Chan }
1752c577b8a1SJoseph Chan 
17534a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
175409a9ad69STakashi Iwai 			  int chs, bool check_dac, struct nid_path *path)
1755c577b8a1SJoseph Chan {
17564a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1757c577b8a1SJoseph Chan 	char name[32];
175809a9ad69STakashi Iwai 	hda_nid_t dac, pin, sel, nid;
175909a9ad69STakashi Iwai 	int err;
1760a934d5a9STakashi Iwai 
176109a9ad69STakashi Iwai 	dac = check_dac ? path->path[0] : 0;
176209a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
176309a9ad69STakashi Iwai 	sel = path->depth > 1 ? path->path[1] : 0;
1764c577b8a1SJoseph Chan 
17658df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
17664a79616dSTakashi Iwai 		nid = dac;
17678df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
17684a79616dSTakashi Iwai 		nid = pin;
1769a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1770a934d5a9STakashi Iwai 		nid = sel;
17714a79616dSTakashi Iwai 	else
17724a79616dSTakashi Iwai 		nid = 0;
17734a79616dSTakashi Iwai 	if (nid) {
17744a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1775c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1776a00a5fadSLydia Wang 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1777c577b8a1SJoseph Chan 		if (err < 0)
1778c577b8a1SJoseph Chan 			return err;
177909a9ad69STakashi Iwai 		path->vol_ctl = nid;
1780c577b8a1SJoseph Chan 	}
17814a79616dSTakashi Iwai 
17828df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
17834a79616dSTakashi Iwai 		nid = dac;
17848df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
17854a79616dSTakashi Iwai 		nid = pin;
1786a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1787a934d5a9STakashi Iwai 		nid = sel;
17884a79616dSTakashi Iwai 	else
17894a79616dSTakashi Iwai 		nid = 0;
17904a79616dSTakashi Iwai 	if (nid) {
17914a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
17924a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
17934a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
17944a79616dSTakashi Iwai 		if (err < 0)
17954a79616dSTakashi Iwai 			return err;
179609a9ad69STakashi Iwai 		path->mute_ctl = nid;
17974a79616dSTakashi Iwai 	}
17984a79616dSTakashi Iwai 	return 0;
17994a79616dSTakashi Iwai }
18004a79616dSTakashi Iwai 
1801f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1802f4a7828bSTakashi Iwai {
1803f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1804f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
18050f98c24bSTakashi Iwai 	struct auto_pin_cfg_item *ins = cfg->inputs;
18060f98c24bSTakashi Iwai 	int i, j, nums, attr;
18070f98c24bSTakashi Iwai 	int pins[AUTO_CFG_MAX_INS];
1808f4a7828bSTakashi Iwai 
18090f98c24bSTakashi Iwai 	for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
18100f98c24bSTakashi Iwai 		nums = 0;
1811f4a7828bSTakashi Iwai 		for (i = 0; i < cfg->num_inputs; i++) {
18120f98c24bSTakashi Iwai 			unsigned int def;
18130f98c24bSTakashi Iwai 			if (ins[i].type > AUTO_PIN_LINE_IN)
18140f98c24bSTakashi Iwai 				continue;
18150f98c24bSTakashi Iwai 			def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
18160f98c24bSTakashi Iwai 			if (snd_hda_get_input_pin_attr(def) != attr)
18170f98c24bSTakashi Iwai 				continue;
18180f98c24bSTakashi Iwai 			for (j = 0; j < nums; j++)
18190f98c24bSTakashi Iwai 				if (ins[pins[j]].type < ins[i].type) {
18200f98c24bSTakashi Iwai 					memmove(pins + j + 1, pins + j,
182121d45d2bSTakashi Iwai 						(nums - j) * sizeof(int));
18220f98c24bSTakashi Iwai 					break;
18230f98c24bSTakashi Iwai 				}
18240f98c24bSTakashi Iwai 			pins[j] = i;
1825e3d7a143STakashi Iwai 			nums++;
1826e3d7a143STakashi Iwai 		}
1827e3d7a143STakashi Iwai 		if (cfg->line_outs + nums < 3)
1828f4a7828bSTakashi Iwai 			continue;
18290f98c24bSTakashi Iwai 		for (i = 0; i < nums; i++) {
18300f98c24bSTakashi Iwai 			hda_nid_t pin = ins[pins[i]].pin;
18310f98c24bSTakashi Iwai 			spec->smart51_pins[spec->smart51_nums++] = pin;
18320f98c24bSTakashi Iwai 			cfg->line_out_pins[cfg->line_outs++] = pin;
1833f4a7828bSTakashi Iwai 			if (cfg->line_outs == 3)
1834f4a7828bSTakashi Iwai 				break;
1835f4a7828bSTakashi Iwai 		}
18360f98c24bSTakashi Iwai 		return;
18370f98c24bSTakashi Iwai 	}
1838f4a7828bSTakashi Iwai }
1839f4a7828bSTakashi Iwai 
18404a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
18414a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
18424a79616dSTakashi Iwai {
18434a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1844f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
1845*3214b966STakashi Iwai 	struct nid_path *path;
18464a79616dSTakashi Iwai 	static const char * const chname[4] = {
18474a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
18484a79616dSTakashi Iwai 	};
18494a79616dSTakashi Iwai 	int i, idx, err;
1850f4a7828bSTakashi Iwai 	int old_line_outs;
1851f4a7828bSTakashi Iwai 
1852f4a7828bSTakashi Iwai 	/* check smart51 */
1853f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
1854f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
1855f4a7828bSTakashi Iwai 		mangle_smart51(codec);
18564a79616dSTakashi Iwai 
1857e3d7a143STakashi Iwai 	err = via_auto_fill_dac_nids(codec);
1858e3d7a143STakashi Iwai 	if (err < 0)
1859e3d7a143STakashi Iwai 		return err;
1860e3d7a143STakashi Iwai 
18615c9a5615SLydia Wang 	if (spec->multiout.num_dacs < 3) {
18625c9a5615SLydia Wang 		spec->smart51_nums = 0;
18635c9a5615SLydia Wang 		cfg->line_outs = old_line_outs;
18645c9a5615SLydia Wang 	}
18654a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
18664a79616dSTakashi Iwai 		hda_nid_t pin, dac;
18674a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
18684a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
18694a79616dSTakashi Iwai 		if (!pin || !dac)
18704a79616dSTakashi Iwai 			continue;
1871*3214b966STakashi Iwai 		path = spec->out_path + i;
18720fe0adf8STakashi Iwai 		if (i == HDA_CLFE) {
1873*3214b966STakashi Iwai 			err = create_ch_ctls(codec, "Center", 1, true, path);
18744a79616dSTakashi Iwai 			if (err < 0)
18754a79616dSTakashi Iwai 				return err;
1876*3214b966STakashi Iwai 			err = create_ch_ctls(codec, "LFE", 2, true, path);
18774a79616dSTakashi Iwai 			if (err < 0)
18784a79616dSTakashi Iwai 				return err;
18794a79616dSTakashi Iwai 		} else {
18806aadf41dSTakashi Iwai 			const char *pfx = chname[i];
18816aadf41dSTakashi Iwai 			if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
18826aadf41dSTakashi Iwai 			    cfg->line_outs == 1)
18836aadf41dSTakashi Iwai 				pfx = "Speaker";
1884*3214b966STakashi Iwai 			err = create_ch_ctls(codec, pfx, 3, true, path);
18854a79616dSTakashi Iwai 			if (err < 0)
18864a79616dSTakashi Iwai 				return err;
18874a79616dSTakashi Iwai 		}
1888*3214b966STakashi Iwai 		if (path != spec->out_path + i) {
1889*3214b966STakashi Iwai 			spec->out_path[i].vol_ctl = path->vol_ctl;
1890*3214b966STakashi Iwai 			spec->out_path[i].mute_ctl = path->mute_ctl;
1891*3214b966STakashi Iwai 		}
1892*3214b966STakashi Iwai 		if (path == spec->out_path && spec->out_mix_path.depth) {
1893*3214b966STakashi Iwai 			spec->out_mix_path.vol_ctl = path->vol_ctl;
1894*3214b966STakashi Iwai 			spec->out_mix_path.mute_ctl = path->mute_ctl;
1895*3214b966STakashi Iwai 		}
18964a79616dSTakashi Iwai 	}
18974a79616dSTakashi Iwai 
18984a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
18994a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
19004a79616dSTakashi Iwai 	if (idx >= 0) {
19014a79616dSTakashi Iwai 		/* add control to mixer */
1902*3214b966STakashi Iwai 		const char *name;
1903*3214b966STakashi Iwai 		name = spec->out_mix_path.depth ?
1904*3214b966STakashi Iwai 			"PCM Loopback Playback Volume" : "PCM Playback Volume";
1905*3214b966STakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
19064a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
19074a79616dSTakashi Iwai 							  idx, HDA_INPUT));
19084a79616dSTakashi Iwai 		if (err < 0)
19094a79616dSTakashi Iwai 			return err;
1910*3214b966STakashi Iwai 		name = spec->out_mix_path.depth ?
1911*3214b966STakashi Iwai 			"PCM Loopback Playback Switch" : "PCM Playback Switch";
1912*3214b966STakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
19134a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
19144a79616dSTakashi Iwai 							  idx, HDA_INPUT));
19154a79616dSTakashi Iwai 		if (err < 0)
19164a79616dSTakashi Iwai 			return err;
1917c577b8a1SJoseph Chan 	}
1918c577b8a1SJoseph Chan 
1919f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
1920f4a7828bSTakashi Iwai 
1921c577b8a1SJoseph Chan 	return 0;
1922c577b8a1SJoseph Chan }
1923c577b8a1SJoseph Chan 
19244a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
1925c577b8a1SJoseph Chan {
19264a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
192709a9ad69STakashi Iwai 	struct nid_path *path;
192818bd2c44STakashi Iwai 	bool check_dac;
1929*3214b966STakashi Iwai 	int i, err;
1930c577b8a1SJoseph Chan 
1931c577b8a1SJoseph Chan 	if (!pin)
1932c577b8a1SJoseph Chan 		return 0;
1933c577b8a1SJoseph Chan 
1934*3214b966STakashi Iwai 	if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
1935*3214b966STakashi Iwai 		for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
1936*3214b966STakashi Iwai 			if (i < spec->multiout.num_dacs &&
193725250505STakashi Iwai 			    parse_output_path(codec, pin,
1938*3214b966STakashi Iwai 					      spec->multiout.dac_nids[i], 0,
1939*3214b966STakashi Iwai 					      &spec->hp_indep_path)) {
1940*3214b966STakashi Iwai 				spec->hp_indep_shared = i;
1941*3214b966STakashi Iwai 				break;
194225250505STakashi Iwai 			}
1943*3214b966STakashi Iwai 		}
1944*3214b966STakashi Iwai 	}
1945*3214b966STakashi Iwai 	if (spec->hp_indep_path.depth) {
1946*3214b966STakashi Iwai 		spec->hp_dac_nid = spec->hp_indep_path.path[0];
1947*3214b966STakashi Iwai 		if (!spec->hp_indep_shared)
1948*3214b966STakashi Iwai 			spec->hp_path = spec->hp_indep_path;
1949*3214b966STakashi Iwai 	}
1950*3214b966STakashi Iwai 	/* optionally check front-path w/o AA-mix */
1951*3214b966STakashi Iwai 	if (!spec->hp_path.depth)
1952*3214b966STakashi Iwai 		parse_output_path(codec, pin,
1953*3214b966STakashi Iwai 				  spec->multiout.dac_nids[HDA_FRONT], 0,
1954*3214b966STakashi Iwai 				  &spec->hp_path);
19554a79616dSTakashi Iwai 
1956ece8d043STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1957*3214b966STakashi Iwai 			       1, &spec->hp_mix_path) && !spec->hp_path.depth)
1958ece8d043STakashi Iwai 		return 0;
1959ece8d043STakashi Iwai 
1960*3214b966STakashi Iwai 	if (spec->hp_path.depth) {
196109a9ad69STakashi Iwai 		path = &spec->hp_path;
196218bd2c44STakashi Iwai 		check_dac = true;
196318bd2c44STakashi Iwai 	} else {
1964*3214b966STakashi Iwai 		path = &spec->hp_mix_path;
196518bd2c44STakashi Iwai 		check_dac = false;
196618bd2c44STakashi Iwai 	}
196718bd2c44STakashi Iwai 	err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
19684a79616dSTakashi Iwai 	if (err < 0)
19694a79616dSTakashi Iwai 		return err;
1970*3214b966STakashi Iwai 	if (check_dac) {
1971*3214b966STakashi Iwai 		spec->hp_mix_path.vol_ctl = path->vol_ctl;
1972*3214b966STakashi Iwai 		spec->hp_mix_path.mute_ctl = path->mute_ctl;
1973*3214b966STakashi Iwai 	} else {
1974*3214b966STakashi Iwai 		spec->hp_path.vol_ctl = path->vol_ctl;
1975*3214b966STakashi Iwai 		spec->hp_path.mute_ctl = path->mute_ctl;
197609a9ad69STakashi Iwai 	}
1977c577b8a1SJoseph Chan 	return 0;
1978c577b8a1SJoseph Chan }
1979c577b8a1SJoseph Chan 
19804a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec)
19814a918ffeSTakashi Iwai {
19824a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
1983*3214b966STakashi Iwai 	struct nid_path *path;
1984*3214b966STakashi Iwai 	bool check_dac;
19854a918ffeSTakashi Iwai 	hda_nid_t pin, dac;
1986*3214b966STakashi Iwai 	int err;
19874a918ffeSTakashi Iwai 
19884a918ffeSTakashi Iwai 	pin = spec->autocfg.speaker_pins[0];
19894a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs || !pin)
19904a918ffeSTakashi Iwai 		return 0;
19914a918ffeSTakashi Iwai 
1992*3214b966STakashi Iwai 	if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
19938e3679dcSTakashi Iwai 		dac = spec->speaker_path.path[0];
1994*3214b966STakashi Iwai 	if (!dac)
1995*3214b966STakashi Iwai 		parse_output_path(codec, pin,
1996*3214b966STakashi Iwai 				  spec->multiout.dac_nids[HDA_FRONT], 0,
199709a9ad69STakashi Iwai 				  &spec->speaker_path);
1998*3214b966STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1999*3214b966STakashi Iwai 			       1, &spec->speaker_mix_path) && !dac)
2000*3214b966STakashi Iwai 		return 0;
20014a918ffeSTakashi Iwai 
2002*3214b966STakashi Iwai 	/* no AA-path for front? */
2003*3214b966STakashi Iwai 	if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2004*3214b966STakashi Iwai 		dac = 0;
2005*3214b966STakashi Iwai 
2006*3214b966STakashi Iwai 	spec->speaker_dac_nid = dac;
2007*3214b966STakashi Iwai 	spec->multiout.extra_out_nid[0] = dac;
2008*3214b966STakashi Iwai 	if (dac) {
2009*3214b966STakashi Iwai 		path = &spec->speaker_path;
2010*3214b966STakashi Iwai 		check_dac = true;
2011*3214b966STakashi Iwai 	} else {
2012*3214b966STakashi Iwai 		path = &spec->speaker_mix_path;
2013*3214b966STakashi Iwai 		check_dac = false;
2014*3214b966STakashi Iwai 	}
2015*3214b966STakashi Iwai 	err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2016*3214b966STakashi Iwai 	if (err < 0)
2017*3214b966STakashi Iwai 		return err;
2018*3214b966STakashi Iwai 	if (check_dac) {
2019*3214b966STakashi Iwai 		spec->speaker_mix_path.vol_ctl = path->vol_ctl;
2020*3214b966STakashi Iwai 		spec->speaker_mix_path.mute_ctl = path->mute_ctl;
2021*3214b966STakashi Iwai 	} else {
2022*3214b966STakashi Iwai 		spec->speaker_path.vol_ctl = path->vol_ctl;
2023*3214b966STakashi Iwai 		spec->speaker_path.mute_ctl = path->mute_ctl;
2024*3214b966STakashi Iwai 	}
2025*3214b966STakashi Iwai 	return 0;
2026*3214b966STakashi Iwai }
2027*3214b966STakashi Iwai 
2028*3214b966STakashi Iwai #define via_aamix_ctl_info	via_pin_power_ctl_info
2029*3214b966STakashi Iwai 
2030*3214b966STakashi Iwai static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2031*3214b966STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
2032*3214b966STakashi Iwai {
2033*3214b966STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2034*3214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
2035*3214b966STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2036*3214b966STakashi Iwai 	return 0;
2037*3214b966STakashi Iwai }
2038*3214b966STakashi Iwai 
2039*3214b966STakashi Iwai static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2040*3214b966STakashi Iwai 			       struct nid_path *nomix, struct nid_path *mix)
2041*3214b966STakashi Iwai {
2042*3214b966STakashi Iwai 	if (do_mix) {
2043*3214b966STakashi Iwai 		activate_output_path(codec, nomix, false, false);
2044*3214b966STakashi Iwai 		activate_output_path(codec, mix, true, false);
2045*3214b966STakashi Iwai 	} else {
2046*3214b966STakashi Iwai 		activate_output_path(codec, mix, false, false);
2047*3214b966STakashi Iwai 		activate_output_path(codec, nomix, true, false);
2048*3214b966STakashi Iwai 	}
2049*3214b966STakashi Iwai }
2050*3214b966STakashi Iwai 
2051*3214b966STakashi Iwai static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2052*3214b966STakashi Iwai 			     struct snd_ctl_elem_value *ucontrol)
2053*3214b966STakashi Iwai {
2054*3214b966STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2055*3214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
2056*3214b966STakashi Iwai 	unsigned int val = ucontrol->value.enumerated.item[0];
2057*3214b966STakashi Iwai 
2058*3214b966STakashi Iwai 	if (val == spec->aamix_mode)
2059*3214b966STakashi Iwai 		return 0;
2060*3214b966STakashi Iwai 	spec->aamix_mode = val;
2061*3214b966STakashi Iwai 	/* update front path */
2062*3214b966STakashi Iwai 	update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2063*3214b966STakashi Iwai 	/* update HP path */
2064*3214b966STakashi Iwai 	if (!spec->hp_independent_mode) {
2065*3214b966STakashi Iwai 		update_aamix_paths(codec, val, &spec->hp_path,
2066*3214b966STakashi Iwai 				   &spec->hp_mix_path);
2067*3214b966STakashi Iwai 	}
2068*3214b966STakashi Iwai 	/* update speaker path */
2069*3214b966STakashi Iwai 	update_aamix_paths(codec, val, &spec->speaker_path,
2070*3214b966STakashi Iwai 			   &spec->speaker_mix_path);
2071*3214b966STakashi Iwai 	return 1;
2072*3214b966STakashi Iwai }
2073*3214b966STakashi Iwai 
2074*3214b966STakashi Iwai static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2075*3214b966STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2076*3214b966STakashi Iwai 	.name = "Loopback Mixing",
2077*3214b966STakashi Iwai 	.info = via_aamix_ctl_info,
2078*3214b966STakashi Iwai 	.get = via_aamix_ctl_get,
2079*3214b966STakashi Iwai 	.put = via_aamix_ctl_put,
2080*3214b966STakashi Iwai };
2081*3214b966STakashi Iwai 
2082*3214b966STakashi Iwai static int via_auto_create_loopback_switch(struct hda_codec *codec)
2083*3214b966STakashi Iwai {
2084*3214b966STakashi Iwai 	struct via_spec *spec = codec->spec;
2085*3214b966STakashi Iwai 
2086*3214b966STakashi Iwai 	if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2087*3214b966STakashi Iwai 		return 0; /* no loopback switching available */
2088*3214b966STakashi Iwai 	if (!via_clone_control(spec, &via_aamix_ctl_enum))
2089*3214b966STakashi Iwai 		return -ENOMEM;
20904a918ffeSTakashi Iwai 	return 0;
20914a918ffeSTakashi Iwai }
20924a918ffeSTakashi Iwai 
2093a766d0d7STakashi Iwai /* look for ADCs */
2094a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
2095a766d0d7STakashi Iwai {
2096a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
2097a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
2098a766d0d7STakashi Iwai 	int i;
2099a766d0d7STakashi Iwai 
2100a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
2101a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
2102a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2103a766d0d7STakashi Iwai 			continue;
2104a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
2105a766d0d7STakashi Iwai 			continue;
2106a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
2107a766d0d7STakashi Iwai 			continue;
2108a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2109a766d0d7STakashi Iwai 			return -ENOMEM;
2110a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
2111a766d0d7STakashi Iwai 	}
2112a766d0d7STakashi Iwai 	return 0;
2113a766d0d7STakashi Iwai }
2114a766d0d7STakashi Iwai 
2115a86a88eaSTakashi Iwai /* input-src control */
2116a86a88eaSTakashi Iwai static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2117a86a88eaSTakashi Iwai 			     struct snd_ctl_elem_info *uinfo)
2118a86a88eaSTakashi Iwai {
2119a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2120a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2121a86a88eaSTakashi Iwai 
2122a86a88eaSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2123a86a88eaSTakashi Iwai 	uinfo->count = 1;
2124a86a88eaSTakashi Iwai 	uinfo->value.enumerated.items = spec->num_inputs;
2125a86a88eaSTakashi Iwai 	if (uinfo->value.enumerated.item >= spec->num_inputs)
2126a86a88eaSTakashi Iwai 		uinfo->value.enumerated.item = spec->num_inputs - 1;
2127a86a88eaSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
2128a86a88eaSTakashi Iwai 	       spec->inputs[uinfo->value.enumerated.item].label);
2129a86a88eaSTakashi Iwai 	return 0;
2130a86a88eaSTakashi Iwai }
2131a86a88eaSTakashi Iwai 
2132a86a88eaSTakashi Iwai static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2133a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
2134a86a88eaSTakashi Iwai {
2135a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2136a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2137a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2138a86a88eaSTakashi Iwai 
2139a86a88eaSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2140a86a88eaSTakashi Iwai 	return 0;
2141a86a88eaSTakashi Iwai }
2142a86a88eaSTakashi Iwai 
2143a86a88eaSTakashi Iwai static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2144a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
2145a86a88eaSTakashi Iwai {
2146a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2147a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2148a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2149a86a88eaSTakashi Iwai 	hda_nid_t mux;
2150a86a88eaSTakashi Iwai 	int cur;
2151a86a88eaSTakashi Iwai 
2152a86a88eaSTakashi Iwai 	cur = ucontrol->value.enumerated.item[0];
2153a86a88eaSTakashi Iwai 	if (cur < 0 || cur >= spec->num_inputs)
2154a86a88eaSTakashi Iwai 		return -EINVAL;
2155a86a88eaSTakashi Iwai 	if (spec->cur_mux[idx] == cur)
2156a86a88eaSTakashi Iwai 		return 0;
2157a86a88eaSTakashi Iwai 	spec->cur_mux[idx] = cur;
2158a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch) {
2159a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[cur].adc_idx;
2160a86a88eaSTakashi Iwai 		mux = spec->mux_nids[adc_idx];
2161a86a88eaSTakashi Iwai 		via_dyn_adc_pcm_resetup(codec, cur);
2162a86a88eaSTakashi Iwai 	} else {
2163a86a88eaSTakashi Iwai 		mux = spec->mux_nids[idx];
2164a86a88eaSTakashi Iwai 		if (snd_BUG_ON(!mux))
2165a86a88eaSTakashi Iwai 			return -EINVAL;
2166a86a88eaSTakashi Iwai 	}
2167a86a88eaSTakashi Iwai 
2168a86a88eaSTakashi Iwai 	if (mux) {
2169a86a88eaSTakashi Iwai 		/* switch to D0 beofre change index */
2170a86a88eaSTakashi Iwai 		if (snd_hda_codec_read(codec, mux, 0,
2171a86a88eaSTakashi Iwai 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2172a86a88eaSTakashi Iwai 			snd_hda_codec_write(codec, mux, 0,
2173a86a88eaSTakashi Iwai 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2174a86a88eaSTakashi Iwai 		snd_hda_codec_write(codec, mux, 0,
2175a86a88eaSTakashi Iwai 				    AC_VERB_SET_CONNECT_SEL,
2176a86a88eaSTakashi Iwai 				    spec->inputs[cur].mux_idx);
2177a86a88eaSTakashi Iwai 	}
2178a86a88eaSTakashi Iwai 
2179a86a88eaSTakashi Iwai 	/* update jack power state */
2180a86a88eaSTakashi Iwai 	set_widgets_power_state(codec);
2181a86a88eaSTakashi Iwai 	return 0;
2182a86a88eaSTakashi Iwai }
2183a766d0d7STakashi Iwai 
2184d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
2185d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2186d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
2187d7a99cceSTakashi Iwai 	 * So call somewhat different..
2188d7a99cceSTakashi Iwai 	 */
2189d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
2190d7a99cceSTakashi Iwai 	.name = "Input Source",
2191d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
2192d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
2193d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
2194d7a99cceSTakashi Iwai };
2195d7a99cceSTakashi Iwai 
2196a86a88eaSTakashi Iwai static int create_input_src_ctls(struct hda_codec *codec, int count)
2197a86a88eaSTakashi Iwai {
2198a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2199a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
2200a86a88eaSTakashi Iwai 
2201a86a88eaSTakashi Iwai 	if (spec->num_inputs <= 1 || !count)
2202a86a88eaSTakashi Iwai 		return 0; /* no need for single src */
2203a86a88eaSTakashi Iwai 
2204a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_input_src_ctl);
2205a86a88eaSTakashi Iwai 	if (!knew)
2206a86a88eaSTakashi Iwai 		return -ENOMEM;
2207a86a88eaSTakashi Iwai 	knew->count = count;
2208a86a88eaSTakashi Iwai 	return 0;
2209a86a88eaSTakashi Iwai }
2210a86a88eaSTakashi Iwai 
2211a86a88eaSTakashi Iwai /* add the powersave loopback-list entry */
221213af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
221313af8e77STakashi Iwai {
221413af8e77STakashi Iwai 	struct hda_amp_list *list;
221513af8e77STakashi Iwai 
221613af8e77STakashi Iwai 	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
221713af8e77STakashi Iwai 		return;
221813af8e77STakashi Iwai 	list = spec->loopback_list + spec->num_loopbacks;
221913af8e77STakashi Iwai 	list->nid = mix;
222013af8e77STakashi Iwai 	list->dir = HDA_INPUT;
222113af8e77STakashi Iwai 	list->idx = idx;
222213af8e77STakashi Iwai 	spec->num_loopbacks++;
222313af8e77STakashi Iwai 	spec->loopback.amplist = spec->loopback_list;
222413af8e77STakashi Iwai }
222513af8e77STakashi Iwai 
2226a86a88eaSTakashi Iwai static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
22278d087c76STakashi Iwai 			     hda_nid_t dst)
2228a86a88eaSTakashi Iwai {
22298d087c76STakashi Iwai 	return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
2230a86a88eaSTakashi Iwai }
2231a86a88eaSTakashi Iwai 
2232a86a88eaSTakashi Iwai /* add the input-route to the given pin */
2233a86a88eaSTakashi Iwai static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
2234c577b8a1SJoseph Chan {
223510a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
2236a86a88eaSTakashi Iwai 	int c, idx;
2237a86a88eaSTakashi Iwai 
2238a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].adc_idx = -1;
2239a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].pin = pin;
2240a86a88eaSTakashi Iwai 	for (c = 0; c < spec->num_adc_nids; c++) {
2241a86a88eaSTakashi Iwai 		if (spec->mux_nids[c]) {
2242a86a88eaSTakashi Iwai 			idx = get_connection_index(codec, spec->mux_nids[c],
2243a86a88eaSTakashi Iwai 						   pin);
2244a86a88eaSTakashi Iwai 			if (idx < 0)
2245a86a88eaSTakashi Iwai 				continue;
2246a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs].mux_idx = idx;
2247a86a88eaSTakashi Iwai 		} else {
22488d087c76STakashi Iwai 			if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
2249a86a88eaSTakashi Iwai 				continue;
2250a86a88eaSTakashi Iwai 		}
2251a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs].adc_idx = c;
2252a86a88eaSTakashi Iwai 		/* Can primary ADC satisfy all inputs? */
2253a86a88eaSTakashi Iwai 		if (!spec->dyn_adc_switch &&
2254a86a88eaSTakashi Iwai 		    spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2255a86a88eaSTakashi Iwai 			snd_printd(KERN_INFO
2256a86a88eaSTakashi Iwai 				   "via: dynamic ADC switching enabled\n");
2257a86a88eaSTakashi Iwai 			spec->dyn_adc_switch = 1;
2258a86a88eaSTakashi Iwai 		}
2259a86a88eaSTakashi Iwai 		return true;
2260a86a88eaSTakashi Iwai 	}
2261a86a88eaSTakashi Iwai 	return false;
2262a86a88eaSTakashi Iwai }
2263a86a88eaSTakashi Iwai 
2264a86a88eaSTakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2265a86a88eaSTakashi Iwai 
2266a86a88eaSTakashi Iwai /* parse input-routes; fill ADCs, MUXs and input-src entries */
2267a86a88eaSTakashi Iwai static int parse_analog_inputs(struct hda_codec *codec)
2268a86a88eaSTakashi Iwai {
2269a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2270a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2271a86a88eaSTakashi Iwai 	int i, err;
2272a766d0d7STakashi Iwai 
2273a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2274a766d0d7STakashi Iwai 	if (err < 0)
2275a766d0d7STakashi Iwai 		return err;
2276a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2277a766d0d7STakashi Iwai 	if (err < 0)
2278a766d0d7STakashi Iwai 		return err;
2279a766d0d7STakashi Iwai 
2280a86a88eaSTakashi Iwai 	/* fill all input-routes */
2281a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2282a86a88eaSTakashi Iwai 		if (add_input_route(codec, cfg->inputs[i].pin))
2283a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs++].label =
2284a86a88eaSTakashi Iwai 				hda_get_autocfg_input_label(codec, cfg, i);
2285a86a88eaSTakashi Iwai 	}
2286a86a88eaSTakashi Iwai 
2287a86a88eaSTakashi Iwai 	/* check for internal loopback recording */
2288a86a88eaSTakashi Iwai 	if (spec->aa_mix_nid &&
2289a86a88eaSTakashi Iwai 	    add_input_route(codec, spec->aa_mix_nid))
2290a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2291a86a88eaSTakashi Iwai 
2292a86a88eaSTakashi Iwai 	return 0;
2293a86a88eaSTakashi Iwai }
2294a86a88eaSTakashi Iwai 
2295a86a88eaSTakashi Iwai /* create analog-loopback volume/switch controls */
2296a86a88eaSTakashi Iwai static int create_loopback_ctls(struct hda_codec *codec)
2297a86a88eaSTakashi Iwai {
2298a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2299a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2300a86a88eaSTakashi Iwai 	const char *prev_label = NULL;
2301a86a88eaSTakashi Iwai 	int type_idx = 0;
2302a86a88eaSTakashi Iwai 	int i, j, err, idx;
2303a86a88eaSTakashi Iwai 
2304a86a88eaSTakashi Iwai 	if (!spec->aa_mix_nid)
2305a766d0d7STakashi Iwai 		return 0;
2306c577b8a1SJoseph Chan 
23077b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2308a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2309a86a88eaSTakashi Iwai 		const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2310a86a88eaSTakashi Iwai 
23111e11cae1STakashi Iwai 		if (prev_label && !strcmp(label, prev_label))
23127b315bb4STakashi Iwai 			type_idx++;
23137b315bb4STakashi Iwai 		else
23147b315bb4STakashi Iwai 			type_idx = 0;
23151e11cae1STakashi Iwai 		prev_label = label;
2316a86a88eaSTakashi Iwai 		idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2317a86a88eaSTakashi Iwai 		if (idx >= 0) {
231816922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2319a86a88eaSTakashi Iwai 						   idx, spec->aa_mix_nid);
2320c577b8a1SJoseph Chan 			if (err < 0)
2321c577b8a1SJoseph Chan 				return err;
2322a86a88eaSTakashi Iwai 			add_loopback_list(spec, spec->aa_mix_nid, idx);
232313af8e77STakashi Iwai 		}
2324e3d7a143STakashi Iwai 
2325e3d7a143STakashi Iwai 		/* remember the label for smart51 control */
2326e3d7a143STakashi Iwai 		for (j = 0; j < spec->smart51_nums; j++) {
2327a86a88eaSTakashi Iwai 			if (spec->smart51_pins[j] == pin) {
2328e3d7a143STakashi Iwai 				spec->smart51_idxs[j] = idx;
2329e3d7a143STakashi Iwai 				spec->smart51_labels[j] = label;
2330e3d7a143STakashi Iwai 				break;
2331e3d7a143STakashi Iwai 			}
2332e3d7a143STakashi Iwai 		}
2333c577b8a1SJoseph Chan 	}
2334a86a88eaSTakashi Iwai 	return 0;
2335a86a88eaSTakashi Iwai }
2336a86a88eaSTakashi Iwai 
2337a86a88eaSTakashi Iwai /* create mic-boost controls (if present) */
2338a86a88eaSTakashi Iwai static int create_mic_boost_ctls(struct hda_codec *codec)
2339a86a88eaSTakashi Iwai {
2340a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2341a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2342a86a88eaSTakashi Iwai 	int i, err;
2343a86a88eaSTakashi Iwai 
2344a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2345a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2346a86a88eaSTakashi Iwai 		unsigned int caps;
2347a86a88eaSTakashi Iwai 		const char *label;
2348a86a88eaSTakashi Iwai 		char name[32];
2349a86a88eaSTakashi Iwai 
2350a86a88eaSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
2351a86a88eaSTakashi Iwai 			continue;
2352a86a88eaSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
2353a86a88eaSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2354a86a88eaSTakashi Iwai 			continue;
2355a86a88eaSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2356a86a88eaSTakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Volume", label);
2357a86a88eaSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2358a86a88eaSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2359a86a88eaSTakashi Iwai 		if (err < 0)
2360a86a88eaSTakashi Iwai 			return err;
2361a86a88eaSTakashi Iwai 	}
2362a86a88eaSTakashi Iwai 	return 0;
2363a86a88eaSTakashi Iwai }
2364a86a88eaSTakashi Iwai 
2365a86a88eaSTakashi Iwai /* create capture and input-src controls for multiple streams */
2366a86a88eaSTakashi Iwai static int create_multi_adc_ctls(struct hda_codec *codec)
2367a86a88eaSTakashi Iwai {
2368a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2369a86a88eaSTakashi Iwai 	int i, err;
2370d7a99cceSTakashi Iwai 
2371d7a99cceSTakashi Iwai 	/* create capture mixer elements */
2372d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2373d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
2374d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2375d7a99cceSTakashi Iwai 					"Capture Volume", i,
2376d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2377d7a99cceSTakashi Iwai 							    HDA_INPUT));
2378d7a99cceSTakashi Iwai 		if (err < 0)
2379d7a99cceSTakashi Iwai 			return err;
2380d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2381d7a99cceSTakashi Iwai 					"Capture Switch", i,
2382d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2383d7a99cceSTakashi Iwai 							    HDA_INPUT));
2384d7a99cceSTakashi Iwai 		if (err < 0)
2385d7a99cceSTakashi Iwai 			return err;
2386d7a99cceSTakashi Iwai 	}
2387d7a99cceSTakashi Iwai 
2388d7a99cceSTakashi Iwai 	/* input-source control */
2389d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2390d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
2391d7a99cceSTakashi Iwai 			break;
2392a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, i);
2393d7a99cceSTakashi Iwai 	if (err < 0)
2394d7a99cceSTakashi Iwai 		return err;
2395a86a88eaSTakashi Iwai 	return 0;
2396d7a99cceSTakashi Iwai }
2397d7a99cceSTakashi Iwai 
2398a86a88eaSTakashi Iwai /* bind capture volume/switch */
2399a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2400a86a88eaSTakashi Iwai 	HDA_BIND_VOL("Capture Volume", 0);
2401a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2402a86a88eaSTakashi Iwai 	HDA_BIND_SW("Capture Switch", 0);
2403a86a88eaSTakashi Iwai 
2404a86a88eaSTakashi Iwai static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2405a86a88eaSTakashi Iwai 			 struct hda_ctl_ops *ops)
2406a86a88eaSTakashi Iwai {
2407a86a88eaSTakashi Iwai 	struct hda_bind_ctls *ctl;
2408a86a88eaSTakashi Iwai 	int i;
2409a86a88eaSTakashi Iwai 
2410a86a88eaSTakashi Iwai 	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2411a86a88eaSTakashi Iwai 	if (!ctl)
2412a86a88eaSTakashi Iwai 		return -ENOMEM;
2413a86a88eaSTakashi Iwai 	ctl->ops = ops;
2414a86a88eaSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2415a86a88eaSTakashi Iwai 		ctl->values[i] =
2416a86a88eaSTakashi Iwai 			HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2417a86a88eaSTakashi Iwai 	*ctl_ret = ctl;
2418a86a88eaSTakashi Iwai 	return 0;
2419a86a88eaSTakashi Iwai }
2420a86a88eaSTakashi Iwai 
2421a86a88eaSTakashi Iwai /* create capture and input-src controls for dynamic ADC-switch case */
2422a86a88eaSTakashi Iwai static int create_dyn_adc_ctls(struct hda_codec *codec)
2423a86a88eaSTakashi Iwai {
2424a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2425a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
2426a86a88eaSTakashi Iwai 	int err;
2427a86a88eaSTakashi Iwai 
2428a86a88eaSTakashi Iwai 	/* set up the bind capture ctls */
2429a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2430a86a88eaSTakashi Iwai 	if (err < 0)
2431a86a88eaSTakashi Iwai 		return err;
2432a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2433a86a88eaSTakashi Iwai 	if (err < 0)
2434a86a88eaSTakashi Iwai 		return err;
2435a86a88eaSTakashi Iwai 
2436a86a88eaSTakashi Iwai 	/* create capture mixer elements */
2437a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2438a86a88eaSTakashi Iwai 	if (!knew)
2439a86a88eaSTakashi Iwai 		return -ENOMEM;
2440a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_vol;
2441a86a88eaSTakashi Iwai 
2442a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2443a86a88eaSTakashi Iwai 	if (!knew)
2444a86a88eaSTakashi Iwai 		return -ENOMEM;
2445a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_sw;
2446a86a88eaSTakashi Iwai 
2447a86a88eaSTakashi Iwai 	/* input-source control */
2448a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, 1);
2449a86a88eaSTakashi Iwai 	if (err < 0)
2450a86a88eaSTakashi Iwai 		return err;
2451a86a88eaSTakashi Iwai 	return 0;
2452a86a88eaSTakashi Iwai }
2453a86a88eaSTakashi Iwai 
2454a86a88eaSTakashi Iwai /* parse and create capture-related stuff */
2455a86a88eaSTakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2456a86a88eaSTakashi Iwai {
2457a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2458a86a88eaSTakashi Iwai 	int err;
2459a86a88eaSTakashi Iwai 
2460a86a88eaSTakashi Iwai 	err = parse_analog_inputs(codec);
2461a86a88eaSTakashi Iwai 	if (err < 0)
2462a86a88eaSTakashi Iwai 		return err;
2463a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch)
2464a86a88eaSTakashi Iwai 		err = create_dyn_adc_ctls(codec);
2465a86a88eaSTakashi Iwai 	else
2466a86a88eaSTakashi Iwai 		err = create_multi_adc_ctls(codec);
2467a86a88eaSTakashi Iwai 	if (err < 0)
2468a86a88eaSTakashi Iwai 		return err;
2469a86a88eaSTakashi Iwai 	err = create_loopback_ctls(codec);
2470a86a88eaSTakashi Iwai 	if (err < 0)
2471a86a88eaSTakashi Iwai 		return err;
2472a86a88eaSTakashi Iwai 	err = create_mic_boost_ctls(codec);
2473a86a88eaSTakashi Iwai 	if (err < 0)
2474a86a88eaSTakashi Iwai 		return err;
2475c577b8a1SJoseph Chan 	return 0;
2476c577b8a1SJoseph Chan }
2477c577b8a1SJoseph Chan 
247876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
247976d9b0ddSHarald Welte {
248076d9b0ddSHarald Welte 	unsigned int def_conf;
248176d9b0ddSHarald Welte 	unsigned char seqassoc;
248276d9b0ddSHarald Welte 
24832f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
248476d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
248576d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
248682ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
248782ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
248876d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
24892f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
249076d9b0ddSHarald Welte 	}
249176d9b0ddSHarald Welte 
249276d9b0ddSHarald Welte 	return;
249376d9b0ddSHarald Welte }
249476d9b0ddSHarald Welte 
2495e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
24961f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
24971f2e99feSLydia Wang {
24981f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24991f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
25001f2e99feSLydia Wang 
25011f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25021f2e99feSLydia Wang 		return 0;
2503e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
25041f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2505e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
25061f2e99feSLydia Wang 	return 0;
25071f2e99feSLydia Wang }
25081f2e99feSLydia Wang 
2509e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
25101f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
25111f2e99feSLydia Wang {
25121f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
25131f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
25141f2e99feSLydia Wang 	int change;
25151f2e99feSLydia Wang 
25161f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
25171f2e99feSLydia Wang 		return 0;
2518e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
25191f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2520e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2521e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
25221f2e99feSLydia Wang 		mute_aa_path(codec, 1);
25231f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
25241f2e99feSLydia Wang 	}
25251f2e99feSLydia Wang 	return change;
25261f2e99feSLydia Wang }
25271f2e99feSLydia Wang 
2528e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
25291f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25301f2e99feSLydia Wang 	.name = "Jack Detect",
25311f2e99feSLydia Wang 	.count = 1,
25321f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2533e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2534e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
25351f2e99feSLydia Wang };
25361f2e99feSLydia Wang 
253712daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec);
253812daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec);
253912daef65STakashi Iwai 
254012daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
2541c577b8a1SJoseph Chan {
2542c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2543c577b8a1SJoseph Chan 	int err;
2544c577b8a1SJoseph Chan 
2545c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2546c577b8a1SJoseph Chan 	if (err < 0)
2547c577b8a1SJoseph Chan 		return err;
2548c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
25497f0df88cSTakashi Iwai 		return -EINVAL;
2550c577b8a1SJoseph Chan 
25514a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2552c577b8a1SJoseph Chan 	if (err < 0)
2553c577b8a1SJoseph Chan 		return err;
25544a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2555c577b8a1SJoseph Chan 	if (err < 0)
2556c577b8a1SJoseph Chan 		return err;
25574a918ffeSTakashi Iwai 	err = via_auto_create_speaker_ctls(codec);
25584a918ffeSTakashi Iwai 	if (err < 0)
25594a918ffeSTakashi Iwai 		return err;
2560*3214b966STakashi Iwai 	err = via_auto_create_loopback_switch(codec);
2561*3214b966STakashi Iwai 	if (err < 0)
2562*3214b966STakashi Iwai 		return err;
2563a86a88eaSTakashi Iwai 	err = via_auto_create_analog_input_ctls(codec);
2564c577b8a1SJoseph Chan 	if (err < 0)
2565c577b8a1SJoseph Chan 		return err;
2566c577b8a1SJoseph Chan 
2567c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2568c577b8a1SJoseph Chan 
256912daef65STakashi Iwai 	fill_dig_outs(codec);
257012daef65STakashi Iwai 	fill_dig_in(codec);
2571c577b8a1SJoseph Chan 
2572603c4019STakashi Iwai 	if (spec->kctls.list)
2573603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2574c577b8a1SJoseph Chan 
2575c577b8a1SJoseph Chan 
2576*3214b966STakashi Iwai 	if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
2577ece8d043STakashi Iwai 		err = via_hp_build(codec);
2578ece8d043STakashi Iwai 		if (err < 0)
2579ece8d043STakashi Iwai 			return err;
2580ece8d043STakashi Iwai 	}
2581c577b8a1SJoseph Chan 
2582f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2583f4a7828bSTakashi Iwai 	if (err < 0)
2584f4a7828bSTakashi Iwai 		return err;
2585f4a7828bSTakashi Iwai 
25865d41762aSTakashi Iwai 	/* assign slave outs */
25875d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
25885d41762aSTakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
25895d41762aSTakashi Iwai 
2590c577b8a1SJoseph Chan 	return 1;
2591c577b8a1SJoseph Chan }
2592c577b8a1SJoseph Chan 
25935d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec)
2594c577b8a1SJoseph Chan {
259525eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
25965d41762aSTakashi Iwai 	if (spec->multiout.dig_out_nid)
25975d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
25985d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
25995d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
26005d41762aSTakashi Iwai }
260125eaba2fSLydia Wang 
26025d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec)
26035d41762aSTakashi Iwai {
26045d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
26055d41762aSTakashi Iwai 	if (!spec->dig_in_nid)
26065d41762aSTakashi Iwai 		return;
26075d41762aSTakashi Iwai 	snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
26085d41762aSTakashi Iwai 			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
26095d41762aSTakashi Iwai }
26105d41762aSTakashi Iwai 
26114a918ffeSTakashi Iwai /* initialize the unsolicited events */
26124a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec)
26134a918ffeSTakashi Iwai {
26144a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
26154a918ffeSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
26164a918ffeSTakashi Iwai 	unsigned int ev;
26174a918ffeSTakashi Iwai 	int i;
26184a918ffeSTakashi Iwai 
26194a918ffeSTakashi Iwai 	if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
26204a918ffeSTakashi Iwai 		snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
26214a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
26224a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
26234a918ffeSTakashi Iwai 
26244a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
26254a918ffeSTakashi Iwai 		ev = VIA_LINE_EVENT;
26264a918ffeSTakashi Iwai 	else
26274a918ffeSTakashi Iwai 		ev = 0;
26284a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
26294a918ffeSTakashi Iwai 		if (cfg->line_out_pins[i] &&
26304a918ffeSTakashi Iwai 		    is_jack_detectable(codec, cfg->line_out_pins[i]))
263163f10d2cSLydia Wang 			snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
26324a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
26334a918ffeSTakashi Iwai 				AC_USRSP_EN | ev | VIA_JACK_EVENT);
26344a918ffeSTakashi Iwai 	}
26354a918ffeSTakashi Iwai 
26364a918ffeSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
26374a918ffeSTakashi Iwai 		if (is_jack_detectable(codec, cfg->inputs[i].pin))
26384a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
26394a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
26404a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_JACK_EVENT);
26414a918ffeSTakashi Iwai 	}
26424a918ffeSTakashi Iwai }
26434a918ffeSTakashi Iwai 
26445d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
26455d41762aSTakashi Iwai {
26465d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
26475d41762aSTakashi Iwai 	int i;
26485d41762aSTakashi Iwai 
26495d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
26505d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
26515d41762aSTakashi Iwai 
2652c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2653c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
26544a918ffeSTakashi Iwai 	via_auto_init_speaker_out(codec);
2655c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
26565d41762aSTakashi Iwai 	via_auto_init_dig_outs(codec);
26575d41762aSTakashi Iwai 	via_auto_init_dig_in(codec);
265811890956SLydia Wang 
26594a918ffeSTakashi Iwai 	via_auto_init_unsol_event(codec);
26604a918ffeSTakashi Iwai 
266125eaba2fSLydia Wang 	via_hp_automute(codec);
266225eaba2fSLydia Wang 
2663c577b8a1SJoseph Chan 	return 0;
2664c577b8a1SJoseph Chan }
2665c577b8a1SJoseph Chan 
26661f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
26671f2e99feSLydia Wang {
26681f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
26691f2e99feSLydia Wang 					     vt1708_hp_work.work);
26701f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
26711f2e99feSLydia Wang 		return;
26721f2e99feSLydia Wang 	/* if jack state toggled */
26731f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2674d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
26751f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
26761f2e99feSLydia Wang 		via_hp_automute(spec->codec);
26771f2e99feSLydia Wang 	}
26781f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
26791f2e99feSLydia Wang }
26801f2e99feSLydia Wang 
2681337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2682337b9d02STakashi Iwai {
2683337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2684337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2685337b9d02STakashi Iwai 	unsigned int type;
2686337b9d02STakashi Iwai 	int i, n;
2687337b9d02STakashi Iwai 
2688337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2689337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2690337b9d02STakashi Iwai 		while (nid) {
2691a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
26921c55d521STakashi Iwai 			if (type == AC_WID_PIN)
26931c55d521STakashi Iwai 				break;
2694337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2695337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2696337b9d02STakashi Iwai 			if (n <= 0)
2697337b9d02STakashi Iwai 				break;
2698337b9d02STakashi Iwai 			if (n > 1) {
2699337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2700337b9d02STakashi Iwai 				break;
2701337b9d02STakashi Iwai 			}
2702337b9d02STakashi Iwai 			nid = conn[0];
2703337b9d02STakashi Iwai 		}
2704337b9d02STakashi Iwai 	}
27051c55d521STakashi Iwai 	return 0;
2706337b9d02STakashi Iwai }
2707337b9d02STakashi Iwai 
2708c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2709c577b8a1SJoseph Chan {
2710c577b8a1SJoseph Chan 	struct via_spec *spec;
2711c577b8a1SJoseph Chan 	int err;
2712c577b8a1SJoseph Chan 
2713c577b8a1SJoseph Chan 	/* create a codec specific record */
27145b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2715c577b8a1SJoseph Chan 	if (spec == NULL)
2716c577b8a1SJoseph Chan 		return -ENOMEM;
2717c577b8a1SJoseph Chan 
2718620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2719620e2b28STakashi Iwai 
272012daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
272112daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
272212daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
272312daef65STakashi Iwai 
2724c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
272512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2726c577b8a1SJoseph Chan 	if (err < 0) {
2727c577b8a1SJoseph Chan 		via_free(codec);
2728c577b8a1SJoseph Chan 		return err;
2729c577b8a1SJoseph Chan 	}
2730c577b8a1SJoseph Chan 
273112daef65STakashi Iwai 	/* add jack detect on/off control */
273212daef65STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
273312daef65STakashi Iwai 		return -ENOMEM;
273412daef65STakashi Iwai 
2735bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2736bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2737bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2738c577b8a1SJoseph Chan 
2739e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2740e322a36dSLydia Wang 
2741c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2742c577b8a1SJoseph Chan 
27431f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2744c577b8a1SJoseph Chan 	return 0;
2745c577b8a1SJoseph Chan }
2746c577b8a1SJoseph Chan 
2747ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
2748c577b8a1SJoseph Chan {
2749c577b8a1SJoseph Chan 	struct via_spec *spec;
2750c577b8a1SJoseph Chan 	int err;
2751c577b8a1SJoseph Chan 
2752c577b8a1SJoseph Chan 	/* create a codec specific record */
27535b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2754c577b8a1SJoseph Chan 	if (spec == NULL)
2755c577b8a1SJoseph Chan 		return -ENOMEM;
2756c577b8a1SJoseph Chan 
2757620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2758620e2b28STakashi Iwai 
275912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2760c577b8a1SJoseph Chan 	if (err < 0) {
2761c577b8a1SJoseph Chan 		via_free(codec);
2762c577b8a1SJoseph Chan 		return err;
2763c577b8a1SJoseph Chan 	}
2764c577b8a1SJoseph Chan 
2765c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2766c577b8a1SJoseph Chan 
2767f7278fd0SJosepch Chan 	return 0;
2768f7278fd0SJosepch Chan }
2769f7278fd0SJosepch Chan 
27703e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
27713e95b9abSLydia Wang {
27723e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
27733e95b9abSLydia Wang 	int imux_is_smixer;
27743e95b9abSLydia Wang 	unsigned int parm;
27753e95b9abSLydia Wang 	int is_8ch = 0;
2776bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2777bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
27783e95b9abSLydia Wang 		is_8ch = 1;
27793e95b9abSLydia Wang 
27803e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
27813e95b9abSLydia Wang 	imux_is_smixer =
27823e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
27833e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
27843e95b9abSLydia Wang 	/* inputs */
27853e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
27863e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
27883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
27893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
27903e95b9abSLydia Wang 	if (imux_is_smixer)
27913e95b9abSLydia Wang 		parm = AC_PWRST_D0;
27923e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
27933e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
27943e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
27953e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
27963e95b9abSLydia Wang 
27973e95b9abSLydia Wang 	/* outputs */
27983e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
27993e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
28013e95b9abSLydia Wang 	if (spec->smart51_enabled)
28023e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
28033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
28043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
28053e95b9abSLydia Wang 
28063e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
28073e95b9abSLydia Wang 	if (is_8ch) {
28083e95b9abSLydia Wang 		parm = AC_PWRST_D3;
28093e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
28103e95b9abSLydia Wang 		if (spec->smart51_enabled)
28113e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
28123e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
28133e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28143e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
28153e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2816bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2817bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2818bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2819bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2820bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2821bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2822bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2823bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2824bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2825bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28263e95b9abSLydia Wang 	}
28273e95b9abSLydia Wang 
28283e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
28293e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28303e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
28313e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
28323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
28333e95b9abSLydia Wang 	if (is_8ch)
28343e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
28353e95b9abSLydia Wang 
28363e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
28373e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
28383e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
28393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
28403e95b9abSLydia Wang 	if (is_8ch) {
28413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
28423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
28443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2845bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2846bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2847bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28483e95b9abSLydia Wang }
28493e95b9abSLydia Wang 
2850518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2851ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
2852f7278fd0SJosepch Chan {
2853f7278fd0SJosepch Chan 	struct via_spec *spec;
2854f7278fd0SJosepch Chan 	int err;
2855f7278fd0SJosepch Chan 
2856518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2857518bf3baSLydia Wang 		return patch_vt1708S(codec);
2858ddd304d8STakashi Iwai 
2859f7278fd0SJosepch Chan 	/* create a codec specific record */
28605b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2861f7278fd0SJosepch Chan 	if (spec == NULL)
2862f7278fd0SJosepch Chan 		return -ENOMEM;
2863f7278fd0SJosepch Chan 
2864620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2865620e2b28STakashi Iwai 
2866f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
286712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2868f7278fd0SJosepch Chan 	if (err < 0) {
2869f7278fd0SJosepch Chan 		via_free(codec);
2870f7278fd0SJosepch Chan 		return err;
2871f7278fd0SJosepch Chan 	}
2872f7278fd0SJosepch Chan 
2873f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2874f7278fd0SJosepch Chan 
28753e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
28763e95b9abSLydia Wang 
2877f7278fd0SJosepch Chan 	return 0;
2878f7278fd0SJosepch Chan }
2879f7278fd0SJosepch Chan 
2880d949cac1SHarald Welte /* Patch for VT1708S */
2881096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
2882d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
2883d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
2884bc7e7e5cSLydia Wang 	/* don't bybass mixer */
2885bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
2886d949cac1SHarald Welte 	{ }
2887d949cac1SHarald Welte };
2888d949cac1SHarald Welte 
28899da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
28909da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
28919da29271STakashi Iwai {
28929da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
28939da29271STakashi Iwai 	int i;
28949da29271STakashi Iwai 
28959da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
28969da29271STakashi Iwai 		hda_nid_t nid;
28979da29271STakashi Iwai 		int conn;
28989da29271STakashi Iwai 
28999da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
29009da29271STakashi Iwai 		if (!nid)
29019da29271STakashi Iwai 			continue;
29029da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
29039da29271STakashi Iwai 		if (conn < 1)
29049da29271STakashi Iwai 			continue;
29059da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
29069da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
29079da29271STakashi Iwai 		else {
29089da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
29099da29271STakashi Iwai 			break; /* at most two dig outs */
29109da29271STakashi Iwai 		}
29119da29271STakashi Iwai 	}
29129da29271STakashi Iwai }
29139da29271STakashi Iwai 
291412daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec)
2915d949cac1SHarald Welte {
2916d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
291712daef65STakashi Iwai 	hda_nid_t dig_nid;
291812daef65STakashi Iwai 	int i, err;
2919d949cac1SHarald Welte 
292012daef65STakashi Iwai 	if (!spec->autocfg.dig_in_pin)
292112daef65STakashi Iwai 		return;
2922d949cac1SHarald Welte 
292312daef65STakashi Iwai 	dig_nid = codec->start_nid;
292412daef65STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
292512daef65STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, dig_nid);
292612daef65STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
292712daef65STakashi Iwai 			continue;
292812daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_DIGITAL))
292912daef65STakashi Iwai 			continue;
293012daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
293112daef65STakashi Iwai 			continue;
293212daef65STakashi Iwai 		err = get_connection_index(codec, dig_nid,
293312daef65STakashi Iwai 					   spec->autocfg.dig_in_pin);
293412daef65STakashi Iwai 		if (err >= 0) {
293512daef65STakashi Iwai 			spec->dig_in_nid = dig_nid;
293612daef65STakashi Iwai 			break;
293712daef65STakashi Iwai 		}
293812daef65STakashi Iwai 	}
2939d949cac1SHarald Welte }
2940d949cac1SHarald Welte 
29416369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
29426369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
29436369bcfcSLydia Wang {
29446369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
29456369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
29466369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
29476369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
29486369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
29496369bcfcSLydia Wang }
29506369bcfcSLydia Wang 
2951d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
2952d949cac1SHarald Welte {
2953d949cac1SHarald Welte 	struct via_spec *spec;
2954d949cac1SHarald Welte 	int err;
2955d949cac1SHarald Welte 
2956d949cac1SHarald Welte 	/* create a codec specific record */
29575b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2958d949cac1SHarald Welte 	if (spec == NULL)
2959d949cac1SHarald Welte 		return -ENOMEM;
2960d949cac1SHarald Welte 
2961620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2962d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
2963d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
2964620e2b28STakashi Iwai 
2965d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
296612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2967d949cac1SHarald Welte 	if (err < 0) {
2968d949cac1SHarald Welte 		via_free(codec);
2969d949cac1SHarald Welte 		return err;
2970d949cac1SHarald Welte 	}
2971d949cac1SHarald Welte 
2972096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
2973d949cac1SHarald Welte 
2974d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2975d949cac1SHarald Welte 
2976518bf3baSLydia Wang 	/* correct names for VT1708BCE */
2977518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
2978518bf3baSLydia Wang 		kfree(codec->chip_name);
2979518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2980518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
2981518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
2982518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2983970f630fSLydia Wang 	}
2984bc92df7fSLydia Wang 	/* correct names for VT1705 */
2985bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
2986bc92df7fSLydia Wang 		kfree(codec->chip_name);
2987bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2988bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
2989bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
2990bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2991bc92df7fSLydia Wang 	}
29923e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
2993d949cac1SHarald Welte 	return 0;
2994d949cac1SHarald Welte }
2995d949cac1SHarald Welte 
2996d949cac1SHarald Welte /* Patch for VT1702 */
2997d949cac1SHarald Welte 
2998096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
2999bc7e7e5cSLydia Wang 	/* mixer enable */
3000bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
3001bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
3002bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
3003d949cac1SHarald Welte 	{ }
3004d949cac1SHarald Welte };
3005d949cac1SHarald Welte 
30063e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
30073e95b9abSLydia Wang {
30083e95b9abSLydia Wang 	int imux_is_smixer =
30093e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
30103e95b9abSLydia Wang 	unsigned int parm;
30113e95b9abSLydia Wang 	/* inputs */
30123e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
30133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
30153e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
30163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
30173e95b9abSLydia Wang 	if (imux_is_smixer)
30183e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
30193e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
30203e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
30213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
30223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
30233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
30243e95b9abSLydia Wang 
30253e95b9abSLydia Wang 	/* outputs */
30263e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
30273e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
30293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
30303e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
30313e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
30323e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
30333e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
30343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
30353e95b9abSLydia Wang }
30363e95b9abSLydia Wang 
3037d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
3038d949cac1SHarald Welte {
3039d949cac1SHarald Welte 	struct via_spec *spec;
3040d949cac1SHarald Welte 	int err;
3041d949cac1SHarald Welte 
3042d949cac1SHarald Welte 	/* create a codec specific record */
30435b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3044d949cac1SHarald Welte 	if (spec == NULL)
3045d949cac1SHarald Welte 		return -ENOMEM;
3046d949cac1SHarald Welte 
3047620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
3048620e2b28STakashi Iwai 
304912daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
305012daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
305112daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
305212daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
305312daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
305412daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
305512daef65STakashi Iwai 
3056d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
305712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3058d949cac1SHarald Welte 	if (err < 0) {
3059d949cac1SHarald Welte 		via_free(codec);
3060d949cac1SHarald Welte 		return err;
3061d949cac1SHarald Welte 	}
3062d949cac1SHarald Welte 
3063096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
3064d949cac1SHarald Welte 
3065d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
3066d949cac1SHarald Welte 
30673e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
3068d949cac1SHarald Welte 	return 0;
3069d949cac1SHarald Welte }
3070d949cac1SHarald Welte 
3071eb7188caSLydia Wang /* Patch for VT1718S */
3072eb7188caSLydia Wang 
3073096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
30744ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
30754ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
3076eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
3077eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
30785d41762aSTakashi Iwai 
3079eb7188caSLydia Wang 	{ }
3080eb7188caSLydia Wang };
3081eb7188caSLydia Wang 
30823e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
30833e95b9abSLydia Wang {
30843e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
30853e95b9abSLydia Wang 	int imux_is_smixer;
30863e95b9abSLydia Wang 	unsigned int parm;
30873e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
30883e95b9abSLydia Wang 	imux_is_smixer =
30893e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
30903e95b9abSLydia Wang 	/* inputs */
30913e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
30923e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
30943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
30953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
30963e95b9abSLydia Wang 	if (imux_is_smixer)
30973e95b9abSLydia Wang 		parm = AC_PWRST_D0;
30983e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
30993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
31003e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
31013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
31023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
31033e95b9abSLydia Wang 
31043e95b9abSLydia Wang 	/* outputs */
31053e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
31063e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
31083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
31093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
31103e95b9abSLydia Wang 
31113e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
31123e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31133e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
31143e95b9abSLydia Wang 	if (spec->smart51_enabled)
31153e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
31163e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
31173e95b9abSLydia Wang 
31183e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
31193e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
31213e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
31223e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
31233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
31243e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
31253e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
31263e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
31273e95b9abSLydia Wang 
31283e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
31293e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31303e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
31313e95b9abSLydia Wang 	if (spec->smart51_enabled)
31323e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
31333e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
31343e95b9abSLydia Wang 
31353e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
31363e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
31373e95b9abSLydia Wang 		parm = AC_PWRST_D3;
31383e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
31393e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
31403e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
31423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
31443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31453e95b9abSLydia Wang 	}
31463e95b9abSLydia Wang }
31473e95b9abSLydia Wang 
314830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
314930b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
315030b45033STakashi Iwai  */
315130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
315230b45033STakashi Iwai {
315330b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
315430b45033STakashi Iwai 	int i, nums;
315530b45033STakashi Iwai 	hda_nid_t conn[8];
315630b45033STakashi Iwai 	hda_nid_t nid;
315730b45033STakashi Iwai 
315830b45033STakashi Iwai 	if (!spec->aa_mix_nid)
315930b45033STakashi Iwai 		return 0;
316030b45033STakashi Iwai 	nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
316130b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
316230b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
316330b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
316430b45033STakashi Iwai 			return 0;
316530b45033STakashi Iwai 	}
316630b45033STakashi Iwai 
316730b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
316830b45033STakashi Iwai 	nid = codec->start_nid;
316930b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
317030b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
317130b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
317230b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
317330b45033STakashi Iwai 			conn[nums++] = nid;
317430b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
317530b45033STakashi Iwai 							  spec->aa_mix_nid,
317630b45033STakashi Iwai 							  nums, conn);
317730b45033STakashi Iwai 		}
317830b45033STakashi Iwai 	}
317930b45033STakashi Iwai 	return 0;
318030b45033STakashi Iwai }
318130b45033STakashi Iwai 
318230b45033STakashi Iwai 
3183eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
3184eb7188caSLydia Wang {
3185eb7188caSLydia Wang 	struct via_spec *spec;
3186eb7188caSLydia Wang 	int err;
3187eb7188caSLydia Wang 
3188eb7188caSLydia Wang 	/* create a codec specific record */
31895b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3190eb7188caSLydia Wang 	if (spec == NULL)
3191eb7188caSLydia Wang 		return -ENOMEM;
3192eb7188caSLydia Wang 
3193620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3194d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3195d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
319630b45033STakashi Iwai 	add_secret_dac_path(codec);
3197620e2b28STakashi Iwai 
3198eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
319912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3200eb7188caSLydia Wang 	if (err < 0) {
3201eb7188caSLydia Wang 		via_free(codec);
3202eb7188caSLydia Wang 		return err;
3203eb7188caSLydia Wang 	}
3204eb7188caSLydia Wang 
3205096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
3206eb7188caSLydia Wang 
3207eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
3208eb7188caSLydia Wang 
32093e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
32103e95b9abSLydia Wang 
3211eb7188caSLydia Wang 	return 0;
3212eb7188caSLydia Wang }
3213f3db423dSLydia Wang 
3214f3db423dSLydia Wang /* Patch for VT1716S */
3215f3db423dSLydia Wang 
3216f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3217f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
3218f3db423dSLydia Wang {
3219f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3220f3db423dSLydia Wang 	uinfo->count = 1;
3221f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
3222f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
3223f3db423dSLydia Wang 	return 0;
3224f3db423dSLydia Wang }
3225f3db423dSLydia Wang 
3226f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3227f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3228f3db423dSLydia Wang {
3229f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3230f3db423dSLydia Wang 	int index = 0;
3231f3db423dSLydia Wang 
3232f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
3233f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
3234f3db423dSLydia Wang 	if (index != -1)
3235f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
3236f3db423dSLydia Wang 
3237f3db423dSLydia Wang 	return 0;
3238f3db423dSLydia Wang }
3239f3db423dSLydia Wang 
3240f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3241f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
3242f3db423dSLydia Wang {
3243f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3244f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
3245f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
3246f3db423dSLydia Wang 
3247f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
3248f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
3249f3db423dSLydia Wang 	spec->dmic_enabled = index;
32503e95b9abSLydia Wang 	set_widgets_power_state(codec);
3251f3db423dSLydia Wang 	return 1;
3252f3db423dSLydia Wang }
3253f3db423dSLydia Wang 
325490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
3255f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3256f3db423dSLydia Wang 	{
3257f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3258f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
32595b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
3260f3db423dSLydia Wang 	 .count = 1,
3261f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
3262f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
3263f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
3264f3db423dSLydia Wang 	 },
3265f3db423dSLydia Wang 	{}			/* end */
3266f3db423dSLydia Wang };
3267f3db423dSLydia Wang 
3268f3db423dSLydia Wang 
3269f3db423dSLydia Wang /* mono-out mixer elements */
327090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3271f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3272f3db423dSLydia Wang 	{ } /* end */
3273f3db423dSLydia Wang };
3274f3db423dSLydia Wang 
3275096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
3276f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
3277f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
3278f3db423dSLydia Wang 	/* don't bybass mixer */
3279f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
3280f3db423dSLydia Wang 	/* Enable mono output */
3281f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
3282f3db423dSLydia Wang 	{ }
3283f3db423dSLydia Wang };
3284f3db423dSLydia Wang 
32853e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
32863e95b9abSLydia Wang {
32873e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
32883e95b9abSLydia Wang 	int imux_is_smixer;
32893e95b9abSLydia Wang 	unsigned int parm;
32903e95b9abSLydia Wang 	unsigned int mono_out, present;
32913e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
32923e95b9abSLydia Wang 	imux_is_smixer =
32933e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
32943e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
32953e95b9abSLydia Wang 	/* inputs */
32963e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
32973e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
32993e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
33003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
33013e95b9abSLydia Wang 	if (imux_is_smixer)
33023e95b9abSLydia Wang 		parm = AC_PWRST_D0;
33033e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
33043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
33053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
33063e95b9abSLydia Wang 
33073e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
33093e95b9abSLydia Wang 	/* PW11 (22h) */
33103e95b9abSLydia Wang 	if (spec->dmic_enabled)
33113e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
33123e95b9abSLydia Wang 	else
33133e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
33143e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33153e95b9abSLydia Wang 
33163e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
33173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
33183e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
33193e95b9abSLydia Wang 
33203e95b9abSLydia Wang 	/* outputs */
33213e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
33223e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33233e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
33243e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
33253e95b9abSLydia Wang 	if (spec->smart51_enabled)
33263e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
33273e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
33283e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
33293e95b9abSLydia Wang 
33303e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
33313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
33333e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
33343e95b9abSLydia Wang 	if (spec->smart51_enabled)
33353e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
33363e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
33373e95b9abSLydia Wang 
33383e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
33393e95b9abSLydia Wang 	if (spec->smart51_enabled)
33403e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
33413e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
33423e95b9abSLydia Wang 
33433e95b9abSLydia Wang 	/* Mono out */
33443e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
33453e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
33463e95b9abSLydia Wang 
33473e95b9abSLydia Wang 	if (present)
33483e95b9abSLydia Wang 		mono_out = 0;
33493e95b9abSLydia Wang 	else {
33503e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
33513e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
33523e95b9abSLydia Wang 			mono_out = 0;
33533e95b9abSLydia Wang 		else
33543e95b9abSLydia Wang 			mono_out = 1;
33553e95b9abSLydia Wang 	}
33563e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
33573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
33583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
33593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
33603e95b9abSLydia Wang 
33613e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
33623e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33633e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
33643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
33653e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
33663e95b9abSLydia Wang 	if (spec->hp_independent_mode)
33673e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
33683e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
33693e95b9abSLydia Wang 
33703e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
33713e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
33723e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
33733e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
33743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
33753e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
33763e95b9abSLydia Wang }
33773e95b9abSLydia Wang 
3378f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3379f3db423dSLydia Wang {
3380f3db423dSLydia Wang 	struct via_spec *spec;
3381f3db423dSLydia Wang 	int err;
3382f3db423dSLydia Wang 
3383f3db423dSLydia Wang 	/* create a codec specific record */
33845b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3385f3db423dSLydia Wang 	if (spec == NULL)
3386f3db423dSLydia Wang 		return -ENOMEM;
3387f3db423dSLydia Wang 
3388620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3389d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3390d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3391620e2b28STakashi Iwai 
3392f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
339312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3394f3db423dSLydia Wang 	if (err < 0) {
3395f3db423dSLydia Wang 		via_free(codec);
3396f3db423dSLydia Wang 		return err;
3397f3db423dSLydia Wang 	}
3398f3db423dSLydia Wang 
3399096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
3400f3db423dSLydia Wang 
3401f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3402f3db423dSLydia Wang 	spec->num_mixers++;
3403f3db423dSLydia Wang 
3404f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3405f3db423dSLydia Wang 
3406f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3407f3db423dSLydia Wang 
34083e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
3409f3db423dSLydia Wang 	return 0;
3410f3db423dSLydia Wang }
341125eaba2fSLydia Wang 
341225eaba2fSLydia Wang /* for vt2002P */
341325eaba2fSLydia Wang 
3414096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
3415eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
3416eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
3417eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
3418eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
341925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
342025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
342125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
342225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
342325eaba2fSLydia Wang 	{ }
342425eaba2fSLydia Wang };
34254a918ffeSTakashi Iwai 
3426096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
342711890956SLydia Wang 	/* Enable Boost Volume backdoor */
342811890956SLydia Wang 	{0x1, 0xfb9, 0x24},
342911890956SLydia Wang 	/* Enable AOW0 to MW9 */
343011890956SLydia Wang 	{0x1, 0xfb8, 0x88},
343111890956SLydia Wang 	{ }
343211890956SLydia Wang };
343325eaba2fSLydia Wang 
34343e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
34353e95b9abSLydia Wang {
34363e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
34373e95b9abSLydia Wang 	int imux_is_smixer;
34383e95b9abSLydia Wang 	unsigned int parm;
34393e95b9abSLydia Wang 	unsigned int present;
34403e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
34413e95b9abSLydia Wang 	imux_is_smixer =
34423e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
34433e95b9abSLydia Wang 	/* inputs */
34443e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
34453e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34463e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
34473e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
34483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
34493e95b9abSLydia Wang 	parm = AC_PWRST_D0;
34503e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
34513e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
34523e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
34533e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
34543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
34553e95b9abSLydia Wang 
34563e95b9abSLydia Wang 	/* outputs */
34573e95b9abSLydia Wang 	/* AOW0 (8h)*/
34583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
34593e95b9abSLydia Wang 
346011890956SLydia Wang 	if (spec->codec_type == VT1802) {
346111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
346211890956SLydia Wang 		parm = AC_PWRST_D3;
346311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
346411890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
346511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
346611890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
346711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
346811890956SLydia Wang 	} else {
34693e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
34703e95b9abSLydia Wang 		parm = AC_PWRST_D3;
34713e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
34723e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
34733e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
34743e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
34753e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
347611890956SLydia Wang 	}
34773e95b9abSLydia Wang 
347811890956SLydia Wang 	if (spec->codec_type == VT1802) {
347911890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
348011890956SLydia Wang 		parm = AC_PWRST_D3;
348111890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
348211890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
348311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
348411890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
348511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
348611890956SLydia Wang 	} else {
34873e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
34883e95b9abSLydia Wang 		parm = AC_PWRST_D3;
34893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
34903e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
34913e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
34923e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
34933e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
349411890956SLydia Wang 	}
34953e95b9abSLydia Wang 
34963e95b9abSLydia Wang 	if (spec->hp_independent_mode)
34973e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
34983e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
34993e95b9abSLydia Wang 
35003e95b9abSLydia Wang 	/* Class-D */
35013e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
35023e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
35033e95b9abSLydia Wang 
35043e95b9abSLydia Wang 	parm = AC_PWRST_D3;
35053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
35063e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
350711890956SLydia Wang 	if (spec->codec_type == VT1802)
350811890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
350911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
351011890956SLydia Wang 	else
35113e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
35123e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
35143e95b9abSLydia Wang 
35153e95b9abSLydia Wang 	/* Mono Out */
35163e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
35173e95b9abSLydia Wang 
35183e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
351911890956SLydia Wang 	if (spec->codec_type == VT1802) {
352011890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
352111890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
352211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
352311890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
352411890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
352511890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
352611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
352711890956SLydia Wang 	} else {
35283e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
35293e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
35303e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35313e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
35323e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
35333e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
35343e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
353511890956SLydia Wang 	}
35363e95b9abSLydia Wang 	/* MW9 (21h) */
35373e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
35383e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
35393e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
35403e95b9abSLydia Wang 	else
35413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
35423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
35433e95b9abSLydia Wang }
354425eaba2fSLydia Wang 
354525eaba2fSLydia Wang /* patch for vt2002P */
354625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
354725eaba2fSLydia Wang {
354825eaba2fSLydia Wang 	struct via_spec *spec;
354925eaba2fSLydia Wang 	int err;
355025eaba2fSLydia Wang 
355125eaba2fSLydia Wang 	/* create a codec specific record */
35525b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
355325eaba2fSLydia Wang 	if (spec == NULL)
355425eaba2fSLydia Wang 		return -ENOMEM;
355525eaba2fSLydia Wang 
3556620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3557d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3558d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
355930b45033STakashi Iwai 	add_secret_dac_path(codec);
3560620e2b28STakashi Iwai 
356125eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
356212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
356325eaba2fSLydia Wang 	if (err < 0) {
356425eaba2fSLydia Wang 		via_free(codec);
356525eaba2fSLydia Wang 		return err;
356625eaba2fSLydia Wang 	}
356725eaba2fSLydia Wang 
356811890956SLydia Wang 	if (spec->codec_type == VT1802)
35694a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
357011890956SLydia Wang 	else
35714a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
357211890956SLydia Wang 
357325eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
357425eaba2fSLydia Wang 
35753e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
357625eaba2fSLydia Wang 	return 0;
357725eaba2fSLydia Wang }
3578ab6734e7SLydia Wang 
3579ab6734e7SLydia Wang /* for vt1812 */
3580ab6734e7SLydia Wang 
3581096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
3582ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
3583ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
3584ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
3585ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
3586ab6734e7SLydia Wang 	{ }
3587ab6734e7SLydia Wang };
3588ab6734e7SLydia Wang 
35893e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
35903e95b9abSLydia Wang {
35913e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
35923e95b9abSLydia Wang 	int imux_is_smixer =
35933e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
35943e95b9abSLydia Wang 	unsigned int parm;
35953e95b9abSLydia Wang 	unsigned int present;
35963e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
35973e95b9abSLydia Wang 	imux_is_smixer =
35983e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
35993e95b9abSLydia Wang 	/* inputs */
36003e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
36013e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36023e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
36033e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
36043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
36053e95b9abSLydia Wang 	parm = AC_PWRST_D0;
36063e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
36073e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
36083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
36093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
36103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
36113e95b9abSLydia Wang 
36123e95b9abSLydia Wang 	/* outputs */
36133e95b9abSLydia Wang 	/* AOW0 (8h)*/
36143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
36153e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36163e95b9abSLydia Wang 
36173e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
36183e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
36203e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
36213e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
36223e95b9abSLydia Wang 
36233e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
36243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
36263e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
36273e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
36283e95b9abSLydia Wang 	if (spec->hp_independent_mode)
36293e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
36303e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36313e95b9abSLydia Wang 
36323e95b9abSLydia Wang 	/* Internal Speaker */
36333e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
36343e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
36353e95b9abSLydia Wang 
36363e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
36383e95b9abSLydia Wang 	if (present) {
36393e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
36403e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
36413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
36423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
36433e95b9abSLydia Wang 	} else {
36443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
36453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
36473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36483e95b9abSLydia Wang 	}
36493e95b9abSLydia Wang 
36503e95b9abSLydia Wang 
36513e95b9abSLydia Wang 	/* Mono Out */
36523e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
36533e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
36543e95b9abSLydia Wang 
36553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
36573e95b9abSLydia Wang 	if (present) {
36583e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
36593e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
36603e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
36613e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
36623e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
36633e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
36643e95b9abSLydia Wang 	} else {
36653e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
36663e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36673e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
36683e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36693e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
36703e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
36713e95b9abSLydia Wang 	}
36723e95b9abSLydia Wang 
36733e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
36743e95b9abSLydia Wang 	parm = AC_PWRST_D3;
36753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
36763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
36773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
36783e95b9abSLydia Wang 
36793e95b9abSLydia Wang }
3680ab6734e7SLydia Wang 
3681ab6734e7SLydia Wang /* patch for vt1812 */
3682ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
3683ab6734e7SLydia Wang {
3684ab6734e7SLydia Wang 	struct via_spec *spec;
3685ab6734e7SLydia Wang 	int err;
3686ab6734e7SLydia Wang 
3687ab6734e7SLydia Wang 	/* create a codec specific record */
36885b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3689ab6734e7SLydia Wang 	if (spec == NULL)
3690ab6734e7SLydia Wang 		return -ENOMEM;
3691ab6734e7SLydia Wang 
3692620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3693d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3694d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
369530b45033STakashi Iwai 	add_secret_dac_path(codec);
3696620e2b28STakashi Iwai 
3697ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
369812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3699ab6734e7SLydia Wang 	if (err < 0) {
3700ab6734e7SLydia Wang 		via_free(codec);
3701ab6734e7SLydia Wang 		return err;
3702ab6734e7SLydia Wang 	}
3703ab6734e7SLydia Wang 
3704096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
3705ab6734e7SLydia Wang 
3706ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
3707ab6734e7SLydia Wang 
37083e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
3709ab6734e7SLydia Wang 	return 0;
3710ab6734e7SLydia Wang }
3711ab6734e7SLydia Wang 
3712c577b8a1SJoseph Chan /*
3713c577b8a1SJoseph Chan  * patch entries
3714c577b8a1SJoseph Chan  */
371590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
37163218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
37173218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
37183218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
37193218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
37203218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
3721ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37223218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
3723ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37243218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
3725ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37263218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
3727ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37283218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
3729ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37303218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
3731ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37323218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
3733ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37343218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
3735ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
37363218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
3737ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37383218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
3739ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37403218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
3741ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37423218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
3743ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37443218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
3745ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37463218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
3747ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37483218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
3749ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37503218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
3751ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
37523218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
3753d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37543218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
3755d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37563218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
3757d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37583218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
3759d949cac1SHarald Welte 	  .patch = patch_vt1708S},
3760bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
3761d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37623218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
3763d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37643218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
3765d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37663218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
3767d949cac1SHarald Welte 	  .patch = patch_vt1708S},
37683218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
3769d949cac1SHarald Welte 	  .patch = patch_vt1702},
37703218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
3771d949cac1SHarald Welte 	  .patch = patch_vt1702},
37723218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
3773d949cac1SHarald Welte 	  .patch = patch_vt1702},
37743218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
3775d949cac1SHarald Welte 	  .patch = patch_vt1702},
37763218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
3777d949cac1SHarald Welte 	  .patch = patch_vt1702},
37783218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
3779d949cac1SHarald Welte 	  .patch = patch_vt1702},
37803218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
3781d949cac1SHarald Welte 	  .patch = patch_vt1702},
37823218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
3783d949cac1SHarald Welte 	  .patch = patch_vt1702},
3784eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
3785eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3786eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
3787eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3788bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
3789bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3790bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
3791bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3792f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
3793f3db423dSLydia Wang 	  .patch = patch_vt1716S},
3794f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
3795f3db423dSLydia Wang 	  .patch = patch_vt1716S},
379625eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
379725eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
3798ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
379936dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
380036dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
380111890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
380211890956SLydia Wang 		.patch = patch_vt2002P},
380311890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
380411890956SLydia Wang 		.patch = patch_vt2002P},
3805c577b8a1SJoseph Chan 	{} /* terminator */
3806c577b8a1SJoseph Chan };
38071289e9e8STakashi Iwai 
38081289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
38091289e9e8STakashi Iwai 
38101289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
38111289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
38121289e9e8STakashi Iwai 	.owner = THIS_MODULE,
38131289e9e8STakashi Iwai };
38141289e9e8STakashi Iwai 
38151289e9e8STakashi Iwai MODULE_LICENSE("GPL");
38161289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
38171289e9e8STakashi Iwai 
38181289e9e8STakashi Iwai static int __init patch_via_init(void)
38191289e9e8STakashi Iwai {
38201289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
38211289e9e8STakashi Iwai }
38221289e9e8STakashi Iwai 
38231289e9e8STakashi Iwai static void __exit patch_via_exit(void)
38241289e9e8STakashi Iwai {
38251289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
38261289e9e8STakashi Iwai }
38271289e9e8STakashi Iwai 
38281289e9e8STakashi Iwai module_init(patch_via_init)
38291289e9e8STakashi Iwai module_exit(patch_via_exit)
3830