xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision a00a5fad9ddbabc7cd03d143520b9a4730edc75d)
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 
864a79616dSTakashi Iwai struct nid_path {
874a79616dSTakashi Iwai 	int depth;
888e3679dcSTakashi Iwai 	hda_nid_t path[MAX_NID_PATH_DEPTH];
898e3679dcSTakashi Iwai 	short idx[MAX_NID_PATH_DEPTH];
904a79616dSTakashi Iwai };
914a79616dSTakashi Iwai 
921f2e99feSLydia Wang struct via_spec {
931f2e99feSLydia Wang 	/* codec parameterization */
9490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
951f2e99feSLydia Wang 	unsigned int num_mixers;
961f2e99feSLydia Wang 
9790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
981f2e99feSLydia Wang 	unsigned int num_iverbs;
991f2e99feSLydia Wang 
10082673bc8STakashi Iwai 	char stream_name_analog[32];
1017eb56e84STakashi Iwai 	char stream_name_hp[32];
10290dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
10390dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1041f2e99feSLydia Wang 
10582673bc8STakashi Iwai 	char stream_name_digital[32];
10690dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
10790dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1081f2e99feSLydia Wang 
1091f2e99feSLydia Wang 	/* playback */
1101f2e99feSLydia Wang 	struct hda_multi_out multiout;
1111f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
112ece8d043STakashi Iwai 	hda_nid_t hp_dac_nid;
113ada509ecSTakashi Iwai 	int num_active_streams;
1141f2e99feSLydia Wang 
1154a79616dSTakashi Iwai 	struct nid_path out_path[4];
1164a79616dSTakashi Iwai 	struct nid_path hp_path;
1174a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
1184a918ffeSTakashi Iwai 	struct nid_path speaker_path;
1194a79616dSTakashi Iwai 
1201f2e99feSLydia Wang 	/* capture */
1211f2e99feSLydia Wang 	unsigned int num_adc_nids;
122a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1231f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
124620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1251f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1261f2e99feSLydia Wang 
1271f2e99feSLydia Wang 	/* capture source */
1281f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1291f2e99feSLydia Wang 	unsigned int cur_mux[3];
1301f2e99feSLydia Wang 
1311f2e99feSLydia Wang 	/* PCM information */
1321f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1331f2e99feSLydia Wang 
1341f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1351f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1361f2e99feSLydia Wang 	struct snd_array kctls;
1371f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1381f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1391f2e99feSLydia Wang 
1401f2e99feSLydia Wang 	/* HP mode source */
1411f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1421f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1431f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
144f3db423dSLydia Wang 	unsigned int dmic_enabled;
14524088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1461f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1471f2e99feSLydia Wang 
148e3d7a143STakashi Iwai 	/* smart51 setup */
149e3d7a143STakashi Iwai 	unsigned int smart51_nums;
150e3d7a143STakashi Iwai 	hda_nid_t smart51_pins[2];
151e3d7a143STakashi Iwai 	int smart51_idxs[2];
152e3d7a143STakashi Iwai 	const char *smart51_labels[2];
153e3d7a143STakashi Iwai 	unsigned int smart51_enabled;
154e3d7a143STakashi Iwai 
1551f2e99feSLydia Wang 	/* work to check hp jack state */
1561f2e99feSLydia Wang 	struct hda_codec *codec;
1571f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
158e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1591f2e99feSLydia Wang 	int vt1708_hp_present;
1603e95b9abSLydia Wang 
1613e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1623e95b9abSLydia Wang 
1631f2e99feSLydia Wang 	struct hda_loopback_check loopback;
16413af8e77STakashi Iwai 	int num_loopbacks;
16513af8e77STakashi Iwai 	struct hda_amp_list loopback_list[8];
1661f2e99feSLydia Wang };
1671f2e99feSLydia Wang 
1680341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1695b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1705b0cb1d8SJaroslav Kysela {
1715b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1725b0cb1d8SJaroslav Kysela 
1735b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1745b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1755b0cb1d8SJaroslav Kysela 		return NULL;
1765b0cb1d8SJaroslav Kysela 
1775b0cb1d8SJaroslav Kysela 	codec->spec = spec;
1785b0cb1d8SJaroslav Kysela 	spec->codec = codec;
1790341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1800341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1810341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1820341ccd7SLydia Wang 		spec->codec_type = VT1708S;
1835b0cb1d8SJaroslav Kysela 	return spec;
1845b0cb1d8SJaroslav Kysela }
1855b0cb1d8SJaroslav Kysela 
186744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
187d7426329SHarald Welte {
188744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
189d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
190d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
191d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
192d7426329SHarald Welte 
193d7426329SHarald Welte 	/* get codec type */
194d7426329SHarald Welte 	if (ven_id != 0x1106)
195d7426329SHarald Welte 		codec_type = UNKNOWN;
196d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
197d7426329SHarald Welte 		codec_type = VT1708;
198d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
199d7426329SHarald Welte 		codec_type = VT1709_10CH;
200d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
201d7426329SHarald Welte 		codec_type = VT1709_6CH;
202518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
203d7426329SHarald Welte 		codec_type = VT1708B_8CH;
204518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
205518bf3baSLydia Wang 			codec_type = VT1708BCE;
206518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
207d7426329SHarald Welte 		codec_type = VT1708B_4CH;
208d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
209d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
210d7426329SHarald Welte 		codec_type = VT1708S;
211d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
212d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
213d7426329SHarald Welte 		codec_type = VT1702;
214eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
215eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
216eb7188caSLydia Wang 		codec_type = VT1718S;
217f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
218f3db423dSLydia Wang 		codec_type = VT1716S;
219bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
220bb3c6bfcSLydia Wang 		codec_type = VT1718S;
22125eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
22225eaba2fSLydia Wang 		codec_type = VT2002P;
223ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
224ab6734e7SLydia Wang 		codec_type = VT1812;
22536dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
22636dd5c4aSLydia Wang 		codec_type = VT1708S;
22711890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
22811890956SLydia Wang 		codec_type = VT1802;
229d7426329SHarald Welte 	else
230d7426329SHarald Welte 		codec_type = UNKNOWN;
231d7426329SHarald Welte 	return codec_type;
232d7426329SHarald Welte };
233d7426329SHarald Welte 
234ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
23569e52a80SHarald Welte #define VIA_HP_EVENT		0x01
23669e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
2374a918ffeSTakashi Iwai #define VIA_LINE_EVENT		0x03
23869e52a80SHarald Welte 
239c577b8a1SJoseph Chan enum {
240c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
241c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
242f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
243c577b8a1SJoseph Chan };
244c577b8a1SJoseph Chan 
245ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
246ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
2471f2e99feSLydia Wang 
2481f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2491f2e99feSLydia Wang {
2501f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2511f2e99feSLydia Wang 		return;
2521f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
253e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2541f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2551f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2561f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2571f2e99feSLydia Wang }
2581f2e99feSLydia Wang 
2591f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2601f2e99feSLydia Wang {
2611f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2621f2e99feSLydia Wang 		return;
2631f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2641f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2651f2e99feSLydia Wang 		return;
2661f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
267e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2685b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2691f2e99feSLydia Wang }
270f5271101SLydia Wang 
2713e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2723e95b9abSLydia Wang {
2733e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2743e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2753e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2763e95b9abSLydia Wang }
27725eaba2fSLydia Wang 
278f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
279f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
280f5271101SLydia Wang {
281f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
282f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
283f5271101SLydia Wang 
2843e95b9abSLydia Wang 	set_widgets_power_state(codec);
285ada509ecSTakashi Iwai 	analog_low_current_mode(snd_kcontrol_chip(kcontrol));
2861f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
2871f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
2881f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
2891f2e99feSLydia Wang 		else
2901f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
2911f2e99feSLydia Wang 	}
292f5271101SLydia Wang 	return change;
293f5271101SLydia Wang }
294f5271101SLydia Wang 
295f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
296f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
297f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
298f5271101SLydia Wang 			.name = NULL,					\
299f5271101SLydia Wang 			.index = 0,					\
300f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
301f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
302f5271101SLydia Wang 			.put = analog_input_switch_put,			\
303f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
304f5271101SLydia Wang 
30590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
306c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
307c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
308f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
309c577b8a1SJoseph Chan };
310c577b8a1SJoseph Chan 
311ab6734e7SLydia Wang 
312c577b8a1SJoseph Chan /* add dynamic controls */
313291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
314291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
315291c9e33STakashi Iwai 				const char *name)
316c577b8a1SJoseph Chan {
317c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
318c577b8a1SJoseph Chan 
319603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
320603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
321c577b8a1SJoseph Chan 	if (!knew)
322291c9e33STakashi Iwai 		return NULL;
323291c9e33STakashi Iwai 	*knew = *tmpl;
324291c9e33STakashi Iwai 	if (!name)
325291c9e33STakashi Iwai 		name = tmpl->name;
326291c9e33STakashi Iwai 	if (name) {
327c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
328c577b8a1SJoseph Chan 		if (!knew->name)
329291c9e33STakashi Iwai 			return NULL;
330291c9e33STakashi Iwai 	}
331291c9e33STakashi Iwai 	return knew;
332291c9e33STakashi Iwai }
333291c9e33STakashi Iwai 
334291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
335291c9e33STakashi Iwai 			     int idx, unsigned long val)
336291c9e33STakashi Iwai {
337291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
338291c9e33STakashi Iwai 
339291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
340291c9e33STakashi Iwai 	if (!knew)
341c577b8a1SJoseph Chan 		return -ENOMEM;
342d7a99cceSTakashi Iwai 	knew->index = idx;
3434d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
3445e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
345c577b8a1SJoseph Chan 	knew->private_value = val;
346c577b8a1SJoseph Chan 	return 0;
347c577b8a1SJoseph Chan }
348c577b8a1SJoseph Chan 
3497b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
3507b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
3517b315bb4STakashi Iwai 
352291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
3535b0cb1d8SJaroslav Kysela 
354603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
355603c4019STakashi Iwai {
356603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
357603c4019STakashi Iwai 
358603c4019STakashi Iwai 	if (spec->kctls.list) {
359603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
360603c4019STakashi Iwai 		int i;
361603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
362603c4019STakashi Iwai 			kfree(kctl[i].name);
363603c4019STakashi Iwai 	}
364603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
365603c4019STakashi Iwai }
366603c4019STakashi Iwai 
367c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
3689510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
3697b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
370c577b8a1SJoseph Chan {
371c577b8a1SJoseph Chan 	char name[32];
372c577b8a1SJoseph Chan 	int err;
373c577b8a1SJoseph Chan 
374c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
3757b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
376c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
377c577b8a1SJoseph Chan 	if (err < 0)
378c577b8a1SJoseph Chan 		return err;
379c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
3807b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
381c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
382c577b8a1SJoseph Chan 	if (err < 0)
383c577b8a1SJoseph Chan 		return err;
384c577b8a1SJoseph Chan 	return 0;
385c577b8a1SJoseph Chan }
386c577b8a1SJoseph Chan 
3875d41762aSTakashi Iwai /* return the index of the given widget nid as the source of mux;
3885d41762aSTakashi Iwai  * return -1 if not found;
3895d41762aSTakashi Iwai  * if num_conns is non-NULL, set the total number of connections
3905d41762aSTakashi Iwai  */
3915d41762aSTakashi Iwai static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
3925d41762aSTakashi Iwai 				  hda_nid_t nid, int *num_conns)
393c577b8a1SJoseph Chan {
3945d41762aSTakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
3955d41762aSTakashi Iwai 	int i, nums;
3965d41762aSTakashi Iwai 
3975d41762aSTakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
3985d41762aSTakashi Iwai 	if (num_conns)
3995d41762aSTakashi Iwai 		*num_conns = nums;
4005d41762aSTakashi Iwai 	for (i = 0; i < nums; i++)
4015d41762aSTakashi Iwai 		if (conn[i] == nid)
4025d41762aSTakashi Iwai 			return i;
4035d41762aSTakashi Iwai 	return -1;
4045d41762aSTakashi Iwai }
4055d41762aSTakashi Iwai 
4065d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \
4075d41762aSTakashi Iwai 	__get_connection_index(codec, mux, nid, NULL)
4085d41762aSTakashi Iwai 
4095d41762aSTakashi Iwai /* unmute input amp and select the specificed source */
4105d41762aSTakashi Iwai static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
4115d41762aSTakashi Iwai 			      hda_nid_t src, hda_nid_t mix)
4125d41762aSTakashi Iwai {
4135d41762aSTakashi Iwai 	int idx, num_conns;
4145d41762aSTakashi Iwai 
4155d41762aSTakashi Iwai 	idx = __get_connection_index(codec, nid, src, &num_conns);
4165d41762aSTakashi Iwai 	if (idx < 0)
4175d41762aSTakashi Iwai 		return;
4185d41762aSTakashi Iwai 
4195d41762aSTakashi Iwai 	/* select the route explicitly when multiple connections exist */
4208e3679dcSTakashi Iwai 	if (num_conns > 1 &&
4218e3679dcSTakashi Iwai 	    get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
422d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
4235d41762aSTakashi Iwai 				    AC_VERB_SET_CONNECT_SEL, idx);
4248e3679dcSTakashi Iwai 
4255d41762aSTakashi Iwai 	/* unmute if the input amp is present */
4268e3679dcSTakashi Iwai 	if (query_amp_caps(codec, nid, HDA_INPUT) &
4278e3679dcSTakashi Iwai 	    (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
4285d41762aSTakashi Iwai 		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4295d41762aSTakashi Iwai 				    AMP_IN_UNMUTE(idx));
4305d41762aSTakashi Iwai 
4318e3679dcSTakashi Iwai 	/* unmute the src output */
4328e3679dcSTakashi Iwai 	if (query_amp_caps(codec, src, HDA_OUTPUT) &
4338e3679dcSTakashi Iwai 	    (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
4348e3679dcSTakashi Iwai 		snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4358e3679dcSTakashi Iwai 				    AMP_OUT_UNMUTE);
4368e3679dcSTakashi Iwai 
4375d41762aSTakashi Iwai 	/* unmute AA-path if present */
4385d41762aSTakashi Iwai 	if (!mix)
4395d41762aSTakashi Iwai 		return;
4405d41762aSTakashi Iwai 	idx = __get_connection_index(codec, nid, mix, NULL);
4415d41762aSTakashi Iwai 	if (idx >= 0)
4425d41762aSTakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
4435d41762aSTakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
4445d41762aSTakashi Iwai 				    AMP_IN_UNMUTE(idx));
4455d41762aSTakashi Iwai }
4465d41762aSTakashi Iwai 
4475d41762aSTakashi Iwai /* set the given pin as output */
4485d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
4495d41762aSTakashi Iwai 			    int pin_type)
4505d41762aSTakashi Iwai {
4515d41762aSTakashi Iwai 	if (!pin)
4525d41762aSTakashi Iwai 		return;
4535d41762aSTakashi Iwai 	snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4545d41762aSTakashi Iwai 			    pin_type);
4555d41762aSTakashi Iwai 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
4565d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0,
457d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
458c577b8a1SJoseph Chan }
459c577b8a1SJoseph Chan 
4605d41762aSTakashi Iwai static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
4615d41762aSTakashi Iwai 				 int pin_type, struct nid_path *path)
4625d41762aSTakashi Iwai {
4635d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
4645d41762aSTakashi Iwai 	unsigned int caps;
4655d41762aSTakashi Iwai 	hda_nid_t nid;
4665d41762aSTakashi Iwai 	int i;
4675d41762aSTakashi Iwai 
4685d41762aSTakashi Iwai 	if (!pin)
4695d41762aSTakashi Iwai 		return;
4705d41762aSTakashi Iwai 
4715d41762aSTakashi Iwai 	init_output_pin(codec, pin, pin_type);
4725d41762aSTakashi Iwai 	caps = query_amp_caps(codec, pin, HDA_OUTPUT);
4735d41762aSTakashi Iwai 	if (caps & AC_AMPCAP_MUTE) {
4745d41762aSTakashi Iwai 		unsigned int val;
4755d41762aSTakashi Iwai 		val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
4765d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4775d41762aSTakashi Iwai 				    AMP_OUT_MUTE | val);
4785d41762aSTakashi Iwai 	}
4795d41762aSTakashi Iwai 
4805d41762aSTakashi Iwai 	/* initialize the output path */
4818e3679dcSTakashi Iwai 	for (i = path->depth - 1; i > 0; i--) {
4828e3679dcSTakashi Iwai 		nid = path->path[i - 1];
4838e3679dcSTakashi Iwai 		unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
4845d41762aSTakashi Iwai 	}
4855d41762aSTakashi Iwai }
4865d41762aSTakashi Iwai 
487c577b8a1SJoseph Chan 
488c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
489c577b8a1SJoseph Chan {
490c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
491c577b8a1SJoseph Chan 	int i;
492c577b8a1SJoseph Chan 
493e3d7a143STakashi Iwai 	for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
4945d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
4955d41762aSTakashi Iwai 				     PIN_OUT, &spec->out_path[i]);
496c577b8a1SJoseph Chan }
497c577b8a1SJoseph Chan 
498c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
499c577b8a1SJoseph Chan {
500c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
501c577b8a1SJoseph Chan 
5025d41762aSTakashi Iwai 	if (spec->hp_dac_nid)
5035d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
5045d41762aSTakashi Iwai 				     &spec->hp_path);
5055d41762aSTakashi Iwai 	else
5065d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
5075d41762aSTakashi Iwai 				     &spec->hp_dep_path);
50825eaba2fSLydia Wang }
509c577b8a1SJoseph Chan 
5104a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec)
5114a918ffeSTakashi Iwai {
5124a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
5134a918ffeSTakashi Iwai 
5144a918ffeSTakashi Iwai 	if (spec->autocfg.speaker_outs)
5154a918ffeSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
5164a918ffeSTakashi Iwai 				     PIN_OUT, &spec->speaker_path);
5174a918ffeSTakashi Iwai }
5184a918ffeSTakashi Iwai 
519f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
52032e0191dSClemens Ladisch 
521c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
522c577b8a1SJoseph Chan {
523c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5247b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
525096a8854STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
52632e0191dSClemens Ladisch 	unsigned int ctl;
527096a8854STakashi Iwai 	int i, num_conns;
528c577b8a1SJoseph Chan 
529096a8854STakashi Iwai 	/* init ADCs */
530096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
531096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
532096a8854STakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
533096a8854STakashi Iwai 				    AMP_IN_UNMUTE(0));
534096a8854STakashi Iwai 	}
535096a8854STakashi Iwai 
536096a8854STakashi Iwai 	/* init pins */
5377b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5387b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
539f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
54032e0191dSClemens Ladisch 			ctl = PIN_OUT;
54130649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
54232e0191dSClemens Ladisch 			ctl = PIN_VREF50;
54332e0191dSClemens Ladisch 		else
54432e0191dSClemens Ladisch 			ctl = PIN_IN;
545c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
54632e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
547c577b8a1SJoseph Chan 	}
548096a8854STakashi Iwai 
549096a8854STakashi Iwai 	/* init input-src */
550096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
551096a8854STakashi Iwai 		const struct hda_input_mux *imux = spec->input_mux;
552096a8854STakashi Iwai 		if (!imux || !spec->mux_nids[i])
553096a8854STakashi Iwai 			continue;
554096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->mux_nids[i], 0,
555096a8854STakashi Iwai 				    AC_VERB_SET_CONNECT_SEL,
556096a8854STakashi Iwai 				    imux->items[spec->cur_mux[i]].index);
557096a8854STakashi Iwai 	}
558096a8854STakashi Iwai 
559096a8854STakashi Iwai 	/* init aa-mixer */
560096a8854STakashi Iwai 	if (!spec->aa_mix_nid)
561096a8854STakashi Iwai 		return;
562096a8854STakashi Iwai 	num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
563096a8854STakashi Iwai 					    ARRAY_SIZE(conn));
564096a8854STakashi Iwai 	for (i = 0; i < num_conns; i++) {
565096a8854STakashi Iwai 		unsigned int caps = get_wcaps(codec, conn[i]);
566096a8854STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_PIN)
567096a8854STakashi Iwai 			snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
568096a8854STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
569096a8854STakashi Iwai 					    AMP_IN_MUTE(i));
570096a8854STakashi Iwai 	}
571c577b8a1SJoseph Chan }
572f5271101SLydia Wang 
573f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
574f5271101SLydia Wang 				unsigned int *affected_parm)
575f5271101SLydia Wang {
576f5271101SLydia Wang 	unsigned parm;
577f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
578f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
579f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
580f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
5811564b287SLydia Wang 	struct via_spec *spec = codec->spec;
58224088a58STakashi Iwai 	unsigned present = 0;
58324088a58STakashi Iwai 
58424088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
58524088a58STakashi Iwai 	if (!no_presence)
58624088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
587f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
5881564b287SLydia Wang 	    || ((no_presence || present)
5891564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
590f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
591f5271101SLydia Wang 		parm = AC_PWRST_D0;
592f5271101SLydia Wang 	} else
593f5271101SLydia Wang 		parm = AC_PWRST_D3;
594f5271101SLydia Wang 
595f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
596f5271101SLydia Wang }
597f5271101SLydia Wang 
59824088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
59924088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
60024088a58STakashi Iwai {
60124088a58STakashi Iwai 	static const char * const texts[] = {
60224088a58STakashi Iwai 		"Disabled", "Enabled"
60324088a58STakashi Iwai 	};
60424088a58STakashi Iwai 
60524088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
60624088a58STakashi Iwai 	uinfo->count = 1;
60724088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
60824088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
60924088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
61024088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
61124088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
61224088a58STakashi Iwai 	return 0;
61324088a58STakashi Iwai }
61424088a58STakashi Iwai 
61524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
61624088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
61724088a58STakashi Iwai {
61824088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
61924088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
62024088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
62124088a58STakashi Iwai 	return 0;
62224088a58STakashi Iwai }
62324088a58STakashi Iwai 
62424088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
62524088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
62624088a58STakashi Iwai {
62724088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
62824088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
62924088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
63024088a58STakashi Iwai 
63124088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
63224088a58STakashi Iwai 		return 0;
63324088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
63424088a58STakashi Iwai 	set_widgets_power_state(codec);
63524088a58STakashi Iwai 	return 1;
63624088a58STakashi Iwai }
63724088a58STakashi Iwai 
63824088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
63924088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
64024088a58STakashi Iwai 	.name = "Dynamic Power-Control",
64124088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
64224088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
64324088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
64424088a58STakashi Iwai };
64524088a58STakashi Iwai 
64624088a58STakashi Iwai 
647c577b8a1SJoseph Chan /*
648c577b8a1SJoseph Chan  * input MUX handling
649c577b8a1SJoseph Chan  */
650c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
651c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
652c577b8a1SJoseph Chan {
653c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
654c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
655c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
656c577b8a1SJoseph Chan }
657c577b8a1SJoseph Chan 
658c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
659c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
660c577b8a1SJoseph Chan {
661c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
662c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
663c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
664c577b8a1SJoseph Chan 
665c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
666c577b8a1SJoseph Chan 	return 0;
667c577b8a1SJoseph Chan }
668c577b8a1SJoseph Chan 
669c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
670c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
671c577b8a1SJoseph Chan {
672c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
673c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
674c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
675bff5fbf5SLydia Wang 	int ret;
676c577b8a1SJoseph Chan 
677337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
678337b9d02STakashi Iwai 		return -EINVAL;
679a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
680a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
681a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
682a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
683a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
684bff5fbf5SLydia Wang 
685bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
686bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
687bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
688a80e6e3cSLydia Wang 	/* update jack power state */
6893e95b9abSLydia Wang 	set_widgets_power_state(codec);
690a80e6e3cSLydia Wang 
691bff5fbf5SLydia Wang 	return ret;
692c577b8a1SJoseph Chan }
693c577b8a1SJoseph Chan 
6940aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6950aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6960aa62aefSHarald Welte {
6970aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
6980aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
6990aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
7000aa62aefSHarald Welte }
7010aa62aefSHarald Welte 
7020aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7030aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7040aa62aefSHarald Welte {
7050aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
706cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
707cdc1784dSLydia Wang 
708ece8d043STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
709cdc1784dSLydia Wang 	return 0;
710cdc1784dSLydia Wang }
711cdc1784dSLydia Wang 
7120aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7130aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7140aa62aefSHarald Welte {
7150aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7160aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7175b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7180aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
719cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
720cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
721cdc1784dSLydia Wang 		? 1 : 0;
722ece8d043STakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
7230aa62aefSHarald Welte 
724ce0e5a9eSLydia Wang 	/* update jack power state */
7253e95b9abSLydia Wang 	set_widgets_power_state(codec);
7260aa62aefSHarald Welte 	return 0;
7270aa62aefSHarald Welte }
7280aa62aefSHarald Welte 
729ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = {
7300aa62aefSHarald Welte 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7310aa62aefSHarald Welte 	.name = "Independent HP",
7320aa62aefSHarald Welte 	.info = via_independent_hp_info,
7330aa62aefSHarald Welte 	.get = via_independent_hp_get,
7340aa62aefSHarald Welte 	.put = via_independent_hp_put,
7350aa62aefSHarald Welte };
7360aa62aefSHarald Welte 
7373d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
7385b0cb1d8SJaroslav Kysela {
7393d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
7405b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
7415b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
7425b0cb1d8SJaroslav Kysela 
7435b0cb1d8SJaroslav Kysela 	nid = spec->autocfg.hp_pins[0];
744ece8d043STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer);
7453d83e577STakashi Iwai 	if (knew == NULL)
7463d83e577STakashi Iwai 		return -ENOMEM;
7473d83e577STakashi Iwai 
7485b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
7495b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
7505b0cb1d8SJaroslav Kysela 
7515b0cb1d8SJaroslav Kysela 	return 0;
7525b0cb1d8SJaroslav Kysela }
7535b0cb1d8SJaroslav Kysela 
7541564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
7551564b287SLydia Wang {
756e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
7571564b287SLydia Wang 	int i;
7581564b287SLydia Wang 
759e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
760e3d7a143STakashi Iwai 		struct snd_kcontrol *ctl;
761e3d7a143STakashi Iwai 		struct snd_ctl_elem_id id;
7621564b287SLydia Wang 		memset(&id, 0, sizeof(id));
7631564b287SLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
764e3d7a143STakashi Iwai 		sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
765525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
766525566cbSLydia Wang 		if (ctl)
767525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
768525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
769525566cbSLydia Wang 					&ctl->id);
7701564b287SLydia Wang 	}
7711564b287SLydia Wang }
7721564b287SLydia Wang 
7731564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
7741564b287SLydia Wang {
7751564b287SLydia Wang 	struct via_spec *spec = codec->spec;
7761564b287SLydia Wang 	int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
777e3d7a143STakashi Iwai 	int i;
778e3d7a143STakashi Iwai 
779e3d7a143STakashi Iwai 	/* check AA path's mute status */
780e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
781e3d7a143STakashi Iwai 		if (spec->smart51_idxs[i] < 0)
782e3d7a143STakashi Iwai 			continue;
783e3d7a143STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
784e3d7a143STakashi Iwai 					 HDA_INPUT, spec->smart51_idxs[i],
7851564b287SLydia Wang 					 HDA_AMP_MUTE, val);
7861564b287SLydia Wang 	}
7871564b287SLydia Wang }
788f4a7828bSTakashi Iwai 
789e3d7a143STakashi Iwai static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
7901564b287SLydia Wang {
791f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
7927b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
7937b315bb4STakashi Iwai 	int i;
7947b315bb4STakashi Iwai 
7957b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
796f4a7828bSTakashi Iwai 		unsigned int defcfg;
797f4a7828bSTakashi Iwai 		if (pin != cfg->inputs[i].pin)
798f4a7828bSTakashi Iwai 			continue;
799f4a7828bSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
800f4a7828bSTakashi Iwai 			return false;
801f4a7828bSTakashi Iwai 		defcfg = snd_hda_codec_get_pincfg(codec, pin);
802f4a7828bSTakashi Iwai 		if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
803f4a7828bSTakashi Iwai 			return false;
804f4a7828bSTakashi Iwai 		return true;
8051564b287SLydia Wang 	}
806f4a7828bSTakashi Iwai 	return false;
8071564b287SLydia Wang }
8081564b287SLydia Wang 
809e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
810e3d7a143STakashi Iwai {
811e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
812e3d7a143STakashi Iwai 	int i;
813e3d7a143STakashi Iwai 
814e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++)
815e3d7a143STakashi Iwai 		if (spec->smart51_pins[i] == pin)
816e3d7a143STakashi Iwai 			return true;
817e3d7a143STakashi Iwai 	return false;
818e3d7a143STakashi Iwai }
819e3d7a143STakashi Iwai 
8201564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
8211564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
8221564b287SLydia Wang {
8231564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
8241564b287SLydia Wang 	uinfo->count = 1;
8251564b287SLydia Wang 	uinfo->value.integer.min = 0;
8261564b287SLydia Wang 	uinfo->value.integer.max = 1;
8271564b287SLydia Wang 	return 0;
8281564b287SLydia Wang }
8291564b287SLydia Wang 
8301564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
8311564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8321564b287SLydia Wang {
8331564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8341564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8351564b287SLydia Wang 	int on = 1;
8361564b287SLydia Wang 	int i;
8371564b287SLydia Wang 
838e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
839e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
840f4a7828bSTakashi Iwai 		unsigned int ctl;
841f4a7828bSTakashi Iwai 		ctl = snd_hda_codec_read(codec, nid, 0,
842f4a7828bSTakashi Iwai 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8437b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
8441564b287SLydia Wang 			on = 0;
8451564b287SLydia Wang 	}
8461564b287SLydia Wang 	*ucontrol->value.integer.value = on;
8471564b287SLydia Wang 	return 0;
8481564b287SLydia Wang }
8491564b287SLydia Wang 
8501564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
8511564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8521564b287SLydia Wang {
8531564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8541564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8551564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
8561564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
8571564b287SLydia Wang 	int i;
8581564b287SLydia Wang 
859e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
860e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
8617b315bb4STakashi Iwai 		unsigned int parm;
8627b315bb4STakashi Iwai 
8637b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
8641564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8651564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
8661564b287SLydia Wang 		parm |= out_in;
8671564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
8681564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
8691564b287SLydia Wang 				    parm);
8701564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
8711564b287SLydia Wang 			mute_aa_path(codec, 1);
8721564b287SLydia Wang 			notify_aa_path_ctls(codec);
8731564b287SLydia Wang 		}
8741564b287SLydia Wang 	}
8751564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
8763e95b9abSLydia Wang 	set_widgets_power_state(codec);
8771564b287SLydia Wang 	return 1;
8781564b287SLydia Wang }
8791564b287SLydia Wang 
8805f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
8811564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8821564b287SLydia Wang 	.name = "Smart 5.1",
8831564b287SLydia Wang 	.count = 1,
8841564b287SLydia Wang 	.info = via_smart51_info,
8851564b287SLydia Wang 	.get = via_smart51_get,
8861564b287SLydia Wang 	.put = via_smart51_put,
8871564b287SLydia Wang };
8881564b287SLydia Wang 
889f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
8905b0cb1d8SJaroslav Kysela {
891f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
8925b0cb1d8SJaroslav Kysela 
893e3d7a143STakashi Iwai 	if (!spec->smart51_nums)
894cb34c207SLydia Wang 		return 0;
895e3d7a143STakashi Iwai 	if (!via_clone_control(spec, &via_smart51_mixer))
8965b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8975b0cb1d8SJaroslav Kysela 	return 0;
8985b0cb1d8SJaroslav Kysela }
8995b0cb1d8SJaroslav Kysela 
900f5271101SLydia Wang /* check AA path's mute status */
901ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
902ada509ecSTakashi Iwai {
903ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
904ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
905ada509ecSTakashi Iwai 	int i, ch, v;
906ada509ecSTakashi Iwai 
907ada509ecSTakashi Iwai 	for (i = 0; i < spec->num_loopbacks; i++) {
908ada509ecSTakashi Iwai 		p = &spec->loopback_list[i];
909ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
910ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
911ada509ecSTakashi Iwai 						   p->idx);
912ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
913ada509ecSTakashi Iwai 				return false;
914f5271101SLydia Wang 		}
915f5271101SLydia Wang 	}
916ada509ecSTakashi Iwai 	return true;
917f5271101SLydia Wang }
918f5271101SLydia Wang 
919f5271101SLydia Wang /* enter/exit analog low-current mode */
920ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
921f5271101SLydia Wang {
922f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
923ada509ecSTakashi Iwai 	bool enable;
924ada509ecSTakashi Iwai 	unsigned int verb, parm;
925f5271101SLydia Wang 
926ada509ecSTakashi Iwai 	enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
927f5271101SLydia Wang 
928f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
929f5271101SLydia Wang 	switch (spec->codec_type) {
930f5271101SLydia Wang 	case VT1708B_8CH:
931f5271101SLydia Wang 	case VT1708B_4CH:
932f5271101SLydia Wang 		verb = 0xf70;
933f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
934f5271101SLydia Wang 		break;
935f5271101SLydia Wang 	case VT1708S:
936eb7188caSLydia Wang 	case VT1718S:
937f3db423dSLydia Wang 	case VT1716S:
938f5271101SLydia Wang 		verb = 0xf73;
939f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
940f5271101SLydia Wang 		break;
941f5271101SLydia Wang 	case VT1702:
942f5271101SLydia Wang 		verb = 0xf73;
943f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
944f5271101SLydia Wang 		break;
94525eaba2fSLydia Wang 	case VT2002P:
946ab6734e7SLydia Wang 	case VT1812:
94711890956SLydia Wang 	case VT1802:
94825eaba2fSLydia Wang 		verb = 0xf93;
94925eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
95025eaba2fSLydia Wang 		break;
951f5271101SLydia Wang 	default:
952f5271101SLydia Wang 		return;		/* other codecs are not supported */
953f5271101SLydia Wang 	}
954f5271101SLydia Wang 	/* send verb */
955f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
956f5271101SLydia Wang }
957f5271101SLydia Wang 
958c577b8a1SJoseph Chan /*
959c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
960c577b8a1SJoseph Chan  */
961096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
962aa266fccSLydia Wang 	/* power down jack detect function */
963aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
964f7278fd0SJosepch Chan 	{ }
965c577b8a1SJoseph Chan };
966c577b8a1SJoseph Chan 
967ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active)
9687eb56e84STakashi Iwai {
969ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
970ada509ecSTakashi Iwai 
971ada509ecSTakashi Iwai 	if (active)
972ada509ecSTakashi Iwai 		spec->num_active_streams++;
973ada509ecSTakashi Iwai 	else
974ada509ecSTakashi Iwai 		spec->num_active_streams--;
975ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
9767eb56e84STakashi Iwai }
9777eb56e84STakashi Iwai 
978ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
979c577b8a1SJoseph Chan 				 struct hda_codec *codec,
980c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
981c577b8a1SJoseph Chan {
982c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
983ada509ecSTakashi Iwai 	int err;
984ece8d043STakashi Iwai 
985ece8d043STakashi Iwai 	if (!spec->hp_independent_mode)
986ece8d043STakashi Iwai 		spec->multiout.hp_nid = spec->hp_dac_nid;
987ada509ecSTakashi Iwai 	set_stream_active(codec, true);
988ada509ecSTakashi Iwai 	err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
9899a08160bSTakashi Iwai 					    hinfo);
990ada509ecSTakashi Iwai 	if (err < 0) {
991ada509ecSTakashi Iwai 		spec->multiout.hp_nid = 0;
992ada509ecSTakashi Iwai 		set_stream_active(codec, false);
993ada509ecSTakashi Iwai 		return err;
994ada509ecSTakashi Iwai 	}
995ada509ecSTakashi Iwai 	return 0;
996c577b8a1SJoseph Chan }
997c577b8a1SJoseph Chan 
998ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
9999af74210STakashi Iwai 				  struct hda_codec *codec,
10009af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
10019af74210STakashi Iwai {
1002ece8d043STakashi Iwai 	struct via_spec *spec = codec->spec;
1003ece8d043STakashi Iwai 
1004ece8d043STakashi Iwai 	spec->multiout.hp_nid = 0;
1005ada509ecSTakashi Iwai 	set_stream_active(codec, false);
10069af74210STakashi Iwai 	return 0;
10079af74210STakashi Iwai }
10089af74210STakashi Iwai 
10097eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
10107eb56e84STakashi Iwai 				    struct hda_codec *codec,
10117eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
10127eb56e84STakashi Iwai {
10137eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
10147eb56e84STakashi Iwai 
1015ece8d043STakashi Iwai 	if (snd_BUG_ON(!spec->hp_dac_nid))
10167eb56e84STakashi Iwai 		return -EINVAL;
1017ece8d043STakashi Iwai 	if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1018ece8d043STakashi Iwai 		return -EBUSY;
1019ada509ecSTakashi Iwai 	set_stream_active(codec, true);
1020ece8d043STakashi Iwai 	return 0;
1021ece8d043STakashi Iwai }
1022ece8d043STakashi Iwai 
1023ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1024ece8d043STakashi Iwai 				     struct hda_codec *codec,
1025ece8d043STakashi Iwai 				     struct snd_pcm_substream *substream)
1026ece8d043STakashi Iwai {
1027ada509ecSTakashi Iwai 	set_stream_active(codec, false);
10287eb56e84STakashi Iwai 	return 0;
10297eb56e84STakashi Iwai }
10307eb56e84STakashi Iwai 
10317eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
10327eb56e84STakashi Iwai 					  struct hda_codec *codec,
10330aa62aefSHarald Welte 					  unsigned int stream_tag,
10340aa62aefSHarald Welte 					  unsigned int format,
10350aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
10360aa62aefSHarald Welte {
10370aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10380aa62aefSHarald Welte 
1039ece8d043STakashi Iwai 	snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1040ece8d043STakashi Iwai 					 format, substream);
10417eb56e84STakashi Iwai 	vt1708_start_hp_work(spec);
10427eb56e84STakashi Iwai 	return 0;
10430aa62aefSHarald Welte }
10440aa62aefSHarald Welte 
10457eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
10460aa62aefSHarald Welte 				       struct hda_codec *codec,
10470aa62aefSHarald Welte 				       unsigned int stream_tag,
10480aa62aefSHarald Welte 				       unsigned int format,
10490aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
10500aa62aefSHarald Welte {
10510aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10520aa62aefSHarald Welte 
1053ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1054ece8d043STakashi Iwai 				   stream_tag, 0, format);
10551f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
10560aa62aefSHarald Welte 	return 0;
10570aa62aefSHarald Welte }
10580aa62aefSHarald Welte 
10590aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
10600aa62aefSHarald Welte 				    struct hda_codec *codec,
10610aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
10620aa62aefSHarald Welte {
10630aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10640aa62aefSHarald Welte 
1065ece8d043STakashi Iwai 	snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
10667eb56e84STakashi Iwai 	vt1708_stop_hp_work(spec);
10677eb56e84STakashi Iwai 	return 0;
10680aa62aefSHarald Welte }
10697eb56e84STakashi Iwai 
10707eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
10717eb56e84STakashi Iwai 				       struct hda_codec *codec,
10727eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
10737eb56e84STakashi Iwai {
10747eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
10757eb56e84STakashi Iwai 
1076ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
10771f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
10780aa62aefSHarald Welte 	return 0;
10790aa62aefSHarald Welte }
10800aa62aefSHarald Welte 
1081c577b8a1SJoseph Chan /*
1082c577b8a1SJoseph Chan  * Digital out
1083c577b8a1SJoseph Chan  */
1084c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1085c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1086c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1087c577b8a1SJoseph Chan {
1088c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1089c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1090c577b8a1SJoseph Chan }
1091c577b8a1SJoseph Chan 
1092c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1093c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1094c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1095c577b8a1SJoseph Chan {
1096c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1097c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1098c577b8a1SJoseph Chan }
1099c577b8a1SJoseph Chan 
11005691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
110198aa34c0SHarald Welte 					struct hda_codec *codec,
110298aa34c0SHarald Welte 					unsigned int stream_tag,
110398aa34c0SHarald Welte 					unsigned int format,
110498aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
110598aa34c0SHarald Welte {
110698aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
11079da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
11089da29271STakashi Iwai 					     stream_tag, format, substream);
11099da29271STakashi Iwai }
11105691ec7fSHarald Welte 
11119da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
11129da29271STakashi Iwai 					struct hda_codec *codec,
11139da29271STakashi Iwai 					struct snd_pcm_substream *substream)
11149da29271STakashi Iwai {
11159da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
11169da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
111798aa34c0SHarald Welte 	return 0;
111898aa34c0SHarald Welte }
111998aa34c0SHarald Welte 
1120c577b8a1SJoseph Chan /*
1121c577b8a1SJoseph Chan  * Analog capture
1122c577b8a1SJoseph Chan  */
1123c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1124c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1125c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1126c577b8a1SJoseph Chan 				   unsigned int format,
1127c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1128c577b8a1SJoseph Chan {
1129c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1130c577b8a1SJoseph Chan 
1131c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1132c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1133c577b8a1SJoseph Chan 	return 0;
1134c577b8a1SJoseph Chan }
1135c577b8a1SJoseph Chan 
1136c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1137c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1138c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1139c577b8a1SJoseph Chan {
1140c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1141888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1142c577b8a1SJoseph Chan 	return 0;
1143c577b8a1SJoseph Chan }
1144c577b8a1SJoseph Chan 
11459af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
11467eb56e84STakashi Iwai 	.substreams = 1,
1147c577b8a1SJoseph Chan 	.channels_min = 2,
1148c577b8a1SJoseph Chan 	.channels_max = 8,
11499af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1150c577b8a1SJoseph Chan 	.ops = {
1151ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1152ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
11530aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
11540aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1155c577b8a1SJoseph Chan 	},
1156c577b8a1SJoseph Chan };
1157c577b8a1SJoseph Chan 
11587eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
11597eb56e84STakashi Iwai 	.substreams = 1,
11607eb56e84STakashi Iwai 	.channels_min = 2,
11617eb56e84STakashi Iwai 	.channels_max = 2,
11627eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
11637eb56e84STakashi Iwai 	.ops = {
11647eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
1165ece8d043STakashi Iwai 		.close = via_playback_hp_pcm_close,
11667eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
11677eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
11687eb56e84STakashi Iwai 	},
11697eb56e84STakashi Iwai };
11707eb56e84STakashi Iwai 
117190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
11727eb56e84STakashi Iwai 	.substreams = 1,
1173bc9b5623STakashi Iwai 	.channels_min = 2,
1174bc9b5623STakashi Iwai 	.channels_max = 8,
11759af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1176bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1177bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1178bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1179bc9b5623STakashi Iwai 	 */
1180bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1181bc9b5623STakashi Iwai 	.ops = {
1182ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1183ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
1184c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1185c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1186bc9b5623STakashi Iwai 	},
1187bc9b5623STakashi Iwai };
1188bc9b5623STakashi Iwai 
11899af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
11907eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1191c577b8a1SJoseph Chan 	.channels_min = 2,
1192c577b8a1SJoseph Chan 	.channels_max = 2,
11939af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1194c577b8a1SJoseph Chan 	.ops = {
1195c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1196c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1197c577b8a1SJoseph Chan 	},
1198c577b8a1SJoseph Chan };
1199c577b8a1SJoseph Chan 
12009af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1201c577b8a1SJoseph Chan 	.substreams = 1,
1202c577b8a1SJoseph Chan 	.channels_min = 2,
1203c577b8a1SJoseph Chan 	.channels_max = 2,
1204c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1205c577b8a1SJoseph Chan 	.ops = {
1206c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
12076b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
12089da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
12099da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1210c577b8a1SJoseph Chan 	},
1211c577b8a1SJoseph Chan };
1212c577b8a1SJoseph Chan 
12139af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1214c577b8a1SJoseph Chan 	.substreams = 1,
1215c577b8a1SJoseph Chan 	.channels_min = 2,
1216c577b8a1SJoseph Chan 	.channels_max = 2,
1217c577b8a1SJoseph Chan };
1218c577b8a1SJoseph Chan 
1219370bafbdSTakashi Iwai /*
1220370bafbdSTakashi Iwai  * slave controls for virtual master
1221370bafbdSTakashi Iwai  */
1222370bafbdSTakashi Iwai static const char * const via_slave_vols[] = {
1223370bafbdSTakashi Iwai 	"Front Playback Volume",
1224370bafbdSTakashi Iwai 	"Surround Playback Volume",
1225370bafbdSTakashi Iwai 	"Center Playback Volume",
1226370bafbdSTakashi Iwai 	"LFE Playback Volume",
1227370bafbdSTakashi Iwai 	"Side Playback Volume",
1228370bafbdSTakashi Iwai 	"Headphone Playback Volume",
1229370bafbdSTakashi Iwai 	"Speaker Playback Volume",
1230370bafbdSTakashi Iwai 	NULL,
1231370bafbdSTakashi Iwai };
1232370bafbdSTakashi Iwai 
1233370bafbdSTakashi Iwai static const char * const via_slave_sws[] = {
1234370bafbdSTakashi Iwai 	"Front Playback Switch",
1235370bafbdSTakashi Iwai 	"Surround Playback Switch",
1236370bafbdSTakashi Iwai 	"Center Playback Switch",
1237370bafbdSTakashi Iwai 	"LFE Playback Switch",
1238370bafbdSTakashi Iwai 	"Side Playback Switch",
1239370bafbdSTakashi Iwai 	"Headphone Playback Switch",
1240370bafbdSTakashi Iwai 	"Speaker Playback Switch",
1241370bafbdSTakashi Iwai 	NULL,
1242370bafbdSTakashi Iwai };
1243370bafbdSTakashi Iwai 
1244c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1245c577b8a1SJoseph Chan {
1246c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
12475b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
12485b0cb1d8SJaroslav Kysela 	int err, i;
1249c577b8a1SJoseph Chan 
125024088a58STakashi Iwai 	if (spec->set_widgets_power_state)
125124088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
125224088a58STakashi Iwai 			return -ENOMEM;
125324088a58STakashi Iwai 
1254c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1255c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1256c577b8a1SJoseph Chan 		if (err < 0)
1257c577b8a1SJoseph Chan 			return err;
1258c577b8a1SJoseph Chan 	}
1259c577b8a1SJoseph Chan 
1260c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1261c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
126274b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1263c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1264c577b8a1SJoseph Chan 		if (err < 0)
1265c577b8a1SJoseph Chan 			return err;
12669a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
12679a08160bSTakashi Iwai 						    &spec->multiout);
12689a08160bSTakashi Iwai 		if (err < 0)
12699a08160bSTakashi Iwai 			return err;
12709a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1271c577b8a1SJoseph Chan 	}
1272c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1273c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1274c577b8a1SJoseph Chan 		if (err < 0)
1275c577b8a1SJoseph Chan 			return err;
1276c577b8a1SJoseph Chan 	}
127717314379SLydia Wang 
1278370bafbdSTakashi Iwai 	/* if we have no master control, let's create it */
1279370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1280370bafbdSTakashi Iwai 		unsigned int vmaster_tlv[4];
1281370bafbdSTakashi Iwai 		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1282370bafbdSTakashi Iwai 					HDA_OUTPUT, vmaster_tlv);
1283370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1284370bafbdSTakashi Iwai 					  vmaster_tlv, via_slave_vols);
1285370bafbdSTakashi Iwai 		if (err < 0)
1286370bafbdSTakashi Iwai 			return err;
1287370bafbdSTakashi Iwai 	}
1288370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1289370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1290370bafbdSTakashi Iwai 					  NULL, via_slave_sws);
1291370bafbdSTakashi Iwai 		if (err < 0)
1292370bafbdSTakashi Iwai 			return err;
1293370bafbdSTakashi Iwai 	}
1294370bafbdSTakashi Iwai 
12955b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
12965b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
12975b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
129821949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
12995b0cb1d8SJaroslav Kysela 		if (err < 0)
13005b0cb1d8SJaroslav Kysela 			return err;
13015b0cb1d8SJaroslav Kysela 	}
13025b0cb1d8SJaroslav Kysela 
130317314379SLydia Wang 	/* init power states */
13043e95b9abSLydia Wang 	set_widgets_power_state(codec);
1305ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
130617314379SLydia Wang 
1307603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1308c577b8a1SJoseph Chan 	return 0;
1309c577b8a1SJoseph Chan }
1310c577b8a1SJoseph Chan 
1311c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1312c577b8a1SJoseph Chan {
1313c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1314c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1315c577b8a1SJoseph Chan 
1316c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1317c577b8a1SJoseph Chan 	codec->pcm_info = info;
1318c577b8a1SJoseph Chan 
131982673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
132082673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1321c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
13229af74210STakashi Iwai 
13239af74210STakashi Iwai 	if (!spec->stream_analog_playback)
13249af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1325377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
13269af74210STakashi Iwai 		*spec->stream_analog_playback;
1327377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1328377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1329c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1330c577b8a1SJoseph Chan 		spec->multiout.max_channels;
13319af74210STakashi Iwai 
13329af74210STakashi Iwai 	if (!spec->stream_analog_capture)
13339af74210STakashi Iwai 		spec->stream_analog_capture = &via_pcm_analog_capture;
13349af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
13359af74210STakashi Iwai 		*spec->stream_analog_capture;
13369af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
13379af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
13389af74210STakashi Iwai 		spec->num_adc_nids;
1339c577b8a1SJoseph Chan 
1340c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1341c577b8a1SJoseph Chan 		codec->num_pcms++;
1342c577b8a1SJoseph Chan 		info++;
134382673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
134482673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
134582673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1346c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
13477ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1348c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
13499af74210STakashi Iwai 			if (!spec->stream_digital_playback)
13509af74210STakashi Iwai 				spec->stream_digital_playback =
13519af74210STakashi Iwai 					&via_pcm_digital_playback;
1352c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
13539af74210STakashi Iwai 				*spec->stream_digital_playback;
1354c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1355c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1356c577b8a1SJoseph Chan 		}
1357c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
13589af74210STakashi Iwai 			if (!spec->stream_digital_capture)
13599af74210STakashi Iwai 				spec->stream_digital_capture =
13609af74210STakashi Iwai 					&via_pcm_digital_capture;
1361c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
13629af74210STakashi Iwai 				*spec->stream_digital_capture;
1363c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1364c577b8a1SJoseph Chan 				spec->dig_in_nid;
1365c577b8a1SJoseph Chan 		}
1366c577b8a1SJoseph Chan 	}
1367c577b8a1SJoseph Chan 
1368ece8d043STakashi Iwai 	if (spec->hp_dac_nid) {
13697eb56e84STakashi Iwai 		codec->num_pcms++;
13707eb56e84STakashi Iwai 		info++;
13717eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
13727eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
13737eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
13747eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
13757eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1376ece8d043STakashi Iwai 			spec->hp_dac_nid;
13777eb56e84STakashi Iwai 	}
1378c577b8a1SJoseph Chan 	return 0;
1379c577b8a1SJoseph Chan }
1380c577b8a1SJoseph Chan 
1381c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1382c577b8a1SJoseph Chan {
1383c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1384c577b8a1SJoseph Chan 
1385c577b8a1SJoseph Chan 	if (!spec)
1386c577b8a1SJoseph Chan 		return;
1387c577b8a1SJoseph Chan 
1388603c4019STakashi Iwai 	via_free_kctls(codec);
13891f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1390c577b8a1SJoseph Chan 	kfree(codec->spec);
1391c577b8a1SJoseph Chan }
1392c577b8a1SJoseph Chan 
139364be285bSTakashi Iwai /* mute/unmute outputs */
139464be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
139564be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
139664be285bSTakashi Iwai {
139764be285bSTakashi Iwai 	int i;
139864be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
139964be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
140064be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
140164be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
140264be285bSTakashi Iwai }
140364be285bSTakashi Iwai 
14044a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */
14054a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present)
14064a918ffeSTakashi Iwai {
14074a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
14084a918ffeSTakashi Iwai 
14094a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs)
14104a918ffeSTakashi Iwai 		return;
14114a918ffeSTakashi Iwai 	if (!present)
14124a918ffeSTakashi Iwai 		present = snd_hda_jack_detect(codec,
14134a918ffeSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
14144a918ffeSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
14154a918ffeSTakashi Iwai 			    spec->autocfg.speaker_pins,
14164a918ffeSTakashi Iwai 			    present);
14174a918ffeSTakashi Iwai }
14184a918ffeSTakashi Iwai 
141969e52a80SHarald Welte /* mute internal speaker if HP is plugged */
142069e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
142169e52a80SHarald Welte {
14224a918ffeSTakashi Iwai 	int present = 0;
142369e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
142469e52a80SHarald Welte 
14254a918ffeSTakashi Iwai 	if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1426d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
142764be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
142864be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
142964be285bSTakashi Iwai 				    present);
143069e52a80SHarald Welte 	}
14314a918ffeSTakashi Iwai 	via_line_automute(codec, present);
1432f3db423dSLydia Wang }
1433f3db423dSLydia Wang 
143469e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
143569e52a80SHarald Welte {
143669e52a80SHarald Welte 	unsigned int gpio_data;
143769e52a80SHarald Welte 	unsigned int vol_counter;
143869e52a80SHarald Welte 	unsigned int vol;
143969e52a80SHarald Welte 	unsigned int master_vol;
144069e52a80SHarald Welte 
144169e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
144269e52a80SHarald Welte 
144369e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
144469e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
144569e52a80SHarald Welte 
144669e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
144769e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
144869e52a80SHarald Welte 
144969e52a80SHarald Welte 	vol = vol_counter & 0x1F;
145069e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
145169e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
145269e52a80SHarald Welte 					AC_AMP_GET_INPUT);
145369e52a80SHarald Welte 
145469e52a80SHarald Welte 	if (gpio_data == 0x02) {
145569e52a80SHarald Welte 		/* unmute line out */
14563e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
14573e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
14583e0693e2STakashi Iwai 				    PIN_OUT);
145969e52a80SHarald Welte 		if (vol_counter & 0x20) {
146069e52a80SHarald Welte 			/* decrease volume */
146169e52a80SHarald Welte 			if (vol > master_vol)
146269e52a80SHarald Welte 				vol = master_vol;
146369e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
146469e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
146569e52a80SHarald Welte 						 master_vol-vol);
146669e52a80SHarald Welte 		} else {
146769e52a80SHarald Welte 			/* increase volume */
146869e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
146969e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
147069e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
147169e52a80SHarald Welte 					  (master_vol+vol));
147269e52a80SHarald Welte 		}
147369e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
147469e52a80SHarald Welte 		/* mute line out */
14753e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
14763e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
14773e0693e2STakashi Iwai 				    0);
147869e52a80SHarald Welte 	}
147969e52a80SHarald Welte }
148069e52a80SHarald Welte 
148169e52a80SHarald Welte /* unsolicited event for jack sensing */
148269e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
148369e52a80SHarald Welte 				  unsigned int res)
148469e52a80SHarald Welte {
148569e52a80SHarald Welte 	res >>= 26;
1486ec7e7e42SLydia Wang 
1487a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
14883e95b9abSLydia Wang 		set_widgets_power_state(codec);
1489ec7e7e42SLydia Wang 
1490ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1491ec7e7e42SLydia Wang 
1492ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1493ec7e7e42SLydia Wang 		via_hp_automute(codec);
1494ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1495ec7e7e42SLydia Wang 		via_gpio_control(codec);
14964a918ffeSTakashi Iwai 	else if (res == VIA_LINE_EVENT)
14974a918ffeSTakashi Iwai 		via_line_automute(codec, false);
149869e52a80SHarald Welte }
149969e52a80SHarald Welte 
15001f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
15011f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
15021f2e99feSLydia Wang {
15031f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
15041f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
15051f2e99feSLydia Wang 	return 0;
15061f2e99feSLydia Wang }
15071f2e99feSLydia Wang #endif
15081f2e99feSLydia Wang 
1509cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1510cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1511cb53c626STakashi Iwai {
1512cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1513cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1514cb53c626STakashi Iwai }
1515cb53c626STakashi Iwai #endif
1516cb53c626STakashi Iwai 
1517c577b8a1SJoseph Chan /*
1518c577b8a1SJoseph Chan  */
15195d41762aSTakashi Iwai 
15205d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
15215d41762aSTakashi Iwai 
152290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1523c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1524c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1525c577b8a1SJoseph Chan 	.init = via_init,
1526c577b8a1SJoseph Chan 	.free = via_free,
15274a918ffeSTakashi Iwai 	.unsol_event = via_unsol_event,
15281f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
15291f2e99feSLydia Wang 	.suspend = via_suspend,
15301f2e99feSLydia Wang #endif
1531cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1532cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1533cb53c626STakashi Iwai #endif
1534c577b8a1SJoseph Chan };
1535c577b8a1SJoseph Chan 
15364a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1537c577b8a1SJoseph Chan {
15384a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
15394a79616dSTakashi Iwai 	int i;
15404a79616dSTakashi Iwai 
15414a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
15424a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
15434a79616dSTakashi Iwai 			return false;
15444a79616dSTakashi Iwai 	}
1545ece8d043STakashi Iwai 	if (spec->hp_dac_nid == dac)
15464a79616dSTakashi Iwai 		return false;
15474a79616dSTakashi Iwai 	return true;
15484a79616dSTakashi Iwai }
15494a79616dSTakashi Iwai 
15508e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
15514a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
15524a79616dSTakashi Iwai 			      int depth, int wid_type)
15534a79616dSTakashi Iwai {
15544a79616dSTakashi Iwai 	hda_nid_t conn[8];
15554a79616dSTakashi Iwai 	int i, nums;
15564a79616dSTakashi Iwai 
15574a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
15584a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
15594a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
15604a79616dSTakashi Iwai 			continue;
15614a79616dSTakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
15628e3679dcSTakashi Iwai 			path->path[0] = conn[i];
15638e3679dcSTakashi Iwai 			path->idx[0] = i;
15648e3679dcSTakashi Iwai 			path->depth = 1;
15654a79616dSTakashi Iwai 			return true;
15664a79616dSTakashi Iwai 		}
15674a79616dSTakashi Iwai 	}
15688e3679dcSTakashi Iwai 	if (depth >= MAX_NID_PATH_DEPTH)
15694a79616dSTakashi Iwai 		return false;
15704a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
15714a79616dSTakashi Iwai 		unsigned int type;
15724a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
15734a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
15744a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
15754a79616dSTakashi Iwai 			continue;
15768e3679dcSTakashi Iwai 		if (__parse_output_path(codec, conn[i], target_dac,
15774a79616dSTakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL)) {
15788e3679dcSTakashi Iwai 			path->path[path->depth] = conn[i];
15798e3679dcSTakashi Iwai 			path->idx[path->depth] = i;
15808e3679dcSTakashi Iwai 			path->depth++;
15814a79616dSTakashi Iwai 			return true;
15824a79616dSTakashi Iwai 		}
15834a79616dSTakashi Iwai 	}
15844a79616dSTakashi Iwai 	return false;
15854a79616dSTakashi Iwai }
15864a79616dSTakashi Iwai 
15878e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
15888e3679dcSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path)
15898e3679dcSTakashi Iwai {
15908e3679dcSTakashi Iwai 	if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
15918e3679dcSTakashi Iwai 		path->path[path->depth] = nid;
15928e3679dcSTakashi Iwai 		path->depth++;
15938e3679dcSTakashi Iwai 		return true;
15948e3679dcSTakashi Iwai 	}
15958e3679dcSTakashi Iwai 	return false;
15968e3679dcSTakashi Iwai }
15978e3679dcSTakashi Iwai 
15984a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
15994a79616dSTakashi Iwai {
16004a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
16014a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1602c577b8a1SJoseph Chan 	int i;
1603c577b8a1SJoseph Chan 	hda_nid_t nid;
1604c577b8a1SJoseph Chan 
1605c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
16064a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
16074a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1608c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
16094a79616dSTakashi Iwai 		if (!nid)
16104a79616dSTakashi Iwai 			continue;
16118e3679dcSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
16128e3679dcSTakashi Iwai 			spec->private_dac_nids[i] = spec->out_path[i].path[0];
1613c577b8a1SJoseph Chan 	}
1614c577b8a1SJoseph Chan 	return 0;
1615c577b8a1SJoseph Chan }
1616c577b8a1SJoseph Chan 
16174a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
16184a79616dSTakashi Iwai 			  hda_nid_t pin, hda_nid_t dac, int chs)
1619c577b8a1SJoseph Chan {
16204a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1621c577b8a1SJoseph Chan 	char name[32];
16224a79616dSTakashi Iwai 	hda_nid_t nid;
16234a79616dSTakashi Iwai 	int err;
1624c577b8a1SJoseph Chan 
16254a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
16264a79616dSTakashi Iwai 		nid = dac;
16274a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
16284a79616dSTakashi Iwai 		nid = pin;
16294a79616dSTakashi Iwai 	else
16304a79616dSTakashi Iwai 		nid = 0;
16314a79616dSTakashi Iwai 	if (nid) {
16324a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1633c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1634*a00a5fadSLydia Wang 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1635c577b8a1SJoseph Chan 		if (err < 0)
1636c577b8a1SJoseph Chan 			return err;
1637c577b8a1SJoseph Chan 	}
16384a79616dSTakashi Iwai 
16394a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
16404a79616dSTakashi Iwai 		nid = dac;
16414a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
16424a79616dSTakashi Iwai 		nid = pin;
16434a79616dSTakashi Iwai 	else
16444a79616dSTakashi Iwai 		nid = 0;
16454a79616dSTakashi Iwai 	if (nid) {
16464a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
16474a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
16484a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
16494a79616dSTakashi Iwai 		if (err < 0)
16504a79616dSTakashi Iwai 			return err;
16514a79616dSTakashi Iwai 	}
16524a79616dSTakashi Iwai 	return 0;
16534a79616dSTakashi Iwai }
16544a79616dSTakashi Iwai 
1655f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1656f4a7828bSTakashi Iwai {
1657f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1658f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
1659e3d7a143STakashi Iwai 	int i, nums = 0;
1660f4a7828bSTakashi Iwai 
1661f4a7828bSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1662e3d7a143STakashi Iwai 		if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1663e3d7a143STakashi Iwai 			nums++;
1664e3d7a143STakashi Iwai 	}
1665e3d7a143STakashi Iwai 	if (cfg->line_outs + nums < 3)
1666e3d7a143STakashi Iwai 		return;
1667e3d7a143STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1668e3d7a143STakashi Iwai 		if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
1669f4a7828bSTakashi Iwai 			continue;
1670e3d7a143STakashi Iwai 		spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
1671f4a7828bSTakashi Iwai 		cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1672f4a7828bSTakashi Iwai 		if (cfg->line_outs == 3)
1673f4a7828bSTakashi Iwai 			break;
1674f4a7828bSTakashi Iwai 	}
1675f4a7828bSTakashi Iwai }
1676f4a7828bSTakashi Iwai 
16774a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
16784a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
16794a79616dSTakashi Iwai {
16804a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1681f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
16824a79616dSTakashi Iwai 	static const char * const chname[4] = {
16834a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
16844a79616dSTakashi Iwai 	};
16854a79616dSTakashi Iwai 	int i, idx, err;
1686f4a7828bSTakashi Iwai 	int old_line_outs;
1687f4a7828bSTakashi Iwai 
1688f4a7828bSTakashi Iwai 	/* check smart51 */
1689f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
1690f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
1691f4a7828bSTakashi Iwai 		mangle_smart51(codec);
16924a79616dSTakashi Iwai 
1693e3d7a143STakashi Iwai 	err = via_auto_fill_dac_nids(codec);
1694e3d7a143STakashi Iwai 	if (err < 0)
1695e3d7a143STakashi Iwai 		return err;
1696e3d7a143STakashi Iwai 
16974a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
16984a79616dSTakashi Iwai 		hda_nid_t pin, dac;
16994a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
17004a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
17014a79616dSTakashi Iwai 		if (!pin || !dac)
17024a79616dSTakashi Iwai 			continue;
17030fe0adf8STakashi Iwai 		if (i == HDA_CLFE) {
17044a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "Center", pin, dac, 1);
17054a79616dSTakashi Iwai 			if (err < 0)
17064a79616dSTakashi Iwai 				return err;
17074a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "LFE", pin, dac, 2);
17084a79616dSTakashi Iwai 			if (err < 0)
17094a79616dSTakashi Iwai 				return err;
17104a79616dSTakashi Iwai 		} else {
17116aadf41dSTakashi Iwai 			const char *pfx = chname[i];
17126aadf41dSTakashi Iwai 			if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
17136aadf41dSTakashi Iwai 			    cfg->line_outs == 1)
17146aadf41dSTakashi Iwai 				pfx = "Speaker";
17156aadf41dSTakashi Iwai 			err = create_ch_ctls(codec, pfx, pin, dac, 3);
17164a79616dSTakashi Iwai 			if (err < 0)
17174a79616dSTakashi Iwai 				return err;
17184a79616dSTakashi Iwai 		}
17194a79616dSTakashi Iwai 	}
17204a79616dSTakashi Iwai 
17214a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
17224a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
17234a79616dSTakashi Iwai 	if (idx >= 0) {
17244a79616dSTakashi Iwai 		/* add control to mixer */
17254a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
17264a79616dSTakashi Iwai 				      "PCM Playback Volume",
17274a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17284a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17294a79616dSTakashi Iwai 		if (err < 0)
17304a79616dSTakashi Iwai 			return err;
17314a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
17324a79616dSTakashi Iwai 				      "PCM Playback Switch",
17334a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17344a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17354a79616dSTakashi Iwai 		if (err < 0)
17364a79616dSTakashi Iwai 			return err;
1737c577b8a1SJoseph Chan 	}
1738c577b8a1SJoseph Chan 
1739f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
1740f4a7828bSTakashi Iwai 
1741c577b8a1SJoseph Chan 	return 0;
1742c577b8a1SJoseph Chan }
1743c577b8a1SJoseph Chan 
17440aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
17450aa62aefSHarald Welte {
17460aa62aefSHarald Welte 	int i;
17470aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
1748ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
17490aa62aefSHarald Welte 
17500aa62aefSHarald Welte 	/* for hp mode select */
175110a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
175210a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
17530aa62aefSHarald Welte 
17540aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
17550aa62aefSHarald Welte }
17560aa62aefSHarald Welte 
17574a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
1758c577b8a1SJoseph Chan {
17594a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1760c577b8a1SJoseph Chan 	int err;
1761c577b8a1SJoseph Chan 
1762c577b8a1SJoseph Chan 	if (!pin)
1763c577b8a1SJoseph Chan 		return 0;
1764c577b8a1SJoseph Chan 
17658e3679dcSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path)) {
17668e3679dcSTakashi Iwai 		spec->hp_dac_nid = spec->hp_path.path[0];
17678e3679dcSTakashi Iwai 		spec->hp_independent_mode_index = spec->hp_path.idx[0];
17680aa62aefSHarald Welte 		create_hp_imux(spec);
17694a79616dSTakashi Iwai 	}
17704a79616dSTakashi Iwai 
1771ece8d043STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
17728e3679dcSTakashi Iwai 			       &spec->hp_dep_path) &&
1773ece8d043STakashi Iwai 	    !spec->hp_dac_nid)
1774ece8d043STakashi Iwai 		return 0;
1775ece8d043STakashi Iwai 
1776ece8d043STakashi Iwai 
1777ece8d043STakashi Iwai 	err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
17784a79616dSTakashi Iwai 	if (err < 0)
17794a79616dSTakashi Iwai 		return err;
17800aa62aefSHarald Welte 
1781c577b8a1SJoseph Chan 	return 0;
1782c577b8a1SJoseph Chan }
1783c577b8a1SJoseph Chan 
17844a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec)
17854a918ffeSTakashi Iwai {
17864a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
17874a918ffeSTakashi Iwai 	hda_nid_t pin, dac;
17884a918ffeSTakashi Iwai 
17894a918ffeSTakashi Iwai 	pin = spec->autocfg.speaker_pins[0];
17904a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs || !pin)
17914a918ffeSTakashi Iwai 		return 0;
17924a918ffeSTakashi Iwai 
17938e3679dcSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
17948e3679dcSTakashi Iwai 		dac = spec->speaker_path.path[0];
17954a918ffeSTakashi Iwai 		spec->multiout.extra_out_nid[0] = dac;
17964a918ffeSTakashi Iwai 		return create_ch_ctls(codec, "Speaker", pin, dac, 3);
17974a918ffeSTakashi Iwai 	}
17984a918ffeSTakashi Iwai 	if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
17998e3679dcSTakashi Iwai 			      &spec->speaker_path))
18008e3679dcSTakashi Iwai 		return create_ch_ctls(codec, "Speaker", pin, 0, 3);
18014a918ffeSTakashi Iwai 
18024a918ffeSTakashi Iwai 	return 0;
18034a918ffeSTakashi Iwai }
18044a918ffeSTakashi Iwai 
1805a766d0d7STakashi Iwai /* look for ADCs */
1806a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
1807a766d0d7STakashi Iwai {
1808a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
1809a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
1810a766d0d7STakashi Iwai 	int i;
1811a766d0d7STakashi Iwai 
1812a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
1813a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
1814a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1815a766d0d7STakashi Iwai 			continue;
1816a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
1817a766d0d7STakashi Iwai 			continue;
1818a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
1819a766d0d7STakashi Iwai 			continue;
1820a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1821a766d0d7STakashi Iwai 			return -ENOMEM;
1822a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
1823a766d0d7STakashi Iwai 	}
1824a766d0d7STakashi Iwai 	return 0;
1825a766d0d7STakashi Iwai }
1826a766d0d7STakashi Iwai 
1827a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
1828a766d0d7STakashi Iwai 
1829d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
1830d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1831d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
1832d7a99cceSTakashi Iwai 	 * So call somewhat different..
1833d7a99cceSTakashi Iwai 	 */
1834d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
1835d7a99cceSTakashi Iwai 	.name = "Input Source",
1836d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
1837d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
1838d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
1839d7a99cceSTakashi Iwai };
1840d7a99cceSTakashi Iwai 
184113af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
184213af8e77STakashi Iwai {
184313af8e77STakashi Iwai 	struct hda_amp_list *list;
184413af8e77STakashi Iwai 
184513af8e77STakashi Iwai 	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
184613af8e77STakashi Iwai 		return;
184713af8e77STakashi Iwai 	list = spec->loopback_list + spec->num_loopbacks;
184813af8e77STakashi Iwai 	list->nid = mix;
184913af8e77STakashi Iwai 	list->dir = HDA_INPUT;
185013af8e77STakashi Iwai 	list->idx = idx;
185113af8e77STakashi Iwai 	spec->num_loopbacks++;
185213af8e77STakashi Iwai 	spec->loopback.amplist = spec->loopback_list;
185313af8e77STakashi Iwai }
185413af8e77STakashi Iwai 
1855c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
1856620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1857620e2b28STakashi Iwai 					     const struct auto_pin_cfg *cfg)
1858c577b8a1SJoseph Chan {
185910a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
18600aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
1861e3d7a143STakashi Iwai 	int i, j, err, idx, idx2, type, type_idx = 0;
1862a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
1863a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
1864a766d0d7STakashi Iwai 	int num_idxs;
1865a766d0d7STakashi Iwai 
1866a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
1867a766d0d7STakashi Iwai 	if (err < 0)
1868a766d0d7STakashi Iwai 		return err;
1869a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
1870a766d0d7STakashi Iwai 	if (err < 0)
1871a766d0d7STakashi Iwai 		return err;
1872a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
1873a766d0d7STakashi Iwai 
1874a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1875a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
1876a766d0d7STakashi Iwai 	if (num_idxs <= 0)
1877a766d0d7STakashi Iwai 		return 0;
1878c577b8a1SJoseph Chan 
1879c577b8a1SJoseph Chan 	/* for internal loopback recording select */
1880f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
1881620e2b28STakashi Iwai 		if (pin_idxs[idx] == spec->aa_mix_nid) {
188210a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
1883f3268512STakashi Iwai 			break;
1884f3268512STakashi Iwai 		}
1885f3268512STakashi Iwai 	}
1886c577b8a1SJoseph Chan 
18877b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
188810a20af7STakashi Iwai 		const char *label;
18897b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
1890f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
18917b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
1892c577b8a1SJoseph Chan 				break;
1893f3268512STakashi Iwai 		if (idx >= num_idxs)
1894f3268512STakashi Iwai 			continue;
18957b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
18967b315bb4STakashi Iwai 			type_idx++;
18977b315bb4STakashi Iwai 		else
18987b315bb4STakashi Iwai 			type_idx = 0;
189910a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
1900620e2b28STakashi Iwai 		idx2 = get_connection_index(codec, spec->aa_mix_nid,
1901620e2b28STakashi Iwai 					    pin_idxs[idx]);
190213af8e77STakashi Iwai 		if (idx2 >= 0) {
190316922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
1904620e2b28STakashi Iwai 						   idx2, spec->aa_mix_nid);
1905c577b8a1SJoseph Chan 			if (err < 0)
1906c577b8a1SJoseph Chan 				return err;
190713af8e77STakashi Iwai 			add_loopback_list(spec, spec->aa_mix_nid, idx2);
190813af8e77STakashi Iwai 		}
190910a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
1910e3d7a143STakashi Iwai 
1911e3d7a143STakashi Iwai 		/* remember the label for smart51 control */
1912e3d7a143STakashi Iwai 		for (j = 0; j < spec->smart51_nums; j++) {
1913e3d7a143STakashi Iwai 			if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1914e3d7a143STakashi Iwai 				spec->smart51_idxs[j] = idx;
1915e3d7a143STakashi Iwai 				spec->smart51_labels[j] = label;
1916e3d7a143STakashi Iwai 				break;
1917e3d7a143STakashi Iwai 			}
1918e3d7a143STakashi Iwai 		}
1919c577b8a1SJoseph Chan 	}
1920d7a99cceSTakashi Iwai 
1921d7a99cceSTakashi Iwai 	/* create capture mixer elements */
1922d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
1923d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
1924d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1925d7a99cceSTakashi Iwai 					"Capture Volume", i,
1926d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1927d7a99cceSTakashi Iwai 							    HDA_INPUT));
1928d7a99cceSTakashi Iwai 		if (err < 0)
1929d7a99cceSTakashi Iwai 			return err;
1930d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1931d7a99cceSTakashi Iwai 					"Capture Switch", i,
1932d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1933d7a99cceSTakashi Iwai 							    HDA_INPUT));
1934d7a99cceSTakashi Iwai 		if (err < 0)
1935d7a99cceSTakashi Iwai 			return err;
1936d7a99cceSTakashi Iwai 	}
1937d7a99cceSTakashi Iwai 
1938d7a99cceSTakashi Iwai 	/* input-source control */
1939d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
1940d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
1941d7a99cceSTakashi Iwai 			break;
1942d7a99cceSTakashi Iwai 	if (i) {
1943d7a99cceSTakashi Iwai 		struct snd_kcontrol_new *knew;
1944d7a99cceSTakashi Iwai 		knew = via_clone_control(spec, &via_input_src_ctl);
1945d7a99cceSTakashi Iwai 		if (!knew)
1946d7a99cceSTakashi Iwai 			return -ENOMEM;
1947d7a99cceSTakashi Iwai 		knew->count = i;
1948d7a99cceSTakashi Iwai 	}
1949d7a99cceSTakashi Iwai 
1950d7a99cceSTakashi Iwai 	/* mic-boosts */
1951d7a99cceSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1952d7a99cceSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
1953d7a99cceSTakashi Iwai 		unsigned int caps;
1954d7a99cceSTakashi Iwai 		const char *label;
1955d7a99cceSTakashi Iwai 		char name[32];
1956d7a99cceSTakashi Iwai 
1957d7a99cceSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
1958d7a99cceSTakashi Iwai 			continue;
1959d7a99cceSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
1960d7a99cceSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1961d7a99cceSTakashi Iwai 			continue;
1962d7a99cceSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
196330f7c5d4STakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Volume", label);
1964d7a99cceSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1965d7a99cceSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1966d7a99cceSTakashi Iwai 		if (err < 0)
1967d7a99cceSTakashi Iwai 			return err;
1968d7a99cceSTakashi Iwai 	}
1969d7a99cceSTakashi Iwai 
1970c577b8a1SJoseph Chan 	return 0;
1971c577b8a1SJoseph Chan }
1972c577b8a1SJoseph Chan 
197376d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
197476d9b0ddSHarald Welte {
197576d9b0ddSHarald Welte 	unsigned int def_conf;
197676d9b0ddSHarald Welte 	unsigned char seqassoc;
197776d9b0ddSHarald Welte 
19782f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
197976d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
198076d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
198182ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
198282ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
198376d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
19842f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
198576d9b0ddSHarald Welte 	}
198676d9b0ddSHarald Welte 
198776d9b0ddSHarald Welte 	return;
198876d9b0ddSHarald Welte }
198976d9b0ddSHarald Welte 
1990e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
19911f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
19921f2e99feSLydia Wang {
19931f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
19941f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
19951f2e99feSLydia Wang 
19961f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
19971f2e99feSLydia Wang 		return 0;
1998e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
19991f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2000e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
20011f2e99feSLydia Wang 	return 0;
20021f2e99feSLydia Wang }
20031f2e99feSLydia Wang 
2004e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
20051f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20061f2e99feSLydia Wang {
20071f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20081f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20091f2e99feSLydia Wang 	int change;
20101f2e99feSLydia Wang 
20111f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20121f2e99feSLydia Wang 		return 0;
2013e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
20141f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2015e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2016e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
20171f2e99feSLydia Wang 		mute_aa_path(codec, 1);
20181f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
20191f2e99feSLydia Wang 	}
20201f2e99feSLydia Wang 	return change;
20211f2e99feSLydia Wang }
20221f2e99feSLydia Wang 
2023e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
20241f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
20251f2e99feSLydia Wang 	.name = "Jack Detect",
20261f2e99feSLydia Wang 	.count = 1,
20271f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2028e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2029e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
20301f2e99feSLydia Wang };
20311f2e99feSLydia Wang 
203212daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec);
203312daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec);
203412daef65STakashi Iwai 
203512daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
2036c577b8a1SJoseph Chan {
2037c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2038c577b8a1SJoseph Chan 	int err;
2039c577b8a1SJoseph Chan 
2040c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2041c577b8a1SJoseph Chan 	if (err < 0)
2042c577b8a1SJoseph Chan 		return err;
2043c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
20447f0df88cSTakashi Iwai 		return -EINVAL;
2045c577b8a1SJoseph Chan 
20464a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2047c577b8a1SJoseph Chan 	if (err < 0)
2048c577b8a1SJoseph Chan 		return err;
20494a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2050c577b8a1SJoseph Chan 	if (err < 0)
2051c577b8a1SJoseph Chan 		return err;
20524a918ffeSTakashi Iwai 	err = via_auto_create_speaker_ctls(codec);
20534a918ffeSTakashi Iwai 	if (err < 0)
20544a918ffeSTakashi Iwai 		return err;
2055620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2056c577b8a1SJoseph Chan 	if (err < 0)
2057c577b8a1SJoseph Chan 		return err;
2058c577b8a1SJoseph Chan 
2059c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2060c577b8a1SJoseph Chan 
206112daef65STakashi Iwai 	fill_dig_outs(codec);
206212daef65STakashi Iwai 	fill_dig_in(codec);
2063c577b8a1SJoseph Chan 
2064603c4019STakashi Iwai 	if (spec->kctls.list)
2065603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2066c577b8a1SJoseph Chan 
2067096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2068c577b8a1SJoseph Chan 
20690aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
20700aa62aefSHarald Welte 
2071ece8d043STakashi Iwai 	if (spec->hp_mux) {
2072ece8d043STakashi Iwai 		err = via_hp_build(codec);
2073ece8d043STakashi Iwai 		if (err < 0)
2074ece8d043STakashi Iwai 			return err;
2075ece8d043STakashi Iwai 	}
2076c577b8a1SJoseph Chan 
2077f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2078f4a7828bSTakashi Iwai 	if (err < 0)
2079f4a7828bSTakashi Iwai 		return err;
2080f4a7828bSTakashi Iwai 
20815d41762aSTakashi Iwai 	/* assign slave outs */
20825d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
20835d41762aSTakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
20845d41762aSTakashi Iwai 
2085c577b8a1SJoseph Chan 	return 1;
2086c577b8a1SJoseph Chan }
2087c577b8a1SJoseph Chan 
20885d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec)
2089c577b8a1SJoseph Chan {
209025eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
20915d41762aSTakashi Iwai 	if (spec->multiout.dig_out_nid)
20925d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
20935d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
20945d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
20955d41762aSTakashi Iwai }
209625eaba2fSLydia Wang 
20975d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec)
20985d41762aSTakashi Iwai {
20995d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
21005d41762aSTakashi Iwai 	if (!spec->dig_in_nid)
21015d41762aSTakashi Iwai 		return;
21025d41762aSTakashi Iwai 	snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
21035d41762aSTakashi Iwai 			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
21045d41762aSTakashi Iwai }
21055d41762aSTakashi Iwai 
21064a918ffeSTakashi Iwai /* initialize the unsolicited events */
21074a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec)
21084a918ffeSTakashi Iwai {
21094a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
21104a918ffeSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
21114a918ffeSTakashi Iwai 	unsigned int ev;
21124a918ffeSTakashi Iwai 	int i;
21134a918ffeSTakashi Iwai 
21144a918ffeSTakashi Iwai 	if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
21154a918ffeSTakashi Iwai 		snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
21164a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21174a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
21184a918ffeSTakashi Iwai 
21194a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
21204a918ffeSTakashi Iwai 		ev = VIA_LINE_EVENT;
21214a918ffeSTakashi Iwai 	else
21224a918ffeSTakashi Iwai 		ev = 0;
21234a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
21244a918ffeSTakashi Iwai 		if (cfg->line_out_pins[i] &&
21254a918ffeSTakashi Iwai 		    is_jack_detectable(codec, cfg->line_out_pins[i]))
21264a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
21274a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21284a918ffeSTakashi Iwai 				AC_USRSP_EN | ev | VIA_JACK_EVENT);
21294a918ffeSTakashi Iwai 	}
21304a918ffeSTakashi Iwai 
21314a918ffeSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
21324a918ffeSTakashi Iwai 		if (is_jack_detectable(codec, cfg->inputs[i].pin))
21334a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
21344a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21354a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_JACK_EVENT);
21364a918ffeSTakashi Iwai 	}
21374a918ffeSTakashi Iwai }
21384a918ffeSTakashi Iwai 
21395d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
21405d41762aSTakashi Iwai {
21415d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
21425d41762aSTakashi Iwai 	int i;
21435d41762aSTakashi Iwai 
21445d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
21455d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
21465d41762aSTakashi Iwai 
2147c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2148c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
21494a918ffeSTakashi Iwai 	via_auto_init_speaker_out(codec);
2150c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
21515d41762aSTakashi Iwai 	via_auto_init_dig_outs(codec);
21525d41762aSTakashi Iwai 	via_auto_init_dig_in(codec);
215311890956SLydia Wang 
21544a918ffeSTakashi Iwai 	via_auto_init_unsol_event(codec);
21554a918ffeSTakashi Iwai 
215625eaba2fSLydia Wang 	via_hp_automute(codec);
21574a918ffeSTakashi Iwai 	via_line_automute(codec, false);
215825eaba2fSLydia Wang 
2159c577b8a1SJoseph Chan 	return 0;
2160c577b8a1SJoseph Chan }
2161c577b8a1SJoseph Chan 
21621f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
21631f2e99feSLydia Wang {
21641f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
21651f2e99feSLydia Wang 					     vt1708_hp_work.work);
21661f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
21671f2e99feSLydia Wang 		return;
21681f2e99feSLydia Wang 	/* if jack state toggled */
21691f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2170d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
21711f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
21721f2e99feSLydia Wang 		via_hp_automute(spec->codec);
21731f2e99feSLydia Wang 	}
21741f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
21751f2e99feSLydia Wang }
21761f2e99feSLydia Wang 
2177337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2178337b9d02STakashi Iwai {
2179337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2180337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2181337b9d02STakashi Iwai 	unsigned int type;
2182337b9d02STakashi Iwai 	int i, n;
2183337b9d02STakashi Iwai 
2184337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2185337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2186337b9d02STakashi Iwai 		while (nid) {
2187a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
21881c55d521STakashi Iwai 			if (type == AC_WID_PIN)
21891c55d521STakashi Iwai 				break;
2190337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2191337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2192337b9d02STakashi Iwai 			if (n <= 0)
2193337b9d02STakashi Iwai 				break;
2194337b9d02STakashi Iwai 			if (n > 1) {
2195337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2196337b9d02STakashi Iwai 				break;
2197337b9d02STakashi Iwai 			}
2198337b9d02STakashi Iwai 			nid = conn[0];
2199337b9d02STakashi Iwai 		}
2200337b9d02STakashi Iwai 	}
22011c55d521STakashi Iwai 	return 0;
2202337b9d02STakashi Iwai }
2203337b9d02STakashi Iwai 
2204c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2205c577b8a1SJoseph Chan {
2206c577b8a1SJoseph Chan 	struct via_spec *spec;
2207c577b8a1SJoseph Chan 	int err;
2208c577b8a1SJoseph Chan 
2209c577b8a1SJoseph Chan 	/* create a codec specific record */
22105b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2211c577b8a1SJoseph Chan 	if (spec == NULL)
2212c577b8a1SJoseph Chan 		return -ENOMEM;
2213c577b8a1SJoseph Chan 
2214620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2215620e2b28STakashi Iwai 
221612daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
221712daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
221812daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
221912daef65STakashi Iwai 
2220c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
222112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2222c577b8a1SJoseph Chan 	if (err < 0) {
2223c577b8a1SJoseph Chan 		via_free(codec);
2224c577b8a1SJoseph Chan 		return err;
2225c577b8a1SJoseph Chan 	}
2226c577b8a1SJoseph Chan 
222712daef65STakashi Iwai 	/* add jack detect on/off control */
222812daef65STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
222912daef65STakashi Iwai 		return -ENOMEM;
223012daef65STakashi Iwai 
2231bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2232bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2233bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2234c577b8a1SJoseph Chan 
2235c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2236c577b8a1SJoseph Chan 
22371f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2238c577b8a1SJoseph Chan 	return 0;
2239c577b8a1SJoseph Chan }
2240c577b8a1SJoseph Chan 
2241c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2242c577b8a1SJoseph Chan {
2243c577b8a1SJoseph Chan 	struct via_spec *spec;
2244c577b8a1SJoseph Chan 	int err;
2245c577b8a1SJoseph Chan 
2246c577b8a1SJoseph Chan 	/* create a codec specific record */
22475b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2248c577b8a1SJoseph Chan 	if (spec == NULL)
2249c577b8a1SJoseph Chan 		return -ENOMEM;
2250c577b8a1SJoseph Chan 
2251620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2252620e2b28STakashi Iwai 
225312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2254c577b8a1SJoseph Chan 	if (err < 0) {
2255c577b8a1SJoseph Chan 		via_free(codec);
2256c577b8a1SJoseph Chan 		return err;
2257c577b8a1SJoseph Chan 	}
2258c577b8a1SJoseph Chan 
2259c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2260c577b8a1SJoseph Chan 
2261c577b8a1SJoseph Chan 	return 0;
2262c577b8a1SJoseph Chan }
2263c577b8a1SJoseph Chan /*
2264c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2265c577b8a1SJoseph Chan  */
2266c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2267c577b8a1SJoseph Chan {
2268c577b8a1SJoseph Chan 	struct via_spec *spec;
2269c577b8a1SJoseph Chan 	int err;
2270c577b8a1SJoseph Chan 
2271c577b8a1SJoseph Chan 	/* create a codec specific record */
22725b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2273c577b8a1SJoseph Chan 	if (spec == NULL)
2274c577b8a1SJoseph Chan 		return -ENOMEM;
2275c577b8a1SJoseph Chan 
2276620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2277620e2b28STakashi Iwai 
227812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2279c577b8a1SJoseph Chan 	if (err < 0) {
2280c577b8a1SJoseph Chan 		via_free(codec);
2281c577b8a1SJoseph Chan 		return err;
2282c577b8a1SJoseph Chan 	}
2283c577b8a1SJoseph Chan 
2284c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2285c577b8a1SJoseph Chan 
2286f7278fd0SJosepch Chan 	return 0;
2287f7278fd0SJosepch Chan }
2288f7278fd0SJosepch Chan 
22893e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
22903e95b9abSLydia Wang {
22913e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
22923e95b9abSLydia Wang 	int imux_is_smixer;
22933e95b9abSLydia Wang 	unsigned int parm;
22943e95b9abSLydia Wang 	int is_8ch = 0;
2295bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2296bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
22973e95b9abSLydia Wang 		is_8ch = 1;
22983e95b9abSLydia Wang 
22993e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
23003e95b9abSLydia Wang 	imux_is_smixer =
23013e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
23023e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
23033e95b9abSLydia Wang 	/* inputs */
23043e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
23053e95b9abSLydia Wang 	parm = AC_PWRST_D3;
23063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
23073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
23083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
23093e95b9abSLydia Wang 	if (imux_is_smixer)
23103e95b9abSLydia Wang 		parm = AC_PWRST_D0;
23113e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
23123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
23133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
23143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
23153e95b9abSLydia Wang 
23163e95b9abSLydia Wang 	/* outputs */
23173e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
23183e95b9abSLydia Wang 	parm = AC_PWRST_D3;
23193e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
23203e95b9abSLydia Wang 	if (spec->smart51_enabled)
23213e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
23223e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
23233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
23243e95b9abSLydia Wang 
23253e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
23263e95b9abSLydia Wang 	if (is_8ch) {
23273e95b9abSLydia Wang 		parm = AC_PWRST_D3;
23283e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
23293e95b9abSLydia Wang 		if (spec->smart51_enabled)
23303e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
23313e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
23323e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
23333e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
23343e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2335bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2336bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2337bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2338bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2339bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2340bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2341bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2342bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2343bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2344bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
23453e95b9abSLydia Wang 	}
23463e95b9abSLydia Wang 
23473e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
23483e95b9abSLydia Wang 	parm = AC_PWRST_D3;
23493e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
23503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
23513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
23523e95b9abSLydia Wang 	if (is_8ch)
23533e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
23543e95b9abSLydia Wang 
23553e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
23563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
23573e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
23583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
23593e95b9abSLydia Wang 	if (is_8ch) {
23603e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
23613e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
23623e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
23633e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2364bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2365bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2366bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
23673e95b9abSLydia Wang }
23683e95b9abSLydia Wang 
2369518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2370f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
2371f7278fd0SJosepch Chan {
2372f7278fd0SJosepch Chan 	struct via_spec *spec;
2373f7278fd0SJosepch Chan 	int err;
2374f7278fd0SJosepch Chan 
2375518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2376518bf3baSLydia Wang 		return patch_vt1708S(codec);
2377f7278fd0SJosepch Chan 	/* create a codec specific record */
23785b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2379f7278fd0SJosepch Chan 	if (spec == NULL)
2380f7278fd0SJosepch Chan 		return -ENOMEM;
2381f7278fd0SJosepch Chan 
2382620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2383620e2b28STakashi Iwai 
2384f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
238512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2386f7278fd0SJosepch Chan 	if (err < 0) {
2387f7278fd0SJosepch Chan 		via_free(codec);
2388f7278fd0SJosepch Chan 		return err;
2389f7278fd0SJosepch Chan 	}
2390f7278fd0SJosepch Chan 
2391f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2392f7278fd0SJosepch Chan 
23933e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
23943e95b9abSLydia Wang 
2395f7278fd0SJosepch Chan 	return 0;
2396f7278fd0SJosepch Chan }
2397f7278fd0SJosepch Chan 
2398f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
2399f7278fd0SJosepch Chan {
2400f7278fd0SJosepch Chan 	struct via_spec *spec;
2401f7278fd0SJosepch Chan 	int err;
2402f7278fd0SJosepch Chan 
2403f7278fd0SJosepch Chan 	/* create a codec specific record */
24045b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2405f7278fd0SJosepch Chan 	if (spec == NULL)
2406f7278fd0SJosepch Chan 		return -ENOMEM;
2407f7278fd0SJosepch Chan 
2408f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
240912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2410f7278fd0SJosepch Chan 	if (err < 0) {
2411f7278fd0SJosepch Chan 		via_free(codec);
2412f7278fd0SJosepch Chan 		return err;
2413f7278fd0SJosepch Chan 	}
2414f7278fd0SJosepch Chan 
2415f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2416f7278fd0SJosepch Chan 
24173e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
24183e95b9abSLydia Wang 
2419c577b8a1SJoseph Chan 	return 0;
2420c577b8a1SJoseph Chan }
2421c577b8a1SJoseph Chan 
2422d949cac1SHarald Welte /* Patch for VT1708S */
2423096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
2424d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
2425d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
2426bc7e7e5cSLydia Wang 	/* don't bybass mixer */
2427bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
2428d949cac1SHarald Welte 	{ }
2429d949cac1SHarald Welte };
2430d949cac1SHarald Welte 
24319da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
24329da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
24339da29271STakashi Iwai {
24349da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
24359da29271STakashi Iwai 	int i;
24369da29271STakashi Iwai 
24379da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
24389da29271STakashi Iwai 		hda_nid_t nid;
24399da29271STakashi Iwai 		int conn;
24409da29271STakashi Iwai 
24419da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
24429da29271STakashi Iwai 		if (!nid)
24439da29271STakashi Iwai 			continue;
24449da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
24459da29271STakashi Iwai 		if (conn < 1)
24469da29271STakashi Iwai 			continue;
24479da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
24489da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
24499da29271STakashi Iwai 		else {
24509da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
24519da29271STakashi Iwai 			break; /* at most two dig outs */
24529da29271STakashi Iwai 		}
24539da29271STakashi Iwai 	}
24549da29271STakashi Iwai }
24559da29271STakashi Iwai 
245612daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec)
2457d949cac1SHarald Welte {
2458d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
245912daef65STakashi Iwai 	hda_nid_t dig_nid;
246012daef65STakashi Iwai 	int i, err;
2461d949cac1SHarald Welte 
246212daef65STakashi Iwai 	if (!spec->autocfg.dig_in_pin)
246312daef65STakashi Iwai 		return;
2464d949cac1SHarald Welte 
246512daef65STakashi Iwai 	dig_nid = codec->start_nid;
246612daef65STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
246712daef65STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, dig_nid);
246812daef65STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
246912daef65STakashi Iwai 			continue;
247012daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_DIGITAL))
247112daef65STakashi Iwai 			continue;
247212daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
247312daef65STakashi Iwai 			continue;
247412daef65STakashi Iwai 		err = get_connection_index(codec, dig_nid,
247512daef65STakashi Iwai 					   spec->autocfg.dig_in_pin);
247612daef65STakashi Iwai 		if (err >= 0) {
247712daef65STakashi Iwai 			spec->dig_in_nid = dig_nid;
247812daef65STakashi Iwai 			break;
247912daef65STakashi Iwai 		}
248012daef65STakashi Iwai 	}
2481d949cac1SHarald Welte }
2482d949cac1SHarald Welte 
24836369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
24846369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
24856369bcfcSLydia Wang {
24866369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
24876369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
24886369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
24896369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
24906369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
24916369bcfcSLydia Wang }
24926369bcfcSLydia Wang 
2493d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
2494d949cac1SHarald Welte {
2495d949cac1SHarald Welte 	struct via_spec *spec;
2496d949cac1SHarald Welte 	int err;
2497d949cac1SHarald Welte 
2498d949cac1SHarald Welte 	/* create a codec specific record */
24995b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2500d949cac1SHarald Welte 	if (spec == NULL)
2501d949cac1SHarald Welte 		return -ENOMEM;
2502d949cac1SHarald Welte 
2503620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2504d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
2505d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
2506620e2b28STakashi Iwai 
2507d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
250812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2509d949cac1SHarald Welte 	if (err < 0) {
2510d949cac1SHarald Welte 		via_free(codec);
2511d949cac1SHarald Welte 		return err;
2512d949cac1SHarald Welte 	}
2513d949cac1SHarald Welte 
2514096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
2515d949cac1SHarald Welte 
2516d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2517d949cac1SHarald Welte 
2518518bf3baSLydia Wang 	/* correct names for VT1708BCE */
2519518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
2520518bf3baSLydia Wang 		kfree(codec->chip_name);
2521518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2522518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
2523518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
2524518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2525970f630fSLydia Wang 	}
2526bc92df7fSLydia Wang 	/* correct names for VT1705 */
2527bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
2528bc92df7fSLydia Wang 		kfree(codec->chip_name);
2529bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2530bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
2531bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
2532bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2533bc92df7fSLydia Wang 	}
25343e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
2535d949cac1SHarald Welte 	return 0;
2536d949cac1SHarald Welte }
2537d949cac1SHarald Welte 
2538d949cac1SHarald Welte /* Patch for VT1702 */
2539d949cac1SHarald Welte 
2540096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
2541bc7e7e5cSLydia Wang 	/* mixer enable */
2542bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
2543bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
2544bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
2545d949cac1SHarald Welte 	{ }
2546d949cac1SHarald Welte };
2547d949cac1SHarald Welte 
25483e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
25493e95b9abSLydia Wang {
25503e95b9abSLydia Wang 	int imux_is_smixer =
25513e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
25523e95b9abSLydia Wang 	unsigned int parm;
25533e95b9abSLydia Wang 	/* inputs */
25543e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
25553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
25563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
25573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
25583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
25593e95b9abSLydia Wang 	if (imux_is_smixer)
25603e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
25613e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
25623e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
25633e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
25643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
25653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
25663e95b9abSLydia Wang 
25673e95b9abSLydia Wang 	/* outputs */
25683e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
25693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
25703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
25713e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
25723e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
25733e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
25743e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
25753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
25763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
25773e95b9abSLydia Wang }
25783e95b9abSLydia Wang 
2579d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
2580d949cac1SHarald Welte {
2581d949cac1SHarald Welte 	struct via_spec *spec;
2582d949cac1SHarald Welte 	int err;
2583d949cac1SHarald Welte 
2584d949cac1SHarald Welte 	/* create a codec specific record */
25855b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2586d949cac1SHarald Welte 	if (spec == NULL)
2587d949cac1SHarald Welte 		return -ENOMEM;
2588d949cac1SHarald Welte 
2589620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
2590620e2b28STakashi Iwai 
259112daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
259212daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
259312daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
259412daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
259512daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
259612daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
259712daef65STakashi Iwai 
2598d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
259912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2600d949cac1SHarald Welte 	if (err < 0) {
2601d949cac1SHarald Welte 		via_free(codec);
2602d949cac1SHarald Welte 		return err;
2603d949cac1SHarald Welte 	}
2604d949cac1SHarald Welte 
2605096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
2606d949cac1SHarald Welte 
2607d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2608d949cac1SHarald Welte 
26093e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
2610d949cac1SHarald Welte 	return 0;
2611d949cac1SHarald Welte }
2612d949cac1SHarald Welte 
2613eb7188caSLydia Wang /* Patch for VT1718S */
2614eb7188caSLydia Wang 
2615096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
26164ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
26174ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
2618eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
2619eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
26205d41762aSTakashi Iwai 
2621eb7188caSLydia Wang 	{ }
2622eb7188caSLydia Wang };
2623eb7188caSLydia Wang 
26243e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
26253e95b9abSLydia Wang {
26263e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
26273e95b9abSLydia Wang 	int imux_is_smixer;
26283e95b9abSLydia Wang 	unsigned int parm;
26293e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
26303e95b9abSLydia Wang 	imux_is_smixer =
26313e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
26323e95b9abSLydia Wang 	/* inputs */
26333e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
26343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
26363e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
26373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
26383e95b9abSLydia Wang 	if (imux_is_smixer)
26393e95b9abSLydia Wang 		parm = AC_PWRST_D0;
26403e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
26413e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
26423e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
26433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
26443e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
26453e95b9abSLydia Wang 
26463e95b9abSLydia Wang 	/* outputs */
26473e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
26483e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
26503e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
26513e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
26523e95b9abSLydia Wang 
26533e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
26543e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
26563e95b9abSLydia Wang 	if (spec->smart51_enabled)
26573e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
26583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
26593e95b9abSLydia Wang 
26603e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
26613e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
26633e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
26643e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
26653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
26663e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
26673e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
26683e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
26693e95b9abSLydia Wang 
26703e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
26713e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26723e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
26733e95b9abSLydia Wang 	if (spec->smart51_enabled)
26743e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
26753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
26763e95b9abSLydia Wang 
26773e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
26783e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
26793e95b9abSLydia Wang 		parm = AC_PWRST_D3;
26803e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
26813e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
26823e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
26833e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
26843e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
26853e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
26863e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
26873e95b9abSLydia Wang 	}
26883e95b9abSLydia Wang }
26893e95b9abSLydia Wang 
2690eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
2691eb7188caSLydia Wang {
2692eb7188caSLydia Wang 	struct via_spec *spec;
2693eb7188caSLydia Wang 	int err;
2694eb7188caSLydia Wang 
2695eb7188caSLydia Wang 	/* create a codec specific record */
26965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2697eb7188caSLydia Wang 	if (spec == NULL)
2698eb7188caSLydia Wang 		return -ENOMEM;
2699eb7188caSLydia Wang 
2700620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
2701d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
2702d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
2703620e2b28STakashi Iwai 
2704eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
270512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2706eb7188caSLydia Wang 	if (err < 0) {
2707eb7188caSLydia Wang 		via_free(codec);
2708eb7188caSLydia Wang 		return err;
2709eb7188caSLydia Wang 	}
2710eb7188caSLydia Wang 
2711096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
2712eb7188caSLydia Wang 
2713eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
2714eb7188caSLydia Wang 
27153e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
27163e95b9abSLydia Wang 
2717eb7188caSLydia Wang 	return 0;
2718eb7188caSLydia Wang }
2719f3db423dSLydia Wang 
2720f3db423dSLydia Wang /* Patch for VT1716S */
2721f3db423dSLydia Wang 
2722f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2723f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
2724f3db423dSLydia Wang {
2725f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2726f3db423dSLydia Wang 	uinfo->count = 1;
2727f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
2728f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
2729f3db423dSLydia Wang 	return 0;
2730f3db423dSLydia Wang }
2731f3db423dSLydia Wang 
2732f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2733f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2734f3db423dSLydia Wang {
2735f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2736f3db423dSLydia Wang 	int index = 0;
2737f3db423dSLydia Wang 
2738f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
2739f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
2740f3db423dSLydia Wang 	if (index != -1)
2741f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
2742f3db423dSLydia Wang 
2743f3db423dSLydia Wang 	return 0;
2744f3db423dSLydia Wang }
2745f3db423dSLydia Wang 
2746f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2747f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2748f3db423dSLydia Wang {
2749f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2750f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
2751f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
2752f3db423dSLydia Wang 
2753f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
2754f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
2755f3db423dSLydia Wang 	spec->dmic_enabled = index;
27563e95b9abSLydia Wang 	set_widgets_power_state(codec);
2757f3db423dSLydia Wang 	return 1;
2758f3db423dSLydia Wang }
2759f3db423dSLydia Wang 
276090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
2761f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2762f3db423dSLydia Wang 	{
2763f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2764f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
27655b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
2766f3db423dSLydia Wang 	 .count = 1,
2767f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
2768f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
2769f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
2770f3db423dSLydia Wang 	 },
2771f3db423dSLydia Wang 	{}			/* end */
2772f3db423dSLydia Wang };
2773f3db423dSLydia Wang 
2774f3db423dSLydia Wang 
2775f3db423dSLydia Wang /* mono-out mixer elements */
277690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
2777f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2778f3db423dSLydia Wang 	{ } /* end */
2779f3db423dSLydia Wang };
2780f3db423dSLydia Wang 
2781096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
2782f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
2783f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
2784f3db423dSLydia Wang 	/* don't bybass mixer */
2785f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
2786f3db423dSLydia Wang 	/* Enable mono output */
2787f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
2788f3db423dSLydia Wang 	{ }
2789f3db423dSLydia Wang };
2790f3db423dSLydia Wang 
27913e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
27923e95b9abSLydia Wang {
27933e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
27943e95b9abSLydia Wang 	int imux_is_smixer;
27953e95b9abSLydia Wang 	unsigned int parm;
27963e95b9abSLydia Wang 	unsigned int mono_out, present;
27973e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
27983e95b9abSLydia Wang 	imux_is_smixer =
27993e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
28003e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
28013e95b9abSLydia Wang 	/* inputs */
28023e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
28033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
28053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
28063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
28073e95b9abSLydia Wang 	if (imux_is_smixer)
28083e95b9abSLydia Wang 		parm = AC_PWRST_D0;
28093e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
28103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
28113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
28123e95b9abSLydia Wang 
28133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
28153e95b9abSLydia Wang 	/* PW11 (22h) */
28163e95b9abSLydia Wang 	if (spec->dmic_enabled)
28173e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
28183e95b9abSLydia Wang 	else
28193e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
28203e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
28213e95b9abSLydia Wang 
28223e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
28233e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
28243e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
28253e95b9abSLydia Wang 
28263e95b9abSLydia Wang 	/* outputs */
28273e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
28283e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
28303e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
28313e95b9abSLydia Wang 	if (spec->smart51_enabled)
28323e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
28333e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
28343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
28353e95b9abSLydia Wang 
28363e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
28373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
28393e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
28403e95b9abSLydia Wang 	if (spec->smart51_enabled)
28413e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
28423e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
28433e95b9abSLydia Wang 
28443e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
28453e95b9abSLydia Wang 	if (spec->smart51_enabled)
28463e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
28473e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
28483e95b9abSLydia Wang 
28493e95b9abSLydia Wang 	/* Mono out */
28503e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
28513e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
28523e95b9abSLydia Wang 
28533e95b9abSLydia Wang 	if (present)
28543e95b9abSLydia Wang 		mono_out = 0;
28553e95b9abSLydia Wang 	else {
28563e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
28573e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
28583e95b9abSLydia Wang 			mono_out = 0;
28593e95b9abSLydia Wang 		else
28603e95b9abSLydia Wang 			mono_out = 1;
28613e95b9abSLydia Wang 	}
28623e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
28633e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
28643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
28653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
28663e95b9abSLydia Wang 
28673e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
28683e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
28703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
28713e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
28723e95b9abSLydia Wang 	if (spec->hp_independent_mode)
28733e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
28743e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28753e95b9abSLydia Wang 
28763e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
28773e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
28783e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
28793e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
28803e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
28813e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
28823e95b9abSLydia Wang }
28833e95b9abSLydia Wang 
2884f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
2885f3db423dSLydia Wang {
2886f3db423dSLydia Wang 	struct via_spec *spec;
2887f3db423dSLydia Wang 	int err;
2888f3db423dSLydia Wang 
2889f3db423dSLydia Wang 	/* create a codec specific record */
28905b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2891f3db423dSLydia Wang 	if (spec == NULL)
2892f3db423dSLydia Wang 		return -ENOMEM;
2893f3db423dSLydia Wang 
2894620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2895d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
2896d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
2897620e2b28STakashi Iwai 
2898f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
289912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2900f3db423dSLydia Wang 	if (err < 0) {
2901f3db423dSLydia Wang 		via_free(codec);
2902f3db423dSLydia Wang 		return err;
2903f3db423dSLydia Wang 	}
2904f3db423dSLydia Wang 
2905096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
2906f3db423dSLydia Wang 
2907f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2908f3db423dSLydia Wang 	spec->num_mixers++;
2909f3db423dSLydia Wang 
2910f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2911f3db423dSLydia Wang 
2912f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
2913f3db423dSLydia Wang 
29143e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
2915f3db423dSLydia Wang 	return 0;
2916f3db423dSLydia Wang }
291725eaba2fSLydia Wang 
291825eaba2fSLydia Wang /* for vt2002P */
291925eaba2fSLydia Wang 
2920096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
2921eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
2922eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
2923eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
2924eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
292525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
292625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
292725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
292825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
292925eaba2fSLydia Wang 	{ }
293025eaba2fSLydia Wang };
29314a918ffeSTakashi Iwai 
2932096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
293311890956SLydia Wang 	/* Enable Boost Volume backdoor */
293411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
293511890956SLydia Wang 	/* Enable AOW0 to MW9 */
293611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
293711890956SLydia Wang 	{ }
293811890956SLydia Wang };
293925eaba2fSLydia Wang 
29403e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
29413e95b9abSLydia Wang {
29423e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
29433e95b9abSLydia Wang 	int imux_is_smixer;
29443e95b9abSLydia Wang 	unsigned int parm;
29453e95b9abSLydia Wang 	unsigned int present;
29463e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
29473e95b9abSLydia Wang 	imux_is_smixer =
29483e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
29493e95b9abSLydia Wang 	/* inputs */
29503e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
29513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
29533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
29543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
29553e95b9abSLydia Wang 	parm = AC_PWRST_D0;
29563e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
29573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
29583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
29593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
29603e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
29613e95b9abSLydia Wang 
29623e95b9abSLydia Wang 	/* outputs */
29633e95b9abSLydia Wang 	/* AOW0 (8h)*/
29643e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
29653e95b9abSLydia Wang 
296611890956SLydia Wang 	if (spec->codec_type == VT1802) {
296711890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
296811890956SLydia Wang 		parm = AC_PWRST_D3;
296911890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
297011890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
297111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
297211890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
297311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
297411890956SLydia Wang 	} else {
29753e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
29763e95b9abSLydia Wang 		parm = AC_PWRST_D3;
29773e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
29783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
29793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29803e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
29813e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
298211890956SLydia Wang 	}
29833e95b9abSLydia Wang 
298411890956SLydia Wang 	if (spec->codec_type == VT1802) {
298511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
298611890956SLydia Wang 		parm = AC_PWRST_D3;
298711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
298811890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
298911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
299011890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
299111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
299211890956SLydia Wang 	} else {
29933e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
29943e95b9abSLydia Wang 		parm = AC_PWRST_D3;
29953e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
29963e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
29973e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29983e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
29993e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
300011890956SLydia Wang 	}
30013e95b9abSLydia Wang 
30023e95b9abSLydia Wang 	if (spec->hp_independent_mode)
30033e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
30043e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
30053e95b9abSLydia Wang 
30063e95b9abSLydia Wang 	/* Class-D */
30073e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
30083e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
30093e95b9abSLydia Wang 
30103e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30113e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
30123e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
301311890956SLydia Wang 	if (spec->codec_type == VT1802)
301411890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
301511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
301611890956SLydia Wang 	else
30173e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
30183e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
30193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
30203e95b9abSLydia Wang 
30213e95b9abSLydia Wang 	/* Mono Out */
30223e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
30233e95b9abSLydia Wang 
30243e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
302511890956SLydia Wang 	if (spec->codec_type == VT1802) {
302611890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
302711890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
302811890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
302911890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
303011890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
303111890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
303211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
303311890956SLydia Wang 	} else {
30343e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
30353e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
30363e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
30373e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
30383e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
30393e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
30403e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
304111890956SLydia Wang 	}
30423e95b9abSLydia Wang 	/* MW9 (21h) */
30433e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
30443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
30453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
30463e95b9abSLydia Wang 	else
30473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
30483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
30493e95b9abSLydia Wang }
305025eaba2fSLydia Wang 
305125eaba2fSLydia Wang /* patch for vt2002P */
305225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
305325eaba2fSLydia Wang {
305425eaba2fSLydia Wang 	struct via_spec *spec;
305525eaba2fSLydia Wang 	int err;
305625eaba2fSLydia Wang 
305725eaba2fSLydia Wang 	/* create a codec specific record */
30585b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
305925eaba2fSLydia Wang 	if (spec == NULL)
306025eaba2fSLydia Wang 		return -ENOMEM;
306125eaba2fSLydia Wang 
3062620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3063d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3064d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3065620e2b28STakashi Iwai 
306625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
306712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
306825eaba2fSLydia Wang 	if (err < 0) {
306925eaba2fSLydia Wang 		via_free(codec);
307025eaba2fSLydia Wang 		return err;
307125eaba2fSLydia Wang 	}
307225eaba2fSLydia Wang 
307311890956SLydia Wang 	if (spec->codec_type == VT1802)
30744a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
307511890956SLydia Wang 	else
30764a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
307711890956SLydia Wang 
307825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
307925eaba2fSLydia Wang 
30803e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
308125eaba2fSLydia Wang 	return 0;
308225eaba2fSLydia Wang }
3083ab6734e7SLydia Wang 
3084ab6734e7SLydia Wang /* for vt1812 */
3085ab6734e7SLydia Wang 
3086096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
3087ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
3088ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
3089ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
3090ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
3091ab6734e7SLydia Wang 	{ }
3092ab6734e7SLydia Wang };
3093ab6734e7SLydia Wang 
30943e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
30953e95b9abSLydia Wang {
30963e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
30973e95b9abSLydia Wang 	int imux_is_smixer =
30983e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
30993e95b9abSLydia Wang 	unsigned int parm;
31003e95b9abSLydia Wang 	unsigned int present;
31013e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
31023e95b9abSLydia Wang 	imux_is_smixer =
31033e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
31043e95b9abSLydia Wang 	/* inputs */
31053e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
31063e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
31083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
31093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
31103e95b9abSLydia Wang 	parm = AC_PWRST_D0;
31113e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
31123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
31133e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
31143e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
31153e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
31163e95b9abSLydia Wang 
31173e95b9abSLydia Wang 	/* outputs */
31183e95b9abSLydia Wang 	/* AOW0 (8h)*/
31193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
31203e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31213e95b9abSLydia Wang 
31223e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
31233e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
31253e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
31263e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
31273e95b9abSLydia Wang 
31283e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
31293e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31303e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
31313e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
31323e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
31333e95b9abSLydia Wang 	if (spec->hp_independent_mode)
31343e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
31353e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31363e95b9abSLydia Wang 
31373e95b9abSLydia Wang 	/* Internal Speaker */
31383e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
31393e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
31403e95b9abSLydia Wang 
31413e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31423e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
31433e95b9abSLydia Wang 	if (present) {
31443e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
31453e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
31463e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
31473e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
31483e95b9abSLydia Wang 	} else {
31493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
31503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31513e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
31523e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31533e95b9abSLydia Wang 	}
31543e95b9abSLydia Wang 
31553e95b9abSLydia Wang 
31563e95b9abSLydia Wang 	/* Mono Out */
31573e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
31583e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
31593e95b9abSLydia Wang 
31603e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
31623e95b9abSLydia Wang 	if (present) {
31633e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
31643e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
31653e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
31663e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
31673e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
31683e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
31693e95b9abSLydia Wang 	} else {
31703e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
31713e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31723e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
31733e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31743e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
31753e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31763e95b9abSLydia Wang 	}
31773e95b9abSLydia Wang 
31783e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
31793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
31813e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
31823e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
31833e95b9abSLydia Wang 
31843e95b9abSLydia Wang }
3185ab6734e7SLydia Wang 
3186ab6734e7SLydia Wang /* patch for vt1812 */
3187ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
3188ab6734e7SLydia Wang {
3189ab6734e7SLydia Wang 	struct via_spec *spec;
3190ab6734e7SLydia Wang 	int err;
3191ab6734e7SLydia Wang 
3192ab6734e7SLydia Wang 	/* create a codec specific record */
31935b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3194ab6734e7SLydia Wang 	if (spec == NULL)
3195ab6734e7SLydia Wang 		return -ENOMEM;
3196ab6734e7SLydia Wang 
3197620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3198d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3199d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3200620e2b28STakashi Iwai 
3201ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
320212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3203ab6734e7SLydia Wang 	if (err < 0) {
3204ab6734e7SLydia Wang 		via_free(codec);
3205ab6734e7SLydia Wang 		return err;
3206ab6734e7SLydia Wang 	}
3207ab6734e7SLydia Wang 
3208096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
3209ab6734e7SLydia Wang 
3210ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
3211ab6734e7SLydia Wang 
32123e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
3213ab6734e7SLydia Wang 	return 0;
3214ab6734e7SLydia Wang }
3215ab6734e7SLydia Wang 
3216c577b8a1SJoseph Chan /*
3217c577b8a1SJoseph Chan  * patch entries
3218c577b8a1SJoseph Chan  */
321990dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
32203218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
32213218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
32223218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
32233218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
32243218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
3225f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
32263218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
3227f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
32283218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
3229f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
32303218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
3231f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
32323218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
3233f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
32343218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
3235f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
32363218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
3237f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
32383218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
3239f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
32403218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
3241f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
32423218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
3243f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
32443218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
3245f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
32463218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
3247f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
32483218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
3249f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
32503218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
3251f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
32523218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
3253f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
32543218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
3255f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
32563218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
3257d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32583218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
3259d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32603218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
3261d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32623218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
3263d949cac1SHarald Welte 	  .patch = patch_vt1708S},
3264bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
3265d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32663218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
3267d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32683218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
3269d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32703218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
3271d949cac1SHarald Welte 	  .patch = patch_vt1708S},
32723218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
3273d949cac1SHarald Welte 	  .patch = patch_vt1702},
32743218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
3275d949cac1SHarald Welte 	  .patch = patch_vt1702},
32763218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
3277d949cac1SHarald Welte 	  .patch = patch_vt1702},
32783218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
3279d949cac1SHarald Welte 	  .patch = patch_vt1702},
32803218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
3281d949cac1SHarald Welte 	  .patch = patch_vt1702},
32823218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
3283d949cac1SHarald Welte 	  .patch = patch_vt1702},
32843218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
3285d949cac1SHarald Welte 	  .patch = patch_vt1702},
32863218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
3287d949cac1SHarald Welte 	  .patch = patch_vt1702},
3288eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
3289eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3290eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
3291eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3292bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
3293bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3294bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
3295bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3296f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
3297f3db423dSLydia Wang 	  .patch = patch_vt1716S},
3298f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
3299f3db423dSLydia Wang 	  .patch = patch_vt1716S},
330025eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
330125eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
3302ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
330336dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
330436dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
330511890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
330611890956SLydia Wang 		.patch = patch_vt2002P},
330711890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
330811890956SLydia Wang 		.patch = patch_vt2002P},
3309c577b8a1SJoseph Chan 	{} /* terminator */
3310c577b8a1SJoseph Chan };
33111289e9e8STakashi Iwai 
33121289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
33131289e9e8STakashi Iwai 
33141289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
33151289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
33161289e9e8STakashi Iwai 	.owner = THIS_MODULE,
33171289e9e8STakashi Iwai };
33181289e9e8STakashi Iwai 
33191289e9e8STakashi Iwai MODULE_LICENSE("GPL");
33201289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
33211289e9e8STakashi Iwai 
33221289e9e8STakashi Iwai static int __init patch_via_init(void)
33231289e9e8STakashi Iwai {
33241289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
33251289e9e8STakashi Iwai }
33261289e9e8STakashi Iwai 
33271289e9e8STakashi Iwai static void __exit patch_via_exit(void)
33281289e9e8STakashi Iwai {
33291289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
33301289e9e8STakashi Iwai }
33311289e9e8STakashi Iwai 
33321289e9e8STakashi Iwai module_init(patch_via_init)
33331289e9e8STakashi Iwai module_exit(patch_via_exit)
3334