xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision e3d7a1431f1d8851d11b2262dda5bb67158450eb)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
575b0cb1d8SJaroslav Kysela #define NID_MAPPING		(-1)
585b0cb1d8SJaroslav Kysela 
59c577b8a1SJoseph Chan /* amp values */
60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT	19
61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK	(0x0f<<19)
62c577b8a1SJoseph Chan 
63c577b8a1SJoseph Chan /* Pin Widget NID */
64c577b8a1SJoseph Chan #define VT1708_HP_NID		0x13
65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID	0x14
66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID	0x16
67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN	0x26
6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
70c577b8a1SJoseph Chan 
71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID	0x28
72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID	0x13
73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID	0x17
74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN	0x25
75f7278fd0SJosepch Chan 
76f7278fd0SJosepch Chan #define VT1708B_HP_NID		0x25
77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID	0x12
78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID	0x15
79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN	0x21
80c577b8a1SJoseph Chan 
81d949cac1SHarald Welte #define VT1708S_HP_NID		0x25
82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID	0x12
83d949cac1SHarald Welte 
84d949cac1SHarald Welte #define VT1702_HP_NID		0x17
85d949cac1SHarald Welte #define VT1702_DIGOUT_NID	0x11
86d949cac1SHarald Welte 
87d7426329SHarald Welte enum VIA_HDA_CODEC {
88d7426329SHarald Welte 	UNKNOWN = -1,
89d7426329SHarald Welte 	VT1708,
90d7426329SHarald Welte 	VT1709_10CH,
91d7426329SHarald Welte 	VT1709_6CH,
92d7426329SHarald Welte 	VT1708B_8CH,
93d7426329SHarald Welte 	VT1708B_4CH,
94d7426329SHarald Welte 	VT1708S,
95518bf3baSLydia Wang 	VT1708BCE,
96d7426329SHarald Welte 	VT1702,
97eb7188caSLydia Wang 	VT1718S,
98f3db423dSLydia Wang 	VT1716S,
9925eaba2fSLydia Wang 	VT2002P,
100ab6734e7SLydia Wang 	VT1812,
10111890956SLydia Wang 	VT1802,
102d7426329SHarald Welte 	CODEC_TYPES,
103d7426329SHarald Welte };
104d7426329SHarald Welte 
10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
10611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
10711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
10811890956SLydia Wang 	 (spec)->codec_type == VT1802)
10911890956SLydia Wang 
1104a79616dSTakashi Iwai struct nid_path {
1114a79616dSTakashi Iwai 	int depth;
1124a79616dSTakashi Iwai 	hda_nid_t path[5];
1134a79616dSTakashi Iwai 	short idx[5];
1144a79616dSTakashi Iwai };
1154a79616dSTakashi Iwai 
1161f2e99feSLydia Wang struct via_spec {
1171f2e99feSLydia Wang 	/* codec parameterization */
11890dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1191f2e99feSLydia Wang 	unsigned int num_mixers;
1201f2e99feSLydia Wang 
12190dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1221f2e99feSLydia Wang 	unsigned int num_iverbs;
1231f2e99feSLydia Wang 
12482673bc8STakashi Iwai 	char stream_name_analog[32];
1257eb56e84STakashi Iwai 	char stream_name_hp[32];
12690dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12790dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1281f2e99feSLydia Wang 
12982673bc8STakashi Iwai 	char stream_name_digital[32];
13090dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
13190dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1321f2e99feSLydia Wang 
1331f2e99feSLydia Wang 	/* playback */
1341f2e99feSLydia Wang 	struct hda_multi_out multiout;
1351f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
136ece8d043STakashi Iwai 	hda_nid_t hp_dac_nid;
1371f2e99feSLydia Wang 
1384a79616dSTakashi Iwai 	struct nid_path out_path[4];
1394a79616dSTakashi Iwai 	struct nid_path hp_path;
1404a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
1414a918ffeSTakashi Iwai 	struct nid_path speaker_path;
1424a79616dSTakashi Iwai 
1431f2e99feSLydia Wang 	/* capture */
1441f2e99feSLydia Wang 	unsigned int num_adc_nids;
145a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1461f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
147620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1481f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1491f2e99feSLydia Wang 	hda_nid_t dig_in_pin;
1501f2e99feSLydia Wang 
1511f2e99feSLydia Wang 	/* capture source */
1521f2e99feSLydia Wang 	const struct hda_input_mux *input_mux;
1531f2e99feSLydia Wang 	unsigned int cur_mux[3];
1541f2e99feSLydia Wang 
1551f2e99feSLydia Wang 	/* PCM information */
1561f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1571f2e99feSLydia Wang 
1581f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1591f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1601f2e99feSLydia Wang 	struct snd_array kctls;
1611f2e99feSLydia Wang 	struct hda_input_mux private_imux[2];
1621f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1631f2e99feSLydia Wang 
1641f2e99feSLydia Wang 	/* HP mode source */
1651f2e99feSLydia Wang 	const struct hda_input_mux *hp_mux;
1661f2e99feSLydia Wang 	unsigned int hp_independent_mode;
1671f2e99feSLydia Wang 	unsigned int hp_independent_mode_index;
168f3db423dSLydia Wang 	unsigned int dmic_enabled;
16924088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1701f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1711f2e99feSLydia Wang 
172*e3d7a143STakashi Iwai 	/* smart51 setup */
173*e3d7a143STakashi Iwai 	unsigned int smart51_nums;
174*e3d7a143STakashi Iwai 	hda_nid_t smart51_pins[2];
175*e3d7a143STakashi Iwai 	int smart51_idxs[2];
176*e3d7a143STakashi Iwai 	const char *smart51_labels[2];
177*e3d7a143STakashi Iwai 	unsigned int smart51_enabled;
178*e3d7a143STakashi Iwai 
1791f2e99feSLydia Wang 	/* work to check hp jack state */
1801f2e99feSLydia Wang 	struct hda_codec *codec;
1811f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
182e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1831f2e99feSLydia Wang 	int vt1708_hp_present;
1843e95b9abSLydia Wang 
1853e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1863e95b9abSLydia Wang 
1871f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
1881f2e99feSLydia Wang 	struct hda_loopback_check loopback;
1891f2e99feSLydia Wang #endif
1901f2e99feSLydia Wang };
1911f2e99feSLydia Wang 
1920341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1935b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1945b0cb1d8SJaroslav Kysela {
1955b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1965b0cb1d8SJaroslav Kysela 
1975b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1985b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1995b0cb1d8SJaroslav Kysela 		return NULL;
2005b0cb1d8SJaroslav Kysela 
2015b0cb1d8SJaroslav Kysela 	codec->spec = spec;
2025b0cb1d8SJaroslav Kysela 	spec->codec = codec;
2030341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
2040341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
2050341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
2060341ccd7SLydia Wang 		spec->codec_type = VT1708S;
2075b0cb1d8SJaroslav Kysela 	return spec;
2085b0cb1d8SJaroslav Kysela }
2095b0cb1d8SJaroslav Kysela 
210744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
211d7426329SHarald Welte {
212744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
213d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
214d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
215d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
216d7426329SHarald Welte 
217d7426329SHarald Welte 	/* get codec type */
218d7426329SHarald Welte 	if (ven_id != 0x1106)
219d7426329SHarald Welte 		codec_type = UNKNOWN;
220d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
221d7426329SHarald Welte 		codec_type = VT1708;
222d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
223d7426329SHarald Welte 		codec_type = VT1709_10CH;
224d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
225d7426329SHarald Welte 		codec_type = VT1709_6CH;
226518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
227d7426329SHarald Welte 		codec_type = VT1708B_8CH;
228518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
229518bf3baSLydia Wang 			codec_type = VT1708BCE;
230518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
231d7426329SHarald Welte 		codec_type = VT1708B_4CH;
232d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
233d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
234d7426329SHarald Welte 		codec_type = VT1708S;
235d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
236d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
237d7426329SHarald Welte 		codec_type = VT1702;
238eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
239eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
240eb7188caSLydia Wang 		codec_type = VT1718S;
241f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
242f3db423dSLydia Wang 		codec_type = VT1716S;
243bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
244bb3c6bfcSLydia Wang 		codec_type = VT1718S;
24525eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
24625eaba2fSLydia Wang 		codec_type = VT2002P;
247ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
248ab6734e7SLydia Wang 		codec_type = VT1812;
24936dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
25036dd5c4aSLydia Wang 		codec_type = VT1708S;
25111890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
25211890956SLydia Wang 		codec_type = VT1802;
253d7426329SHarald Welte 	else
254d7426329SHarald Welte 		codec_type = UNKNOWN;
255d7426329SHarald Welte 	return codec_type;
256d7426329SHarald Welte };
257d7426329SHarald Welte 
258ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
25969e52a80SHarald Welte #define VIA_HP_EVENT		0x01
26069e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
2614a918ffeSTakashi Iwai #define VIA_LINE_EVENT		0x03
26269e52a80SHarald Welte 
263c577b8a1SJoseph Chan enum {
264c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
265c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
266f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
267c577b8a1SJoseph Chan };
268c577b8a1SJoseph Chan 
269f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
2701f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec);
2711f2e99feSLydia Wang 
2721f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2731f2e99feSLydia Wang {
2741f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2751f2e99feSLydia Wang 		return;
2761f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
277e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2781f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2791f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2801f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2811f2e99feSLydia Wang }
2821f2e99feSLydia Wang 
2831f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2841f2e99feSLydia Wang {
2851f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2861f2e99feSLydia Wang 		return;
2871f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2881f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2891f2e99feSLydia Wang 		return;
2901f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
291e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2925b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2931f2e99feSLydia Wang }
294f5271101SLydia Wang 
2953e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2963e95b9abSLydia Wang {
2973e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2983e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2993e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3003e95b9abSLydia Wang }
30125eaba2fSLydia Wang 
302f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
303f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
304f5271101SLydia Wang {
305f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
306f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
307f5271101SLydia Wang 
3083e95b9abSLydia Wang 	set_widgets_power_state(codec);
309f5271101SLydia Wang 	analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
3101f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3111f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3121f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3131f2e99feSLydia Wang 		else
3141f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3151f2e99feSLydia Wang 	}
316f5271101SLydia Wang 	return change;
317f5271101SLydia Wang }
318f5271101SLydia Wang 
319f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
320f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
321f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
322f5271101SLydia Wang 			.name = NULL,					\
323f5271101SLydia Wang 			.index = 0,					\
324f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
325f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
326f5271101SLydia Wang 			.put = analog_input_switch_put,			\
327f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
328f5271101SLydia Wang 
32990dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
330c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
331c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
332f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
333c577b8a1SJoseph Chan };
334c577b8a1SJoseph Chan 
335ab6734e7SLydia Wang 
336c577b8a1SJoseph Chan /* add dynamic controls */
337291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
338291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
339291c9e33STakashi Iwai 				const char *name)
340c577b8a1SJoseph Chan {
341c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
342c577b8a1SJoseph Chan 
343603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
344603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
345c577b8a1SJoseph Chan 	if (!knew)
346291c9e33STakashi Iwai 		return NULL;
347291c9e33STakashi Iwai 	*knew = *tmpl;
348291c9e33STakashi Iwai 	if (!name)
349291c9e33STakashi Iwai 		name = tmpl->name;
350291c9e33STakashi Iwai 	if (name) {
351c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
352c577b8a1SJoseph Chan 		if (!knew->name)
353291c9e33STakashi Iwai 			return NULL;
354291c9e33STakashi Iwai 	}
355291c9e33STakashi Iwai 	return knew;
356291c9e33STakashi Iwai }
357291c9e33STakashi Iwai 
358291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
359291c9e33STakashi Iwai 			     int idx, unsigned long val)
360291c9e33STakashi Iwai {
361291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
362291c9e33STakashi Iwai 
363291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
364291c9e33STakashi Iwai 	if (!knew)
365c577b8a1SJoseph Chan 		return -ENOMEM;
366d7a99cceSTakashi Iwai 	knew->index = idx;
3674d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
3685e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
369c577b8a1SJoseph Chan 	knew->private_value = val;
370c577b8a1SJoseph Chan 	return 0;
371c577b8a1SJoseph Chan }
372c577b8a1SJoseph Chan 
3737b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
3747b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
3757b315bb4STakashi Iwai 
376291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
3775b0cb1d8SJaroslav Kysela 
378603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
379603c4019STakashi Iwai {
380603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
381603c4019STakashi Iwai 
382603c4019STakashi Iwai 	if (spec->kctls.list) {
383603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
384603c4019STakashi Iwai 		int i;
385603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
386603c4019STakashi Iwai 			kfree(kctl[i].name);
387603c4019STakashi Iwai 	}
388603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
389603c4019STakashi Iwai }
390603c4019STakashi Iwai 
391c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
3929510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
3937b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
394c577b8a1SJoseph Chan {
395c577b8a1SJoseph Chan 	char name[32];
396c577b8a1SJoseph Chan 	int err;
397c577b8a1SJoseph Chan 
398c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
3997b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
400c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
401c577b8a1SJoseph Chan 	if (err < 0)
402c577b8a1SJoseph Chan 		return err;
403c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
4047b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
405c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
406c577b8a1SJoseph Chan 	if (err < 0)
407c577b8a1SJoseph Chan 		return err;
408c577b8a1SJoseph Chan 	return 0;
409c577b8a1SJoseph Chan }
410c577b8a1SJoseph Chan 
4115d41762aSTakashi Iwai /* return the index of the given widget nid as the source of mux;
4125d41762aSTakashi Iwai  * return -1 if not found;
4135d41762aSTakashi Iwai  * if num_conns is non-NULL, set the total number of connections
4145d41762aSTakashi Iwai  */
4155d41762aSTakashi Iwai static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
4165d41762aSTakashi Iwai 				  hda_nid_t nid, int *num_conns)
417c577b8a1SJoseph Chan {
4185d41762aSTakashi Iwai 	hda_nid_t conn[HDA_MAX_NUM_INPUTS];
4195d41762aSTakashi Iwai 	int i, nums;
4205d41762aSTakashi Iwai 
4215d41762aSTakashi Iwai 	nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
4225d41762aSTakashi Iwai 	if (num_conns)
4235d41762aSTakashi Iwai 		*num_conns = nums;
4245d41762aSTakashi Iwai 	for (i = 0; i < nums; i++)
4255d41762aSTakashi Iwai 		if (conn[i] == nid)
4265d41762aSTakashi Iwai 			return i;
4275d41762aSTakashi Iwai 	return -1;
4285d41762aSTakashi Iwai }
4295d41762aSTakashi Iwai 
4305d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \
4315d41762aSTakashi Iwai 	__get_connection_index(codec, mux, nid, NULL)
4325d41762aSTakashi Iwai 
4335d41762aSTakashi Iwai /* unmute input amp and select the specificed source */
4345d41762aSTakashi Iwai static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
4355d41762aSTakashi Iwai 			      hda_nid_t src, hda_nid_t mix)
4365d41762aSTakashi Iwai {
4375d41762aSTakashi Iwai 	int idx, num_conns;
4385d41762aSTakashi Iwai 
4395d41762aSTakashi Iwai 	idx = __get_connection_index(codec, nid, src, &num_conns);
4405d41762aSTakashi Iwai 	if (idx < 0)
4415d41762aSTakashi Iwai 		return;
4425d41762aSTakashi Iwai 
4435d41762aSTakashi Iwai 	/* select the route explicitly when multiple connections exist */
4445d41762aSTakashi Iwai 	if (num_conns > 1)
445d3a11e60STakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
4465d41762aSTakashi Iwai 				    AC_VERB_SET_CONNECT_SEL, idx);
4475d41762aSTakashi Iwai 	/* unmute if the input amp is present */
4485d41762aSTakashi Iwai 	if (!(query_amp_caps(codec, nid, HDA_INPUT) &
4495d41762aSTakashi Iwai 	      (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)))
4505d41762aSTakashi Iwai 		return;
4515d41762aSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4525d41762aSTakashi Iwai 			    AMP_IN_UNMUTE(idx));
4535d41762aSTakashi Iwai 
4545d41762aSTakashi Iwai 	/* unmute AA-path if present */
4555d41762aSTakashi Iwai 	if (!mix)
4565d41762aSTakashi Iwai 		return;
4575d41762aSTakashi Iwai 	idx = __get_connection_index(codec, nid, mix, NULL);
4585d41762aSTakashi Iwai 	if (idx >= 0)
4595d41762aSTakashi Iwai 		snd_hda_codec_write(codec, nid, 0,
4605d41762aSTakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
4615d41762aSTakashi Iwai 				    AMP_IN_UNMUTE(idx));
4625d41762aSTakashi Iwai }
4635d41762aSTakashi Iwai 
4645d41762aSTakashi Iwai /* set the given pin as output */
4655d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
4665d41762aSTakashi Iwai 			    int pin_type)
4675d41762aSTakashi Iwai {
4685d41762aSTakashi Iwai 	if (!pin)
4695d41762aSTakashi Iwai 		return;
4705d41762aSTakashi Iwai 	snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4715d41762aSTakashi Iwai 			    pin_type);
4725d41762aSTakashi Iwai 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
4735d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0,
474d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
475c577b8a1SJoseph Chan }
476c577b8a1SJoseph Chan 
4775d41762aSTakashi Iwai static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
4785d41762aSTakashi Iwai 				 int pin_type, struct nid_path *path)
4795d41762aSTakashi Iwai {
4805d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
4815d41762aSTakashi Iwai 	unsigned int caps;
4825d41762aSTakashi Iwai 	hda_nid_t nid;
4835d41762aSTakashi Iwai 	int i;
4845d41762aSTakashi Iwai 
4855d41762aSTakashi Iwai 	if (!pin)
4865d41762aSTakashi Iwai 		return;
4875d41762aSTakashi Iwai 
4885d41762aSTakashi Iwai 	init_output_pin(codec, pin, pin_type);
4895d41762aSTakashi Iwai 	caps = query_amp_caps(codec, pin, HDA_OUTPUT);
4905d41762aSTakashi Iwai 	if (caps & AC_AMPCAP_MUTE) {
4915d41762aSTakashi Iwai 		unsigned int val;
4925d41762aSTakashi Iwai 		val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
4935d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
4945d41762aSTakashi Iwai 				    AMP_OUT_MUTE | val);
4955d41762aSTakashi Iwai 	}
4965d41762aSTakashi Iwai 
4975d41762aSTakashi Iwai 	/* initialize the output path */
4985d41762aSTakashi Iwai 	nid = pin;
4995d41762aSTakashi Iwai 	for (i = 0; i < path->depth; i++) {
5005d41762aSTakashi Iwai 		unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid);
5015d41762aSTakashi Iwai 		nid = path->path[i];
5025d41762aSTakashi Iwai 		if (query_amp_caps(codec, nid, HDA_OUTPUT) &
5035d41762aSTakashi Iwai 		    (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
5045d41762aSTakashi Iwai 			snd_hda_codec_write(codec, nid, 0,
5055d41762aSTakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
5065d41762aSTakashi Iwai 					    AMP_OUT_UNMUTE);
5075d41762aSTakashi Iwai 	}
5085d41762aSTakashi Iwai }
5095d41762aSTakashi Iwai 
510c577b8a1SJoseph Chan 
511c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
512c577b8a1SJoseph Chan {
513c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
514c577b8a1SJoseph Chan 	int i;
515c577b8a1SJoseph Chan 
516*e3d7a143STakashi Iwai 	for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
5175d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
5185d41762aSTakashi Iwai 				     PIN_OUT, &spec->out_path[i]);
519c577b8a1SJoseph Chan }
520c577b8a1SJoseph Chan 
521c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
522c577b8a1SJoseph Chan {
523c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
524c577b8a1SJoseph Chan 
5255d41762aSTakashi Iwai 	if (spec->hp_dac_nid)
5265d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
5275d41762aSTakashi Iwai 				     &spec->hp_path);
5285d41762aSTakashi Iwai 	else
5295d41762aSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
5305d41762aSTakashi Iwai 				     &spec->hp_dep_path);
53125eaba2fSLydia Wang }
532c577b8a1SJoseph Chan 
5334a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec)
5344a918ffeSTakashi Iwai {
5354a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
5364a918ffeSTakashi Iwai 
5374a918ffeSTakashi Iwai 	if (spec->autocfg.speaker_outs)
5384a918ffeSTakashi Iwai 		via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
5394a918ffeSTakashi Iwai 				     PIN_OUT, &spec->speaker_path);
5404a918ffeSTakashi Iwai }
5414a918ffeSTakashi Iwai 
542f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
54332e0191dSClemens Ladisch 
544c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
545c577b8a1SJoseph Chan {
546c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5477b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
548096a8854STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
54932e0191dSClemens Ladisch 	unsigned int ctl;
550096a8854STakashi Iwai 	int i, num_conns;
551c577b8a1SJoseph Chan 
552096a8854STakashi Iwai 	/* init ADCs */
553096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
554096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
555096a8854STakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
556096a8854STakashi Iwai 				    AMP_IN_UNMUTE(0));
557096a8854STakashi Iwai 	}
558096a8854STakashi Iwai 
559096a8854STakashi Iwai 	/* init pins */
5607b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5617b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
562f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
56332e0191dSClemens Ladisch 			ctl = PIN_OUT;
56430649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
56532e0191dSClemens Ladisch 			ctl = PIN_VREF50;
56632e0191dSClemens Ladisch 		else
56732e0191dSClemens Ladisch 			ctl = PIN_IN;
568c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
56932e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
570c577b8a1SJoseph Chan 	}
571096a8854STakashi Iwai 
572096a8854STakashi Iwai 	/* init input-src */
573096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
574096a8854STakashi Iwai 		const struct hda_input_mux *imux = spec->input_mux;
575096a8854STakashi Iwai 		if (!imux || !spec->mux_nids[i])
576096a8854STakashi Iwai 			continue;
577096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->mux_nids[i], 0,
578096a8854STakashi Iwai 				    AC_VERB_SET_CONNECT_SEL,
579096a8854STakashi Iwai 				    imux->items[spec->cur_mux[i]].index);
580096a8854STakashi Iwai 	}
581096a8854STakashi Iwai 
582096a8854STakashi Iwai 	/* init aa-mixer */
583096a8854STakashi Iwai 	if (!spec->aa_mix_nid)
584096a8854STakashi Iwai 		return;
585096a8854STakashi Iwai 	num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
586096a8854STakashi Iwai 					    ARRAY_SIZE(conn));
587096a8854STakashi Iwai 	for (i = 0; i < num_conns; i++) {
588096a8854STakashi Iwai 		unsigned int caps = get_wcaps(codec, conn[i]);
589096a8854STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_PIN)
590096a8854STakashi Iwai 			snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
591096a8854STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
592096a8854STakashi Iwai 					    AMP_IN_MUTE(i));
593096a8854STakashi Iwai 	}
594c577b8a1SJoseph Chan }
595f5271101SLydia Wang 
596f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
597f5271101SLydia Wang 				unsigned int *affected_parm)
598f5271101SLydia Wang {
599f5271101SLydia Wang 	unsigned parm;
600f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
601f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
602f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
603f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
6041564b287SLydia Wang 	struct via_spec *spec = codec->spec;
60524088a58STakashi Iwai 	unsigned present = 0;
60624088a58STakashi Iwai 
60724088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
60824088a58STakashi Iwai 	if (!no_presence)
60924088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
610f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
6111564b287SLydia Wang 	    || ((no_presence || present)
6121564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
613f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
614f5271101SLydia Wang 		parm = AC_PWRST_D0;
615f5271101SLydia Wang 	} else
616f5271101SLydia Wang 		parm = AC_PWRST_D3;
617f5271101SLydia Wang 
618f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
619f5271101SLydia Wang }
620f5271101SLydia Wang 
62124088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
62224088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
62324088a58STakashi Iwai {
62424088a58STakashi Iwai 	static const char * const texts[] = {
62524088a58STakashi Iwai 		"Disabled", "Enabled"
62624088a58STakashi Iwai 	};
62724088a58STakashi Iwai 
62824088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
62924088a58STakashi Iwai 	uinfo->count = 1;
63024088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
63124088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
63224088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
63324088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
63424088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
63524088a58STakashi Iwai 	return 0;
63624088a58STakashi Iwai }
63724088a58STakashi Iwai 
63824088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
63924088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
64024088a58STakashi Iwai {
64124088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
64224088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
64324088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
64424088a58STakashi Iwai 	return 0;
64524088a58STakashi Iwai }
64624088a58STakashi Iwai 
64724088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
64824088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
64924088a58STakashi Iwai {
65024088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
65124088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
65224088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
65324088a58STakashi Iwai 
65424088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
65524088a58STakashi Iwai 		return 0;
65624088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
65724088a58STakashi Iwai 	set_widgets_power_state(codec);
65824088a58STakashi Iwai 	return 1;
65924088a58STakashi Iwai }
66024088a58STakashi Iwai 
66124088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
66224088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
66324088a58STakashi Iwai 	.name = "Dynamic Power-Control",
66424088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
66524088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
66624088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
66724088a58STakashi Iwai };
66824088a58STakashi Iwai 
66924088a58STakashi Iwai 
670c577b8a1SJoseph Chan /*
671c577b8a1SJoseph Chan  * input MUX handling
672c577b8a1SJoseph Chan  */
673c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
674c577b8a1SJoseph Chan 			     struct snd_ctl_elem_info *uinfo)
675c577b8a1SJoseph Chan {
676c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
677c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
678c577b8a1SJoseph Chan 	return snd_hda_input_mux_info(spec->input_mux, uinfo);
679c577b8a1SJoseph Chan }
680c577b8a1SJoseph Chan 
681c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
682c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
683c577b8a1SJoseph Chan {
684c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
685c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
686c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
687c577b8a1SJoseph Chan 
688c577b8a1SJoseph Chan 	ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
689c577b8a1SJoseph Chan 	return 0;
690c577b8a1SJoseph Chan }
691c577b8a1SJoseph Chan 
692c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
693c577b8a1SJoseph Chan 			    struct snd_ctl_elem_value *ucontrol)
694c577b8a1SJoseph Chan {
695c577b8a1SJoseph Chan 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
696c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
697c577b8a1SJoseph Chan 	unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
698bff5fbf5SLydia Wang 	int ret;
699c577b8a1SJoseph Chan 
700337b9d02STakashi Iwai 	if (!spec->mux_nids[adc_idx])
701337b9d02STakashi Iwai 		return -EINVAL;
702a80e6e3cSLydia Wang 	/* switch to D0 beofre change index */
703a80e6e3cSLydia Wang 	if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
704a80e6e3cSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
705a80e6e3cSLydia Wang 		snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
706a80e6e3cSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
707bff5fbf5SLydia Wang 
708bff5fbf5SLydia Wang 	ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
709bff5fbf5SLydia Wang 				     spec->mux_nids[adc_idx],
710bff5fbf5SLydia Wang 				     &spec->cur_mux[adc_idx]);
711a80e6e3cSLydia Wang 	/* update jack power state */
7123e95b9abSLydia Wang 	set_widgets_power_state(codec);
713a80e6e3cSLydia Wang 
714bff5fbf5SLydia Wang 	return ret;
715c577b8a1SJoseph Chan }
716c577b8a1SJoseph Chan 
7170aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
7180aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
7190aa62aefSHarald Welte {
7200aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7210aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7220aa62aefSHarald Welte 	return snd_hda_input_mux_info(spec->hp_mux, uinfo);
7230aa62aefSHarald Welte }
7240aa62aefSHarald Welte 
7250aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7260aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7270aa62aefSHarald Welte {
7280aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
729cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
730cdc1784dSLydia Wang 
731ece8d043STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
732cdc1784dSLydia Wang 	return 0;
733cdc1784dSLydia Wang }
734cdc1784dSLydia Wang 
7350aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7360aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7370aa62aefSHarald Welte {
7380aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7390aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7405b0cb1d8SJaroslav Kysela 	hda_nid_t nid = kcontrol->private_value;
7410aa62aefSHarald Welte 	unsigned int pinsel = ucontrol->value.enumerated.item[0];
742cdc1784dSLydia Wang 	/* Get Independent Mode index of headphone pin widget */
743cdc1784dSLydia Wang 	spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
744cdc1784dSLydia Wang 		? 1 : 0;
745ece8d043STakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
7460aa62aefSHarald Welte 
747ce0e5a9eSLydia Wang 	/* update jack power state */
7483e95b9abSLydia Wang 	set_widgets_power_state(codec);
7490aa62aefSHarald Welte 	return 0;
7500aa62aefSHarald Welte }
7510aa62aefSHarald Welte 
752ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = {
7530aa62aefSHarald Welte 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7540aa62aefSHarald Welte 	.name = "Independent HP",
7550aa62aefSHarald Welte 	.info = via_independent_hp_info,
7560aa62aefSHarald Welte 	.get = via_independent_hp_get,
7570aa62aefSHarald Welte 	.put = via_independent_hp_put,
7580aa62aefSHarald Welte };
7590aa62aefSHarald Welte 
7603d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
7615b0cb1d8SJaroslav Kysela {
7623d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
7635b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
7645b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
7655b0cb1d8SJaroslav Kysela 
7665b0cb1d8SJaroslav Kysela 	nid = spec->autocfg.hp_pins[0];
767ece8d043STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer);
7683d83e577STakashi Iwai 	if (knew == NULL)
7693d83e577STakashi Iwai 		return -ENOMEM;
7703d83e577STakashi Iwai 
7715b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
7725b0cb1d8SJaroslav Kysela 	knew->private_value = nid;
7735b0cb1d8SJaroslav Kysela 
7745b0cb1d8SJaroslav Kysela 	return 0;
7755b0cb1d8SJaroslav Kysela }
7765b0cb1d8SJaroslav Kysela 
7771564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
7781564b287SLydia Wang {
779*e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
7801564b287SLydia Wang 	int i;
7811564b287SLydia Wang 
782*e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
783*e3d7a143STakashi Iwai 		struct snd_kcontrol *ctl;
784*e3d7a143STakashi Iwai 		struct snd_ctl_elem_id id;
7851564b287SLydia Wang 		memset(&id, 0, sizeof(id));
7861564b287SLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
787*e3d7a143STakashi Iwai 		sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
788525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
789525566cbSLydia Wang 		if (ctl)
790525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
791525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
792525566cbSLydia Wang 					&ctl->id);
7931564b287SLydia Wang 	}
7941564b287SLydia Wang }
7951564b287SLydia Wang 
7961564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
7971564b287SLydia Wang {
7981564b287SLydia Wang 	struct via_spec *spec = codec->spec;
7991564b287SLydia Wang 	int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
800*e3d7a143STakashi Iwai 	int i;
801*e3d7a143STakashi Iwai 
802*e3d7a143STakashi Iwai 	/* check AA path's mute status */
803*e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
804*e3d7a143STakashi Iwai 		if (spec->smart51_idxs[i] < 0)
805*e3d7a143STakashi Iwai 			continue;
806*e3d7a143STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
807*e3d7a143STakashi Iwai 					 HDA_INPUT, spec->smart51_idxs[i],
8081564b287SLydia Wang 					 HDA_AMP_MUTE, val);
8091564b287SLydia Wang 	}
8101564b287SLydia Wang }
811f4a7828bSTakashi Iwai 
812*e3d7a143STakashi Iwai static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
8131564b287SLydia Wang {
814f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
8157b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
8167b315bb4STakashi Iwai 	int i;
8177b315bb4STakashi Iwai 
8187b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
819f4a7828bSTakashi Iwai 		unsigned int defcfg;
820f4a7828bSTakashi Iwai 		if (pin != cfg->inputs[i].pin)
821f4a7828bSTakashi Iwai 			continue;
822f4a7828bSTakashi Iwai 		if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
823f4a7828bSTakashi Iwai 			return false;
824f4a7828bSTakashi Iwai 		defcfg = snd_hda_codec_get_pincfg(codec, pin);
825f4a7828bSTakashi Iwai 		if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
826f4a7828bSTakashi Iwai 			return false;
827f4a7828bSTakashi Iwai 		return true;
8281564b287SLydia Wang 	}
829f4a7828bSTakashi Iwai 	return false;
8301564b287SLydia Wang }
8311564b287SLydia Wang 
832*e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
833*e3d7a143STakashi Iwai {
834*e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
835*e3d7a143STakashi Iwai 	int i;
836*e3d7a143STakashi Iwai 
837*e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++)
838*e3d7a143STakashi Iwai 		if (spec->smart51_pins[i] == pin)
839*e3d7a143STakashi Iwai 			return true;
840*e3d7a143STakashi Iwai 	return false;
841*e3d7a143STakashi Iwai }
842*e3d7a143STakashi Iwai 
8431564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol,
8441564b287SLydia Wang 			    struct snd_ctl_elem_info *uinfo)
8451564b287SLydia Wang {
8461564b287SLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
8471564b287SLydia Wang 	uinfo->count = 1;
8481564b287SLydia Wang 	uinfo->value.integer.min = 0;
8491564b287SLydia Wang 	uinfo->value.integer.max = 1;
8501564b287SLydia Wang 	return 0;
8511564b287SLydia Wang }
8521564b287SLydia Wang 
8531564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
8541564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8551564b287SLydia Wang {
8561564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8571564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8581564b287SLydia Wang 	int on = 1;
8591564b287SLydia Wang 	int i;
8601564b287SLydia Wang 
861*e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
862*e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
863f4a7828bSTakashi Iwai 		unsigned int ctl;
864f4a7828bSTakashi Iwai 		ctl = snd_hda_codec_read(codec, nid, 0,
865f4a7828bSTakashi Iwai 					 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8667b315bb4STakashi Iwai 		if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
8671564b287SLydia Wang 			on = 0;
8681564b287SLydia Wang 	}
8691564b287SLydia Wang 	*ucontrol->value.integer.value = on;
8701564b287SLydia Wang 	return 0;
8711564b287SLydia Wang }
8721564b287SLydia Wang 
8731564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
8741564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8751564b287SLydia Wang {
8761564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8771564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8781564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
8791564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
8801564b287SLydia Wang 	int i;
8811564b287SLydia Wang 
882*e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
883*e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
8847b315bb4STakashi Iwai 		unsigned int parm;
8857b315bb4STakashi Iwai 
8867b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
8871564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8881564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
8891564b287SLydia Wang 		parm |= out_in;
8901564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
8911564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
8921564b287SLydia Wang 				    parm);
8931564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
8941564b287SLydia Wang 			mute_aa_path(codec, 1);
8951564b287SLydia Wang 			notify_aa_path_ctls(codec);
8961564b287SLydia Wang 		}
8971564b287SLydia Wang 	}
8981564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
8993e95b9abSLydia Wang 	set_widgets_power_state(codec);
9001564b287SLydia Wang 	return 1;
9011564b287SLydia Wang }
9021564b287SLydia Wang 
9035f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
9041564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
9051564b287SLydia Wang 	.name = "Smart 5.1",
9061564b287SLydia Wang 	.count = 1,
9071564b287SLydia Wang 	.info = via_smart51_info,
9081564b287SLydia Wang 	.get = via_smart51_get,
9091564b287SLydia Wang 	.put = via_smart51_put,
9101564b287SLydia Wang };
9111564b287SLydia Wang 
912f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
9135b0cb1d8SJaroslav Kysela {
914f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
9155b0cb1d8SJaroslav Kysela 
916*e3d7a143STakashi Iwai 	if (!spec->smart51_nums)
917cb34c207SLydia Wang 		return 0;
918*e3d7a143STakashi Iwai 	if (!via_clone_control(spec, &via_smart51_mixer))
9195b0cb1d8SJaroslav Kysela 		return -ENOMEM;
9205b0cb1d8SJaroslav Kysela 	return 0;
9215b0cb1d8SJaroslav Kysela }
9225b0cb1d8SJaroslav Kysela 
923f5271101SLydia Wang /* check AA path's mute statue */
924f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec)
925f5271101SLydia Wang {
926f5271101SLydia Wang 	int mute = 1;
927f5271101SLydia Wang 	int start_idx;
928f5271101SLydia Wang 	int end_idx;
929f5271101SLydia Wang 	int i;
930f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
931f5271101SLydia Wang 	/* get nid of MW0 and start & end index */
932f5271101SLydia Wang 	switch (spec->codec_type) {
933f5271101SLydia Wang 	case VT1708B_8CH:
934f5271101SLydia Wang 	case VT1708B_4CH:
935f5271101SLydia Wang 	case VT1708S:
936f3db423dSLydia Wang 	case VT1716S:
937f5271101SLydia Wang 		start_idx = 2;
938f5271101SLydia Wang 		end_idx = 4;
939f5271101SLydia Wang 		break;
940f5271101SLydia Wang 	case VT1702:
941f5271101SLydia Wang 		start_idx = 1;
942f5271101SLydia Wang 		end_idx = 3;
943f5271101SLydia Wang 		break;
944eb7188caSLydia Wang 	case VT1718S:
945eb7188caSLydia Wang 		start_idx = 1;
946eb7188caSLydia Wang 		end_idx = 3;
947eb7188caSLydia Wang 		break;
94825eaba2fSLydia Wang 	case VT2002P:
949ab6734e7SLydia Wang 	case VT1812:
95011890956SLydia Wang 	case VT1802:
95125eaba2fSLydia Wang 		start_idx = 0;
95225eaba2fSLydia Wang 		end_idx = 2;
95325eaba2fSLydia Wang 		break;
954f5271101SLydia Wang 	default:
955f5271101SLydia Wang 		return 0;
956f5271101SLydia Wang 	}
957f5271101SLydia Wang 	/* check AA path's mute status */
958f5271101SLydia Wang 	for (i = start_idx; i <= end_idx; i++) {
959f5271101SLydia Wang 		unsigned int con_list = snd_hda_codec_read(
960620e2b28STakashi Iwai 			codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
961f5271101SLydia Wang 		int shift = 8 * (i % 4);
962f5271101SLydia Wang 		hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
963f5271101SLydia Wang 		unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
964f5271101SLydia Wang 		if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
965f5271101SLydia Wang 			/* check mute status while the pin is connected */
966620e2b28STakashi Iwai 			int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
967f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
968620e2b28STakashi Iwai 			int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
969f5271101SLydia Wang 							    HDA_INPUT, i) >> 7;
970f5271101SLydia Wang 			if (!mute_l || !mute_r) {
971f5271101SLydia Wang 				mute = 0;
972f5271101SLydia Wang 				break;
973f5271101SLydia Wang 			}
974f5271101SLydia Wang 		}
975f5271101SLydia Wang 	}
976f5271101SLydia Wang 	return mute;
977f5271101SLydia Wang }
978f5271101SLydia Wang 
979f5271101SLydia Wang /* enter/exit analog low-current mode */
980f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
981f5271101SLydia Wang {
982f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
983f5271101SLydia Wang 	static int saved_stream_idle = 1; /* saved stream idle status */
984f5271101SLydia Wang 	int enable = is_aa_path_mute(codec);
985f5271101SLydia Wang 	unsigned int verb = 0;
986f5271101SLydia Wang 	unsigned int parm = 0;
987f5271101SLydia Wang 
988f5271101SLydia Wang 	if (stream_idle == -1)	/* stream status did not change */
989f5271101SLydia Wang 		enable = enable && saved_stream_idle;
990f5271101SLydia Wang 	else {
991f5271101SLydia Wang 		enable = enable && stream_idle;
992f5271101SLydia Wang 		saved_stream_idle = stream_idle;
993f5271101SLydia Wang 	}
994f5271101SLydia Wang 
995f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
996f5271101SLydia Wang 	switch (spec->codec_type) {
997f5271101SLydia Wang 	case VT1708B_8CH:
998f5271101SLydia Wang 	case VT1708B_4CH:
999f5271101SLydia Wang 		verb = 0xf70;
1000f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1001f5271101SLydia Wang 		break;
1002f5271101SLydia Wang 	case VT1708S:
1003eb7188caSLydia Wang 	case VT1718S:
1004f3db423dSLydia Wang 	case VT1716S:
1005f5271101SLydia Wang 		verb = 0xf73;
1006f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1007f5271101SLydia Wang 		break;
1008f5271101SLydia Wang 	case VT1702:
1009f5271101SLydia Wang 		verb = 0xf73;
1010f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1011f5271101SLydia Wang 		break;
101225eaba2fSLydia Wang 	case VT2002P:
1013ab6734e7SLydia Wang 	case VT1812:
101411890956SLydia Wang 	case VT1802:
101525eaba2fSLydia Wang 		verb = 0xf93;
101625eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
101725eaba2fSLydia Wang 		break;
1018f5271101SLydia Wang 	default:
1019f5271101SLydia Wang 		return;		/* other codecs are not supported */
1020f5271101SLydia Wang 	}
1021f5271101SLydia Wang 	/* send verb */
1022f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1023f5271101SLydia Wang }
1024f5271101SLydia Wang 
1025c577b8a1SJoseph Chan /*
1026c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
1027c577b8a1SJoseph Chan  */
1028096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
1029aa266fccSLydia Wang 	/* power down jack detect function */
1030aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
1031f7278fd0SJosepch Chan 	{ }
1032c577b8a1SJoseph Chan };
1033c577b8a1SJoseph Chan 
10347eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec,
10357eb56e84STakashi Iwai 			       struct snd_pcm_substream *substream)
10367eb56e84STakashi Iwai {
10377eb56e84STakashi Iwai 	int idle = substream->pstr->substream_opened == 1
10387eb56e84STakashi Iwai 		&& substream->ref_count == 0;
10397eb56e84STakashi Iwai 	analog_low_current_mode(codec, idle);
10407eb56e84STakashi Iwai }
10417eb56e84STakashi Iwai 
1042ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
1043c577b8a1SJoseph Chan 				 struct hda_codec *codec,
1044c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
1045c577b8a1SJoseph Chan {
1046c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1047ece8d043STakashi Iwai 
1048ece8d043STakashi Iwai 	if (!spec->hp_independent_mode)
1049ece8d043STakashi Iwai 		spec->multiout.hp_nid = spec->hp_dac_nid;
10507eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
10519a08160bSTakashi Iwai 	return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
10529a08160bSTakashi Iwai 					     hinfo);
1053c577b8a1SJoseph Chan }
1054c577b8a1SJoseph Chan 
1055ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
10569af74210STakashi Iwai 				  struct hda_codec *codec,
10579af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
10589af74210STakashi Iwai {
1059ece8d043STakashi Iwai 	struct via_spec *spec = codec->spec;
1060ece8d043STakashi Iwai 
1061ece8d043STakashi Iwai 	spec->multiout.hp_nid = 0;
10627eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
10639af74210STakashi Iwai 	return 0;
10649af74210STakashi Iwai }
10659af74210STakashi Iwai 
10667eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
10677eb56e84STakashi Iwai 				    struct hda_codec *codec,
10687eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
10697eb56e84STakashi Iwai {
10707eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
10717eb56e84STakashi Iwai 
1072ece8d043STakashi Iwai 	if (snd_BUG_ON(!spec->hp_dac_nid))
10737eb56e84STakashi Iwai 		return -EINVAL;
1074ece8d043STakashi Iwai 	if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1075ece8d043STakashi Iwai 		return -EBUSY;
1076ece8d043STakashi Iwai 	substream_set_idle(codec, substream);
1077ece8d043STakashi Iwai 	return 0;
1078ece8d043STakashi Iwai }
1079ece8d043STakashi Iwai 
1080ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1081ece8d043STakashi Iwai 				     struct hda_codec *codec,
1082ece8d043STakashi Iwai 				     struct snd_pcm_substream *substream)
1083ece8d043STakashi Iwai {
10847eb56e84STakashi Iwai 	substream_set_idle(codec, substream);
10857eb56e84STakashi Iwai 	return 0;
10867eb56e84STakashi Iwai }
10877eb56e84STakashi Iwai 
10887eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
10897eb56e84STakashi Iwai 					  struct hda_codec *codec,
10900aa62aefSHarald Welte 					  unsigned int stream_tag,
10910aa62aefSHarald Welte 					  unsigned int format,
10920aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
10930aa62aefSHarald Welte {
10940aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10950aa62aefSHarald Welte 
1096ece8d043STakashi Iwai 	snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1097ece8d043STakashi Iwai 					 format, substream);
10987eb56e84STakashi Iwai 	vt1708_start_hp_work(spec);
10997eb56e84STakashi Iwai 	return 0;
11000aa62aefSHarald Welte }
11010aa62aefSHarald Welte 
11027eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
11030aa62aefSHarald Welte 				       struct hda_codec *codec,
11040aa62aefSHarald Welte 				       unsigned int stream_tag,
11050aa62aefSHarald Welte 				       unsigned int format,
11060aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
11070aa62aefSHarald Welte {
11080aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11090aa62aefSHarald Welte 
1110ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1111ece8d043STakashi Iwai 				   stream_tag, 0, format);
11121f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
11130aa62aefSHarald Welte 	return 0;
11140aa62aefSHarald Welte }
11150aa62aefSHarald Welte 
11160aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
11170aa62aefSHarald Welte 				    struct hda_codec *codec,
11180aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
11190aa62aefSHarald Welte {
11200aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
11210aa62aefSHarald Welte 
1122ece8d043STakashi Iwai 	snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
11237eb56e84STakashi Iwai 	vt1708_stop_hp_work(spec);
11247eb56e84STakashi Iwai 	return 0;
11250aa62aefSHarald Welte }
11267eb56e84STakashi Iwai 
11277eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
11287eb56e84STakashi Iwai 				       struct hda_codec *codec,
11297eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
11307eb56e84STakashi Iwai {
11317eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
11327eb56e84STakashi Iwai 
1133ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
11341f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
11350aa62aefSHarald Welte 	return 0;
11360aa62aefSHarald Welte }
11370aa62aefSHarald Welte 
1138c577b8a1SJoseph Chan /*
1139c577b8a1SJoseph Chan  * Digital out
1140c577b8a1SJoseph Chan  */
1141c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1142c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1143c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1144c577b8a1SJoseph Chan {
1145c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1146c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1147c577b8a1SJoseph Chan }
1148c577b8a1SJoseph Chan 
1149c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1150c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1151c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1152c577b8a1SJoseph Chan {
1153c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1154c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1155c577b8a1SJoseph Chan }
1156c577b8a1SJoseph Chan 
11575691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
115898aa34c0SHarald Welte 					struct hda_codec *codec,
115998aa34c0SHarald Welte 					unsigned int stream_tag,
116098aa34c0SHarald Welte 					unsigned int format,
116198aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
116298aa34c0SHarald Welte {
116398aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
11649da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
11659da29271STakashi Iwai 					     stream_tag, format, substream);
11669da29271STakashi Iwai }
11675691ec7fSHarald Welte 
11689da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
11699da29271STakashi Iwai 					struct hda_codec *codec,
11709da29271STakashi Iwai 					struct snd_pcm_substream *substream)
11719da29271STakashi Iwai {
11729da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
11739da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
117498aa34c0SHarald Welte 	return 0;
117598aa34c0SHarald Welte }
117698aa34c0SHarald Welte 
1177c577b8a1SJoseph Chan /*
1178c577b8a1SJoseph Chan  * Analog capture
1179c577b8a1SJoseph Chan  */
1180c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1181c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1182c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1183c577b8a1SJoseph Chan 				   unsigned int format,
1184c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1185c577b8a1SJoseph Chan {
1186c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1187c577b8a1SJoseph Chan 
1188c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1189c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1190c577b8a1SJoseph Chan 	return 0;
1191c577b8a1SJoseph Chan }
1192c577b8a1SJoseph Chan 
1193c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1194c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1195c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1196c577b8a1SJoseph Chan {
1197c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1198888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1199c577b8a1SJoseph Chan 	return 0;
1200c577b8a1SJoseph Chan }
1201c577b8a1SJoseph Chan 
12029af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
12037eb56e84STakashi Iwai 	.substreams = 1,
1204c577b8a1SJoseph Chan 	.channels_min = 2,
1205c577b8a1SJoseph Chan 	.channels_max = 8,
12069af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1207c577b8a1SJoseph Chan 	.ops = {
1208ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1209ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
12100aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
12110aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1212c577b8a1SJoseph Chan 	},
1213c577b8a1SJoseph Chan };
1214c577b8a1SJoseph Chan 
12157eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
12167eb56e84STakashi Iwai 	.substreams = 1,
12177eb56e84STakashi Iwai 	.channels_min = 2,
12187eb56e84STakashi Iwai 	.channels_max = 2,
12197eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
12207eb56e84STakashi Iwai 	.ops = {
12217eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
1222ece8d043STakashi Iwai 		.close = via_playback_hp_pcm_close,
12237eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
12247eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
12257eb56e84STakashi Iwai 	},
12267eb56e84STakashi Iwai };
12277eb56e84STakashi Iwai 
122890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
12297eb56e84STakashi Iwai 	.substreams = 1,
1230bc9b5623STakashi Iwai 	.channels_min = 2,
1231bc9b5623STakashi Iwai 	.channels_max = 8,
12329af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1233bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1234bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1235bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1236bc9b5623STakashi Iwai 	 */
1237bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1238bc9b5623STakashi Iwai 	.ops = {
1239ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1240ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
1241c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1242c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1243bc9b5623STakashi Iwai 	},
1244bc9b5623STakashi Iwai };
1245bc9b5623STakashi Iwai 
12469af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
12477eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1248c577b8a1SJoseph Chan 	.channels_min = 2,
1249c577b8a1SJoseph Chan 	.channels_max = 2,
12509af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1251c577b8a1SJoseph Chan 	.ops = {
1252c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1253c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1254c577b8a1SJoseph Chan 	},
1255c577b8a1SJoseph Chan };
1256c577b8a1SJoseph Chan 
12579af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1258c577b8a1SJoseph Chan 	.substreams = 1,
1259c577b8a1SJoseph Chan 	.channels_min = 2,
1260c577b8a1SJoseph Chan 	.channels_max = 2,
1261c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1262c577b8a1SJoseph Chan 	.ops = {
1263c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
12646b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
12659da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
12669da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1267c577b8a1SJoseph Chan 	},
1268c577b8a1SJoseph Chan };
1269c577b8a1SJoseph Chan 
12709af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1271c577b8a1SJoseph Chan 	.substreams = 1,
1272c577b8a1SJoseph Chan 	.channels_min = 2,
1273c577b8a1SJoseph Chan 	.channels_max = 2,
1274c577b8a1SJoseph Chan };
1275c577b8a1SJoseph Chan 
1276370bafbdSTakashi Iwai /*
1277370bafbdSTakashi Iwai  * slave controls for virtual master
1278370bafbdSTakashi Iwai  */
1279370bafbdSTakashi Iwai static const char * const via_slave_vols[] = {
1280370bafbdSTakashi Iwai 	"Front Playback Volume",
1281370bafbdSTakashi Iwai 	"Surround Playback Volume",
1282370bafbdSTakashi Iwai 	"Center Playback Volume",
1283370bafbdSTakashi Iwai 	"LFE Playback Volume",
1284370bafbdSTakashi Iwai 	"Side Playback Volume",
1285370bafbdSTakashi Iwai 	"Headphone Playback Volume",
1286370bafbdSTakashi Iwai 	"Speaker Playback Volume",
1287370bafbdSTakashi Iwai 	NULL,
1288370bafbdSTakashi Iwai };
1289370bafbdSTakashi Iwai 
1290370bafbdSTakashi Iwai static const char * const via_slave_sws[] = {
1291370bafbdSTakashi Iwai 	"Front Playback Switch",
1292370bafbdSTakashi Iwai 	"Surround Playback Switch",
1293370bafbdSTakashi Iwai 	"Center Playback Switch",
1294370bafbdSTakashi Iwai 	"LFE Playback Switch",
1295370bafbdSTakashi Iwai 	"Side Playback Switch",
1296370bafbdSTakashi Iwai 	"Headphone Playback Switch",
1297370bafbdSTakashi Iwai 	"Speaker Playback Switch",
1298370bafbdSTakashi Iwai 	NULL,
1299370bafbdSTakashi Iwai };
1300370bafbdSTakashi Iwai 
1301c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1302c577b8a1SJoseph Chan {
1303c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
13045b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
130590dd48a1STakashi Iwai 	const struct snd_kcontrol_new *knew;
13065b0cb1d8SJaroslav Kysela 	int err, i;
1307c577b8a1SJoseph Chan 
130824088a58STakashi Iwai 	if (spec->set_widgets_power_state)
130924088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
131024088a58STakashi Iwai 			return -ENOMEM;
131124088a58STakashi Iwai 
1312c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1313c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1314c577b8a1SJoseph Chan 		if (err < 0)
1315c577b8a1SJoseph Chan 			return err;
1316c577b8a1SJoseph Chan 	}
1317c577b8a1SJoseph Chan 
1318c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1319c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
132074b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1321c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1322c577b8a1SJoseph Chan 		if (err < 0)
1323c577b8a1SJoseph Chan 			return err;
13249a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
13259a08160bSTakashi Iwai 						    &spec->multiout);
13269a08160bSTakashi Iwai 		if (err < 0)
13279a08160bSTakashi Iwai 			return err;
13289a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1329c577b8a1SJoseph Chan 	}
1330c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1331c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1332c577b8a1SJoseph Chan 		if (err < 0)
1333c577b8a1SJoseph Chan 			return err;
1334c577b8a1SJoseph Chan 	}
133517314379SLydia Wang 
1336370bafbdSTakashi Iwai 	/* if we have no master control, let's create it */
1337370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1338370bafbdSTakashi Iwai 		unsigned int vmaster_tlv[4];
1339370bafbdSTakashi Iwai 		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1340370bafbdSTakashi Iwai 					HDA_OUTPUT, vmaster_tlv);
1341370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1342370bafbdSTakashi Iwai 					  vmaster_tlv, via_slave_vols);
1343370bafbdSTakashi Iwai 		if (err < 0)
1344370bafbdSTakashi Iwai 			return err;
1345370bafbdSTakashi Iwai 	}
1346370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1347370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1348370bafbdSTakashi Iwai 					  NULL, via_slave_sws);
1349370bafbdSTakashi Iwai 		if (err < 0)
1350370bafbdSTakashi Iwai 			return err;
1351370bafbdSTakashi Iwai 	}
1352370bafbdSTakashi Iwai 
13535b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
13545b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
13555b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
135621949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
13575b0cb1d8SJaroslav Kysela 		if (err < 0)
13585b0cb1d8SJaroslav Kysela 			return err;
13595b0cb1d8SJaroslav Kysela 	}
13605b0cb1d8SJaroslav Kysela 
13615b0cb1d8SJaroslav Kysela 	/* other nid->control mapping */
13625b0cb1d8SJaroslav Kysela 	for (i = 0; i < spec->num_mixers; i++) {
13635b0cb1d8SJaroslav Kysela 		for (knew = spec->mixers[i]; knew->name; knew++) {
13645b0cb1d8SJaroslav Kysela 			if (knew->iface != NID_MAPPING)
13655b0cb1d8SJaroslav Kysela 				continue;
13665b0cb1d8SJaroslav Kysela 			kctl = snd_hda_find_mixer_ctl(codec, knew->name);
13675b0cb1d8SJaroslav Kysela 			if (kctl == NULL)
13685b0cb1d8SJaroslav Kysela 				continue;
13695b0cb1d8SJaroslav Kysela 			err = snd_hda_add_nid(codec, kctl, 0,
13705b0cb1d8SJaroslav Kysela 					      knew->subdevice);
13715b0cb1d8SJaroslav Kysela 		}
13725b0cb1d8SJaroslav Kysela 	}
13735b0cb1d8SJaroslav Kysela 
137417314379SLydia Wang 	/* init power states */
13753e95b9abSLydia Wang 	set_widgets_power_state(codec);
137617314379SLydia Wang 	analog_low_current_mode(codec, 1);
137717314379SLydia Wang 
1378603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1379c577b8a1SJoseph Chan 	return 0;
1380c577b8a1SJoseph Chan }
1381c577b8a1SJoseph Chan 
1382c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1383c577b8a1SJoseph Chan {
1384c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1385c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1386c577b8a1SJoseph Chan 
1387c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1388c577b8a1SJoseph Chan 	codec->pcm_info = info;
1389c577b8a1SJoseph Chan 
139082673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
139182673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1392c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
13939af74210STakashi Iwai 
13949af74210STakashi Iwai 	if (!spec->stream_analog_playback)
13959af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1396377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
13979af74210STakashi Iwai 		*spec->stream_analog_playback;
1398377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1399377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1400c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1401c577b8a1SJoseph Chan 		spec->multiout.max_channels;
14029af74210STakashi Iwai 
14039af74210STakashi Iwai 	if (!spec->stream_analog_capture)
14049af74210STakashi Iwai 		spec->stream_analog_capture = &via_pcm_analog_capture;
14059af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
14069af74210STakashi Iwai 		*spec->stream_analog_capture;
14079af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
14089af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
14099af74210STakashi Iwai 		spec->num_adc_nids;
1410c577b8a1SJoseph Chan 
1411c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1412c577b8a1SJoseph Chan 		codec->num_pcms++;
1413c577b8a1SJoseph Chan 		info++;
141482673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
141582673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
141682673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1417c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
14187ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1419c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
14209af74210STakashi Iwai 			if (!spec->stream_digital_playback)
14219af74210STakashi Iwai 				spec->stream_digital_playback =
14229af74210STakashi Iwai 					&via_pcm_digital_playback;
1423c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
14249af74210STakashi Iwai 				*spec->stream_digital_playback;
1425c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1426c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1427c577b8a1SJoseph Chan 		}
1428c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
14299af74210STakashi Iwai 			if (!spec->stream_digital_capture)
14309af74210STakashi Iwai 				spec->stream_digital_capture =
14319af74210STakashi Iwai 					&via_pcm_digital_capture;
1432c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
14339af74210STakashi Iwai 				*spec->stream_digital_capture;
1434c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1435c577b8a1SJoseph Chan 				spec->dig_in_nid;
1436c577b8a1SJoseph Chan 		}
1437c577b8a1SJoseph Chan 	}
1438c577b8a1SJoseph Chan 
1439ece8d043STakashi Iwai 	if (spec->hp_dac_nid) {
14407eb56e84STakashi Iwai 		codec->num_pcms++;
14417eb56e84STakashi Iwai 		info++;
14427eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
14437eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
14447eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
14457eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
14467eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1447ece8d043STakashi Iwai 			spec->hp_dac_nid;
14487eb56e84STakashi Iwai 	}
1449c577b8a1SJoseph Chan 	return 0;
1450c577b8a1SJoseph Chan }
1451c577b8a1SJoseph Chan 
1452c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1453c577b8a1SJoseph Chan {
1454c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1455c577b8a1SJoseph Chan 
1456c577b8a1SJoseph Chan 	if (!spec)
1457c577b8a1SJoseph Chan 		return;
1458c577b8a1SJoseph Chan 
1459603c4019STakashi Iwai 	via_free_kctls(codec);
14601f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1461c577b8a1SJoseph Chan 	kfree(codec->spec);
1462c577b8a1SJoseph Chan }
1463c577b8a1SJoseph Chan 
146464be285bSTakashi Iwai /* mute/unmute outputs */
146564be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
146664be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
146764be285bSTakashi Iwai {
146864be285bSTakashi Iwai 	int i;
146964be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
147064be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
147164be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
147264be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
147364be285bSTakashi Iwai }
147464be285bSTakashi Iwai 
14754a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */
14764a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present)
14774a918ffeSTakashi Iwai {
14784a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
14794a918ffeSTakashi Iwai 
14804a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs)
14814a918ffeSTakashi Iwai 		return;
14824a918ffeSTakashi Iwai 	if (!present)
14834a918ffeSTakashi Iwai 		present = snd_hda_jack_detect(codec,
14844a918ffeSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
14854a918ffeSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
14864a918ffeSTakashi Iwai 			    spec->autocfg.speaker_pins,
14874a918ffeSTakashi Iwai 			    present);
14884a918ffeSTakashi Iwai }
14894a918ffeSTakashi Iwai 
149069e52a80SHarald Welte /* mute internal speaker if HP is plugged */
149169e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
149269e52a80SHarald Welte {
14934a918ffeSTakashi Iwai 	int present = 0;
149469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
149569e52a80SHarald Welte 
14964a918ffeSTakashi Iwai 	if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1497d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
149864be285bSTakashi Iwai 		toggle_output_mutes(codec, spec->autocfg.line_outs,
149964be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
150064be285bSTakashi Iwai 				    present);
150169e52a80SHarald Welte 	}
15024a918ffeSTakashi Iwai 	via_line_automute(codec, present);
1503f3db423dSLydia Wang }
1504f3db423dSLydia Wang 
150569e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
150669e52a80SHarald Welte {
150769e52a80SHarald Welte 	unsigned int gpio_data;
150869e52a80SHarald Welte 	unsigned int vol_counter;
150969e52a80SHarald Welte 	unsigned int vol;
151069e52a80SHarald Welte 	unsigned int master_vol;
151169e52a80SHarald Welte 
151269e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
151369e52a80SHarald Welte 
151469e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
151569e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
151669e52a80SHarald Welte 
151769e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
151869e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
151969e52a80SHarald Welte 
152069e52a80SHarald Welte 	vol = vol_counter & 0x1F;
152169e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
152269e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
152369e52a80SHarald Welte 					AC_AMP_GET_INPUT);
152469e52a80SHarald Welte 
152569e52a80SHarald Welte 	if (gpio_data == 0x02) {
152669e52a80SHarald Welte 		/* unmute line out */
15273e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
15283e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
15293e0693e2STakashi Iwai 				    PIN_OUT);
153069e52a80SHarald Welte 		if (vol_counter & 0x20) {
153169e52a80SHarald Welte 			/* decrease volume */
153269e52a80SHarald Welte 			if (vol > master_vol)
153369e52a80SHarald Welte 				vol = master_vol;
153469e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
153569e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
153669e52a80SHarald Welte 						 master_vol-vol);
153769e52a80SHarald Welte 		} else {
153869e52a80SHarald Welte 			/* increase volume */
153969e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
154069e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
154169e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
154269e52a80SHarald Welte 					  (master_vol+vol));
154369e52a80SHarald Welte 		}
154469e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
154569e52a80SHarald Welte 		/* mute line out */
15463e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
15473e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
15483e0693e2STakashi Iwai 				    0);
154969e52a80SHarald Welte 	}
155069e52a80SHarald Welte }
155169e52a80SHarald Welte 
155269e52a80SHarald Welte /* unsolicited event for jack sensing */
155369e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
155469e52a80SHarald Welte 				  unsigned int res)
155569e52a80SHarald Welte {
155669e52a80SHarald Welte 	res >>= 26;
1557ec7e7e42SLydia Wang 
1558a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
15593e95b9abSLydia Wang 		set_widgets_power_state(codec);
1560ec7e7e42SLydia Wang 
1561ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1562ec7e7e42SLydia Wang 
1563ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1564ec7e7e42SLydia Wang 		via_hp_automute(codec);
1565ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1566ec7e7e42SLydia Wang 		via_gpio_control(codec);
15674a918ffeSTakashi Iwai 	else if (res == VIA_LINE_EVENT)
15684a918ffeSTakashi Iwai 		via_line_automute(codec, false);
156969e52a80SHarald Welte }
157069e52a80SHarald Welte 
15711f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
15721f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
15731f2e99feSLydia Wang {
15741f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
15751f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
15761f2e99feSLydia Wang 	return 0;
15771f2e99feSLydia Wang }
15781f2e99feSLydia Wang #endif
15791f2e99feSLydia Wang 
1580cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1581cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1582cb53c626STakashi Iwai {
1583cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1584cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1585cb53c626STakashi Iwai }
1586cb53c626STakashi Iwai #endif
1587cb53c626STakashi Iwai 
1588c577b8a1SJoseph Chan /*
1589c577b8a1SJoseph Chan  */
15905d41762aSTakashi Iwai 
15915d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
15925d41762aSTakashi Iwai 
159390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1594c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1595c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1596c577b8a1SJoseph Chan 	.init = via_init,
1597c577b8a1SJoseph Chan 	.free = via_free,
15984a918ffeSTakashi Iwai 	.unsol_event = via_unsol_event,
15991f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
16001f2e99feSLydia Wang 	.suspend = via_suspend,
16011f2e99feSLydia Wang #endif
1602cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1603cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1604cb53c626STakashi Iwai #endif
1605c577b8a1SJoseph Chan };
1606c577b8a1SJoseph Chan 
16074a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1608c577b8a1SJoseph Chan {
16094a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
16104a79616dSTakashi Iwai 	int i;
16114a79616dSTakashi Iwai 
16124a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
16134a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
16144a79616dSTakashi Iwai 			return false;
16154a79616dSTakashi Iwai 	}
1616ece8d043STakashi Iwai 	if (spec->hp_dac_nid == dac)
16174a79616dSTakashi Iwai 		return false;
16184a79616dSTakashi Iwai 	return true;
16194a79616dSTakashi Iwai }
16204a79616dSTakashi Iwai 
16214a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
16224a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
16234a79616dSTakashi Iwai 			      int depth, int wid_type)
16244a79616dSTakashi Iwai {
16254a79616dSTakashi Iwai 	hda_nid_t conn[8];
16264a79616dSTakashi Iwai 	int i, nums;
16274a79616dSTakashi Iwai 
16284a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
16294a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
16304a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
16314a79616dSTakashi Iwai 			continue;
16324a79616dSTakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
16334a79616dSTakashi Iwai 			path->path[depth] = conn[i];
16344a79616dSTakashi Iwai 			path->idx[depth] = i;
16354a79616dSTakashi Iwai 			path->depth = ++depth;
16364a79616dSTakashi Iwai 			return true;
16374a79616dSTakashi Iwai 		}
16384a79616dSTakashi Iwai 	}
16394a79616dSTakashi Iwai 	if (depth > 4)
16404a79616dSTakashi Iwai 		return false;
16414a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
16424a79616dSTakashi Iwai 		unsigned int type;
16434a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
16444a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
16454a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
16464a79616dSTakashi Iwai 			continue;
16474a79616dSTakashi Iwai 		if (parse_output_path(codec, conn[i], target_dac,
16484a79616dSTakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL)) {
16494a79616dSTakashi Iwai 			path->path[depth] = conn[i];
16504a79616dSTakashi Iwai 			path->idx[depth] = i;
16514a79616dSTakashi Iwai 			return true;
16524a79616dSTakashi Iwai 		}
16534a79616dSTakashi Iwai 	}
16544a79616dSTakashi Iwai 	return false;
16554a79616dSTakashi Iwai }
16564a79616dSTakashi Iwai 
16574a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
16584a79616dSTakashi Iwai {
16594a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
16604a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1661c577b8a1SJoseph Chan 	int i;
1662c577b8a1SJoseph Chan 	hda_nid_t nid;
1663c577b8a1SJoseph Chan 
1664c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
16654a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
16664a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1667c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
16684a79616dSTakashi Iwai 		if (!nid)
16694a79616dSTakashi Iwai 			continue;
16704a79616dSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
16714a79616dSTakashi Iwai 			spec->private_dac_nids[i] =
16724a79616dSTakashi Iwai 				spec->out_path[i].path[spec->out_path[i].depth - 1];
1673c577b8a1SJoseph Chan 	}
1674c577b8a1SJoseph Chan 	return 0;
1675c577b8a1SJoseph Chan }
1676c577b8a1SJoseph Chan 
16774a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
16784a79616dSTakashi Iwai 			  hda_nid_t pin, hda_nid_t dac, int chs)
1679c577b8a1SJoseph Chan {
16804a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1681c577b8a1SJoseph Chan 	char name[32];
16824a79616dSTakashi Iwai 	hda_nid_t nid;
16834a79616dSTakashi Iwai 	int err;
1684c577b8a1SJoseph Chan 
16854a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
16864a79616dSTakashi Iwai 		nid = dac;
16874a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
16884a79616dSTakashi Iwai 		nid = pin;
16894a79616dSTakashi Iwai 	else
16904a79616dSTakashi Iwai 		nid = 0;
16914a79616dSTakashi Iwai 	if (nid) {
16924a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1693c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
16944a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1695c577b8a1SJoseph Chan 		if (err < 0)
1696c577b8a1SJoseph Chan 			return err;
1697c577b8a1SJoseph Chan 	}
16984a79616dSTakashi Iwai 
16994a79616dSTakashi Iwai 	if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
17004a79616dSTakashi Iwai 		nid = dac;
17014a79616dSTakashi Iwai 	else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
17024a79616dSTakashi Iwai 		nid = pin;
17034a79616dSTakashi Iwai 	else
17044a79616dSTakashi Iwai 		nid = 0;
17054a79616dSTakashi Iwai 	if (nid) {
17064a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
17074a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
17084a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
17094a79616dSTakashi Iwai 		if (err < 0)
17104a79616dSTakashi Iwai 			return err;
17114a79616dSTakashi Iwai 	}
17124a79616dSTakashi Iwai 	return 0;
17134a79616dSTakashi Iwai }
17144a79616dSTakashi Iwai 
1715f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1716f4a7828bSTakashi Iwai {
1717f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1718f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
1719*e3d7a143STakashi Iwai 	int i, nums = 0;
1720f4a7828bSTakashi Iwai 
1721f4a7828bSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1722*e3d7a143STakashi Iwai 		if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1723*e3d7a143STakashi Iwai 			nums++;
1724*e3d7a143STakashi Iwai 	}
1725*e3d7a143STakashi Iwai 	if (cfg->line_outs + nums < 3)
1726*e3d7a143STakashi Iwai 		return;
1727*e3d7a143STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1728*e3d7a143STakashi Iwai 		if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
1729f4a7828bSTakashi Iwai 			continue;
1730*e3d7a143STakashi Iwai 		spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
1731f4a7828bSTakashi Iwai 		cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1732f4a7828bSTakashi Iwai 		if (cfg->line_outs == 3)
1733f4a7828bSTakashi Iwai 			break;
1734f4a7828bSTakashi Iwai 	}
1735f4a7828bSTakashi Iwai }
1736f4a7828bSTakashi Iwai 
17374a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */
17384a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
17394a79616dSTakashi Iwai {
17404a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1741f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
17424a79616dSTakashi Iwai 	static const char * const chname[4] = {
17434a79616dSTakashi Iwai 		"Front", "Surround", "C/LFE", "Side"
17444a79616dSTakashi Iwai 	};
17454a79616dSTakashi Iwai 	int i, idx, err;
1746f4a7828bSTakashi Iwai 	int old_line_outs;
1747f4a7828bSTakashi Iwai 
1748f4a7828bSTakashi Iwai 	/* check smart51 */
1749f4a7828bSTakashi Iwai 	old_line_outs = cfg->line_outs;
1750f4a7828bSTakashi Iwai 	if (cfg->line_outs == 1)
1751f4a7828bSTakashi Iwai 		mangle_smart51(codec);
17524a79616dSTakashi Iwai 
1753*e3d7a143STakashi Iwai 	err = via_auto_fill_dac_nids(codec);
1754*e3d7a143STakashi Iwai 	if (err < 0)
1755*e3d7a143STakashi Iwai 		return err;
1756*e3d7a143STakashi Iwai 
17574a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
17584a79616dSTakashi Iwai 		hda_nid_t pin, dac;
17594a79616dSTakashi Iwai 		pin = cfg->line_out_pins[i];
17604a79616dSTakashi Iwai 		dac = spec->multiout.dac_nids[i];
17614a79616dSTakashi Iwai 		if (!pin || !dac)
17624a79616dSTakashi Iwai 			continue;
17630fe0adf8STakashi Iwai 		if (i == HDA_CLFE) {
17644a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "Center", pin, dac, 1);
17654a79616dSTakashi Iwai 			if (err < 0)
17664a79616dSTakashi Iwai 				return err;
17674a79616dSTakashi Iwai 			err = create_ch_ctls(codec, "LFE", pin, dac, 2);
17684a79616dSTakashi Iwai 			if (err < 0)
17694a79616dSTakashi Iwai 				return err;
17704a79616dSTakashi Iwai 		} else {
17714a79616dSTakashi Iwai 			err = create_ch_ctls(codec, chname[i], pin, dac, 3);
17724a79616dSTakashi Iwai 			if (err < 0)
17734a79616dSTakashi Iwai 				return err;
17744a79616dSTakashi Iwai 		}
17754a79616dSTakashi Iwai 	}
17764a79616dSTakashi Iwai 
17774a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
17784a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
17794a79616dSTakashi Iwai 	if (idx >= 0) {
17804a79616dSTakashi Iwai 		/* add control to mixer */
17814a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
17824a79616dSTakashi Iwai 				      "PCM Playback Volume",
17834a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17844a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17854a79616dSTakashi Iwai 		if (err < 0)
17864a79616dSTakashi Iwai 			return err;
17874a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
17884a79616dSTakashi Iwai 				      "PCM Playback Switch",
17894a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17904a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17914a79616dSTakashi Iwai 		if (err < 0)
17924a79616dSTakashi Iwai 			return err;
1793c577b8a1SJoseph Chan 	}
1794c577b8a1SJoseph Chan 
1795f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
1796f4a7828bSTakashi Iwai 
1797c577b8a1SJoseph Chan 	return 0;
1798c577b8a1SJoseph Chan }
1799c577b8a1SJoseph Chan 
18000aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec)
18010aa62aefSHarald Welte {
18020aa62aefSHarald Welte 	int i;
18030aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[1];
1804ea734963STakashi Iwai 	static const char * const texts[] = { "OFF", "ON", NULL};
18050aa62aefSHarald Welte 
18060aa62aefSHarald Welte 	/* for hp mode select */
180710a20af7STakashi Iwai 	for (i = 0; texts[i]; i++)
180810a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, texts[i], i, NULL);
18090aa62aefSHarald Welte 
18100aa62aefSHarald Welte 	spec->hp_mux = &spec->private_imux[1];
18110aa62aefSHarald Welte }
18120aa62aefSHarald Welte 
18134a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
1814c577b8a1SJoseph Chan {
18154a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1816c577b8a1SJoseph Chan 	int err;
1817c577b8a1SJoseph Chan 
1818c577b8a1SJoseph Chan 	if (!pin)
1819c577b8a1SJoseph Chan 		return 0;
1820c577b8a1SJoseph Chan 
18214a79616dSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
1822ece8d043STakashi Iwai 		spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
18234a79616dSTakashi Iwai 		spec->hp_independent_mode_index =
18244a79616dSTakashi Iwai 			spec->hp_path.idx[spec->hp_path.depth - 1];
18250aa62aefSHarald Welte 		create_hp_imux(spec);
18264a79616dSTakashi Iwai 	}
18274a79616dSTakashi Iwai 
1828ece8d043STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1829ece8d043STakashi Iwai 			       &spec->hp_dep_path, 0, -1) &&
1830ece8d043STakashi Iwai 	    !spec->hp_dac_nid)
1831ece8d043STakashi Iwai 		return 0;
1832ece8d043STakashi Iwai 
1833ece8d043STakashi Iwai 
1834ece8d043STakashi Iwai 	err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
18354a79616dSTakashi Iwai 	if (err < 0)
18364a79616dSTakashi Iwai 		return err;
18370aa62aefSHarald Welte 
1838c577b8a1SJoseph Chan 	return 0;
1839c577b8a1SJoseph Chan }
1840c577b8a1SJoseph Chan 
18414a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec)
18424a918ffeSTakashi Iwai {
18434a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
18444a918ffeSTakashi Iwai 	hda_nid_t pin, dac;
18454a918ffeSTakashi Iwai 
18464a918ffeSTakashi Iwai 	pin = spec->autocfg.speaker_pins[0];
18474a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs || !pin)
18484a918ffeSTakashi Iwai 		return 0;
18494a918ffeSTakashi Iwai 
18504a918ffeSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
18514a918ffeSTakashi Iwai 		dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
18524a918ffeSTakashi Iwai 		spec->multiout.extra_out_nid[0] = dac;
18534a918ffeSTakashi Iwai 		return create_ch_ctls(codec, "Speaker", pin, dac, 3);
18544a918ffeSTakashi Iwai 	}
18554a918ffeSTakashi Iwai 	if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
18564a918ffeSTakashi Iwai 			      &spec->speaker_path, 0, -1))
18574a918ffeSTakashi Iwai 		return create_ch_ctls(codec, "Headphone", pin, 0, 3);
18584a918ffeSTakashi Iwai 
18594a918ffeSTakashi Iwai 	return 0;
18604a918ffeSTakashi Iwai }
18614a918ffeSTakashi Iwai 
1862a766d0d7STakashi Iwai /* look for ADCs */
1863a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec)
1864a766d0d7STakashi Iwai {
1865a766d0d7STakashi Iwai 	struct via_spec *spec = codec->spec;
1866a766d0d7STakashi Iwai 	hda_nid_t nid = codec->start_nid;
1867a766d0d7STakashi Iwai 	int i;
1868a766d0d7STakashi Iwai 
1869a766d0d7STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
1870a766d0d7STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, nid);
1871a766d0d7STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1872a766d0d7STakashi Iwai 			continue;
1873a766d0d7STakashi Iwai 		if (wcaps & AC_WCAP_DIGITAL)
1874a766d0d7STakashi Iwai 			continue;
1875a766d0d7STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
1876a766d0d7STakashi Iwai 			continue;
1877a766d0d7STakashi Iwai 		if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1878a766d0d7STakashi Iwai 			return -ENOMEM;
1879a766d0d7STakashi Iwai 		spec->adc_nids[spec->num_adc_nids++] = nid;
1880a766d0d7STakashi Iwai 	}
1881a766d0d7STakashi Iwai 	return 0;
1882a766d0d7STakashi Iwai }
1883a766d0d7STakashi Iwai 
1884a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec);
1885a766d0d7STakashi Iwai 
1886d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
1887d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1888d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
1889d7a99cceSTakashi Iwai 	 * So call somewhat different..
1890d7a99cceSTakashi Iwai 	 */
1891d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
1892d7a99cceSTakashi Iwai 	.name = "Input Source",
1893d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
1894d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
1895d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
1896d7a99cceSTakashi Iwai };
1897d7a99cceSTakashi Iwai 
1898c577b8a1SJoseph Chan /* create playback/capture controls for input pins */
1899620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1900620e2b28STakashi Iwai 					     const struct auto_pin_cfg *cfg)
1901c577b8a1SJoseph Chan {
190210a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
19030aa62aefSHarald Welte 	struct hda_input_mux *imux = &spec->private_imux[0];
1904*e3d7a143STakashi Iwai 	int i, j, err, idx, idx2, type, type_idx = 0;
1905a766d0d7STakashi Iwai 	hda_nid_t cap_nid;
1906a766d0d7STakashi Iwai 	hda_nid_t pin_idxs[8];
1907a766d0d7STakashi Iwai 	int num_idxs;
1908a766d0d7STakashi Iwai 
1909a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
1910a766d0d7STakashi Iwai 	if (err < 0)
1911a766d0d7STakashi Iwai 		return err;
1912a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
1913a766d0d7STakashi Iwai 	if (err < 0)
1914a766d0d7STakashi Iwai 		return err;
1915a766d0d7STakashi Iwai 	cap_nid = spec->mux_nids[0];
1916a766d0d7STakashi Iwai 
1917a766d0d7STakashi Iwai 	num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1918a766d0d7STakashi Iwai 					   ARRAY_SIZE(pin_idxs));
1919a766d0d7STakashi Iwai 	if (num_idxs <= 0)
1920a766d0d7STakashi Iwai 		return 0;
1921c577b8a1SJoseph Chan 
1922c577b8a1SJoseph Chan 	/* for internal loopback recording select */
1923f3268512STakashi Iwai 	for (idx = 0; idx < num_idxs; idx++) {
1924620e2b28STakashi Iwai 		if (pin_idxs[idx] == spec->aa_mix_nid) {
192510a20af7STakashi Iwai 			snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
1926f3268512STakashi Iwai 			break;
1927f3268512STakashi Iwai 		}
1928f3268512STakashi Iwai 	}
1929c577b8a1SJoseph Chan 
19307b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
193110a20af7STakashi Iwai 		const char *label;
19327b315bb4STakashi Iwai 		type = cfg->inputs[i].type;
1933f3268512STakashi Iwai 		for (idx = 0; idx < num_idxs; idx++)
19347b315bb4STakashi Iwai 			if (pin_idxs[idx] == cfg->inputs[i].pin)
1935c577b8a1SJoseph Chan 				break;
1936f3268512STakashi Iwai 		if (idx >= num_idxs)
1937f3268512STakashi Iwai 			continue;
19387b315bb4STakashi Iwai 		if (i > 0 && type == cfg->inputs[i - 1].type)
19397b315bb4STakashi Iwai 			type_idx++;
19407b315bb4STakashi Iwai 		else
19417b315bb4STakashi Iwai 			type_idx = 0;
194210a20af7STakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
1943620e2b28STakashi Iwai 		idx2 = get_connection_index(codec, spec->aa_mix_nid,
1944620e2b28STakashi Iwai 					    pin_idxs[idx]);
1945a766d0d7STakashi Iwai 		if (idx2 >= 0)
194616922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
1947620e2b28STakashi Iwai 						   idx2, spec->aa_mix_nid);
1948c577b8a1SJoseph Chan 		if (err < 0)
1949c577b8a1SJoseph Chan 			return err;
195010a20af7STakashi Iwai 		snd_hda_add_imux_item(imux, label, idx, NULL);
1951*e3d7a143STakashi Iwai 
1952*e3d7a143STakashi Iwai 		/* remember the label for smart51 control */
1953*e3d7a143STakashi Iwai 		for (j = 0; j < spec->smart51_nums; j++) {
1954*e3d7a143STakashi Iwai 			if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1955*e3d7a143STakashi Iwai 				spec->smart51_idxs[j] = idx;
1956*e3d7a143STakashi Iwai 				spec->smart51_labels[j] = label;
1957*e3d7a143STakashi Iwai 				break;
1958*e3d7a143STakashi Iwai 			}
1959*e3d7a143STakashi Iwai 		}
1960c577b8a1SJoseph Chan 	}
1961d7a99cceSTakashi Iwai 
1962d7a99cceSTakashi Iwai 	/* create capture mixer elements */
1963d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
1964d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
1965d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1966d7a99cceSTakashi Iwai 					"Capture Volume", i,
1967d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1968d7a99cceSTakashi Iwai 							    HDA_INPUT));
1969d7a99cceSTakashi Iwai 		if (err < 0)
1970d7a99cceSTakashi Iwai 			return err;
1971d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1972d7a99cceSTakashi Iwai 					"Capture Switch", i,
1973d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1974d7a99cceSTakashi Iwai 							    HDA_INPUT));
1975d7a99cceSTakashi Iwai 		if (err < 0)
1976d7a99cceSTakashi Iwai 			return err;
1977d7a99cceSTakashi Iwai 	}
1978d7a99cceSTakashi Iwai 
1979d7a99cceSTakashi Iwai 	/* input-source control */
1980d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
1981d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
1982d7a99cceSTakashi Iwai 			break;
1983d7a99cceSTakashi Iwai 	if (i) {
1984d7a99cceSTakashi Iwai 		struct snd_kcontrol_new *knew;
1985d7a99cceSTakashi Iwai 		knew = via_clone_control(spec, &via_input_src_ctl);
1986d7a99cceSTakashi Iwai 		if (!knew)
1987d7a99cceSTakashi Iwai 			return -ENOMEM;
1988d7a99cceSTakashi Iwai 		knew->count = i;
1989d7a99cceSTakashi Iwai 	}
1990d7a99cceSTakashi Iwai 
1991d7a99cceSTakashi Iwai 	/* mic-boosts */
1992d7a99cceSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
1993d7a99cceSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
1994d7a99cceSTakashi Iwai 		unsigned int caps;
1995d7a99cceSTakashi Iwai 		const char *label;
1996d7a99cceSTakashi Iwai 		char name[32];
1997d7a99cceSTakashi Iwai 
1998d7a99cceSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
1999d7a99cceSTakashi Iwai 			continue;
2000d7a99cceSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
2001d7a99cceSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2002d7a99cceSTakashi Iwai 			continue;
2003d7a99cceSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2004d7a99cceSTakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2005d7a99cceSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2006d7a99cceSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2007d7a99cceSTakashi Iwai 		if (err < 0)
2008d7a99cceSTakashi Iwai 			return err;
2009d7a99cceSTakashi Iwai 	}
2010d7a99cceSTakashi Iwai 
2011c577b8a1SJoseph Chan 	return 0;
2012c577b8a1SJoseph Chan }
2013c577b8a1SJoseph Chan 
2014cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
201590dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = {
2016cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 1 },
2017cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 2 },
2018cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 3 },
2019cb53c626STakashi Iwai 	{ 0x17, HDA_INPUT, 4 },
2020cb53c626STakashi Iwai 	{ } /* end */
2021cb53c626STakashi Iwai };
2022cb53c626STakashi Iwai #endif
2023cb53c626STakashi Iwai 
202476d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
202576d9b0ddSHarald Welte {
202676d9b0ddSHarald Welte 	unsigned int def_conf;
202776d9b0ddSHarald Welte 	unsigned char seqassoc;
202876d9b0ddSHarald Welte 
20292f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
203076d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
203176d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
203282ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
203382ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
203476d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
20352f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
203676d9b0ddSHarald Welte 	}
203776d9b0ddSHarald Welte 
203876d9b0ddSHarald Welte 	return;
203976d9b0ddSHarald Welte }
204076d9b0ddSHarald Welte 
2041e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
20421f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20431f2e99feSLydia Wang {
20441f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20451f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20461f2e99feSLydia Wang 
20471f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20481f2e99feSLydia Wang 		return 0;
2049e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
20501f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2051e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
20521f2e99feSLydia Wang 	return 0;
20531f2e99feSLydia Wang }
20541f2e99feSLydia Wang 
2055e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
20561f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
20571f2e99feSLydia Wang {
20581f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
20591f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
20601f2e99feSLydia Wang 	int change;
20611f2e99feSLydia Wang 
20621f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
20631f2e99feSLydia Wang 		return 0;
2064e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
20651f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2066e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2067e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
20681f2e99feSLydia Wang 		mute_aa_path(codec, 1);
20691f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
20701f2e99feSLydia Wang 	}
20711f2e99feSLydia Wang 	return change;
20721f2e99feSLydia Wang }
20731f2e99feSLydia Wang 
2074e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
20751f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
20761f2e99feSLydia Wang 	.name = "Jack Detect",
20771f2e99feSLydia Wang 	.count = 1,
20781f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2079e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2080e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
20811f2e99feSLydia Wang };
20821f2e99feSLydia Wang 
208312daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec);
208412daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec);
208512daef65STakashi Iwai 
208612daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
2087c577b8a1SJoseph Chan {
2088c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2089c577b8a1SJoseph Chan 	int err;
2090c577b8a1SJoseph Chan 
2091c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2092c577b8a1SJoseph Chan 	if (err < 0)
2093c577b8a1SJoseph Chan 		return err;
2094c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
20957f0df88cSTakashi Iwai 		return -EINVAL;
2096c577b8a1SJoseph Chan 
20974a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2098c577b8a1SJoseph Chan 	if (err < 0)
2099c577b8a1SJoseph Chan 		return err;
21004a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2101c577b8a1SJoseph Chan 	if (err < 0)
2102c577b8a1SJoseph Chan 		return err;
21034a918ffeSTakashi Iwai 	err = via_auto_create_speaker_ctls(codec);
21044a918ffeSTakashi Iwai 	if (err < 0)
21054a918ffeSTakashi Iwai 		return err;
2106620e2b28STakashi Iwai 	err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
2107c577b8a1SJoseph Chan 	if (err < 0)
2108c577b8a1SJoseph Chan 		return err;
2109c577b8a1SJoseph Chan 
2110c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2111c577b8a1SJoseph Chan 
211212daef65STakashi Iwai 	fill_dig_outs(codec);
211312daef65STakashi Iwai 	fill_dig_in(codec);
2114c577b8a1SJoseph Chan 
2115603c4019STakashi Iwai 	if (spec->kctls.list)
2116603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2117c577b8a1SJoseph Chan 
2118096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2119c577b8a1SJoseph Chan 
21200aa62aefSHarald Welte 	spec->input_mux = &spec->private_imux[0];
21210aa62aefSHarald Welte 
2122ece8d043STakashi Iwai 	if (spec->hp_mux) {
2123ece8d043STakashi Iwai 		err = via_hp_build(codec);
2124ece8d043STakashi Iwai 		if (err < 0)
2125ece8d043STakashi Iwai 			return err;
2126ece8d043STakashi Iwai 	}
2127c577b8a1SJoseph Chan 
2128f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2129f4a7828bSTakashi Iwai 	if (err < 0)
2130f4a7828bSTakashi Iwai 		return err;
2131f4a7828bSTakashi Iwai 
21325d41762aSTakashi Iwai 	/* assign slave outs */
21335d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
21345d41762aSTakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
21355d41762aSTakashi Iwai 
2136c577b8a1SJoseph Chan 	return 1;
2137c577b8a1SJoseph Chan }
2138c577b8a1SJoseph Chan 
21395d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec)
2140c577b8a1SJoseph Chan {
214125eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
21425d41762aSTakashi Iwai 	if (spec->multiout.dig_out_nid)
21435d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
21445d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
21455d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
21465d41762aSTakashi Iwai }
214725eaba2fSLydia Wang 
21485d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec)
21495d41762aSTakashi Iwai {
21505d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
21515d41762aSTakashi Iwai 	if (!spec->dig_in_nid)
21525d41762aSTakashi Iwai 		return;
21535d41762aSTakashi Iwai 	snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
21545d41762aSTakashi Iwai 			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
21555d41762aSTakashi Iwai }
21565d41762aSTakashi Iwai 
21574a918ffeSTakashi Iwai /* initialize the unsolicited events */
21584a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec)
21594a918ffeSTakashi Iwai {
21604a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
21614a918ffeSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
21624a918ffeSTakashi Iwai 	unsigned int ev;
21634a918ffeSTakashi Iwai 	int i;
21644a918ffeSTakashi Iwai 
21654a918ffeSTakashi Iwai 	if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
21664a918ffeSTakashi Iwai 		snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
21674a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21684a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
21694a918ffeSTakashi Iwai 
21704a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
21714a918ffeSTakashi Iwai 		ev = VIA_LINE_EVENT;
21724a918ffeSTakashi Iwai 	else
21734a918ffeSTakashi Iwai 		ev = 0;
21744a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
21754a918ffeSTakashi Iwai 		if (cfg->line_out_pins[i] &&
21764a918ffeSTakashi Iwai 		    is_jack_detectable(codec, cfg->line_out_pins[i]))
21774a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
21784a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21794a918ffeSTakashi Iwai 				AC_USRSP_EN | ev | VIA_JACK_EVENT);
21804a918ffeSTakashi Iwai 	}
21814a918ffeSTakashi Iwai 
21824a918ffeSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
21834a918ffeSTakashi Iwai 		if (is_jack_detectable(codec, cfg->inputs[i].pin))
21844a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
21854a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
21864a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_JACK_EVENT);
21874a918ffeSTakashi Iwai 	}
21884a918ffeSTakashi Iwai }
21894a918ffeSTakashi Iwai 
21905d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
21915d41762aSTakashi Iwai {
21925d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
21935d41762aSTakashi Iwai 	int i;
21945d41762aSTakashi Iwai 
21955d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
21965d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
21975d41762aSTakashi Iwai 
2198c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2199c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
22004a918ffeSTakashi Iwai 	via_auto_init_speaker_out(codec);
2201c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
22025d41762aSTakashi Iwai 	via_auto_init_dig_outs(codec);
22035d41762aSTakashi Iwai 	via_auto_init_dig_in(codec);
220411890956SLydia Wang 
22054a918ffeSTakashi Iwai 	via_auto_init_unsol_event(codec);
22064a918ffeSTakashi Iwai 
220725eaba2fSLydia Wang 	via_hp_automute(codec);
22084a918ffeSTakashi Iwai 	via_line_automute(codec, false);
220925eaba2fSLydia Wang 
2210c577b8a1SJoseph Chan 	return 0;
2211c577b8a1SJoseph Chan }
2212c577b8a1SJoseph Chan 
22131f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
22141f2e99feSLydia Wang {
22151f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
22161f2e99feSLydia Wang 					     vt1708_hp_work.work);
22171f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22181f2e99feSLydia Wang 		return;
22191f2e99feSLydia Wang 	/* if jack state toggled */
22201f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2221d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
22221f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
22231f2e99feSLydia Wang 		via_hp_automute(spec->codec);
22241f2e99feSLydia Wang 	}
22251f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
22261f2e99feSLydia Wang }
22271f2e99feSLydia Wang 
2228337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2229337b9d02STakashi Iwai {
2230337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2231337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2232337b9d02STakashi Iwai 	unsigned int type;
2233337b9d02STakashi Iwai 	int i, n;
2234337b9d02STakashi Iwai 
2235337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2236337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2237337b9d02STakashi Iwai 		while (nid) {
2238a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
22391c55d521STakashi Iwai 			if (type == AC_WID_PIN)
22401c55d521STakashi Iwai 				break;
2241337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2242337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2243337b9d02STakashi Iwai 			if (n <= 0)
2244337b9d02STakashi Iwai 				break;
2245337b9d02STakashi Iwai 			if (n > 1) {
2246337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2247337b9d02STakashi Iwai 				break;
2248337b9d02STakashi Iwai 			}
2249337b9d02STakashi Iwai 			nid = conn[0];
2250337b9d02STakashi Iwai 		}
2251337b9d02STakashi Iwai 	}
22521c55d521STakashi Iwai 	return 0;
2253337b9d02STakashi Iwai }
2254337b9d02STakashi Iwai 
2255c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2256c577b8a1SJoseph Chan {
2257c577b8a1SJoseph Chan 	struct via_spec *spec;
2258c577b8a1SJoseph Chan 	int err;
2259c577b8a1SJoseph Chan 
2260c577b8a1SJoseph Chan 	/* create a codec specific record */
22615b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2262c577b8a1SJoseph Chan 	if (spec == NULL)
2263c577b8a1SJoseph Chan 		return -ENOMEM;
2264c577b8a1SJoseph Chan 
2265620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2266620e2b28STakashi Iwai 
226712daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
226812daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
226912daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
227012daef65STakashi Iwai 
2271c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
227212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2273c577b8a1SJoseph Chan 	if (err < 0) {
2274c577b8a1SJoseph Chan 		via_free(codec);
2275c577b8a1SJoseph Chan 		return err;
2276c577b8a1SJoseph Chan 	}
2277c577b8a1SJoseph Chan 
227812daef65STakashi Iwai 	/* add jack detect on/off control */
227912daef65STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
228012daef65STakashi Iwai 		return -ENOMEM;
228112daef65STakashi Iwai 
2282bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2283bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2284bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2285c577b8a1SJoseph Chan 
2286c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2287c577b8a1SJoseph Chan 
2288cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2289cb53c626STakashi Iwai 	spec->loopback.amplist = vt1708_loopbacks;
2290cb53c626STakashi Iwai #endif
22911f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2292c577b8a1SJoseph Chan 	return 0;
2293c577b8a1SJoseph Chan }
2294c577b8a1SJoseph Chan 
2295c577b8a1SJoseph Chan /*
2296c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2297c577b8a1SJoseph Chan  */
2298cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
229990dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = {
2300cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 1 },
2301cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 2 },
2302cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 3 },
2303cb53c626STakashi Iwai 	{ 0x18, HDA_INPUT, 4 },
2304cb53c626STakashi Iwai 	{ } /* end */
2305cb53c626STakashi Iwai };
2306cb53c626STakashi Iwai #endif
2307cb53c626STakashi Iwai 
2308c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec)
2309c577b8a1SJoseph Chan {
2310c577b8a1SJoseph Chan 	struct via_spec *spec;
2311c577b8a1SJoseph Chan 	int err;
2312c577b8a1SJoseph Chan 
2313c577b8a1SJoseph Chan 	/* create a codec specific record */
23145b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2315c577b8a1SJoseph Chan 	if (spec == NULL)
2316c577b8a1SJoseph Chan 		return -ENOMEM;
2317c577b8a1SJoseph Chan 
2318620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2319620e2b28STakashi Iwai 
232012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2321c577b8a1SJoseph Chan 	if (err < 0) {
2322c577b8a1SJoseph Chan 		via_free(codec);
2323c577b8a1SJoseph Chan 		return err;
2324c577b8a1SJoseph Chan 	}
2325c577b8a1SJoseph Chan 
2326c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2327c577b8a1SJoseph Chan 
2328cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2329cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2330cb53c626STakashi Iwai #endif
2331c577b8a1SJoseph Chan 
2332c577b8a1SJoseph Chan 	return 0;
2333c577b8a1SJoseph Chan }
2334c577b8a1SJoseph Chan /*
2335c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
2336c577b8a1SJoseph Chan  */
2337c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec)
2338c577b8a1SJoseph Chan {
2339c577b8a1SJoseph Chan 	struct via_spec *spec;
2340c577b8a1SJoseph Chan 	int err;
2341c577b8a1SJoseph Chan 
2342c577b8a1SJoseph Chan 	/* create a codec specific record */
23435b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2344c577b8a1SJoseph Chan 	if (spec == NULL)
2345c577b8a1SJoseph Chan 		return -ENOMEM;
2346c577b8a1SJoseph Chan 
2347620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2348620e2b28STakashi Iwai 
234912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2350c577b8a1SJoseph Chan 	if (err < 0) {
2351c577b8a1SJoseph Chan 		via_free(codec);
2352c577b8a1SJoseph Chan 		return err;
2353c577b8a1SJoseph Chan 	}
2354c577b8a1SJoseph Chan 
2355c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2356c577b8a1SJoseph Chan 
2357cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
2358cb53c626STakashi Iwai 	spec->loopback.amplist = vt1709_loopbacks;
2359cb53c626STakashi Iwai #endif
2360f7278fd0SJosepch Chan 	return 0;
2361f7278fd0SJosepch Chan }
2362f7278fd0SJosepch Chan 
2363f7278fd0SJosepch Chan /*
2364f7278fd0SJosepch Chan  * generic initialization of ADC, input mixers and output mixers
2365f7278fd0SJosepch Chan  */
2366f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
236790dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = {
2368f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 1 },
2369f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 2 },
2370f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 3 },
2371f7278fd0SJosepch Chan 	{ 0x16, HDA_INPUT, 4 },
2372f7278fd0SJosepch Chan 	{ } /* end */
2373f7278fd0SJosepch Chan };
2374f7278fd0SJosepch Chan #endif
23753e95b9abSLydia Wang 
23763e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
23773e95b9abSLydia Wang {
23783e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
23793e95b9abSLydia Wang 	int imux_is_smixer;
23803e95b9abSLydia Wang 	unsigned int parm;
23813e95b9abSLydia Wang 	int is_8ch = 0;
2382bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2383bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
23843e95b9abSLydia Wang 		is_8ch = 1;
23853e95b9abSLydia Wang 
23863e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
23873e95b9abSLydia Wang 	imux_is_smixer =
23883e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
23893e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
23903e95b9abSLydia Wang 	/* inputs */
23913e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
23923e95b9abSLydia Wang 	parm = AC_PWRST_D3;
23933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
23943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
23953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
23963e95b9abSLydia Wang 	if (imux_is_smixer)
23973e95b9abSLydia Wang 		parm = AC_PWRST_D0;
23983e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
23993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
24003e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
24013e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
24023e95b9abSLydia Wang 
24033e95b9abSLydia Wang 	/* outputs */
24043e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
24053e95b9abSLydia Wang 	parm = AC_PWRST_D3;
24063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
24073e95b9abSLydia Wang 	if (spec->smart51_enabled)
24083e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
24093e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
24103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
24113e95b9abSLydia Wang 
24123e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
24133e95b9abSLydia Wang 	if (is_8ch) {
24143e95b9abSLydia Wang 		parm = AC_PWRST_D3;
24153e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
24163e95b9abSLydia Wang 		if (spec->smart51_enabled)
24173e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
24183e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
24193e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
24203e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
24213e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2422bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2423bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2424bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2425bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2426bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2427bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2428bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2429bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2430bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2431bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
24323e95b9abSLydia Wang 	}
24333e95b9abSLydia Wang 
24343e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
24353e95b9abSLydia Wang 	parm = AC_PWRST_D3;
24363e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
24373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
24383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
24393e95b9abSLydia Wang 	if (is_8ch)
24403e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
24413e95b9abSLydia Wang 
24423e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
24433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
24443e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
24453e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
24463e95b9abSLydia Wang 	if (is_8ch) {
24473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
24483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
24493e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
24503e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2451bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2452bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2453bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
24543e95b9abSLydia Wang }
24553e95b9abSLydia Wang 
2456518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2457f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec)
2458f7278fd0SJosepch Chan {
2459f7278fd0SJosepch Chan 	struct via_spec *spec;
2460f7278fd0SJosepch Chan 	int err;
2461f7278fd0SJosepch Chan 
2462518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2463518bf3baSLydia Wang 		return patch_vt1708S(codec);
2464f7278fd0SJosepch Chan 	/* create a codec specific record */
24655b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2466f7278fd0SJosepch Chan 	if (spec == NULL)
2467f7278fd0SJosepch Chan 		return -ENOMEM;
2468f7278fd0SJosepch Chan 
2469620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2470620e2b28STakashi Iwai 
2471f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
247212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2473f7278fd0SJosepch Chan 	if (err < 0) {
2474f7278fd0SJosepch Chan 		via_free(codec);
2475f7278fd0SJosepch Chan 		return err;
2476f7278fd0SJosepch Chan 	}
2477f7278fd0SJosepch Chan 
2478f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2479f7278fd0SJosepch Chan 
2480f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2481f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2482f7278fd0SJosepch Chan #endif
2483f7278fd0SJosepch Chan 
24843e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
24853e95b9abSLydia Wang 
2486f7278fd0SJosepch Chan 	return 0;
2487f7278fd0SJosepch Chan }
2488f7278fd0SJosepch Chan 
2489f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec)
2490f7278fd0SJosepch Chan {
2491f7278fd0SJosepch Chan 	struct via_spec *spec;
2492f7278fd0SJosepch Chan 	int err;
2493f7278fd0SJosepch Chan 
2494f7278fd0SJosepch Chan 	/* create a codec specific record */
24955b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2496f7278fd0SJosepch Chan 	if (spec == NULL)
2497f7278fd0SJosepch Chan 		return -ENOMEM;
2498f7278fd0SJosepch Chan 
2499f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
250012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2501f7278fd0SJosepch Chan 	if (err < 0) {
2502f7278fd0SJosepch Chan 		via_free(codec);
2503f7278fd0SJosepch Chan 		return err;
2504f7278fd0SJosepch Chan 	}
2505f7278fd0SJosepch Chan 
2506f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2507f7278fd0SJosepch Chan 
2508f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE
2509f7278fd0SJosepch Chan 	spec->loopback.amplist = vt1708B_loopbacks;
2510f7278fd0SJosepch Chan #endif
2511c577b8a1SJoseph Chan 
25123e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
25133e95b9abSLydia Wang 
2514c577b8a1SJoseph Chan 	return 0;
2515c577b8a1SJoseph Chan }
2516c577b8a1SJoseph Chan 
2517d949cac1SHarald Welte /* Patch for VT1708S */
2518096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
2519d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
2520d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
2521bc7e7e5cSLydia Wang 	/* don't bybass mixer */
2522bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
2523d949cac1SHarald Welte 	{ }
2524d949cac1SHarald Welte };
2525d949cac1SHarald Welte 
25269da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
25279da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
25289da29271STakashi Iwai {
25299da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
25309da29271STakashi Iwai 	int i;
25319da29271STakashi Iwai 
25329da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
25339da29271STakashi Iwai 		hda_nid_t nid;
25349da29271STakashi Iwai 		int conn;
25359da29271STakashi Iwai 
25369da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
25379da29271STakashi Iwai 		if (!nid)
25389da29271STakashi Iwai 			continue;
25399da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
25409da29271STakashi Iwai 		if (conn < 1)
25419da29271STakashi Iwai 			continue;
25429da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
25439da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
25449da29271STakashi Iwai 		else {
25459da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
25469da29271STakashi Iwai 			break; /* at most two dig outs */
25479da29271STakashi Iwai 		}
25489da29271STakashi Iwai 	}
25499da29271STakashi Iwai }
25509da29271STakashi Iwai 
255112daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec)
2552d949cac1SHarald Welte {
2553d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
255412daef65STakashi Iwai 	hda_nid_t dig_nid;
255512daef65STakashi Iwai 	int i, err;
2556d949cac1SHarald Welte 
255712daef65STakashi Iwai 	if (!spec->autocfg.dig_in_pin)
255812daef65STakashi Iwai 		return;
2559d949cac1SHarald Welte 
256012daef65STakashi Iwai 	dig_nid = codec->start_nid;
256112daef65STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
256212daef65STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, dig_nid);
256312daef65STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
256412daef65STakashi Iwai 			continue;
256512daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_DIGITAL))
256612daef65STakashi Iwai 			continue;
256712daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
256812daef65STakashi Iwai 			continue;
256912daef65STakashi Iwai 		err = get_connection_index(codec, dig_nid,
257012daef65STakashi Iwai 					   spec->autocfg.dig_in_pin);
257112daef65STakashi Iwai 		if (err >= 0) {
257212daef65STakashi Iwai 			spec->dig_in_nid = dig_nid;
257312daef65STakashi Iwai 			break;
257412daef65STakashi Iwai 		}
257512daef65STakashi Iwai 	}
2576d949cac1SHarald Welte }
2577d949cac1SHarald Welte 
2578d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
257990dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = {
2580d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 1 },
2581d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 2 },
2582d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 3 },
2583d949cac1SHarald Welte 	{ 0x16, HDA_INPUT, 4 },
2584d949cac1SHarald Welte 	{ } /* end */
2585d949cac1SHarald Welte };
2586d949cac1SHarald Welte #endif
2587d949cac1SHarald Welte 
25886369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
25896369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
25906369bcfcSLydia Wang {
25916369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
25926369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
25936369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
25946369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
25956369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
25966369bcfcSLydia Wang }
25976369bcfcSLydia Wang 
2598d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
2599d949cac1SHarald Welte {
2600d949cac1SHarald Welte 	struct via_spec *spec;
2601d949cac1SHarald Welte 	int err;
2602d949cac1SHarald Welte 
2603d949cac1SHarald Welte 	/* create a codec specific record */
26045b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2605d949cac1SHarald Welte 	if (spec == NULL)
2606d949cac1SHarald Welte 		return -ENOMEM;
2607d949cac1SHarald Welte 
2608620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2609d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
2610d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
2611620e2b28STakashi Iwai 
2612d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
261312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2614d949cac1SHarald Welte 	if (err < 0) {
2615d949cac1SHarald Welte 		via_free(codec);
2616d949cac1SHarald Welte 		return err;
2617d949cac1SHarald Welte 	}
2618d949cac1SHarald Welte 
2619096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
2620d949cac1SHarald Welte 
2621d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2622d949cac1SHarald Welte 
2623d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
2624d949cac1SHarald Welte 	spec->loopback.amplist = vt1708S_loopbacks;
2625d949cac1SHarald Welte #endif
2626d949cac1SHarald Welte 
2627518bf3baSLydia Wang 	/* correct names for VT1708BCE */
2628518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
2629518bf3baSLydia Wang 		kfree(codec->chip_name);
2630518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2631518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
2632518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
2633518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2634970f630fSLydia Wang 	}
2635bc92df7fSLydia Wang 	/* correct names for VT1705 */
2636bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
2637bc92df7fSLydia Wang 		kfree(codec->chip_name);
2638bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2639bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
2640bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
2641bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2642bc92df7fSLydia Wang 	}
26433e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
2644d949cac1SHarald Welte 	return 0;
2645d949cac1SHarald Welte }
2646d949cac1SHarald Welte 
2647d949cac1SHarald Welte /* Patch for VT1702 */
2648d949cac1SHarald Welte 
2649096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
2650bc7e7e5cSLydia Wang 	/* mixer enable */
2651bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
2652bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
2653bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
2654d949cac1SHarald Welte 	{ }
2655d949cac1SHarald Welte };
2656d949cac1SHarald Welte 
2657d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
265890dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = {
2659d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 1 },
2660d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 2 },
2661d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 3 },
2662d949cac1SHarald Welte 	{ 0x1A, HDA_INPUT, 4 },
2663d949cac1SHarald Welte 	{ } /* end */
2664d949cac1SHarald Welte };
2665d949cac1SHarald Welte #endif
2666d949cac1SHarald Welte 
26673e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
26683e95b9abSLydia Wang {
26693e95b9abSLydia Wang 	int imux_is_smixer =
26703e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
26713e95b9abSLydia Wang 	unsigned int parm;
26723e95b9abSLydia Wang 	/* inputs */
26733e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
26743e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
26763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
26773e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
26783e95b9abSLydia Wang 	if (imux_is_smixer)
26793e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
26803e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
26813e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
26823e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
26833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
26843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
26853e95b9abSLydia Wang 
26863e95b9abSLydia Wang 	/* outputs */
26873e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
26883e95b9abSLydia Wang 	parm = AC_PWRST_D3;
26893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
26903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
26913e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
26923e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
26933e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
26943e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
26953e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
26963e95b9abSLydia Wang }
26973e95b9abSLydia Wang 
2698d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
2699d949cac1SHarald Welte {
2700d949cac1SHarald Welte 	struct via_spec *spec;
2701d949cac1SHarald Welte 	int err;
2702d949cac1SHarald Welte 
2703d949cac1SHarald Welte 	/* create a codec specific record */
27045b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2705d949cac1SHarald Welte 	if (spec == NULL)
2706d949cac1SHarald Welte 		return -ENOMEM;
2707d949cac1SHarald Welte 
2708620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
2709620e2b28STakashi Iwai 
271012daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
271112daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
271212daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
271312daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
271412daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
271512daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
271612daef65STakashi Iwai 
2717d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
271812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2719d949cac1SHarald Welte 	if (err < 0) {
2720d949cac1SHarald Welte 		via_free(codec);
2721d949cac1SHarald Welte 		return err;
2722d949cac1SHarald Welte 	}
2723d949cac1SHarald Welte 
2724096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
2725d949cac1SHarald Welte 
2726d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2727d949cac1SHarald Welte 
2728d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE
2729d949cac1SHarald Welte 	spec->loopback.amplist = vt1702_loopbacks;
2730d949cac1SHarald Welte #endif
2731d949cac1SHarald Welte 
27323e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
2733d949cac1SHarald Welte 	return 0;
2734d949cac1SHarald Welte }
2735d949cac1SHarald Welte 
2736eb7188caSLydia Wang /* Patch for VT1718S */
2737eb7188caSLydia Wang 
2738096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
27394ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
27404ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
2741eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
2742eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
27435d41762aSTakashi Iwai 
2744eb7188caSLydia Wang 	{ }
2745eb7188caSLydia Wang };
2746eb7188caSLydia Wang 
2747eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
274890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = {
2749eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
2750eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
2751eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 3 },
2752eb7188caSLydia Wang 	{ 0x21, HDA_INPUT, 4 },
2753eb7188caSLydia Wang 	{ } /* end */
2754eb7188caSLydia Wang };
2755eb7188caSLydia Wang #endif
2756eb7188caSLydia Wang 
27573e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
27583e95b9abSLydia Wang {
27593e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
27603e95b9abSLydia Wang 	int imux_is_smixer;
27613e95b9abSLydia Wang 	unsigned int parm;
27623e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
27633e95b9abSLydia Wang 	imux_is_smixer =
27643e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
27653e95b9abSLydia Wang 	/* inputs */
27663e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
27673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
27693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
27703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
27713e95b9abSLydia Wang 	if (imux_is_smixer)
27723e95b9abSLydia Wang 		parm = AC_PWRST_D0;
27733e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
27743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
27753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
27763e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
27773e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
27783e95b9abSLydia Wang 
27793e95b9abSLydia Wang 	/* outputs */
27803e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
27813e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
27833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
27843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
27853e95b9abSLydia Wang 
27863e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
27873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
27893e95b9abSLydia Wang 	if (spec->smart51_enabled)
27903e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
27913e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
27923e95b9abSLydia Wang 
27933e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
27943e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
27963e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
27973e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
27983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
27993e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
28003e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
28013e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
28023e95b9abSLydia Wang 
28033e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
28043e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
28063e95b9abSLydia Wang 	if (spec->smart51_enabled)
28073e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
28083e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
28093e95b9abSLydia Wang 
28103e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
28113e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
28123e95b9abSLydia Wang 		parm = AC_PWRST_D3;
28133e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
28143e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
28153e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28163e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
28173e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28183e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
28193e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
28203e95b9abSLydia Wang 	}
28213e95b9abSLydia Wang }
28223e95b9abSLydia Wang 
2823eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
2824eb7188caSLydia Wang {
2825eb7188caSLydia Wang 	struct via_spec *spec;
2826eb7188caSLydia Wang 	int err;
2827eb7188caSLydia Wang 
2828eb7188caSLydia Wang 	/* create a codec specific record */
28295b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2830eb7188caSLydia Wang 	if (spec == NULL)
2831eb7188caSLydia Wang 		return -ENOMEM;
2832eb7188caSLydia Wang 
2833620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
2834d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
2835d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
2836620e2b28STakashi Iwai 
2837eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
283812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2839eb7188caSLydia Wang 	if (err < 0) {
2840eb7188caSLydia Wang 		via_free(codec);
2841eb7188caSLydia Wang 		return err;
2842eb7188caSLydia Wang 	}
2843eb7188caSLydia Wang 
2844096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
2845eb7188caSLydia Wang 
2846eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
2847eb7188caSLydia Wang 
2848eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
2849eb7188caSLydia Wang 	spec->loopback.amplist = vt1718S_loopbacks;
2850eb7188caSLydia Wang #endif
2851eb7188caSLydia Wang 
28523e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
28533e95b9abSLydia Wang 
2854eb7188caSLydia Wang 	return 0;
2855eb7188caSLydia Wang }
2856f3db423dSLydia Wang 
2857f3db423dSLydia Wang /* Patch for VT1716S */
2858f3db423dSLydia Wang 
2859f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2860f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
2861f3db423dSLydia Wang {
2862f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2863f3db423dSLydia Wang 	uinfo->count = 1;
2864f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
2865f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
2866f3db423dSLydia Wang 	return 0;
2867f3db423dSLydia Wang }
2868f3db423dSLydia Wang 
2869f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2870f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2871f3db423dSLydia Wang {
2872f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2873f3db423dSLydia Wang 	int index = 0;
2874f3db423dSLydia Wang 
2875f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
2876f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
2877f3db423dSLydia Wang 	if (index != -1)
2878f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
2879f3db423dSLydia Wang 
2880f3db423dSLydia Wang 	return 0;
2881f3db423dSLydia Wang }
2882f3db423dSLydia Wang 
2883f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2884f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2885f3db423dSLydia Wang {
2886f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2887f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
2888f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
2889f3db423dSLydia Wang 
2890f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
2891f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
2892f3db423dSLydia Wang 	spec->dmic_enabled = index;
28933e95b9abSLydia Wang 	set_widgets_power_state(codec);
2894f3db423dSLydia Wang 	return 1;
2895f3db423dSLydia Wang }
2896f3db423dSLydia Wang 
289790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
2898f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2899f3db423dSLydia Wang 	{
2900f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2901f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
29025b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
2903f3db423dSLydia Wang 	 .count = 1,
2904f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
2905f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
2906f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
2907f3db423dSLydia Wang 	 },
2908f3db423dSLydia Wang 	{}			/* end */
2909f3db423dSLydia Wang };
2910f3db423dSLydia Wang 
2911f3db423dSLydia Wang 
2912f3db423dSLydia Wang /* mono-out mixer elements */
291390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
2914f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2915f3db423dSLydia Wang 	{ } /* end */
2916f3db423dSLydia Wang };
2917f3db423dSLydia Wang 
2918096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
2919f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
2920f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
2921f3db423dSLydia Wang 	/* don't bybass mixer */
2922f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
2923f3db423dSLydia Wang 	/* Enable mono output */
2924f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
2925f3db423dSLydia Wang 	{ }
2926f3db423dSLydia Wang };
2927f3db423dSLydia Wang 
2928f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
292990dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = {
2930f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 1 },
2931f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 2 },
2932f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 3 },
2933f3db423dSLydia Wang 	{ 0x16, HDA_INPUT, 4 },
2934f3db423dSLydia Wang 	{ } /* end */
2935f3db423dSLydia Wang };
2936f3db423dSLydia Wang #endif
2937f3db423dSLydia Wang 
29383e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
29393e95b9abSLydia Wang {
29403e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
29413e95b9abSLydia Wang 	int imux_is_smixer;
29423e95b9abSLydia Wang 	unsigned int parm;
29433e95b9abSLydia Wang 	unsigned int mono_out, present;
29443e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
29453e95b9abSLydia Wang 	imux_is_smixer =
29463e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
29473e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
29483e95b9abSLydia Wang 	/* inputs */
29493e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
29503e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29513e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
29523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
29533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
29543e95b9abSLydia Wang 	if (imux_is_smixer)
29553e95b9abSLydia Wang 		parm = AC_PWRST_D0;
29563e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
29573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
29583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
29593e95b9abSLydia Wang 
29603e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
29623e95b9abSLydia Wang 	/* PW11 (22h) */
29633e95b9abSLydia Wang 	if (spec->dmic_enabled)
29643e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
29653e95b9abSLydia Wang 	else
29663e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
29673e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
29683e95b9abSLydia Wang 
29693e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
29703e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
29713e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
29723e95b9abSLydia Wang 
29733e95b9abSLydia Wang 	/* outputs */
29743e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
29753e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
29773e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
29783e95b9abSLydia Wang 	if (spec->smart51_enabled)
29793e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
29803e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
29813e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
29823e95b9abSLydia Wang 
29833e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
29843e95b9abSLydia Wang 	parm = AC_PWRST_D3;
29853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
29863e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
29873e95b9abSLydia Wang 	if (spec->smart51_enabled)
29883e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
29893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
29903e95b9abSLydia Wang 
29913e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
29923e95b9abSLydia Wang 	if (spec->smart51_enabled)
29933e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
29943e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
29953e95b9abSLydia Wang 
29963e95b9abSLydia Wang 	/* Mono out */
29973e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
29983e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
29993e95b9abSLydia Wang 
30003e95b9abSLydia Wang 	if (present)
30013e95b9abSLydia Wang 		mono_out = 0;
30023e95b9abSLydia Wang 	else {
30033e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
30043e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
30053e95b9abSLydia Wang 			mono_out = 0;
30063e95b9abSLydia Wang 		else
30073e95b9abSLydia Wang 			mono_out = 1;
30083e95b9abSLydia Wang 	}
30093e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
30103e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
30113e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
30123e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
30133e95b9abSLydia Wang 
30143e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
30153e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
30173e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
30183e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
30193e95b9abSLydia Wang 	if (spec->hp_independent_mode)
30203e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
30213e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
30223e95b9abSLydia Wang 
30233e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
30243e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
30253e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
30263e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
30273e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
30283e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
30293e95b9abSLydia Wang }
30303e95b9abSLydia Wang 
3031f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3032f3db423dSLydia Wang {
3033f3db423dSLydia Wang 	struct via_spec *spec;
3034f3db423dSLydia Wang 	int err;
3035f3db423dSLydia Wang 
3036f3db423dSLydia Wang 	/* create a codec specific record */
30375b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3038f3db423dSLydia Wang 	if (spec == NULL)
3039f3db423dSLydia Wang 		return -ENOMEM;
3040f3db423dSLydia Wang 
3041620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3042d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3043d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3044620e2b28STakashi Iwai 
3045f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
304612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3047f3db423dSLydia Wang 	if (err < 0) {
3048f3db423dSLydia Wang 		via_free(codec);
3049f3db423dSLydia Wang 		return err;
3050f3db423dSLydia Wang 	}
3051f3db423dSLydia Wang 
3052096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
3053f3db423dSLydia Wang 
3054f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3055f3db423dSLydia Wang 	spec->num_mixers++;
3056f3db423dSLydia Wang 
3057f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3058f3db423dSLydia Wang 
3059f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3060f3db423dSLydia Wang 
3061f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3062f3db423dSLydia Wang 	spec->loopback.amplist = vt1716S_loopbacks;
3063f3db423dSLydia Wang #endif
3064f3db423dSLydia Wang 
30653e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
3066f3db423dSLydia Wang 	return 0;
3067f3db423dSLydia Wang }
306825eaba2fSLydia Wang 
306925eaba2fSLydia Wang /* for vt2002P */
307025eaba2fSLydia Wang 
3071096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
3072eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
3073eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
3074eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
3075eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
307625eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
307725eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
307825eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
307925eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
308025eaba2fSLydia Wang 	{ }
308125eaba2fSLydia Wang };
30824a918ffeSTakashi Iwai 
3083096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
308411890956SLydia Wang 	/* Enable Boost Volume backdoor */
308511890956SLydia Wang 	{0x1, 0xfb9, 0x24},
308611890956SLydia Wang 	/* Enable AOW0 to MW9 */
308711890956SLydia Wang 	{0x1, 0xfb8, 0x88},
308811890956SLydia Wang 	{ }
308911890956SLydia Wang };
309025eaba2fSLydia Wang 
309125eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
309290dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = {
309325eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 0 },
309425eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 1 },
309525eaba2fSLydia Wang 	{ 0x21, HDA_INPUT, 2 },
309625eaba2fSLydia Wang 	{ } /* end */
309725eaba2fSLydia Wang };
309825eaba2fSLydia Wang #endif
309925eaba2fSLydia Wang 
31003e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
31013e95b9abSLydia Wang {
31023e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
31033e95b9abSLydia Wang 	int imux_is_smixer;
31043e95b9abSLydia Wang 	unsigned int parm;
31053e95b9abSLydia Wang 	unsigned int present;
31063e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
31073e95b9abSLydia Wang 	imux_is_smixer =
31083e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
31093e95b9abSLydia Wang 	/* inputs */
31103e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
31113e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
31133e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
31143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
31153e95b9abSLydia Wang 	parm = AC_PWRST_D0;
31163e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
31173e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
31183e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
31193e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
31203e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
31213e95b9abSLydia Wang 
31223e95b9abSLydia Wang 	/* outputs */
31233e95b9abSLydia Wang 	/* AOW0 (8h)*/
31243e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
31253e95b9abSLydia Wang 
312611890956SLydia Wang 	if (spec->codec_type == VT1802) {
312711890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
312811890956SLydia Wang 		parm = AC_PWRST_D3;
312911890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
313011890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
313111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
313211890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
313311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
313411890956SLydia Wang 	} else {
31353e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
31363e95b9abSLydia Wang 		parm = AC_PWRST_D3;
31373e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
31383e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
31393e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31403e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
31413e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
314211890956SLydia Wang 	}
31433e95b9abSLydia Wang 
314411890956SLydia Wang 	if (spec->codec_type == VT1802) {
314511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
314611890956SLydia Wang 		parm = AC_PWRST_D3;
314711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
314811890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
314911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
315011890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
315111890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
315211890956SLydia Wang 	} else {
31533e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
31543e95b9abSLydia Wang 		parm = AC_PWRST_D3;
31553e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
31563e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
31573e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31583e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
31593e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
316011890956SLydia Wang 	}
31613e95b9abSLydia Wang 
31623e95b9abSLydia Wang 	if (spec->hp_independent_mode)
31633e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
31643e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
31653e95b9abSLydia Wang 
31663e95b9abSLydia Wang 	/* Class-D */
31673e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
31683e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
31693e95b9abSLydia Wang 
31703e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31713e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
31723e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
317311890956SLydia Wang 	if (spec->codec_type == VT1802)
317411890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
317511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
317611890956SLydia Wang 	else
31773e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
31783e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31793e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
31803e95b9abSLydia Wang 
31813e95b9abSLydia Wang 	/* Mono Out */
31823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
31833e95b9abSLydia Wang 
31843e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
318511890956SLydia Wang 	if (spec->codec_type == VT1802) {
318611890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
318711890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
318811890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
318911890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
319011890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
319111890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
319211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
319311890956SLydia Wang 	} else {
31943e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
31953e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
31963e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31973e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
31983e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
31993e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
32003e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
320111890956SLydia Wang 	}
32023e95b9abSLydia Wang 	/* MW9 (21h) */
32033e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
32043e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
32053e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
32063e95b9abSLydia Wang 	else
32073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
32083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
32093e95b9abSLydia Wang }
321025eaba2fSLydia Wang 
321125eaba2fSLydia Wang /* patch for vt2002P */
321225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
321325eaba2fSLydia Wang {
321425eaba2fSLydia Wang 	struct via_spec *spec;
321525eaba2fSLydia Wang 	int err;
321625eaba2fSLydia Wang 
321725eaba2fSLydia Wang 	/* create a codec specific record */
32185b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
321925eaba2fSLydia Wang 	if (spec == NULL)
322025eaba2fSLydia Wang 		return -ENOMEM;
322125eaba2fSLydia Wang 
3222620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3223d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3224d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3225620e2b28STakashi Iwai 
322625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
322712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
322825eaba2fSLydia Wang 	if (err < 0) {
322925eaba2fSLydia Wang 		via_free(codec);
323025eaba2fSLydia Wang 		return err;
323125eaba2fSLydia Wang 	}
323225eaba2fSLydia Wang 
323311890956SLydia Wang 	if (spec->codec_type == VT1802)
32344a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
323511890956SLydia Wang 	else
32364a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
323711890956SLydia Wang 
323825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
323925eaba2fSLydia Wang 
324025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
324125eaba2fSLydia Wang 	spec->loopback.amplist = vt2002P_loopbacks;
324225eaba2fSLydia Wang #endif
324325eaba2fSLydia Wang 
32443e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
324525eaba2fSLydia Wang 	return 0;
324625eaba2fSLydia Wang }
3247ab6734e7SLydia Wang 
3248ab6734e7SLydia Wang /* for vt1812 */
3249ab6734e7SLydia Wang 
3250096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
3251ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
3252ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
3253ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
3254ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
3255ab6734e7SLydia Wang 	{ }
3256ab6734e7SLydia Wang };
3257ab6734e7SLydia Wang 
3258ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
325990dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = {
3260ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 0 },
3261ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 1 },
3262ab6734e7SLydia Wang 	{ 0x21, HDA_INPUT, 2 },
3263ab6734e7SLydia Wang 	{ } /* end */
3264ab6734e7SLydia Wang };
3265ab6734e7SLydia Wang #endif
3266ab6734e7SLydia Wang 
32673e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
32683e95b9abSLydia Wang {
32693e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
32703e95b9abSLydia Wang 	int imux_is_smixer =
32713e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
32723e95b9abSLydia Wang 	unsigned int parm;
32733e95b9abSLydia Wang 	unsigned int present;
32743e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
32753e95b9abSLydia Wang 	imux_is_smixer =
32763e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
32773e95b9abSLydia Wang 	/* inputs */
32783e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
32793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
32813e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
32823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
32833e95b9abSLydia Wang 	parm = AC_PWRST_D0;
32843e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
32853e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
32863e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
32873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
32883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
32893e95b9abSLydia Wang 
32903e95b9abSLydia Wang 	/* outputs */
32913e95b9abSLydia Wang 	/* AOW0 (8h)*/
32923e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
32933e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
32943e95b9abSLydia Wang 
32953e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
32963e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
32983e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
32993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
33003e95b9abSLydia Wang 
33013e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
33023e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33033e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
33043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
33053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
33063e95b9abSLydia Wang 	if (spec->hp_independent_mode)
33073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
33083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33093e95b9abSLydia Wang 
33103e95b9abSLydia Wang 	/* Internal Speaker */
33113e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
33123e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
33133e95b9abSLydia Wang 
33143e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33153e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
33163e95b9abSLydia Wang 	if (present) {
33173e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
33183e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33193e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
33203e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33213e95b9abSLydia Wang 	} else {
33223e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
33233e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33243e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
33253e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33263e95b9abSLydia Wang 	}
33273e95b9abSLydia Wang 
33283e95b9abSLydia Wang 
33293e95b9abSLydia Wang 	/* Mono Out */
33303e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
33313e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
33323e95b9abSLydia Wang 
33333e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
33353e95b9abSLydia Wang 	if (present) {
33363e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
33373e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33383e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
33393e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33403e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
33413e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33423e95b9abSLydia Wang 	} else {
33433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
33443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33453e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
33463e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33473e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
33483e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33493e95b9abSLydia Wang 	}
33503e95b9abSLydia Wang 
33513e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
33523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
33543e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
33553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
33563e95b9abSLydia Wang 
33573e95b9abSLydia Wang }
3358ab6734e7SLydia Wang 
3359ab6734e7SLydia Wang /* patch for vt1812 */
3360ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
3361ab6734e7SLydia Wang {
3362ab6734e7SLydia Wang 	struct via_spec *spec;
3363ab6734e7SLydia Wang 	int err;
3364ab6734e7SLydia Wang 
3365ab6734e7SLydia Wang 	/* create a codec specific record */
33665b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3367ab6734e7SLydia Wang 	if (spec == NULL)
3368ab6734e7SLydia Wang 		return -ENOMEM;
3369ab6734e7SLydia Wang 
3370620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3371d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3372d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3373620e2b28STakashi Iwai 
3374ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
337512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3376ab6734e7SLydia Wang 	if (err < 0) {
3377ab6734e7SLydia Wang 		via_free(codec);
3378ab6734e7SLydia Wang 		return err;
3379ab6734e7SLydia Wang 	}
3380ab6734e7SLydia Wang 
3381096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
3382ab6734e7SLydia Wang 
3383ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
3384ab6734e7SLydia Wang 
3385ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE
3386ab6734e7SLydia Wang 	spec->loopback.amplist = vt1812_loopbacks;
3387ab6734e7SLydia Wang #endif
3388ab6734e7SLydia Wang 
33893e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
3390ab6734e7SLydia Wang 	return 0;
3391ab6734e7SLydia Wang }
3392ab6734e7SLydia Wang 
3393c577b8a1SJoseph Chan /*
3394c577b8a1SJoseph Chan  * patch entries
3395c577b8a1SJoseph Chan  */
339690dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
33973218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
33983218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
33993218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
34003218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
34013218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
3402f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
34033218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
3404f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
34053218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
3406f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
34073218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
3408f7278fd0SJosepch Chan 	  .patch = patch_vt1709_10ch},
34093218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
3410f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
34113218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
3412f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
34133218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
3414f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
34153218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
3416f7278fd0SJosepch Chan 	  .patch = patch_vt1709_6ch},
34173218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
3418f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
34193218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
3420f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
34213218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
3422f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
34233218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
3424f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_8ch},
34253218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
3426f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
34273218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
3428f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
34293218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
3430f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
34313218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
3432f7278fd0SJosepch Chan 	  .patch = patch_vt1708B_4ch},
34333218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
3434d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34353218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
3436d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34373218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
3438d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34393218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
3440d949cac1SHarald Welte 	  .patch = patch_vt1708S},
3441bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
3442d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34433218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
3444d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34453218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
3446d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34473218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
3448d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34493218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
3450d949cac1SHarald Welte 	  .patch = patch_vt1702},
34513218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
3452d949cac1SHarald Welte 	  .patch = patch_vt1702},
34533218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
3454d949cac1SHarald Welte 	  .patch = patch_vt1702},
34553218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
3456d949cac1SHarald Welte 	  .patch = patch_vt1702},
34573218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
3458d949cac1SHarald Welte 	  .patch = patch_vt1702},
34593218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
3460d949cac1SHarald Welte 	  .patch = patch_vt1702},
34613218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
3462d949cac1SHarald Welte 	  .patch = patch_vt1702},
34633218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
3464d949cac1SHarald Welte 	  .patch = patch_vt1702},
3465eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
3466eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3467eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
3468eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3469bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
3470bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3471bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
3472bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3473f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
3474f3db423dSLydia Wang 	  .patch = patch_vt1716S},
3475f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
3476f3db423dSLydia Wang 	  .patch = patch_vt1716S},
347725eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
347825eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
3479ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
348036dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
348136dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
348211890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
348311890956SLydia Wang 		.patch = patch_vt2002P},
348411890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
348511890956SLydia Wang 		.patch = patch_vt2002P},
3486c577b8a1SJoseph Chan 	{} /* terminator */
3487c577b8a1SJoseph Chan };
34881289e9e8STakashi Iwai 
34891289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
34901289e9e8STakashi Iwai 
34911289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
34921289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
34931289e9e8STakashi Iwai 	.owner = THIS_MODULE,
34941289e9e8STakashi Iwai };
34951289e9e8STakashi Iwai 
34961289e9e8STakashi Iwai MODULE_LICENSE("GPL");
34971289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
34981289e9e8STakashi Iwai 
34991289e9e8STakashi Iwai static int __init patch_via_init(void)
35001289e9e8STakashi Iwai {
35011289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
35021289e9e8STakashi Iwai }
35031289e9e8STakashi Iwai 
35041289e9e8STakashi Iwai static void __exit patch_via_exit(void)
35051289e9e8STakashi Iwai {
35061289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
35071289e9e8STakashi Iwai }
35081289e9e8STakashi Iwai 
35091289e9e8STakashi Iwai module_init(patch_via_init)
35101289e9e8STakashi Iwai module_exit(patch_via_exit)
3511