xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 8d087c7600499463b7b8e3d4da4da40669cb8bfa)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52c577b8a1SJoseph Chan #include <sound/core.h>
530aa62aefSHarald Welte #include <sound/asoundef.h>
54c577b8a1SJoseph Chan #include "hda_codec.h"
55c577b8a1SJoseph Chan #include "hda_local.h"
56c577b8a1SJoseph Chan 
57c577b8a1SJoseph Chan /* Pin Widget NID */
5876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
5976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
60c577b8a1SJoseph Chan 
61d7426329SHarald Welte enum VIA_HDA_CODEC {
62d7426329SHarald Welte 	UNKNOWN = -1,
63d7426329SHarald Welte 	VT1708,
64d7426329SHarald Welte 	VT1709_10CH,
65d7426329SHarald Welte 	VT1709_6CH,
66d7426329SHarald Welte 	VT1708B_8CH,
67d7426329SHarald Welte 	VT1708B_4CH,
68d7426329SHarald Welte 	VT1708S,
69518bf3baSLydia Wang 	VT1708BCE,
70d7426329SHarald Welte 	VT1702,
71eb7188caSLydia Wang 	VT1718S,
72f3db423dSLydia Wang 	VT1716S,
7325eaba2fSLydia Wang 	VT2002P,
74ab6734e7SLydia Wang 	VT1812,
7511890956SLydia Wang 	VT1802,
76d7426329SHarald Welte 	CODEC_TYPES,
77d7426329SHarald Welte };
78d7426329SHarald Welte 
7911890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8011890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8111890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8211890956SLydia Wang 	 (spec)->codec_type == VT1802)
8311890956SLydia Wang 
848e3679dcSTakashi Iwai #define MAX_NID_PATH_DEPTH	5
858e3679dcSTakashi Iwai 
8609a9ad69STakashi Iwai /* output-path: DAC -> ... -> pin
8709a9ad69STakashi Iwai  * idx[] contains the source index number of the next widget;
8809a9ad69STakashi Iwai  * e.g. idx[0] is the index of the DAC selected by path[1] widget
8909a9ad69STakashi Iwai  * multi[] indicates whether it's a selector widget with multi-connectors
9009a9ad69STakashi Iwai  * (i.e. the connection selection is mandatory)
9109a9ad69STakashi Iwai  * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
9209a9ad69STakashi Iwai  */
934a79616dSTakashi Iwai struct nid_path {
944a79616dSTakashi Iwai 	int depth;
958e3679dcSTakashi Iwai 	hda_nid_t path[MAX_NID_PATH_DEPTH];
9609a9ad69STakashi Iwai 	unsigned char idx[MAX_NID_PATH_DEPTH];
9709a9ad69STakashi Iwai 	unsigned char multi[MAX_NID_PATH_DEPTH];
9809a9ad69STakashi Iwai 	unsigned int vol_ctl;
9909a9ad69STakashi Iwai 	unsigned int mute_ctl;
1004a79616dSTakashi Iwai };
1014a79616dSTakashi Iwai 
102a86a88eaSTakashi Iwai /* input-path */
103a86a88eaSTakashi Iwai struct via_input {
104a86a88eaSTakashi Iwai 	hda_nid_t pin;	/* input-pin or aa-mix */
105a86a88eaSTakashi Iwai 	int adc_idx;	/* ADC index to be used */
106a86a88eaSTakashi Iwai 	int mux_idx;	/* MUX index (if any) */
107a86a88eaSTakashi Iwai 	const char *label;	/* input-source label */
108a86a88eaSTakashi Iwai };
109a86a88eaSTakashi Iwai 
1101f2e99feSLydia Wang struct via_spec {
1111f2e99feSLydia Wang 	/* codec parameterization */
11290dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
1131f2e99feSLydia Wang 	unsigned int num_mixers;
1141f2e99feSLydia Wang 
11590dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
1161f2e99feSLydia Wang 	unsigned int num_iverbs;
1171f2e99feSLydia Wang 
11882673bc8STakashi Iwai 	char stream_name_analog[32];
1197eb56e84STakashi Iwai 	char stream_name_hp[32];
12090dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_playback;
12190dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_analog_capture;
1221f2e99feSLydia Wang 
12382673bc8STakashi Iwai 	char stream_name_digital[32];
12490dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_playback;
12590dd48a1STakashi Iwai 	const struct hda_pcm_stream *stream_digital_capture;
1261f2e99feSLydia Wang 
1271f2e99feSLydia Wang 	/* playback */
1281f2e99feSLydia Wang 	struct hda_multi_out multiout;
1291f2e99feSLydia Wang 	hda_nid_t slave_dig_outs[2];
130ece8d043STakashi Iwai 	hda_nid_t hp_dac_nid;
131ada509ecSTakashi Iwai 	int num_active_streams;
1321f2e99feSLydia Wang 
1334a79616dSTakashi Iwai 	struct nid_path out_path[4];
1344a79616dSTakashi Iwai 	struct nid_path hp_path;
1354a79616dSTakashi Iwai 	struct nid_path hp_dep_path;
1364a918ffeSTakashi Iwai 	struct nid_path speaker_path;
1374a79616dSTakashi Iwai 
1381f2e99feSLydia Wang 	/* capture */
1391f2e99feSLydia Wang 	unsigned int num_adc_nids;
140a766d0d7STakashi Iwai 	hda_nid_t adc_nids[3];
1411f2e99feSLydia Wang 	hda_nid_t mux_nids[3];
142620e2b28STakashi Iwai 	hda_nid_t aa_mix_nid;
1431f2e99feSLydia Wang 	hda_nid_t dig_in_nid;
1441f2e99feSLydia Wang 
1451f2e99feSLydia Wang 	/* capture source */
146a86a88eaSTakashi Iwai 	bool dyn_adc_switch;
147a86a88eaSTakashi Iwai 	int num_inputs;
148a86a88eaSTakashi Iwai 	struct via_input inputs[AUTO_CFG_MAX_INS + 1];
1491f2e99feSLydia Wang 	unsigned int cur_mux[3];
1501f2e99feSLydia Wang 
151a86a88eaSTakashi Iwai 	/* dynamic ADC switching */
152a86a88eaSTakashi Iwai 	hda_nid_t cur_adc;
153a86a88eaSTakashi Iwai 	unsigned int cur_adc_stream_tag;
154a86a88eaSTakashi Iwai 	unsigned int cur_adc_format;
155a86a88eaSTakashi Iwai 
1561f2e99feSLydia Wang 	/* PCM information */
1571f2e99feSLydia Wang 	struct hda_pcm pcm_rec[3];
1581f2e99feSLydia Wang 
1591f2e99feSLydia Wang 	/* dynamic controls, init_verbs and input_mux */
1601f2e99feSLydia Wang 	struct auto_pin_cfg autocfg;
1611f2e99feSLydia Wang 	struct snd_array kctls;
1621f2e99feSLydia Wang 	hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
1631f2e99feSLydia Wang 
1641f2e99feSLydia Wang 	/* HP mode source */
1651f2e99feSLydia Wang 	unsigned int hp_independent_mode;
166f3db423dSLydia Wang 	unsigned int dmic_enabled;
16724088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1681f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1691f2e99feSLydia Wang 
170e3d7a143STakashi Iwai 	/* smart51 setup */
171e3d7a143STakashi Iwai 	unsigned int smart51_nums;
172e3d7a143STakashi Iwai 	hda_nid_t smart51_pins[2];
173e3d7a143STakashi Iwai 	int smart51_idxs[2];
174e3d7a143STakashi Iwai 	const char *smart51_labels[2];
175e3d7a143STakashi Iwai 	unsigned int smart51_enabled;
176e3d7a143STakashi Iwai 
1771f2e99feSLydia Wang 	/* work to check hp jack state */
1781f2e99feSLydia Wang 	struct hda_codec *codec;
1791f2e99feSLydia Wang 	struct delayed_work vt1708_hp_work;
180e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1811f2e99feSLydia Wang 	int vt1708_hp_present;
1823e95b9abSLydia Wang 
1833e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
1843e95b9abSLydia Wang 
1851f2e99feSLydia Wang 	struct hda_loopback_check loopback;
18613af8e77STakashi Iwai 	int num_loopbacks;
18713af8e77STakashi Iwai 	struct hda_amp_list loopback_list[8];
188a86a88eaSTakashi Iwai 
189a86a88eaSTakashi Iwai 	/* bind capture-volume */
190a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_vol;
191a86a88eaSTakashi Iwai 	struct hda_bind_ctls *bind_cap_sw;
1921f2e99feSLydia Wang };
1931f2e99feSLydia Wang 
1940341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
1955b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec)
1965b0cb1d8SJaroslav Kysela {
1975b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1985b0cb1d8SJaroslav Kysela 
1995b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
2005b0cb1d8SJaroslav Kysela 	if (spec == NULL)
2015b0cb1d8SJaroslav Kysela 		return NULL;
2025b0cb1d8SJaroslav Kysela 
2035b0cb1d8SJaroslav Kysela 	codec->spec = spec;
2045b0cb1d8SJaroslav Kysela 	spec->codec = codec;
2050341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
2060341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
2070341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
2080341ccd7SLydia Wang 		spec->codec_type = VT1708S;
2095b0cb1d8SJaroslav Kysela 	return spec;
2105b0cb1d8SJaroslav Kysela }
2115b0cb1d8SJaroslav Kysela 
212744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
213d7426329SHarald Welte {
214744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
215d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
216d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
217d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
218d7426329SHarald Welte 
219d7426329SHarald Welte 	/* get codec type */
220d7426329SHarald Welte 	if (ven_id != 0x1106)
221d7426329SHarald Welte 		codec_type = UNKNOWN;
222d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
223d7426329SHarald Welte 		codec_type = VT1708;
224d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
225d7426329SHarald Welte 		codec_type = VT1709_10CH;
226d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
227d7426329SHarald Welte 		codec_type = VT1709_6CH;
228518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
229d7426329SHarald Welte 		codec_type = VT1708B_8CH;
230518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
231518bf3baSLydia Wang 			codec_type = VT1708BCE;
232518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
233d7426329SHarald Welte 		codec_type = VT1708B_4CH;
234d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
235d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
236d7426329SHarald Welte 		codec_type = VT1708S;
237d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
238d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
239d7426329SHarald Welte 		codec_type = VT1702;
240eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
241eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
242eb7188caSLydia Wang 		codec_type = VT1718S;
243f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
244f3db423dSLydia Wang 		codec_type = VT1716S;
245bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
246bb3c6bfcSLydia Wang 		codec_type = VT1718S;
24725eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
24825eaba2fSLydia Wang 		codec_type = VT2002P;
249ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
250ab6734e7SLydia Wang 		codec_type = VT1812;
25136dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
25236dd5c4aSLydia Wang 		codec_type = VT1708S;
25311890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
25411890956SLydia Wang 		codec_type = VT1802;
255d7426329SHarald Welte 	else
256d7426329SHarald Welte 		codec_type = UNKNOWN;
257d7426329SHarald Welte 	return codec_type;
258d7426329SHarald Welte };
259d7426329SHarald Welte 
260ec7e7e42SLydia Wang #define VIA_JACK_EVENT		0x20
26169e52a80SHarald Welte #define VIA_HP_EVENT		0x01
26269e52a80SHarald Welte #define VIA_GPIO_EVENT		0x02
2634a918ffeSTakashi Iwai #define VIA_LINE_EVENT		0x03
26469e52a80SHarald Welte 
265c577b8a1SJoseph Chan enum {
266c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_VOL,
267c577b8a1SJoseph Chan 	VIA_CTL_WIDGET_MUTE,
268f5271101SLydia Wang 	VIA_CTL_WIDGET_ANALOG_MUTE,
269c577b8a1SJoseph Chan };
270c577b8a1SJoseph Chan 
271ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
272ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
2731f2e99feSLydia Wang 
2741f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec)
2751f2e99feSLydia Wang {
2761f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2771f2e99feSLydia Wang 		return;
2781f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
279e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2801f2e99feSLydia Wang 	if (!delayed_work_pending(&spec->vt1708_hp_work))
2811f2e99feSLydia Wang 		schedule_delayed_work(&spec->vt1708_hp_work,
2821f2e99feSLydia Wang 				      msecs_to_jiffies(100));
2831f2e99feSLydia Wang }
2841f2e99feSLydia Wang 
2851f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec)
2861f2e99feSLydia Wang {
2871f2e99feSLydia Wang 	if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
2881f2e99feSLydia Wang 		return;
2891f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
2901f2e99feSLydia Wang 	    && !is_aa_path_mute(spec->codec))
2911f2e99feSLydia Wang 		return;
2921f2e99feSLydia Wang 	snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
293e06e5a29STakashi Iwai 			    !spec->vt1708_jack_detect);
2945b84ba26STejun Heo 	cancel_delayed_work_sync(&spec->vt1708_hp_work);
2951f2e99feSLydia Wang }
296f5271101SLydia Wang 
2973e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2983e95b9abSLydia Wang {
2993e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
3003e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
3013e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
3023e95b9abSLydia Wang }
30325eaba2fSLydia Wang 
304f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
305f5271101SLydia Wang 				   struct snd_ctl_elem_value *ucontrol)
306f5271101SLydia Wang {
307f5271101SLydia Wang 	int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
308f5271101SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
309f5271101SLydia Wang 
3103e95b9abSLydia Wang 	set_widgets_power_state(codec);
311ada509ecSTakashi Iwai 	analog_low_current_mode(snd_kcontrol_chip(kcontrol));
3121f2e99feSLydia Wang 	if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
3131f2e99feSLydia Wang 		if (is_aa_path_mute(codec))
3141f2e99feSLydia Wang 			vt1708_start_hp_work(codec->spec);
3151f2e99feSLydia Wang 		else
3161f2e99feSLydia Wang 			vt1708_stop_hp_work(codec->spec);
3171f2e99feSLydia Wang 	}
318f5271101SLydia Wang 	return change;
319f5271101SLydia Wang }
320f5271101SLydia Wang 
321f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */
322f5271101SLydia Wang #define ANALOG_INPUT_MUTE						\
323f5271101SLydia Wang 	{		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,		\
324f5271101SLydia Wang 			.name = NULL,					\
325f5271101SLydia Wang 			.index = 0,					\
326f5271101SLydia Wang 			.info = snd_hda_mixer_amp_switch_info,		\
327f5271101SLydia Wang 			.get = snd_hda_mixer_amp_switch_get,		\
328f5271101SLydia Wang 			.put = analog_input_switch_put,			\
329f5271101SLydia Wang 			.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
330f5271101SLydia Wang 
33190dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = {
332c577b8a1SJoseph Chan 	HDA_CODEC_VOLUME(NULL, 0, 0, 0),
333c577b8a1SJoseph Chan 	HDA_CODEC_MUTE(NULL, 0, 0, 0),
334f5271101SLydia Wang 	ANALOG_INPUT_MUTE,
335c577b8a1SJoseph Chan };
336c577b8a1SJoseph Chan 
337ab6734e7SLydia Wang 
338c577b8a1SJoseph Chan /* add dynamic controls */
339291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
340291c9e33STakashi Iwai 				const struct snd_kcontrol_new *tmpl,
341291c9e33STakashi Iwai 				const char *name)
342c577b8a1SJoseph Chan {
343c577b8a1SJoseph Chan 	struct snd_kcontrol_new *knew;
344c577b8a1SJoseph Chan 
345603c4019STakashi Iwai 	snd_array_init(&spec->kctls, sizeof(*knew), 32);
346603c4019STakashi Iwai 	knew = snd_array_new(&spec->kctls);
347c577b8a1SJoseph Chan 	if (!knew)
348291c9e33STakashi Iwai 		return NULL;
349291c9e33STakashi Iwai 	*knew = *tmpl;
350291c9e33STakashi Iwai 	if (!name)
351291c9e33STakashi Iwai 		name = tmpl->name;
352291c9e33STakashi Iwai 	if (name) {
353c577b8a1SJoseph Chan 		knew->name = kstrdup(name, GFP_KERNEL);
354c577b8a1SJoseph Chan 		if (!knew->name)
355291c9e33STakashi Iwai 			return NULL;
356291c9e33STakashi Iwai 	}
357291c9e33STakashi Iwai 	return knew;
358291c9e33STakashi Iwai }
359291c9e33STakashi Iwai 
360291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name,
361291c9e33STakashi Iwai 			     int idx, unsigned long val)
362291c9e33STakashi Iwai {
363291c9e33STakashi Iwai 	struct snd_kcontrol_new *knew;
364291c9e33STakashi Iwai 
365291c9e33STakashi Iwai 	knew = __via_clone_ctl(spec, &via_control_templates[type], name);
366291c9e33STakashi Iwai 	if (!knew)
367c577b8a1SJoseph Chan 		return -ENOMEM;
368d7a99cceSTakashi Iwai 	knew->index = idx;
3694d02d1b6SJaroslav Kysela 	if (get_amp_nid_(val))
3705e26dfd0SJaroslav Kysela 		knew->subdevice = HDA_SUBDEV_AMP_FLAG;
371c577b8a1SJoseph Chan 	knew->private_value = val;
372c577b8a1SJoseph Chan 	return 0;
373c577b8a1SJoseph Chan }
374c577b8a1SJoseph Chan 
3757b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \
3767b315bb4STakashi Iwai 	__via_add_control(spec, type, name, 0, val)
3777b315bb4STakashi Iwai 
378291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
3795b0cb1d8SJaroslav Kysela 
380603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec)
381603c4019STakashi Iwai {
382603c4019STakashi Iwai 	struct via_spec *spec = codec->spec;
383603c4019STakashi Iwai 
384603c4019STakashi Iwai 	if (spec->kctls.list) {
385603c4019STakashi Iwai 		struct snd_kcontrol_new *kctl = spec->kctls.list;
386603c4019STakashi Iwai 		int i;
387603c4019STakashi Iwai 		for (i = 0; i < spec->kctls.used; i++)
388603c4019STakashi Iwai 			kfree(kctl[i].name);
389603c4019STakashi Iwai 	}
390603c4019STakashi Iwai 	snd_array_free(&spec->kctls);
391603c4019STakashi Iwai }
392603c4019STakashi Iwai 
393c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */
3949510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
3957b315bb4STakashi Iwai 				int type_idx, int idx, int mix_nid)
396c577b8a1SJoseph Chan {
397c577b8a1SJoseph Chan 	char name[32];
398c577b8a1SJoseph Chan 	int err;
399c577b8a1SJoseph Chan 
400c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Volume", ctlname);
4017b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
402c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
403c577b8a1SJoseph Chan 	if (err < 0)
404c577b8a1SJoseph Chan 		return err;
405c577b8a1SJoseph Chan 	sprintf(name, "%s Playback Switch", ctlname);
4067b315bb4STakashi Iwai 	err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
407c577b8a1SJoseph Chan 			      HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
408c577b8a1SJoseph Chan 	if (err < 0)
409c577b8a1SJoseph Chan 		return err;
410c577b8a1SJoseph Chan 	return 0;
411c577b8a1SJoseph Chan }
412c577b8a1SJoseph Chan 
4135d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \
414*8d087c76STakashi Iwai 	snd_hda_get_conn_index(codec, mux, nid, 0)
4155d41762aSTakashi Iwai 
4168df2a312STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
4178df2a312STakashi Iwai 			   unsigned int mask)
4188df2a312STakashi Iwai {
419a934d5a9STakashi Iwai 	unsigned int caps;
420a934d5a9STakashi Iwai 	if (!nid)
421a934d5a9STakashi Iwai 		return false;
422a934d5a9STakashi Iwai 	caps = get_wcaps(codec, nid);
4238df2a312STakashi Iwai 	if (dir == HDA_INPUT)
4248df2a312STakashi Iwai 		caps &= AC_WCAP_IN_AMP;
4258df2a312STakashi Iwai 	else
4268df2a312STakashi Iwai 		caps &= AC_WCAP_OUT_AMP;
4278df2a312STakashi Iwai 	if (!caps)
4288df2a312STakashi Iwai 		return false;
4298df2a312STakashi Iwai 	if (query_amp_caps(codec, nid, dir) & mask)
4308df2a312STakashi Iwai 		return true;
4318df2a312STakashi Iwai 	return false;
4328df2a312STakashi Iwai }
4338df2a312STakashi Iwai 
43409a9ad69STakashi Iwai #define have_mute(codec, nid, dir) \
43509a9ad69STakashi Iwai 	check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
4368df2a312STakashi Iwai 
43709a9ad69STakashi Iwai /* enable/disable the output-route */
43809a9ad69STakashi Iwai static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
43909a9ad69STakashi Iwai 				 bool enable, bool force)
4405d41762aSTakashi Iwai {
44109a9ad69STakashi Iwai 	int i;
44209a9ad69STakashi Iwai 	for (i = 0; i < path->depth; i++) {
44309a9ad69STakashi Iwai 		hda_nid_t src, dst;
44409a9ad69STakashi Iwai 		int idx = path->idx[i];
44509a9ad69STakashi Iwai 		src = path->path[i];
44609a9ad69STakashi Iwai 		if (i < path->depth - 1)
44709a9ad69STakashi Iwai 			dst = path->path[i + 1];
44809a9ad69STakashi Iwai 		else
44909a9ad69STakashi Iwai 			dst = 0;
45009a9ad69STakashi Iwai 		if (enable && path->multi[i])
45109a9ad69STakashi Iwai 			snd_hda_codec_write(codec, dst, 0,
4525d41762aSTakashi Iwai 					    AC_VERB_SET_CONNECT_SEL, idx);
45309a9ad69STakashi Iwai 		if (have_mute(codec, dst, HDA_INPUT)) {
45409a9ad69STakashi Iwai 			int val = enable ? AMP_IN_UNMUTE(idx) :
45509a9ad69STakashi Iwai 				AMP_IN_MUTE(idx);
45609a9ad69STakashi Iwai 			snd_hda_codec_write(codec, dst, 0,
45709a9ad69STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE, val);
45809a9ad69STakashi Iwai 		}
45909a9ad69STakashi Iwai 		if (!force && (src == path->vol_ctl || src == path->mute_ctl))
46009a9ad69STakashi Iwai 			continue;
46109a9ad69STakashi Iwai 		if (have_mute(codec, src, HDA_OUTPUT)) {
46209a9ad69STakashi Iwai 			int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
46309a9ad69STakashi Iwai 			snd_hda_codec_write(codec, src, 0,
46409a9ad69STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE, val);
46509a9ad69STakashi Iwai 		}
46609a9ad69STakashi Iwai 	}
4675d41762aSTakashi Iwai }
4685d41762aSTakashi Iwai 
4695d41762aSTakashi Iwai /* set the given pin as output */
4705d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
4715d41762aSTakashi Iwai 			    int pin_type)
4725d41762aSTakashi Iwai {
4735d41762aSTakashi Iwai 	if (!pin)
4745d41762aSTakashi Iwai 		return;
4755d41762aSTakashi Iwai 	snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
4765d41762aSTakashi Iwai 			    pin_type);
4775d41762aSTakashi Iwai 	if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
4785d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0,
479d3a11e60STakashi Iwai 				    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
480c577b8a1SJoseph Chan }
481c577b8a1SJoseph Chan 
48209a9ad69STakashi Iwai static void via_auto_init_output(struct hda_codec *codec,
48309a9ad69STakashi Iwai 				 struct nid_path *path, int pin_type,
48409a9ad69STakashi Iwai 				 bool force)
4855d41762aSTakashi Iwai {
4865d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
4875d41762aSTakashi Iwai 	unsigned int caps;
48809a9ad69STakashi Iwai 	hda_nid_t pin, nid;
48909a9ad69STakashi Iwai 	int i, idx;
4905d41762aSTakashi Iwai 
49109a9ad69STakashi Iwai 	if (!path->depth)
4925d41762aSTakashi Iwai 		return;
49309a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
4945d41762aSTakashi Iwai 
4955d41762aSTakashi Iwai 	init_output_pin(codec, pin, pin_type);
4965d41762aSTakashi Iwai 	caps = query_amp_caps(codec, pin, HDA_OUTPUT);
4975d41762aSTakashi Iwai 	if (caps & AC_AMPCAP_MUTE) {
4985d41762aSTakashi Iwai 		unsigned int val;
4995d41762aSTakashi Iwai 		val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
5005d41762aSTakashi Iwai 		snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
5015d41762aSTakashi Iwai 				    AMP_OUT_MUTE | val);
5025d41762aSTakashi Iwai 	}
5035d41762aSTakashi Iwai 
50409a9ad69STakashi Iwai 	activate_output_path(codec, path, true, force);
5055d41762aSTakashi Iwai 
50609a9ad69STakashi Iwai 	/* initialize the AA-path */
50709a9ad69STakashi Iwai 	if (!spec->aa_mix_nid)
50809a9ad69STakashi Iwai 		return;
50909a9ad69STakashi Iwai 	for (i = path->depth - 1; i > 0; i--) {
51009a9ad69STakashi Iwai 		nid = path->path[i];
51109a9ad69STakashi Iwai 		idx = get_connection_index(codec, nid, spec->aa_mix_nid);
51209a9ad69STakashi Iwai 		if (idx >= 0) {
51309a9ad69STakashi Iwai 			if (have_mute(codec, nid, HDA_INPUT))
51409a9ad69STakashi Iwai 				snd_hda_codec_write(codec, nid, 0,
51509a9ad69STakashi Iwai 						    AC_VERB_SET_AMP_GAIN_MUTE,
51609a9ad69STakashi Iwai 						    AMP_IN_UNMUTE(idx));
51709a9ad69STakashi Iwai 			break;
51809a9ad69STakashi Iwai 		}
51909a9ad69STakashi Iwai 	}
52009a9ad69STakashi Iwai }
521c577b8a1SJoseph Chan 
522c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec)
523c577b8a1SJoseph Chan {
524c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
525c577b8a1SJoseph Chan 	int i;
526c577b8a1SJoseph Chan 
527e3d7a143STakashi Iwai 	for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
52809a9ad69STakashi Iwai 		via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
529c577b8a1SJoseph Chan }
530c577b8a1SJoseph Chan 
531c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec)
532c577b8a1SJoseph Chan {
533c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
534c577b8a1SJoseph Chan 
53509a9ad69STakashi Iwai 	if (!spec->hp_dac_nid) {
53609a9ad69STakashi Iwai 		via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
53709a9ad69STakashi Iwai 		return;
53809a9ad69STakashi Iwai 	}
53909a9ad69STakashi Iwai 	if (spec->hp_independent_mode) {
54009a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_dep_path, false, false);
54109a9ad69STakashi Iwai 		via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
54209a9ad69STakashi Iwai 	} else {
54309a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
54409a9ad69STakashi Iwai 		via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
54509a9ad69STakashi Iwai 	}
54625eaba2fSLydia Wang }
547c577b8a1SJoseph Chan 
5484a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec)
5494a918ffeSTakashi Iwai {
5504a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
5514a918ffeSTakashi Iwai 
5524a918ffeSTakashi Iwai 	if (spec->autocfg.speaker_outs)
55309a9ad69STakashi Iwai 		via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
5544a918ffeSTakashi Iwai }
5554a918ffeSTakashi Iwai 
556f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
55732e0191dSClemens Ladisch 
558c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec)
559c577b8a1SJoseph Chan {
560c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
5617b315bb4STakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
562096a8854STakashi Iwai 	hda_nid_t conn[HDA_MAX_CONNECTIONS];
56332e0191dSClemens Ladisch 	unsigned int ctl;
564096a8854STakashi Iwai 	int i, num_conns;
565c577b8a1SJoseph Chan 
566096a8854STakashi Iwai 	/* init ADCs */
567096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
568096a8854STakashi Iwai 		snd_hda_codec_write(codec, spec->adc_nids[i], 0,
569096a8854STakashi Iwai 				    AC_VERB_SET_AMP_GAIN_MUTE,
570096a8854STakashi Iwai 				    AMP_IN_UNMUTE(0));
571096a8854STakashi Iwai 	}
572096a8854STakashi Iwai 
573096a8854STakashi Iwai 	/* init pins */
5747b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
5757b315bb4STakashi Iwai 		hda_nid_t nid = cfg->inputs[i].pin;
576f4a7828bSTakashi Iwai 		if (spec->smart51_enabled && is_smart51_pins(codec, nid))
57732e0191dSClemens Ladisch 			ctl = PIN_OUT;
57830649676SDavid Henningsson 		else if (cfg->inputs[i].type == AUTO_PIN_MIC)
57932e0191dSClemens Ladisch 			ctl = PIN_VREF50;
58032e0191dSClemens Ladisch 		else
58132e0191dSClemens Ladisch 			ctl = PIN_IN;
582c577b8a1SJoseph Chan 		snd_hda_codec_write(codec, nid, 0,
58332e0191dSClemens Ladisch 				    AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
584c577b8a1SJoseph Chan 	}
585096a8854STakashi Iwai 
586096a8854STakashi Iwai 	/* init input-src */
587096a8854STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
588a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
589a86a88eaSTakashi Iwai 		if (spec->mux_nids[adc_idx]) {
590a86a88eaSTakashi Iwai 			int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
591a86a88eaSTakashi Iwai 			snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
592096a8854STakashi Iwai 					    AC_VERB_SET_CONNECT_SEL,
593a86a88eaSTakashi Iwai 					    mux_idx);
594a86a88eaSTakashi Iwai 		}
595a86a88eaSTakashi Iwai 		if (spec->dyn_adc_switch)
596a86a88eaSTakashi Iwai 			break; /* only one input-src */
597096a8854STakashi Iwai 	}
598096a8854STakashi Iwai 
599096a8854STakashi Iwai 	/* init aa-mixer */
600096a8854STakashi Iwai 	if (!spec->aa_mix_nid)
601096a8854STakashi Iwai 		return;
602096a8854STakashi Iwai 	num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
603096a8854STakashi Iwai 					    ARRAY_SIZE(conn));
604096a8854STakashi Iwai 	for (i = 0; i < num_conns; i++) {
605096a8854STakashi Iwai 		unsigned int caps = get_wcaps(codec, conn[i]);
606096a8854STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_PIN)
607096a8854STakashi Iwai 			snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
608096a8854STakashi Iwai 					    AC_VERB_SET_AMP_GAIN_MUTE,
609096a8854STakashi Iwai 					    AMP_IN_MUTE(i));
610096a8854STakashi Iwai 	}
611c577b8a1SJoseph Chan }
612f5271101SLydia Wang 
613f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
614f5271101SLydia Wang 				unsigned int *affected_parm)
615f5271101SLydia Wang {
616f5271101SLydia Wang 	unsigned parm;
617f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
618f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
619f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
620f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
6211564b287SLydia Wang 	struct via_spec *spec = codec->spec;
62224088a58STakashi Iwai 	unsigned present = 0;
62324088a58STakashi Iwai 
62424088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
62524088a58STakashi Iwai 	if (!no_presence)
62624088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
627f4a7828bSTakashi Iwai 	if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
6281564b287SLydia Wang 	    || ((no_presence || present)
6291564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
630f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
631f5271101SLydia Wang 		parm = AC_PWRST_D0;
632f5271101SLydia Wang 	} else
633f5271101SLydia Wang 		parm = AC_PWRST_D3;
634f5271101SLydia Wang 
635f5271101SLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
636f5271101SLydia Wang }
637f5271101SLydia Wang 
63824088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
63924088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
64024088a58STakashi Iwai {
64124088a58STakashi Iwai 	static const char * const texts[] = {
64224088a58STakashi Iwai 		"Disabled", "Enabled"
64324088a58STakashi Iwai 	};
64424088a58STakashi Iwai 
64524088a58STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
64624088a58STakashi Iwai 	uinfo->count = 1;
64724088a58STakashi Iwai 	uinfo->value.enumerated.items = 2;
64824088a58STakashi Iwai 	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
64924088a58STakashi Iwai 		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
65024088a58STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
65124088a58STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
65224088a58STakashi Iwai 	return 0;
65324088a58STakashi Iwai }
65424088a58STakashi Iwai 
65524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
65624088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
65724088a58STakashi Iwai {
65824088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
65924088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
66024088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
66124088a58STakashi Iwai 	return 0;
66224088a58STakashi Iwai }
66324088a58STakashi Iwai 
66424088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
66524088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
66624088a58STakashi Iwai {
66724088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
66824088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
66924088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
67024088a58STakashi Iwai 
67124088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
67224088a58STakashi Iwai 		return 0;
67324088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
67424088a58STakashi Iwai 	set_widgets_power_state(codec);
67524088a58STakashi Iwai 	return 1;
67624088a58STakashi Iwai }
67724088a58STakashi Iwai 
67824088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
67924088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
68024088a58STakashi Iwai 	.name = "Dynamic Power-Control",
68124088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
68224088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
68324088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
68424088a58STakashi Iwai };
68524088a58STakashi Iwai 
68624088a58STakashi Iwai 
6870aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
6880aa62aefSHarald Welte 				   struct snd_ctl_elem_info *uinfo)
6890aa62aefSHarald Welte {
6908df2a312STakashi Iwai 	static const char * const texts[] = { "OFF", "ON" };
6918df2a312STakashi Iwai 
6928df2a312STakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
6938df2a312STakashi Iwai 	uinfo->count = 1;
6948df2a312STakashi Iwai 	uinfo->value.enumerated.items = 2;
6958df2a312STakashi Iwai 	if (uinfo->value.enumerated.item >= 2)
6968df2a312STakashi Iwai 		uinfo->value.enumerated.item = 1;
6978df2a312STakashi Iwai 	strcpy(uinfo->value.enumerated.name,
6988df2a312STakashi Iwai 	       texts[uinfo->value.enumerated.item]);
6998df2a312STakashi Iwai 	return 0;
7000aa62aefSHarald Welte }
7010aa62aefSHarald Welte 
7020aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
7030aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7040aa62aefSHarald Welte {
7050aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
706cdc1784dSLydia Wang 	struct via_spec *spec = codec->spec;
707cdc1784dSLydia Wang 
708ece8d043STakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
709cdc1784dSLydia Wang 	return 0;
710cdc1784dSLydia Wang }
711cdc1784dSLydia Wang 
7120aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
7130aa62aefSHarald Welte 				  struct snd_ctl_elem_value *ucontrol)
7140aa62aefSHarald Welte {
7150aa62aefSHarald Welte 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
7160aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
7178df2a312STakashi Iwai 
7188df2a312STakashi Iwai 	spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
71909a9ad69STakashi Iwai 	if (spec->hp_independent_mode) {
72009a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_dep_path, false, false);
72109a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, true, false);
72209a9ad69STakashi Iwai 	} else {
72309a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_path, false, false);
72409a9ad69STakashi Iwai 		activate_output_path(codec, &spec->hp_dep_path, true, false);
7258df2a312STakashi Iwai 	}
7260aa62aefSHarald Welte 
727ce0e5a9eSLydia Wang 	/* update jack power state */
7283e95b9abSLydia Wang 	set_widgets_power_state(codec);
7290aa62aefSHarald Welte 	return 0;
7300aa62aefSHarald Welte }
7310aa62aefSHarald Welte 
732ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = {
7330aa62aefSHarald Welte 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
7340aa62aefSHarald Welte 	.name = "Independent HP",
7350aa62aefSHarald Welte 	.info = via_independent_hp_info,
7360aa62aefSHarald Welte 	.get = via_independent_hp_get,
7370aa62aefSHarald Welte 	.put = via_independent_hp_put,
7380aa62aefSHarald Welte };
7390aa62aefSHarald Welte 
7403d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec)
7415b0cb1d8SJaroslav Kysela {
7423d83e577STakashi Iwai 	struct via_spec *spec = codec->spec;
7435b0cb1d8SJaroslav Kysela 	struct snd_kcontrol_new *knew;
7445b0cb1d8SJaroslav Kysela 	hda_nid_t nid;
7455b0cb1d8SJaroslav Kysela 
7465b0cb1d8SJaroslav Kysela 	nid = spec->autocfg.hp_pins[0];
747ece8d043STakashi Iwai 	knew = via_clone_control(spec, &via_hp_mixer);
7483d83e577STakashi Iwai 	if (knew == NULL)
7493d83e577STakashi Iwai 		return -ENOMEM;
7503d83e577STakashi Iwai 
7515b0cb1d8SJaroslav Kysela 	knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
7525b0cb1d8SJaroslav Kysela 
7535b0cb1d8SJaroslav Kysela 	return 0;
7545b0cb1d8SJaroslav Kysela }
7555b0cb1d8SJaroslav Kysela 
7561564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec)
7571564b287SLydia Wang {
758e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
7591564b287SLydia Wang 	int i;
7601564b287SLydia Wang 
761e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
762e3d7a143STakashi Iwai 		struct snd_kcontrol *ctl;
763e3d7a143STakashi Iwai 		struct snd_ctl_elem_id id;
7641564b287SLydia Wang 		memset(&id, 0, sizeof(id));
7651564b287SLydia Wang 		id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
766e3d7a143STakashi Iwai 		sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
767525566cbSLydia Wang 		ctl = snd_hda_find_mixer_ctl(codec, id.name);
768525566cbSLydia Wang 		if (ctl)
769525566cbSLydia Wang 			snd_ctl_notify(codec->bus->card,
770525566cbSLydia Wang 					SNDRV_CTL_EVENT_MASK_VALUE,
771525566cbSLydia Wang 					&ctl->id);
7721564b287SLydia Wang 	}
7731564b287SLydia Wang }
7741564b287SLydia Wang 
7751564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute)
7761564b287SLydia Wang {
7771564b287SLydia Wang 	struct via_spec *spec = codec->spec;
7781564b287SLydia Wang 	int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
779e3d7a143STakashi Iwai 	int i;
780e3d7a143STakashi Iwai 
781e3d7a143STakashi Iwai 	/* check AA path's mute status */
782e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
783e3d7a143STakashi Iwai 		if (spec->smart51_idxs[i] < 0)
784e3d7a143STakashi Iwai 			continue;
785e3d7a143STakashi Iwai 		snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
786e3d7a143STakashi Iwai 					 HDA_INPUT, spec->smart51_idxs[i],
7871564b287SLydia Wang 					 HDA_AMP_MUTE, val);
7881564b287SLydia Wang 	}
7891564b287SLydia Wang }
790f4a7828bSTakashi Iwai 
791e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
792e3d7a143STakashi Iwai {
793e3d7a143STakashi Iwai 	struct via_spec *spec = codec->spec;
794e3d7a143STakashi Iwai 	int i;
795e3d7a143STakashi Iwai 
796e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++)
797e3d7a143STakashi Iwai 		if (spec->smart51_pins[i] == pin)
798e3d7a143STakashi Iwai 			return true;
799e3d7a143STakashi Iwai 	return false;
800e3d7a143STakashi Iwai }
801e3d7a143STakashi Iwai 
8021564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol,
8031564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8041564b287SLydia Wang {
8051564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8061564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8071564b287SLydia Wang 
808f2b1c9f0STakashi Iwai 	*ucontrol->value.integer.value = spec->smart51_enabled;
8091564b287SLydia Wang 	return 0;
8101564b287SLydia Wang }
8111564b287SLydia Wang 
8121564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol,
8131564b287SLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
8141564b287SLydia Wang {
8151564b287SLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
8161564b287SLydia Wang 	struct via_spec *spec = codec->spec;
8171564b287SLydia Wang 	int out_in = *ucontrol->value.integer.value
8181564b287SLydia Wang 		? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
8191564b287SLydia Wang 	int i;
8201564b287SLydia Wang 
821e3d7a143STakashi Iwai 	for (i = 0; i < spec->smart51_nums; i++) {
822e3d7a143STakashi Iwai 		hda_nid_t nid = spec->smart51_pins[i];
8237b315bb4STakashi Iwai 		unsigned int parm;
8247b315bb4STakashi Iwai 
8257b315bb4STakashi Iwai 		parm = snd_hda_codec_read(codec, nid, 0,
8261564b287SLydia Wang 					  AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
8271564b287SLydia Wang 		parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
8281564b287SLydia Wang 		parm |= out_in;
8291564b287SLydia Wang 		snd_hda_codec_write(codec, nid, 0,
8301564b287SLydia Wang 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
8311564b287SLydia Wang 				    parm);
8321564b287SLydia Wang 		if (out_in == AC_PINCTL_OUT_EN) {
8331564b287SLydia Wang 			mute_aa_path(codec, 1);
8341564b287SLydia Wang 			notify_aa_path_ctls(codec);
8351564b287SLydia Wang 		}
8361564b287SLydia Wang 	}
8371564b287SLydia Wang 	spec->smart51_enabled = *ucontrol->value.integer.value;
8383e95b9abSLydia Wang 	set_widgets_power_state(codec);
8391564b287SLydia Wang 	return 1;
8401564b287SLydia Wang }
8411564b287SLydia Wang 
8425f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = {
8431564b287SLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
8441564b287SLydia Wang 	.name = "Smart 5.1",
8451564b287SLydia Wang 	.count = 1,
846f2b1c9f0STakashi Iwai 	.info = snd_ctl_boolean_mono_info,
8471564b287SLydia Wang 	.get = via_smart51_get,
8481564b287SLydia Wang 	.put = via_smart51_put,
8491564b287SLydia Wang };
8501564b287SLydia Wang 
851f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec)
8525b0cb1d8SJaroslav Kysela {
853f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
8545b0cb1d8SJaroslav Kysela 
855e3d7a143STakashi Iwai 	if (!spec->smart51_nums)
856cb34c207SLydia Wang 		return 0;
857e3d7a143STakashi Iwai 	if (!via_clone_control(spec, &via_smart51_mixer))
8585b0cb1d8SJaroslav Kysela 		return -ENOMEM;
8595b0cb1d8SJaroslav Kysela 	return 0;
8605b0cb1d8SJaroslav Kysela }
8615b0cb1d8SJaroslav Kysela 
862f5271101SLydia Wang /* check AA path's mute status */
863ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
864ada509ecSTakashi Iwai {
865ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
866ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
867ada509ecSTakashi Iwai 	int i, ch, v;
868ada509ecSTakashi Iwai 
869ada509ecSTakashi Iwai 	for (i = 0; i < spec->num_loopbacks; i++) {
870ada509ecSTakashi Iwai 		p = &spec->loopback_list[i];
871ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
872ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
873ada509ecSTakashi Iwai 						   p->idx);
874ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
875ada509ecSTakashi Iwai 				return false;
876f5271101SLydia Wang 		}
877f5271101SLydia Wang 	}
878ada509ecSTakashi Iwai 	return true;
879f5271101SLydia Wang }
880f5271101SLydia Wang 
881f5271101SLydia Wang /* enter/exit analog low-current mode */
882ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
883f5271101SLydia Wang {
884f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
885ada509ecSTakashi Iwai 	bool enable;
886ada509ecSTakashi Iwai 	unsigned int verb, parm;
887f5271101SLydia Wang 
888ada509ecSTakashi Iwai 	enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
889f5271101SLydia Wang 
890f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
891f5271101SLydia Wang 	switch (spec->codec_type) {
892f5271101SLydia Wang 	case VT1708B_8CH:
893f5271101SLydia Wang 	case VT1708B_4CH:
894f5271101SLydia Wang 		verb = 0xf70;
895f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
896f5271101SLydia Wang 		break;
897f5271101SLydia Wang 	case VT1708S:
898eb7188caSLydia Wang 	case VT1718S:
899f3db423dSLydia Wang 	case VT1716S:
900f5271101SLydia Wang 		verb = 0xf73;
901f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
902f5271101SLydia Wang 		break;
903f5271101SLydia Wang 	case VT1702:
904f5271101SLydia Wang 		verb = 0xf73;
905f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
906f5271101SLydia Wang 		break;
90725eaba2fSLydia Wang 	case VT2002P:
908ab6734e7SLydia Wang 	case VT1812:
90911890956SLydia Wang 	case VT1802:
91025eaba2fSLydia Wang 		verb = 0xf93;
91125eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
91225eaba2fSLydia Wang 		break;
913f5271101SLydia Wang 	default:
914f5271101SLydia Wang 		return;		/* other codecs are not supported */
915f5271101SLydia Wang 	}
916f5271101SLydia Wang 	/* send verb */
917f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
918f5271101SLydia Wang }
919f5271101SLydia Wang 
920c577b8a1SJoseph Chan /*
921c577b8a1SJoseph Chan  * generic initialization of ADC, input mixers and output mixers
922c577b8a1SJoseph Chan  */
923096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
924aa266fccSLydia Wang 	/* power down jack detect function */
925aa266fccSLydia Wang 	{0x1, 0xf81, 0x1},
926f7278fd0SJosepch Chan 	{ }
927c577b8a1SJoseph Chan };
928c577b8a1SJoseph Chan 
929ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active)
9307eb56e84STakashi Iwai {
931ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
932ada509ecSTakashi Iwai 
933ada509ecSTakashi Iwai 	if (active)
934ada509ecSTakashi Iwai 		spec->num_active_streams++;
935ada509ecSTakashi Iwai 	else
936ada509ecSTakashi Iwai 		spec->num_active_streams--;
937ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
9387eb56e84STakashi Iwai }
9397eb56e84STakashi Iwai 
940ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
941c577b8a1SJoseph Chan 				 struct hda_codec *codec,
942c577b8a1SJoseph Chan 				 struct snd_pcm_substream *substream)
943c577b8a1SJoseph Chan {
944c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
945ada509ecSTakashi Iwai 	int err;
946ece8d043STakashi Iwai 
947ece8d043STakashi Iwai 	if (!spec->hp_independent_mode)
948ece8d043STakashi Iwai 		spec->multiout.hp_nid = spec->hp_dac_nid;
949ada509ecSTakashi Iwai 	set_stream_active(codec, true);
950ada509ecSTakashi Iwai 	err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
9519a08160bSTakashi Iwai 					    hinfo);
952ada509ecSTakashi Iwai 	if (err < 0) {
953ada509ecSTakashi Iwai 		spec->multiout.hp_nid = 0;
954ada509ecSTakashi Iwai 		set_stream_active(codec, false);
955ada509ecSTakashi Iwai 		return err;
956ada509ecSTakashi Iwai 	}
957ada509ecSTakashi Iwai 	return 0;
958c577b8a1SJoseph Chan }
959c577b8a1SJoseph Chan 
960ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
9619af74210STakashi Iwai 				  struct hda_codec *codec,
9629af74210STakashi Iwai 				  struct snd_pcm_substream *substream)
9639af74210STakashi Iwai {
964ece8d043STakashi Iwai 	struct via_spec *spec = codec->spec;
965ece8d043STakashi Iwai 
966ece8d043STakashi Iwai 	spec->multiout.hp_nid = 0;
967ada509ecSTakashi Iwai 	set_stream_active(codec, false);
9689af74210STakashi Iwai 	return 0;
9699af74210STakashi Iwai }
9709af74210STakashi Iwai 
9717eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
9727eb56e84STakashi Iwai 				    struct hda_codec *codec,
9737eb56e84STakashi Iwai 				    struct snd_pcm_substream *substream)
9747eb56e84STakashi Iwai {
9757eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
9767eb56e84STakashi Iwai 
977ece8d043STakashi Iwai 	if (snd_BUG_ON(!spec->hp_dac_nid))
9787eb56e84STakashi Iwai 		return -EINVAL;
979ece8d043STakashi Iwai 	if (!spec->hp_independent_mode || spec->multiout.hp_nid)
980ece8d043STakashi Iwai 		return -EBUSY;
981ada509ecSTakashi Iwai 	set_stream_active(codec, true);
982ece8d043STakashi Iwai 	return 0;
983ece8d043STakashi Iwai }
984ece8d043STakashi Iwai 
985ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
986ece8d043STakashi Iwai 				     struct hda_codec *codec,
987ece8d043STakashi Iwai 				     struct snd_pcm_substream *substream)
988ece8d043STakashi Iwai {
989ada509ecSTakashi Iwai 	set_stream_active(codec, false);
9907eb56e84STakashi Iwai 	return 0;
9917eb56e84STakashi Iwai }
9927eb56e84STakashi Iwai 
9937eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
9947eb56e84STakashi Iwai 					  struct hda_codec *codec,
9950aa62aefSHarald Welte 					  unsigned int stream_tag,
9960aa62aefSHarald Welte 					  unsigned int format,
9970aa62aefSHarald Welte 					  struct snd_pcm_substream *substream)
9980aa62aefSHarald Welte {
9990aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10000aa62aefSHarald Welte 
1001ece8d043STakashi Iwai 	snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1002ece8d043STakashi Iwai 					 format, substream);
10037eb56e84STakashi Iwai 	vt1708_start_hp_work(spec);
10047eb56e84STakashi Iwai 	return 0;
10050aa62aefSHarald Welte }
10060aa62aefSHarald Welte 
10077eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
10080aa62aefSHarald Welte 				       struct hda_codec *codec,
10090aa62aefSHarald Welte 				       unsigned int stream_tag,
10100aa62aefSHarald Welte 				       unsigned int format,
10110aa62aefSHarald Welte 				       struct snd_pcm_substream *substream)
10120aa62aefSHarald Welte {
10130aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10140aa62aefSHarald Welte 
1015ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1016ece8d043STakashi Iwai 				   stream_tag, 0, format);
10171f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
10180aa62aefSHarald Welte 	return 0;
10190aa62aefSHarald Welte }
10200aa62aefSHarald Welte 
10210aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
10220aa62aefSHarald Welte 				    struct hda_codec *codec,
10230aa62aefSHarald Welte 				    struct snd_pcm_substream *substream)
10240aa62aefSHarald Welte {
10250aa62aefSHarald Welte 	struct via_spec *spec = codec->spec;
10260aa62aefSHarald Welte 
1027ece8d043STakashi Iwai 	snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
10287eb56e84STakashi Iwai 	vt1708_stop_hp_work(spec);
10297eb56e84STakashi Iwai 	return 0;
10300aa62aefSHarald Welte }
10317eb56e84STakashi Iwai 
10327eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
10337eb56e84STakashi Iwai 				       struct hda_codec *codec,
10347eb56e84STakashi Iwai 				       struct snd_pcm_substream *substream)
10357eb56e84STakashi Iwai {
10367eb56e84STakashi Iwai 	struct via_spec *spec = codec->spec;
10377eb56e84STakashi Iwai 
1038ece8d043STakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
10391f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
10400aa62aefSHarald Welte 	return 0;
10410aa62aefSHarald Welte }
10420aa62aefSHarald Welte 
1043c577b8a1SJoseph Chan /*
1044c577b8a1SJoseph Chan  * Digital out
1045c577b8a1SJoseph Chan  */
1046c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1047c577b8a1SJoseph Chan 				     struct hda_codec *codec,
1048c577b8a1SJoseph Chan 				     struct snd_pcm_substream *substream)
1049c577b8a1SJoseph Chan {
1050c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1051c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1052c577b8a1SJoseph Chan }
1053c577b8a1SJoseph Chan 
1054c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1055c577b8a1SJoseph Chan 				      struct hda_codec *codec,
1056c577b8a1SJoseph Chan 				      struct snd_pcm_substream *substream)
1057c577b8a1SJoseph Chan {
1058c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1059c577b8a1SJoseph Chan 	return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1060c577b8a1SJoseph Chan }
1061c577b8a1SJoseph Chan 
10625691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
106398aa34c0SHarald Welte 					struct hda_codec *codec,
106498aa34c0SHarald Welte 					unsigned int stream_tag,
106598aa34c0SHarald Welte 					unsigned int format,
106698aa34c0SHarald Welte 					struct snd_pcm_substream *substream)
106798aa34c0SHarald Welte {
106898aa34c0SHarald Welte 	struct via_spec *spec = codec->spec;
10699da29271STakashi Iwai 	return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
10709da29271STakashi Iwai 					     stream_tag, format, substream);
10719da29271STakashi Iwai }
10725691ec7fSHarald Welte 
10739da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
10749da29271STakashi Iwai 					struct hda_codec *codec,
10759da29271STakashi Iwai 					struct snd_pcm_substream *substream)
10769da29271STakashi Iwai {
10779da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
10789da29271STakashi Iwai 	snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
107998aa34c0SHarald Welte 	return 0;
108098aa34c0SHarald Welte }
108198aa34c0SHarald Welte 
1082c577b8a1SJoseph Chan /*
1083c577b8a1SJoseph Chan  * Analog capture
1084c577b8a1SJoseph Chan  */
1085c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1086c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1087c577b8a1SJoseph Chan 				   unsigned int stream_tag,
1088c577b8a1SJoseph Chan 				   unsigned int format,
1089c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1090c577b8a1SJoseph Chan {
1091c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1092c577b8a1SJoseph Chan 
1093c577b8a1SJoseph Chan 	snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1094c577b8a1SJoseph Chan 				   stream_tag, 0, format);
1095c577b8a1SJoseph Chan 	return 0;
1096c577b8a1SJoseph Chan }
1097c577b8a1SJoseph Chan 
1098c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1099c577b8a1SJoseph Chan 				   struct hda_codec *codec,
1100c577b8a1SJoseph Chan 				   struct snd_pcm_substream *substream)
1101c577b8a1SJoseph Chan {
1102c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1103888afa15STakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
1104c577b8a1SJoseph Chan 	return 0;
1105c577b8a1SJoseph Chan }
1106c577b8a1SJoseph Chan 
1107a86a88eaSTakashi Iwai /* analog capture with dynamic ADC switching */
1108a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1109a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1110a86a88eaSTakashi Iwai 					   unsigned int stream_tag,
1111a86a88eaSTakashi Iwai 					   unsigned int format,
1112a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1113a86a88eaSTakashi Iwai {
1114a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1115a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1116a86a88eaSTakashi Iwai 
1117a86a88eaSTakashi Iwai 	spec->cur_adc = spec->adc_nids[adc_idx];
1118a86a88eaSTakashi Iwai 	spec->cur_adc_stream_tag = stream_tag;
1119a86a88eaSTakashi Iwai 	spec->cur_adc_format = format;
1120a86a88eaSTakashi Iwai 	snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1121a86a88eaSTakashi Iwai 	return 0;
1122a86a88eaSTakashi Iwai }
1123a86a88eaSTakashi Iwai 
1124a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1125a86a88eaSTakashi Iwai 					   struct hda_codec *codec,
1126a86a88eaSTakashi Iwai 					   struct snd_pcm_substream *substream)
1127a86a88eaSTakashi Iwai {
1128a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1129a86a88eaSTakashi Iwai 
1130a86a88eaSTakashi Iwai 	snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1131a86a88eaSTakashi Iwai 	spec->cur_adc = 0;
1132a86a88eaSTakashi Iwai 	return 0;
1133a86a88eaSTakashi Iwai }
1134a86a88eaSTakashi Iwai 
1135a86a88eaSTakashi Iwai /* re-setup the stream if running; called from input-src put */
1136a86a88eaSTakashi Iwai static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1137a86a88eaSTakashi Iwai {
1138a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1139a86a88eaSTakashi Iwai 	int adc_idx = spec->inputs[cur].adc_idx;
1140a86a88eaSTakashi Iwai 	hda_nid_t adc = spec->adc_nids[adc_idx];
1141a86a88eaSTakashi Iwai 
1142a86a88eaSTakashi Iwai 	if (spec->cur_adc && spec->cur_adc != adc) {
1143a86a88eaSTakashi Iwai 		/* stream is running, let's swap the current ADC */
1144a86a88eaSTakashi Iwai 		__snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1145a86a88eaSTakashi Iwai 		spec->cur_adc = adc;
1146a86a88eaSTakashi Iwai 		snd_hda_codec_setup_stream(codec, adc,
1147a86a88eaSTakashi Iwai 					   spec->cur_adc_stream_tag, 0,
1148a86a88eaSTakashi Iwai 					   spec->cur_adc_format);
1149a86a88eaSTakashi Iwai 		return true;
1150a86a88eaSTakashi Iwai 	}
1151a86a88eaSTakashi Iwai 	return false;
1152a86a88eaSTakashi Iwai }
1153a86a88eaSTakashi Iwai 
11549af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = {
11557eb56e84STakashi Iwai 	.substreams = 1,
1156c577b8a1SJoseph Chan 	.channels_min = 2,
1157c577b8a1SJoseph Chan 	.channels_max = 8,
11589af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1159c577b8a1SJoseph Chan 	.ops = {
1160ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1161ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
11620aa62aefSHarald Welte 		.prepare = via_playback_multi_pcm_prepare,
11630aa62aefSHarald Welte 		.cleanup = via_playback_multi_pcm_cleanup
1164c577b8a1SJoseph Chan 	},
1165c577b8a1SJoseph Chan };
1166c577b8a1SJoseph Chan 
11677eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = {
11687eb56e84STakashi Iwai 	.substreams = 1,
11697eb56e84STakashi Iwai 	.channels_min = 2,
11707eb56e84STakashi Iwai 	.channels_max = 2,
11717eb56e84STakashi Iwai 	/* NID is set in via_build_pcms */
11727eb56e84STakashi Iwai 	.ops = {
11737eb56e84STakashi Iwai 		.open = via_playback_hp_pcm_open,
1174ece8d043STakashi Iwai 		.close = via_playback_hp_pcm_close,
11757eb56e84STakashi Iwai 		.prepare = via_playback_hp_pcm_prepare,
11767eb56e84STakashi Iwai 		.cleanup = via_playback_hp_pcm_cleanup
11777eb56e84STakashi Iwai 	},
11787eb56e84STakashi Iwai };
11797eb56e84STakashi Iwai 
118090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
11817eb56e84STakashi Iwai 	.substreams = 1,
1182bc9b5623STakashi Iwai 	.channels_min = 2,
1183bc9b5623STakashi Iwai 	.channels_max = 8,
11849af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1185bc9b5623STakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
1186bc9b5623STakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
1187bc9b5623STakashi Iwai 	 * disable the 24bit format, so far.
1188bc9b5623STakashi Iwai 	 */
1189bc9b5623STakashi Iwai 	.formats = SNDRV_PCM_FMTBIT_S16_LE,
1190bc9b5623STakashi Iwai 	.ops = {
1191ece8d043STakashi Iwai 		.open = via_playback_multi_pcm_open,
1192ece8d043STakashi Iwai 		.close = via_playback_multi_pcm_close,
1193c873cc25SLydia Wang 		.prepare = via_playback_multi_pcm_prepare,
1194c873cc25SLydia Wang 		.cleanup = via_playback_multi_pcm_cleanup
1195bc9b5623STakashi Iwai 	},
1196bc9b5623STakashi Iwai };
1197bc9b5623STakashi Iwai 
11989af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = {
11997eb56e84STakashi Iwai 	.substreams = 1, /* will be changed in via_build_pcms() */
1200c577b8a1SJoseph Chan 	.channels_min = 2,
1201c577b8a1SJoseph Chan 	.channels_max = 2,
12029af74210STakashi Iwai 	/* NID is set in via_build_pcms */
1203c577b8a1SJoseph Chan 	.ops = {
1204c577b8a1SJoseph Chan 		.prepare = via_capture_pcm_prepare,
1205c577b8a1SJoseph Chan 		.cleanup = via_capture_pcm_cleanup
1206c577b8a1SJoseph Chan 	},
1207c577b8a1SJoseph Chan };
1208c577b8a1SJoseph Chan 
1209a86a88eaSTakashi Iwai static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1210a86a88eaSTakashi Iwai 	.substreams = 1,
1211a86a88eaSTakashi Iwai 	.channels_min = 2,
1212a86a88eaSTakashi Iwai 	.channels_max = 2,
1213a86a88eaSTakashi Iwai 	/* NID is set in via_build_pcms */
1214a86a88eaSTakashi Iwai 	.ops = {
1215a86a88eaSTakashi Iwai 		.prepare = via_dyn_adc_capture_pcm_prepare,
1216a86a88eaSTakashi Iwai 		.cleanup = via_dyn_adc_capture_pcm_cleanup,
1217a86a88eaSTakashi Iwai 	},
1218a86a88eaSTakashi Iwai };
1219a86a88eaSTakashi Iwai 
12209af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = {
1221c577b8a1SJoseph Chan 	.substreams = 1,
1222c577b8a1SJoseph Chan 	.channels_min = 2,
1223c577b8a1SJoseph Chan 	.channels_max = 2,
1224c577b8a1SJoseph Chan 	/* NID is set in via_build_pcms */
1225c577b8a1SJoseph Chan 	.ops = {
1226c577b8a1SJoseph Chan 		.open = via_dig_playback_pcm_open,
12276b97eb45STakashi Iwai 		.close = via_dig_playback_pcm_close,
12289da29271STakashi Iwai 		.prepare = via_dig_playback_pcm_prepare,
12299da29271STakashi Iwai 		.cleanup = via_dig_playback_pcm_cleanup
1230c577b8a1SJoseph Chan 	},
1231c577b8a1SJoseph Chan };
1232c577b8a1SJoseph Chan 
12339af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = {
1234c577b8a1SJoseph Chan 	.substreams = 1,
1235c577b8a1SJoseph Chan 	.channels_min = 2,
1236c577b8a1SJoseph Chan 	.channels_max = 2,
1237c577b8a1SJoseph Chan };
1238c577b8a1SJoseph Chan 
1239370bafbdSTakashi Iwai /*
1240370bafbdSTakashi Iwai  * slave controls for virtual master
1241370bafbdSTakashi Iwai  */
1242370bafbdSTakashi Iwai static const char * const via_slave_vols[] = {
1243370bafbdSTakashi Iwai 	"Front Playback Volume",
1244370bafbdSTakashi Iwai 	"Surround Playback Volume",
1245370bafbdSTakashi Iwai 	"Center Playback Volume",
1246370bafbdSTakashi Iwai 	"LFE Playback Volume",
1247370bafbdSTakashi Iwai 	"Side Playback Volume",
1248370bafbdSTakashi Iwai 	"Headphone Playback Volume",
1249370bafbdSTakashi Iwai 	"Speaker Playback Volume",
1250370bafbdSTakashi Iwai 	NULL,
1251370bafbdSTakashi Iwai };
1252370bafbdSTakashi Iwai 
1253370bafbdSTakashi Iwai static const char * const via_slave_sws[] = {
1254370bafbdSTakashi Iwai 	"Front Playback Switch",
1255370bafbdSTakashi Iwai 	"Surround Playback Switch",
1256370bafbdSTakashi Iwai 	"Center Playback Switch",
1257370bafbdSTakashi Iwai 	"LFE Playback Switch",
1258370bafbdSTakashi Iwai 	"Side Playback Switch",
1259370bafbdSTakashi Iwai 	"Headphone Playback Switch",
1260370bafbdSTakashi Iwai 	"Speaker Playback Switch",
1261370bafbdSTakashi Iwai 	NULL,
1262370bafbdSTakashi Iwai };
1263370bafbdSTakashi Iwai 
1264c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
1265c577b8a1SJoseph Chan {
1266c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
12675b0cb1d8SJaroslav Kysela 	struct snd_kcontrol *kctl;
12685b0cb1d8SJaroslav Kysela 	int err, i;
1269c577b8a1SJoseph Chan 
127024088a58STakashi Iwai 	if (spec->set_widgets_power_state)
127124088a58STakashi Iwai 		if (!via_clone_control(spec, &via_pin_power_ctl_enum))
127224088a58STakashi Iwai 			return -ENOMEM;
127324088a58STakashi Iwai 
1274c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
1275c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1276c577b8a1SJoseph Chan 		if (err < 0)
1277c577b8a1SJoseph Chan 			return err;
1278c577b8a1SJoseph Chan 	}
1279c577b8a1SJoseph Chan 
1280c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid) {
1281c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_out_ctls(codec,
128274b654c9SStephen Warren 						    spec->multiout.dig_out_nid,
1283c577b8a1SJoseph Chan 						    spec->multiout.dig_out_nid);
1284c577b8a1SJoseph Chan 		if (err < 0)
1285c577b8a1SJoseph Chan 			return err;
12869a08160bSTakashi Iwai 		err = snd_hda_create_spdif_share_sw(codec,
12879a08160bSTakashi Iwai 						    &spec->multiout);
12889a08160bSTakashi Iwai 		if (err < 0)
12899a08160bSTakashi Iwai 			return err;
12909a08160bSTakashi Iwai 		spec->multiout.share_spdif = 1;
1291c577b8a1SJoseph Chan 	}
1292c577b8a1SJoseph Chan 	if (spec->dig_in_nid) {
1293c577b8a1SJoseph Chan 		err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1294c577b8a1SJoseph Chan 		if (err < 0)
1295c577b8a1SJoseph Chan 			return err;
1296c577b8a1SJoseph Chan 	}
129717314379SLydia Wang 
1298370bafbdSTakashi Iwai 	/* if we have no master control, let's create it */
1299370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1300370bafbdSTakashi Iwai 		unsigned int vmaster_tlv[4];
1301370bafbdSTakashi Iwai 		snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1302370bafbdSTakashi Iwai 					HDA_OUTPUT, vmaster_tlv);
1303370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1304370bafbdSTakashi Iwai 					  vmaster_tlv, via_slave_vols);
1305370bafbdSTakashi Iwai 		if (err < 0)
1306370bafbdSTakashi Iwai 			return err;
1307370bafbdSTakashi Iwai 	}
1308370bafbdSTakashi Iwai 	if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1309370bafbdSTakashi Iwai 		err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1310370bafbdSTakashi Iwai 					  NULL, via_slave_sws);
1311370bafbdSTakashi Iwai 		if (err < 0)
1312370bafbdSTakashi Iwai 			return err;
1313370bafbdSTakashi Iwai 	}
1314370bafbdSTakashi Iwai 
13155b0cb1d8SJaroslav Kysela 	/* assign Capture Source enums to NID */
13165b0cb1d8SJaroslav Kysela 	kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
13175b0cb1d8SJaroslav Kysela 	for (i = 0; kctl && i < kctl->count; i++) {
131821949f00STakashi Iwai 		err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
13195b0cb1d8SJaroslav Kysela 		if (err < 0)
13205b0cb1d8SJaroslav Kysela 			return err;
13215b0cb1d8SJaroslav Kysela 	}
13225b0cb1d8SJaroslav Kysela 
132317314379SLydia Wang 	/* init power states */
13243e95b9abSLydia Wang 	set_widgets_power_state(codec);
1325ada509ecSTakashi Iwai 	analog_low_current_mode(codec);
132617314379SLydia Wang 
1327603c4019STakashi Iwai 	via_free_kctls(codec); /* no longer needed */
1328c577b8a1SJoseph Chan 	return 0;
1329c577b8a1SJoseph Chan }
1330c577b8a1SJoseph Chan 
1331c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec)
1332c577b8a1SJoseph Chan {
1333c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1334c577b8a1SJoseph Chan 	struct hda_pcm *info = spec->pcm_rec;
1335c577b8a1SJoseph Chan 
1336c577b8a1SJoseph Chan 	codec->num_pcms = 1;
1337c577b8a1SJoseph Chan 	codec->pcm_info = info;
1338c577b8a1SJoseph Chan 
133982673bc8STakashi Iwai 	snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
134082673bc8STakashi Iwai 		 "%s Analog", codec->chip_name);
1341c577b8a1SJoseph Chan 	info->name = spec->stream_name_analog;
13429af74210STakashi Iwai 
13439af74210STakashi Iwai 	if (!spec->stream_analog_playback)
13449af74210STakashi Iwai 		spec->stream_analog_playback = &via_pcm_analog_playback;
1345377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
13469af74210STakashi Iwai 		*spec->stream_analog_playback;
1347377ff31aSLydia Wang 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1348377ff31aSLydia Wang 		spec->multiout.dac_nids[0];
1349c577b8a1SJoseph Chan 	info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1350c577b8a1SJoseph Chan 		spec->multiout.max_channels;
13519af74210STakashi Iwai 
1352a86a88eaSTakashi Iwai 	if (!spec->stream_analog_capture) {
1353a86a88eaSTakashi Iwai 		if (spec->dyn_adc_switch)
1354a86a88eaSTakashi Iwai 			spec->stream_analog_capture =
1355a86a88eaSTakashi Iwai 				&via_pcm_dyn_adc_analog_capture;
1356a86a88eaSTakashi Iwai 		else
13579af74210STakashi Iwai 			spec->stream_analog_capture = &via_pcm_analog_capture;
1358a86a88eaSTakashi Iwai 	}
13599af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE] =
13609af74210STakashi Iwai 		*spec->stream_analog_capture;
13619af74210STakashi Iwai 	info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1362a86a88eaSTakashi Iwai 	if (!spec->dyn_adc_switch)
13639af74210STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
13649af74210STakashi Iwai 			spec->num_adc_nids;
1365c577b8a1SJoseph Chan 
1366c577b8a1SJoseph Chan 	if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1367c577b8a1SJoseph Chan 		codec->num_pcms++;
1368c577b8a1SJoseph Chan 		info++;
136982673bc8STakashi Iwai 		snprintf(spec->stream_name_digital,
137082673bc8STakashi Iwai 			 sizeof(spec->stream_name_digital),
137182673bc8STakashi Iwai 			 "%s Digital", codec->chip_name);
1372c577b8a1SJoseph Chan 		info->name = spec->stream_name_digital;
13737ba72ba1STakashi Iwai 		info->pcm_type = HDA_PCM_TYPE_SPDIF;
1374c577b8a1SJoseph Chan 		if (spec->multiout.dig_out_nid) {
13759af74210STakashi Iwai 			if (!spec->stream_digital_playback)
13769af74210STakashi Iwai 				spec->stream_digital_playback =
13779af74210STakashi Iwai 					&via_pcm_digital_playback;
1378c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
13799af74210STakashi Iwai 				*spec->stream_digital_playback;
1380c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1381c577b8a1SJoseph Chan 				spec->multiout.dig_out_nid;
1382c577b8a1SJoseph Chan 		}
1383c577b8a1SJoseph Chan 		if (spec->dig_in_nid) {
13849af74210STakashi Iwai 			if (!spec->stream_digital_capture)
13859af74210STakashi Iwai 				spec->stream_digital_capture =
13869af74210STakashi Iwai 					&via_pcm_digital_capture;
1387c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE] =
13889af74210STakashi Iwai 				*spec->stream_digital_capture;
1389c577b8a1SJoseph Chan 			info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1390c577b8a1SJoseph Chan 				spec->dig_in_nid;
1391c577b8a1SJoseph Chan 		}
1392c577b8a1SJoseph Chan 	}
1393c577b8a1SJoseph Chan 
1394ece8d043STakashi Iwai 	if (spec->hp_dac_nid) {
13957eb56e84STakashi Iwai 		codec->num_pcms++;
13967eb56e84STakashi Iwai 		info++;
13977eb56e84STakashi Iwai 		snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
13987eb56e84STakashi Iwai 			 "%s HP", codec->chip_name);
13997eb56e84STakashi Iwai 		info->name = spec->stream_name_hp;
14007eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
14017eb56e84STakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1402ece8d043STakashi Iwai 			spec->hp_dac_nid;
14037eb56e84STakashi Iwai 	}
1404c577b8a1SJoseph Chan 	return 0;
1405c577b8a1SJoseph Chan }
1406c577b8a1SJoseph Chan 
1407c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
1408c577b8a1SJoseph Chan {
1409c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
1410c577b8a1SJoseph Chan 
1411c577b8a1SJoseph Chan 	if (!spec)
1412c577b8a1SJoseph Chan 		return;
1413c577b8a1SJoseph Chan 
1414603c4019STakashi Iwai 	via_free_kctls(codec);
14151f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
1416a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_vol);
1417a86a88eaSTakashi Iwai 	kfree(spec->bind_cap_sw);
1418a86a88eaSTakashi Iwai 	kfree(spec);
1419c577b8a1SJoseph Chan }
1420c577b8a1SJoseph Chan 
142164be285bSTakashi Iwai /* mute/unmute outputs */
142264be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
142364be285bSTakashi Iwai 				hda_nid_t *pins, bool mute)
142464be285bSTakashi Iwai {
142564be285bSTakashi Iwai 	int i;
142664be285bSTakashi Iwai 	for (i = 0; i < num_pins; i++)
142764be285bSTakashi Iwai 		snd_hda_codec_write(codec, pins[i], 0,
142864be285bSTakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
142964be285bSTakashi Iwai 				    mute ? 0 : PIN_OUT);
143064be285bSTakashi Iwai }
143164be285bSTakashi Iwai 
14324a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */
14334a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present)
14344a918ffeSTakashi Iwai {
14354a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
14364a918ffeSTakashi Iwai 
14374a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs)
14384a918ffeSTakashi Iwai 		return;
14394a918ffeSTakashi Iwai 	if (!present)
14404a918ffeSTakashi Iwai 		present = snd_hda_jack_detect(codec,
14414a918ffeSTakashi Iwai 					      spec->autocfg.line_out_pins[0]);
14424a918ffeSTakashi Iwai 	toggle_output_mutes(codec, spec->autocfg.speaker_outs,
14434a918ffeSTakashi Iwai 			    spec->autocfg.speaker_pins,
14444a918ffeSTakashi Iwai 			    present);
14454a918ffeSTakashi Iwai }
14464a918ffeSTakashi Iwai 
144769e52a80SHarald Welte /* mute internal speaker if HP is plugged */
144869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec)
144969e52a80SHarald Welte {
14504a918ffeSTakashi Iwai 	int present = 0;
145169e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
145269e52a80SHarald Welte 
14534a918ffeSTakashi Iwai 	if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1454f2b1c9f0STakashi Iwai 		int nums;
1455d56757abSTakashi Iwai 		present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
1456f2b1c9f0STakashi Iwai 		if (spec->smart51_enabled)
1457f2b1c9f0STakashi Iwai 			nums = spec->autocfg.line_outs + spec->smart51_nums;
1458f2b1c9f0STakashi Iwai 		else
1459f2b1c9f0STakashi Iwai 			nums = spec->autocfg.line_outs;
1460f2b1c9f0STakashi Iwai 		toggle_output_mutes(codec, nums,
146164be285bSTakashi Iwai 				    spec->autocfg.line_out_pins,
146264be285bSTakashi Iwai 				    present);
146369e52a80SHarald Welte 	}
14644a918ffeSTakashi Iwai 	via_line_automute(codec, present);
1465f3db423dSLydia Wang }
1466f3db423dSLydia Wang 
146769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec)
146869e52a80SHarald Welte {
146969e52a80SHarald Welte 	unsigned int gpio_data;
147069e52a80SHarald Welte 	unsigned int vol_counter;
147169e52a80SHarald Welte 	unsigned int vol;
147269e52a80SHarald Welte 	unsigned int master_vol;
147369e52a80SHarald Welte 
147469e52a80SHarald Welte 	struct via_spec *spec = codec->spec;
147569e52a80SHarald Welte 
147669e52a80SHarald Welte 	gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
147769e52a80SHarald Welte 				       AC_VERB_GET_GPIO_DATA, 0) & 0x03;
147869e52a80SHarald Welte 
147969e52a80SHarald Welte 	vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
148069e52a80SHarald Welte 					  0xF84, 0) & 0x3F0000) >> 16;
148169e52a80SHarald Welte 
148269e52a80SHarald Welte 	vol = vol_counter & 0x1F;
148369e52a80SHarald Welte 	master_vol = snd_hda_codec_read(codec, 0x1A, 0,
148469e52a80SHarald Welte 					AC_VERB_GET_AMP_GAIN_MUTE,
148569e52a80SHarald Welte 					AC_AMP_GET_INPUT);
148669e52a80SHarald Welte 
148769e52a80SHarald Welte 	if (gpio_data == 0x02) {
148869e52a80SHarald Welte 		/* unmute line out */
14893e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
14903e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
14913e0693e2STakashi Iwai 				    PIN_OUT);
149269e52a80SHarald Welte 		if (vol_counter & 0x20) {
149369e52a80SHarald Welte 			/* decrease volume */
149469e52a80SHarald Welte 			if (vol > master_vol)
149569e52a80SHarald Welte 				vol = master_vol;
149669e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
149769e52a80SHarald Welte 						 0, HDA_AMP_VOLMASK,
149869e52a80SHarald Welte 						 master_vol-vol);
149969e52a80SHarald Welte 		} else {
150069e52a80SHarald Welte 			/* increase volume */
150169e52a80SHarald Welte 			snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
150269e52a80SHarald Welte 					 HDA_AMP_VOLMASK,
150369e52a80SHarald Welte 					 ((master_vol+vol) > 0x2A) ? 0x2A :
150469e52a80SHarald Welte 					  (master_vol+vol));
150569e52a80SHarald Welte 		}
150669e52a80SHarald Welte 	} else if (!(gpio_data & 0x02)) {
150769e52a80SHarald Welte 		/* mute line out */
15083e0693e2STakashi Iwai 		snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
15093e0693e2STakashi Iwai 				    AC_VERB_SET_PIN_WIDGET_CONTROL,
15103e0693e2STakashi Iwai 				    0);
151169e52a80SHarald Welte 	}
151269e52a80SHarald Welte }
151369e52a80SHarald Welte 
151469e52a80SHarald Welte /* unsolicited event for jack sensing */
151569e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec,
151669e52a80SHarald Welte 				  unsigned int res)
151769e52a80SHarald Welte {
151869e52a80SHarald Welte 	res >>= 26;
1519ec7e7e42SLydia Wang 
1520a34df19aSLydia Wang 	if (res & VIA_JACK_EVENT)
15213e95b9abSLydia Wang 		set_widgets_power_state(codec);
1522ec7e7e42SLydia Wang 
1523ec7e7e42SLydia Wang 	res &= ~VIA_JACK_EVENT;
1524ec7e7e42SLydia Wang 
1525ec7e7e42SLydia Wang 	if (res == VIA_HP_EVENT)
1526ec7e7e42SLydia Wang 		via_hp_automute(codec);
1527ec7e7e42SLydia Wang 	else if (res == VIA_GPIO_EVENT)
1528ec7e7e42SLydia Wang 		via_gpio_control(codec);
15294a918ffeSTakashi Iwai 	else if (res == VIA_LINE_EVENT)
15304a918ffeSTakashi Iwai 		via_line_automute(codec, false);
153169e52a80SHarald Welte }
153269e52a80SHarald Welte 
15331f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
15341f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state)
15351f2e99feSLydia Wang {
15361f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
15371f2e99feSLydia Wang 	vt1708_stop_hp_work(spec);
15381f2e99feSLydia Wang 	return 0;
15391f2e99feSLydia Wang }
15401f2e99feSLydia Wang #endif
15411f2e99feSLydia Wang 
1542cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1543cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1544cb53c626STakashi Iwai {
1545cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
1546cb53c626STakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1547cb53c626STakashi Iwai }
1548cb53c626STakashi Iwai #endif
1549cb53c626STakashi Iwai 
1550c577b8a1SJoseph Chan /*
1551c577b8a1SJoseph Chan  */
15525d41762aSTakashi Iwai 
15535d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
15545d41762aSTakashi Iwai 
155590dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
1556c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
1557c577b8a1SJoseph Chan 	.build_pcms = via_build_pcms,
1558c577b8a1SJoseph Chan 	.init = via_init,
1559c577b8a1SJoseph Chan 	.free = via_free,
15604a918ffeSTakashi Iwai 	.unsol_event = via_unsol_event,
15611f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME
15621f2e99feSLydia Wang 	.suspend = via_suspend,
15631f2e99feSLydia Wang #endif
1564cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE
1565cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
1566cb53c626STakashi Iwai #endif
1567c577b8a1SJoseph Chan };
1568c577b8a1SJoseph Chan 
15694a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
1570c577b8a1SJoseph Chan {
15714a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
15724a79616dSTakashi Iwai 	int i;
15734a79616dSTakashi Iwai 
15744a79616dSTakashi Iwai 	for (i = 0; i < spec->multiout.num_dacs; i++) {
15754a79616dSTakashi Iwai 		if (spec->multiout.dac_nids[i] == dac)
15764a79616dSTakashi Iwai 			return false;
15774a79616dSTakashi Iwai 	}
1578ece8d043STakashi Iwai 	if (spec->hp_dac_nid == dac)
15794a79616dSTakashi Iwai 		return false;
15804a79616dSTakashi Iwai 	return true;
15814a79616dSTakashi Iwai }
15824a79616dSTakashi Iwai 
15838e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
15844a79616dSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path,
15854a79616dSTakashi Iwai 			      int depth, int wid_type)
15864a79616dSTakashi Iwai {
15874a79616dSTakashi Iwai 	hda_nid_t conn[8];
15884a79616dSTakashi Iwai 	int i, nums;
15894a79616dSTakashi Iwai 
15904a79616dSTakashi Iwai 	nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
15914a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
15924a79616dSTakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
15934a79616dSTakashi Iwai 			continue;
159409a9ad69STakashi Iwai 		if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
159509a9ad69STakashi Iwai 			goto found;
15964a79616dSTakashi Iwai 	}
15978e3679dcSTakashi Iwai 	if (depth >= MAX_NID_PATH_DEPTH)
15984a79616dSTakashi Iwai 		return false;
15994a79616dSTakashi Iwai 	for (i = 0; i < nums; i++) {
16004a79616dSTakashi Iwai 		unsigned int type;
16014a79616dSTakashi Iwai 		type = get_wcaps_type(get_wcaps(codec, conn[i]));
16024a79616dSTakashi Iwai 		if (type == AC_WID_AUD_OUT ||
16034a79616dSTakashi Iwai 		    (wid_type != -1 && type != wid_type))
16044a79616dSTakashi Iwai 			continue;
16058e3679dcSTakashi Iwai 		if (__parse_output_path(codec, conn[i], target_dac,
160609a9ad69STakashi Iwai 				      path, depth + 1, AC_WID_AUD_SEL))
160709a9ad69STakashi Iwai 			goto found;
16084a79616dSTakashi Iwai 	}
16094a79616dSTakashi Iwai 	return false;
161009a9ad69STakashi Iwai 
161109a9ad69STakashi Iwai  found:
161209a9ad69STakashi Iwai 	path->path[path->depth] = conn[i];
161309a9ad69STakashi Iwai 	path->idx[path->depth] = i;
161409a9ad69STakashi Iwai 	if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
161509a9ad69STakashi Iwai 		path->multi[path->depth] = 1;
161609a9ad69STakashi Iwai 	path->depth++;
161709a9ad69STakashi Iwai 	return true;
16184a79616dSTakashi Iwai }
16194a79616dSTakashi Iwai 
16208e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
16218e3679dcSTakashi Iwai 			      hda_nid_t target_dac, struct nid_path *path)
16228e3679dcSTakashi Iwai {
16238e3679dcSTakashi Iwai 	if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
16248e3679dcSTakashi Iwai 		path->path[path->depth] = nid;
16258e3679dcSTakashi Iwai 		path->depth++;
16268e3679dcSTakashi Iwai 		return true;
16278e3679dcSTakashi Iwai 	}
16288e3679dcSTakashi Iwai 	return false;
16298e3679dcSTakashi Iwai }
16308e3679dcSTakashi Iwai 
16314a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec)
16324a79616dSTakashi Iwai {
16334a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
16344a79616dSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
1635c577b8a1SJoseph Chan 	int i;
1636c577b8a1SJoseph Chan 	hda_nid_t nid;
1637c577b8a1SJoseph Chan 
1638c577b8a1SJoseph Chan 	spec->multiout.dac_nids = spec->private_dac_nids;
16394a79616dSTakashi Iwai 	spec->multiout.num_dacs = cfg->line_outs;
16404a79616dSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
1641c577b8a1SJoseph Chan 		nid = cfg->line_out_pins[i];
16424a79616dSTakashi Iwai 		if (!nid)
16434a79616dSTakashi Iwai 			continue;
16448e3679dcSTakashi Iwai 		if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
16458e3679dcSTakashi Iwai 			spec->private_dac_nids[i] = spec->out_path[i].path[0];
1646c577b8a1SJoseph Chan 	}
1647c577b8a1SJoseph Chan 	return 0;
1648c577b8a1SJoseph Chan }
1649c577b8a1SJoseph Chan 
16504a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
165109a9ad69STakashi Iwai 			  int chs, bool check_dac, struct nid_path *path)
1652c577b8a1SJoseph Chan {
16534a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
1654c577b8a1SJoseph Chan 	char name[32];
165509a9ad69STakashi Iwai 	hda_nid_t dac, pin, sel, nid;
165609a9ad69STakashi Iwai 	int err;
1657a934d5a9STakashi Iwai 
165809a9ad69STakashi Iwai 	dac = check_dac ? path->path[0] : 0;
165909a9ad69STakashi Iwai 	pin = path->path[path->depth - 1];
166009a9ad69STakashi Iwai 	sel = path->depth > 1 ? path->path[1] : 0;
1661c577b8a1SJoseph Chan 
16628df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
16634a79616dSTakashi Iwai 		nid = dac;
16648df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
16654a79616dSTakashi Iwai 		nid = pin;
1666a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1667a934d5a9STakashi Iwai 		nid = sel;
16684a79616dSTakashi Iwai 	else
16694a79616dSTakashi Iwai 		nid = 0;
16704a79616dSTakashi Iwai 	if (nid) {
16714a79616dSTakashi Iwai 		sprintf(name, "%s Playback Volume", pfx);
1672c577b8a1SJoseph Chan 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1673a00a5fadSLydia Wang 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1674c577b8a1SJoseph Chan 		if (err < 0)
1675c577b8a1SJoseph Chan 			return err;
167609a9ad69STakashi Iwai 		path->vol_ctl = nid;
1677c577b8a1SJoseph Chan 	}
16784a79616dSTakashi Iwai 
16798df2a312STakashi Iwai 	if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
16804a79616dSTakashi Iwai 		nid = dac;
16818df2a312STakashi Iwai 	else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
16824a79616dSTakashi Iwai 		nid = pin;
1683a934d5a9STakashi Iwai 	else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1684a934d5a9STakashi Iwai 		nid = sel;
16854a79616dSTakashi Iwai 	else
16864a79616dSTakashi Iwai 		nid = 0;
16874a79616dSTakashi Iwai 	if (nid) {
16884a79616dSTakashi Iwai 		sprintf(name, "%s Playback Switch", pfx);
16894a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
16904a79616dSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
16914a79616dSTakashi Iwai 		if (err < 0)
16924a79616dSTakashi Iwai 			return err;
169309a9ad69STakashi Iwai 		path->mute_ctl = nid;
16944a79616dSTakashi Iwai 	}
16954a79616dSTakashi Iwai 	return 0;
16964a79616dSTakashi Iwai }
16974a79616dSTakashi Iwai 
1698f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec)
1699f4a7828bSTakashi Iwai {
1700f4a7828bSTakashi Iwai 	struct via_spec *spec = codec->spec;
1701f4a7828bSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
17020f98c24bSTakashi Iwai 	struct auto_pin_cfg_item *ins = cfg->inputs;
17030f98c24bSTakashi Iwai 	int i, j, nums, attr;
17040f98c24bSTakashi Iwai 	int pins[AUTO_CFG_MAX_INS];
1705f4a7828bSTakashi Iwai 
17060f98c24bSTakashi Iwai 	for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
17070f98c24bSTakashi Iwai 		nums = 0;
1708f4a7828bSTakashi Iwai 		for (i = 0; i < cfg->num_inputs; i++) {
17090f98c24bSTakashi Iwai 			unsigned int def;
17100f98c24bSTakashi Iwai 			if (ins[i].type > AUTO_PIN_LINE_IN)
17110f98c24bSTakashi Iwai 				continue;
17120f98c24bSTakashi Iwai 			def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
17130f98c24bSTakashi Iwai 			if (snd_hda_get_input_pin_attr(def) != attr)
17140f98c24bSTakashi Iwai 				continue;
17150f98c24bSTakashi Iwai 			for (j = 0; j < nums; j++)
17160f98c24bSTakashi Iwai 				if (ins[pins[j]].type < ins[i].type) {
17170f98c24bSTakashi Iwai 					memmove(pins + j + 1, pins + j,
17180f98c24bSTakashi Iwai 						(nums - j - 1) * sizeof(int));
17190f98c24bSTakashi Iwai 					break;
17200f98c24bSTakashi Iwai 				}
17210f98c24bSTakashi Iwai 			pins[j] = i;
1722e3d7a143STakashi Iwai 			nums++;
1723e3d7a143STakashi Iwai 		}
1724e3d7a143STakashi Iwai 		if (cfg->line_outs + nums < 3)
1725f4a7828bSTakashi Iwai 			continue;
17260f98c24bSTakashi Iwai 		for (i = 0; i < nums; i++) {
17270f98c24bSTakashi Iwai 			hda_nid_t pin = ins[pins[i]].pin;
17280f98c24bSTakashi Iwai 			spec->smart51_pins[spec->smart51_nums++] = pin;
17290f98c24bSTakashi Iwai 			cfg->line_out_pins[cfg->line_outs++] = pin;
1730f4a7828bSTakashi Iwai 			if (cfg->line_outs == 3)
1731f4a7828bSTakashi Iwai 				break;
1732f4a7828bSTakashi Iwai 		}
17330f98c24bSTakashi Iwai 		return;
17340f98c24bSTakashi 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 
1753e3d7a143STakashi Iwai 	err = via_auto_fill_dac_nids(codec);
1754e3d7a143STakashi Iwai 	if (err < 0)
1755e3d7a143STakashi Iwai 		return err;
1756e3d7a143STakashi 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) {
176409a9ad69STakashi Iwai 			err = create_ch_ctls(codec, "Center", 1, true,
176509a9ad69STakashi Iwai 					     &spec->out_path[i]);
17664a79616dSTakashi Iwai 			if (err < 0)
17674a79616dSTakashi Iwai 				return err;
176809a9ad69STakashi Iwai 			err = create_ch_ctls(codec, "LFE", 2, true,
176909a9ad69STakashi Iwai 					     &spec->out_path[i]);
17704a79616dSTakashi Iwai 			if (err < 0)
17714a79616dSTakashi Iwai 				return err;
17724a79616dSTakashi Iwai 		} else {
17736aadf41dSTakashi Iwai 			const char *pfx = chname[i];
17746aadf41dSTakashi Iwai 			if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
17756aadf41dSTakashi Iwai 			    cfg->line_outs == 1)
17766aadf41dSTakashi Iwai 				pfx = "Speaker";
177709a9ad69STakashi Iwai 			err = create_ch_ctls(codec, pfx, 3, true,
177809a9ad69STakashi Iwai 					     &spec->out_path[i]);
17794a79616dSTakashi Iwai 			if (err < 0)
17804a79616dSTakashi Iwai 				return err;
17814a79616dSTakashi Iwai 		}
17824a79616dSTakashi Iwai 	}
17834a79616dSTakashi Iwai 
17844a79616dSTakashi Iwai 	idx = get_connection_index(codec, spec->aa_mix_nid,
17854a79616dSTakashi Iwai 				   spec->multiout.dac_nids[0]);
17864a79616dSTakashi Iwai 	if (idx >= 0) {
17874a79616dSTakashi Iwai 		/* add control to mixer */
17884a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
17894a79616dSTakashi Iwai 				      "PCM Playback Volume",
17904a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17914a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17924a79616dSTakashi Iwai 		if (err < 0)
17934a79616dSTakashi Iwai 			return err;
17944a79616dSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
17954a79616dSTakashi Iwai 				      "PCM Playback Switch",
17964a79616dSTakashi Iwai 				      HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
17974a79616dSTakashi Iwai 							  idx, HDA_INPUT));
17984a79616dSTakashi Iwai 		if (err < 0)
17994a79616dSTakashi Iwai 			return err;
1800c577b8a1SJoseph Chan 	}
1801c577b8a1SJoseph Chan 
1802f4a7828bSTakashi Iwai 	cfg->line_outs = old_line_outs;
1803f4a7828bSTakashi Iwai 
1804c577b8a1SJoseph Chan 	return 0;
1805c577b8a1SJoseph Chan }
1806c577b8a1SJoseph Chan 
18074a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
1808c577b8a1SJoseph Chan {
18094a79616dSTakashi Iwai 	struct via_spec *spec = codec->spec;
181009a9ad69STakashi Iwai 	struct nid_path *path;
1811c577b8a1SJoseph Chan 	int err;
1812c577b8a1SJoseph Chan 
1813c577b8a1SJoseph Chan 	if (!pin)
1814c577b8a1SJoseph Chan 		return 0;
1815c577b8a1SJoseph Chan 
18168df2a312STakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->hp_path))
18178e3679dcSTakashi Iwai 		spec->hp_dac_nid = spec->hp_path.path[0];
18184a79616dSTakashi Iwai 
1819ece8d043STakashi Iwai 	if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
18208e3679dcSTakashi Iwai 			       &spec->hp_dep_path) &&
1821ece8d043STakashi Iwai 	    !spec->hp_dac_nid)
1822ece8d043STakashi Iwai 		return 0;
1823ece8d043STakashi Iwai 
182409a9ad69STakashi Iwai 	if (spec->hp_dac_nid)
182509a9ad69STakashi Iwai 		path = &spec->hp_path;
182609a9ad69STakashi Iwai 	else
182709a9ad69STakashi Iwai 		path = &spec->hp_dep_path;
182809a9ad69STakashi Iwai 	err = create_ch_ctls(codec, "Headphone", 3, false, path);
18294a79616dSTakashi Iwai 	if (err < 0)
18304a79616dSTakashi Iwai 		return err;
183109a9ad69STakashi Iwai 	if (spec->hp_dac_nid) {
183209a9ad69STakashi Iwai 		spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
183309a9ad69STakashi Iwai 		spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
183409a9ad69STakashi Iwai 	}
18350aa62aefSHarald Welte 
1836c577b8a1SJoseph Chan 	return 0;
1837c577b8a1SJoseph Chan }
1838c577b8a1SJoseph Chan 
18394a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec)
18404a918ffeSTakashi Iwai {
18414a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
18424a918ffeSTakashi Iwai 	hda_nid_t pin, dac;
18434a918ffeSTakashi Iwai 
18444a918ffeSTakashi Iwai 	pin = spec->autocfg.speaker_pins[0];
18454a918ffeSTakashi Iwai 	if (!spec->autocfg.speaker_outs || !pin)
18464a918ffeSTakashi Iwai 		return 0;
18474a918ffeSTakashi Iwai 
18488e3679dcSTakashi Iwai 	if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
18498e3679dcSTakashi Iwai 		dac = spec->speaker_path.path[0];
18504a918ffeSTakashi Iwai 		spec->multiout.extra_out_nid[0] = dac;
185109a9ad69STakashi Iwai 		return create_ch_ctls(codec, "Speaker", 3, true,
185209a9ad69STakashi Iwai 				      &spec->speaker_path);
18534a918ffeSTakashi Iwai 	}
18544a918ffeSTakashi Iwai 	if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
18558e3679dcSTakashi Iwai 			      &spec->speaker_path))
185609a9ad69STakashi Iwai 		return create_ch_ctls(codec, "Speaker", 3, false,
185709a9ad69STakashi Iwai 				      &spec->speaker_path);
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 
1884a86a88eaSTakashi Iwai /* input-src control */
1885a86a88eaSTakashi Iwai static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1886a86a88eaSTakashi Iwai 			     struct snd_ctl_elem_info *uinfo)
1887a86a88eaSTakashi Iwai {
1888a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1889a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1890a86a88eaSTakashi Iwai 
1891a86a88eaSTakashi Iwai 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1892a86a88eaSTakashi Iwai 	uinfo->count = 1;
1893a86a88eaSTakashi Iwai 	uinfo->value.enumerated.items = spec->num_inputs;
1894a86a88eaSTakashi Iwai 	if (uinfo->value.enumerated.item >= spec->num_inputs)
1895a86a88eaSTakashi Iwai 		uinfo->value.enumerated.item = spec->num_inputs - 1;
1896a86a88eaSTakashi Iwai 	strcpy(uinfo->value.enumerated.name,
1897a86a88eaSTakashi Iwai 	       spec->inputs[uinfo->value.enumerated.item].label);
1898a86a88eaSTakashi Iwai 	return 0;
1899a86a88eaSTakashi Iwai }
1900a86a88eaSTakashi Iwai 
1901a86a88eaSTakashi Iwai static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1902a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
1903a86a88eaSTakashi Iwai {
1904a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1905a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1906a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1907a86a88eaSTakashi Iwai 
1908a86a88eaSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1909a86a88eaSTakashi Iwai 	return 0;
1910a86a88eaSTakashi Iwai }
1911a86a88eaSTakashi Iwai 
1912a86a88eaSTakashi Iwai static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1913a86a88eaSTakashi Iwai 			    struct snd_ctl_elem_value *ucontrol)
1914a86a88eaSTakashi Iwai {
1915a86a88eaSTakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1916a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1917a86a88eaSTakashi Iwai 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1918a86a88eaSTakashi Iwai 	hda_nid_t mux;
1919a86a88eaSTakashi Iwai 	int cur;
1920a86a88eaSTakashi Iwai 
1921a86a88eaSTakashi Iwai 	cur = ucontrol->value.enumerated.item[0];
1922a86a88eaSTakashi Iwai 	if (cur < 0 || cur >= spec->num_inputs)
1923a86a88eaSTakashi Iwai 		return -EINVAL;
1924a86a88eaSTakashi Iwai 	if (spec->cur_mux[idx] == cur)
1925a86a88eaSTakashi Iwai 		return 0;
1926a86a88eaSTakashi Iwai 	spec->cur_mux[idx] = cur;
1927a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch) {
1928a86a88eaSTakashi Iwai 		int adc_idx = spec->inputs[cur].adc_idx;
1929a86a88eaSTakashi Iwai 		mux = spec->mux_nids[adc_idx];
1930a86a88eaSTakashi Iwai 		via_dyn_adc_pcm_resetup(codec, cur);
1931a86a88eaSTakashi Iwai 	} else {
1932a86a88eaSTakashi Iwai 		mux = spec->mux_nids[idx];
1933a86a88eaSTakashi Iwai 		if (snd_BUG_ON(!mux))
1934a86a88eaSTakashi Iwai 			return -EINVAL;
1935a86a88eaSTakashi Iwai 	}
1936a86a88eaSTakashi Iwai 
1937a86a88eaSTakashi Iwai 	if (mux) {
1938a86a88eaSTakashi Iwai 		/* switch to D0 beofre change index */
1939a86a88eaSTakashi Iwai 		if (snd_hda_codec_read(codec, mux, 0,
1940a86a88eaSTakashi Iwai 			       AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1941a86a88eaSTakashi Iwai 			snd_hda_codec_write(codec, mux, 0,
1942a86a88eaSTakashi Iwai 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1943a86a88eaSTakashi Iwai 		snd_hda_codec_write(codec, mux, 0,
1944a86a88eaSTakashi Iwai 				    AC_VERB_SET_CONNECT_SEL,
1945a86a88eaSTakashi Iwai 				    spec->inputs[cur].mux_idx);
1946a86a88eaSTakashi Iwai 	}
1947a86a88eaSTakashi Iwai 
1948a86a88eaSTakashi Iwai 	/* update jack power state */
1949a86a88eaSTakashi Iwai 	set_widgets_power_state(codec);
1950a86a88eaSTakashi Iwai 	return 0;
1951a86a88eaSTakashi Iwai }
1952a766d0d7STakashi Iwai 
1953d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = {
1954d7a99cceSTakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1955d7a99cceSTakashi Iwai 	/* The multiple "Capture Source" controls confuse alsamixer
1956d7a99cceSTakashi Iwai 	 * So call somewhat different..
1957d7a99cceSTakashi Iwai 	 */
1958d7a99cceSTakashi Iwai 	/* .name = "Capture Source", */
1959d7a99cceSTakashi Iwai 	.name = "Input Source",
1960d7a99cceSTakashi Iwai 	.info = via_mux_enum_info,
1961d7a99cceSTakashi Iwai 	.get = via_mux_enum_get,
1962d7a99cceSTakashi Iwai 	.put = via_mux_enum_put,
1963d7a99cceSTakashi Iwai };
1964d7a99cceSTakashi Iwai 
1965a86a88eaSTakashi Iwai static int create_input_src_ctls(struct hda_codec *codec, int count)
1966a86a88eaSTakashi Iwai {
1967a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
1968a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
1969a86a88eaSTakashi Iwai 
1970a86a88eaSTakashi Iwai 	if (spec->num_inputs <= 1 || !count)
1971a86a88eaSTakashi Iwai 		return 0; /* no need for single src */
1972a86a88eaSTakashi Iwai 
1973a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_input_src_ctl);
1974a86a88eaSTakashi Iwai 	if (!knew)
1975a86a88eaSTakashi Iwai 		return -ENOMEM;
1976a86a88eaSTakashi Iwai 	knew->count = count;
1977a86a88eaSTakashi Iwai 	return 0;
1978a86a88eaSTakashi Iwai }
1979a86a88eaSTakashi Iwai 
1980a86a88eaSTakashi Iwai /* add the powersave loopback-list entry */
198113af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
198213af8e77STakashi Iwai {
198313af8e77STakashi Iwai 	struct hda_amp_list *list;
198413af8e77STakashi Iwai 
198513af8e77STakashi Iwai 	if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
198613af8e77STakashi Iwai 		return;
198713af8e77STakashi Iwai 	list = spec->loopback_list + spec->num_loopbacks;
198813af8e77STakashi Iwai 	list->nid = mix;
198913af8e77STakashi Iwai 	list->dir = HDA_INPUT;
199013af8e77STakashi Iwai 	list->idx = idx;
199113af8e77STakashi Iwai 	spec->num_loopbacks++;
199213af8e77STakashi Iwai 	spec->loopback.amplist = spec->loopback_list;
199313af8e77STakashi Iwai }
199413af8e77STakashi Iwai 
1995a86a88eaSTakashi Iwai static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
1996*8d087c76STakashi Iwai 			     hda_nid_t dst)
1997a86a88eaSTakashi Iwai {
1998*8d087c76STakashi Iwai 	return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
1999a86a88eaSTakashi Iwai }
2000a86a88eaSTakashi Iwai 
2001a86a88eaSTakashi Iwai /* add the input-route to the given pin */
2002a86a88eaSTakashi Iwai static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
2003c577b8a1SJoseph Chan {
200410a20af7STakashi Iwai 	struct via_spec *spec = codec->spec;
2005a86a88eaSTakashi Iwai 	int c, idx;
2006a86a88eaSTakashi Iwai 
2007a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].adc_idx = -1;
2008a86a88eaSTakashi Iwai 	spec->inputs[spec->num_inputs].pin = pin;
2009a86a88eaSTakashi Iwai 	for (c = 0; c < spec->num_adc_nids; c++) {
2010a86a88eaSTakashi Iwai 		if (spec->mux_nids[c]) {
2011a86a88eaSTakashi Iwai 			idx = get_connection_index(codec, spec->mux_nids[c],
2012a86a88eaSTakashi Iwai 						   pin);
2013a86a88eaSTakashi Iwai 			if (idx < 0)
2014a86a88eaSTakashi Iwai 				continue;
2015a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs].mux_idx = idx;
2016a86a88eaSTakashi Iwai 		} else {
2017*8d087c76STakashi Iwai 			if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
2018a86a88eaSTakashi Iwai 				continue;
2019a86a88eaSTakashi Iwai 		}
2020a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs].adc_idx = c;
2021a86a88eaSTakashi Iwai 		/* Can primary ADC satisfy all inputs? */
2022a86a88eaSTakashi Iwai 		if (!spec->dyn_adc_switch &&
2023a86a88eaSTakashi Iwai 		    spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2024a86a88eaSTakashi Iwai 			snd_printd(KERN_INFO
2025a86a88eaSTakashi Iwai 				   "via: dynamic ADC switching enabled\n");
2026a86a88eaSTakashi Iwai 			spec->dyn_adc_switch = 1;
2027a86a88eaSTakashi Iwai 		}
2028a86a88eaSTakashi Iwai 		return true;
2029a86a88eaSTakashi Iwai 	}
2030a86a88eaSTakashi Iwai 	return false;
2031a86a88eaSTakashi Iwai }
2032a86a88eaSTakashi Iwai 
2033a86a88eaSTakashi Iwai static int get_mux_nids(struct hda_codec *codec);
2034a86a88eaSTakashi Iwai 
2035a86a88eaSTakashi Iwai /* parse input-routes; fill ADCs, MUXs and input-src entries */
2036a86a88eaSTakashi Iwai static int parse_analog_inputs(struct hda_codec *codec)
2037a86a88eaSTakashi Iwai {
2038a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2039a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2040a86a88eaSTakashi Iwai 	int i, err;
2041a766d0d7STakashi Iwai 
2042a766d0d7STakashi Iwai 	err = via_fill_adcs(codec);
2043a766d0d7STakashi Iwai 	if (err < 0)
2044a766d0d7STakashi Iwai 		return err;
2045a766d0d7STakashi Iwai 	err = get_mux_nids(codec);
2046a766d0d7STakashi Iwai 	if (err < 0)
2047a766d0d7STakashi Iwai 		return err;
2048a766d0d7STakashi Iwai 
2049a86a88eaSTakashi Iwai 	/* fill all input-routes */
2050a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2051a86a88eaSTakashi Iwai 		if (add_input_route(codec, cfg->inputs[i].pin))
2052a86a88eaSTakashi Iwai 			spec->inputs[spec->num_inputs++].label =
2053a86a88eaSTakashi Iwai 				hda_get_autocfg_input_label(codec, cfg, i);
2054a86a88eaSTakashi Iwai 	}
2055a86a88eaSTakashi Iwai 
2056a86a88eaSTakashi Iwai 	/* check for internal loopback recording */
2057a86a88eaSTakashi Iwai 	if (spec->aa_mix_nid &&
2058a86a88eaSTakashi Iwai 	    add_input_route(codec, spec->aa_mix_nid))
2059a86a88eaSTakashi Iwai 		spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2060a86a88eaSTakashi Iwai 
2061a86a88eaSTakashi Iwai 	return 0;
2062a86a88eaSTakashi Iwai }
2063a86a88eaSTakashi Iwai 
2064a86a88eaSTakashi Iwai /* create analog-loopback volume/switch controls */
2065a86a88eaSTakashi Iwai static int create_loopback_ctls(struct hda_codec *codec)
2066a86a88eaSTakashi Iwai {
2067a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2068a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2069a86a88eaSTakashi Iwai 	const char *prev_label = NULL;
2070a86a88eaSTakashi Iwai 	int type_idx = 0;
2071a86a88eaSTakashi Iwai 	int i, j, err, idx;
2072a86a88eaSTakashi Iwai 
2073a86a88eaSTakashi Iwai 	if (!spec->aa_mix_nid)
2074a766d0d7STakashi Iwai 		return 0;
2075c577b8a1SJoseph Chan 
20767b315bb4STakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2077a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2078a86a88eaSTakashi Iwai 		const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2079a86a88eaSTakashi Iwai 
20801e11cae1STakashi Iwai 		if (prev_label && !strcmp(label, prev_label))
20817b315bb4STakashi Iwai 			type_idx++;
20827b315bb4STakashi Iwai 		else
20837b315bb4STakashi Iwai 			type_idx = 0;
20841e11cae1STakashi Iwai 		prev_label = label;
2085a86a88eaSTakashi Iwai 		idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2086a86a88eaSTakashi Iwai 		if (idx >= 0) {
208716922281SLydia Wang 			err = via_new_analog_input(spec, label, type_idx,
2088a86a88eaSTakashi Iwai 						   idx, spec->aa_mix_nid);
2089c577b8a1SJoseph Chan 			if (err < 0)
2090c577b8a1SJoseph Chan 				return err;
2091a86a88eaSTakashi Iwai 			add_loopback_list(spec, spec->aa_mix_nid, idx);
209213af8e77STakashi Iwai 		}
2093e3d7a143STakashi Iwai 
2094e3d7a143STakashi Iwai 		/* remember the label for smart51 control */
2095e3d7a143STakashi Iwai 		for (j = 0; j < spec->smart51_nums; j++) {
2096a86a88eaSTakashi Iwai 			if (spec->smart51_pins[j] == pin) {
2097e3d7a143STakashi Iwai 				spec->smart51_idxs[j] = idx;
2098e3d7a143STakashi Iwai 				spec->smart51_labels[j] = label;
2099e3d7a143STakashi Iwai 				break;
2100e3d7a143STakashi Iwai 			}
2101e3d7a143STakashi Iwai 		}
2102c577b8a1SJoseph Chan 	}
2103a86a88eaSTakashi Iwai 	return 0;
2104a86a88eaSTakashi Iwai }
2105a86a88eaSTakashi Iwai 
2106a86a88eaSTakashi Iwai /* create mic-boost controls (if present) */
2107a86a88eaSTakashi Iwai static int create_mic_boost_ctls(struct hda_codec *codec)
2108a86a88eaSTakashi Iwai {
2109a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2110a86a88eaSTakashi Iwai 	const struct auto_pin_cfg *cfg = &spec->autocfg;
2111a86a88eaSTakashi Iwai 	int i, err;
2112a86a88eaSTakashi Iwai 
2113a86a88eaSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
2114a86a88eaSTakashi Iwai 		hda_nid_t pin = cfg->inputs[i].pin;
2115a86a88eaSTakashi Iwai 		unsigned int caps;
2116a86a88eaSTakashi Iwai 		const char *label;
2117a86a88eaSTakashi Iwai 		char name[32];
2118a86a88eaSTakashi Iwai 
2119a86a88eaSTakashi Iwai 		if (cfg->inputs[i].type != AUTO_PIN_MIC)
2120a86a88eaSTakashi Iwai 			continue;
2121a86a88eaSTakashi Iwai 		caps = query_amp_caps(codec, pin, HDA_INPUT);
2122a86a88eaSTakashi Iwai 		if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2123a86a88eaSTakashi Iwai 			continue;
2124a86a88eaSTakashi Iwai 		label = hda_get_autocfg_input_label(codec, cfg, i);
2125a86a88eaSTakashi Iwai 		snprintf(name, sizeof(name), "%s Boost Volume", label);
2126a86a88eaSTakashi Iwai 		err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2127a86a88eaSTakashi Iwai 			      HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2128a86a88eaSTakashi Iwai 		if (err < 0)
2129a86a88eaSTakashi Iwai 			return err;
2130a86a88eaSTakashi Iwai 	}
2131a86a88eaSTakashi Iwai 	return 0;
2132a86a88eaSTakashi Iwai }
2133a86a88eaSTakashi Iwai 
2134a86a88eaSTakashi Iwai /* create capture and input-src controls for multiple streams */
2135a86a88eaSTakashi Iwai static int create_multi_adc_ctls(struct hda_codec *codec)
2136a86a88eaSTakashi Iwai {
2137a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2138a86a88eaSTakashi Iwai 	int i, err;
2139d7a99cceSTakashi Iwai 
2140d7a99cceSTakashi Iwai 	/* create capture mixer elements */
2141d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2142d7a99cceSTakashi Iwai 		hda_nid_t adc = spec->adc_nids[i];
2143d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2144d7a99cceSTakashi Iwai 					"Capture Volume", i,
2145d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2146d7a99cceSTakashi Iwai 							    HDA_INPUT));
2147d7a99cceSTakashi Iwai 		if (err < 0)
2148d7a99cceSTakashi Iwai 			return err;
2149d7a99cceSTakashi Iwai 		err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2150d7a99cceSTakashi Iwai 					"Capture Switch", i,
2151d7a99cceSTakashi Iwai 					HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2152d7a99cceSTakashi Iwai 							    HDA_INPUT));
2153d7a99cceSTakashi Iwai 		if (err < 0)
2154d7a99cceSTakashi Iwai 			return err;
2155d7a99cceSTakashi Iwai 	}
2156d7a99cceSTakashi Iwai 
2157d7a99cceSTakashi Iwai 	/* input-source control */
2158d7a99cceSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2159d7a99cceSTakashi Iwai 		if (!spec->mux_nids[i])
2160d7a99cceSTakashi Iwai 			break;
2161a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, i);
2162d7a99cceSTakashi Iwai 	if (err < 0)
2163d7a99cceSTakashi Iwai 		return err;
2164a86a88eaSTakashi Iwai 	return 0;
2165d7a99cceSTakashi Iwai }
2166d7a99cceSTakashi Iwai 
2167a86a88eaSTakashi Iwai /* bind capture volume/switch */
2168a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2169a86a88eaSTakashi Iwai 	HDA_BIND_VOL("Capture Volume", 0);
2170a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2171a86a88eaSTakashi Iwai 	HDA_BIND_SW("Capture Switch", 0);
2172a86a88eaSTakashi Iwai 
2173a86a88eaSTakashi Iwai static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2174a86a88eaSTakashi Iwai 			 struct hda_ctl_ops *ops)
2175a86a88eaSTakashi Iwai {
2176a86a88eaSTakashi Iwai 	struct hda_bind_ctls *ctl;
2177a86a88eaSTakashi Iwai 	int i;
2178a86a88eaSTakashi Iwai 
2179a86a88eaSTakashi Iwai 	ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2180a86a88eaSTakashi Iwai 	if (!ctl)
2181a86a88eaSTakashi Iwai 		return -ENOMEM;
2182a86a88eaSTakashi Iwai 	ctl->ops = ops;
2183a86a88eaSTakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++)
2184a86a88eaSTakashi Iwai 		ctl->values[i] =
2185a86a88eaSTakashi Iwai 			HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2186a86a88eaSTakashi Iwai 	*ctl_ret = ctl;
2187a86a88eaSTakashi Iwai 	return 0;
2188a86a88eaSTakashi Iwai }
2189a86a88eaSTakashi Iwai 
2190a86a88eaSTakashi Iwai /* create capture and input-src controls for dynamic ADC-switch case */
2191a86a88eaSTakashi Iwai static int create_dyn_adc_ctls(struct hda_codec *codec)
2192a86a88eaSTakashi Iwai {
2193a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2194a86a88eaSTakashi Iwai 	struct snd_kcontrol_new *knew;
2195a86a88eaSTakashi Iwai 	int err;
2196a86a88eaSTakashi Iwai 
2197a86a88eaSTakashi Iwai 	/* set up the bind capture ctls */
2198a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2199a86a88eaSTakashi Iwai 	if (err < 0)
2200a86a88eaSTakashi Iwai 		return err;
2201a86a88eaSTakashi Iwai 	err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2202a86a88eaSTakashi Iwai 	if (err < 0)
2203a86a88eaSTakashi Iwai 		return err;
2204a86a88eaSTakashi Iwai 
2205a86a88eaSTakashi Iwai 	/* create capture mixer elements */
2206a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2207a86a88eaSTakashi Iwai 	if (!knew)
2208a86a88eaSTakashi Iwai 		return -ENOMEM;
2209a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_vol;
2210a86a88eaSTakashi Iwai 
2211a86a88eaSTakashi Iwai 	knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2212a86a88eaSTakashi Iwai 	if (!knew)
2213a86a88eaSTakashi Iwai 		return -ENOMEM;
2214a86a88eaSTakashi Iwai 	knew->private_value = (long)spec->bind_cap_sw;
2215a86a88eaSTakashi Iwai 
2216a86a88eaSTakashi Iwai 	/* input-source control */
2217a86a88eaSTakashi Iwai 	err = create_input_src_ctls(codec, 1);
2218a86a88eaSTakashi Iwai 	if (err < 0)
2219a86a88eaSTakashi Iwai 		return err;
2220a86a88eaSTakashi Iwai 	return 0;
2221a86a88eaSTakashi Iwai }
2222a86a88eaSTakashi Iwai 
2223a86a88eaSTakashi Iwai /* parse and create capture-related stuff */
2224a86a88eaSTakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2225a86a88eaSTakashi Iwai {
2226a86a88eaSTakashi Iwai 	struct via_spec *spec = codec->spec;
2227a86a88eaSTakashi Iwai 	int err;
2228a86a88eaSTakashi Iwai 
2229a86a88eaSTakashi Iwai 	err = parse_analog_inputs(codec);
2230a86a88eaSTakashi Iwai 	if (err < 0)
2231a86a88eaSTakashi Iwai 		return err;
2232a86a88eaSTakashi Iwai 	if (spec->dyn_adc_switch)
2233a86a88eaSTakashi Iwai 		err = create_dyn_adc_ctls(codec);
2234a86a88eaSTakashi Iwai 	else
2235a86a88eaSTakashi Iwai 		err = create_multi_adc_ctls(codec);
2236a86a88eaSTakashi Iwai 	if (err < 0)
2237a86a88eaSTakashi Iwai 		return err;
2238a86a88eaSTakashi Iwai 	err = create_loopback_ctls(codec);
2239a86a88eaSTakashi Iwai 	if (err < 0)
2240a86a88eaSTakashi Iwai 		return err;
2241a86a88eaSTakashi Iwai 	err = create_mic_boost_ctls(codec);
2242a86a88eaSTakashi Iwai 	if (err < 0)
2243a86a88eaSTakashi Iwai 		return err;
2244c577b8a1SJoseph Chan 	return 0;
2245c577b8a1SJoseph Chan }
2246c577b8a1SJoseph Chan 
224776d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
224876d9b0ddSHarald Welte {
224976d9b0ddSHarald Welte 	unsigned int def_conf;
225076d9b0ddSHarald Welte 	unsigned char seqassoc;
225176d9b0ddSHarald Welte 
22522f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
225376d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
225476d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
225582ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
225682ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
225776d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
22582f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
225976d9b0ddSHarald Welte 	}
226076d9b0ddSHarald Welte 
226176d9b0ddSHarald Welte 	return;
226276d9b0ddSHarald Welte }
226376d9b0ddSHarald Welte 
2264e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
22651f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22661f2e99feSLydia Wang {
22671f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22681f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22691f2e99feSLydia Wang 
22701f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22711f2e99feSLydia Wang 		return 0;
2272e06e5a29STakashi Iwai 	spec->vt1708_jack_detect =
22731f2e99feSLydia Wang 		!((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
2274e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
22751f2e99feSLydia Wang 	return 0;
22761f2e99feSLydia Wang }
22771f2e99feSLydia Wang 
2278e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
22791f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
22801f2e99feSLydia Wang {
22811f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22821f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
22831f2e99feSLydia Wang 	int change;
22841f2e99feSLydia Wang 
22851f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
22861f2e99feSLydia Wang 		return 0;
2287e06e5a29STakashi Iwai 	spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
22881f2e99feSLydia Wang 	change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
2289e06e5a29STakashi Iwai 		== !spec->vt1708_jack_detect;
2290e06e5a29STakashi Iwai 	if (spec->vt1708_jack_detect) {
22911f2e99feSLydia Wang 		mute_aa_path(codec, 1);
22921f2e99feSLydia Wang 		notify_aa_path_ctls(codec);
22931f2e99feSLydia Wang 	}
22941f2e99feSLydia Wang 	return change;
22951f2e99feSLydia Wang }
22961f2e99feSLydia Wang 
2297e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
22981f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
22991f2e99feSLydia Wang 	.name = "Jack Detect",
23001f2e99feSLydia Wang 	.count = 1,
23011f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
2302e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
2303e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
23041f2e99feSLydia Wang };
23051f2e99feSLydia Wang 
230612daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec);
230712daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec);
230812daef65STakashi Iwai 
230912daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
2310c577b8a1SJoseph Chan {
2311c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
2312c577b8a1SJoseph Chan 	int err;
2313c577b8a1SJoseph Chan 
2314c577b8a1SJoseph Chan 	err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2315c577b8a1SJoseph Chan 	if (err < 0)
2316c577b8a1SJoseph Chan 		return err;
2317c577b8a1SJoseph Chan 	if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
23187f0df88cSTakashi Iwai 		return -EINVAL;
2319c577b8a1SJoseph Chan 
23204a79616dSTakashi Iwai 	err = via_auto_create_multi_out_ctls(codec);
2321c577b8a1SJoseph Chan 	if (err < 0)
2322c577b8a1SJoseph Chan 		return err;
23234a79616dSTakashi Iwai 	err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
2324c577b8a1SJoseph Chan 	if (err < 0)
2325c577b8a1SJoseph Chan 		return err;
23264a918ffeSTakashi Iwai 	err = via_auto_create_speaker_ctls(codec);
23274a918ffeSTakashi Iwai 	if (err < 0)
23284a918ffeSTakashi Iwai 		return err;
2329a86a88eaSTakashi Iwai 	err = via_auto_create_analog_input_ctls(codec);
2330c577b8a1SJoseph Chan 	if (err < 0)
2331c577b8a1SJoseph Chan 		return err;
2332c577b8a1SJoseph Chan 
2333c577b8a1SJoseph Chan 	spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2334c577b8a1SJoseph Chan 
233512daef65STakashi Iwai 	fill_dig_outs(codec);
233612daef65STakashi Iwai 	fill_dig_in(codec);
2337c577b8a1SJoseph Chan 
2338603c4019STakashi Iwai 	if (spec->kctls.list)
2339603c4019STakashi Iwai 		spec->mixers[spec->num_mixers++] = spec->kctls.list;
2340c577b8a1SJoseph Chan 
2341096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2342c577b8a1SJoseph Chan 
23438df2a312STakashi Iwai 	if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
2344ece8d043STakashi Iwai 		err = via_hp_build(codec);
2345ece8d043STakashi Iwai 		if (err < 0)
2346ece8d043STakashi Iwai 			return err;
2347ece8d043STakashi Iwai 	}
2348c577b8a1SJoseph Chan 
2349f4a7828bSTakashi Iwai 	err = via_smart51_build(codec);
2350f4a7828bSTakashi Iwai 	if (err < 0)
2351f4a7828bSTakashi Iwai 		return err;
2352f4a7828bSTakashi Iwai 
23535d41762aSTakashi Iwai 	/* assign slave outs */
23545d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
23555d41762aSTakashi Iwai 		codec->slave_dig_outs = spec->slave_dig_outs;
23565d41762aSTakashi Iwai 
2357c577b8a1SJoseph Chan 	return 1;
2358c577b8a1SJoseph Chan }
2359c577b8a1SJoseph Chan 
23605d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec)
2361c577b8a1SJoseph Chan {
236225eaba2fSLydia Wang 	struct via_spec *spec = codec->spec;
23635d41762aSTakashi Iwai 	if (spec->multiout.dig_out_nid)
23645d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
23655d41762aSTakashi Iwai 	if (spec->slave_dig_outs[0])
23665d41762aSTakashi Iwai 		init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
23675d41762aSTakashi Iwai }
236825eaba2fSLydia Wang 
23695d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec)
23705d41762aSTakashi Iwai {
23715d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
23725d41762aSTakashi Iwai 	if (!spec->dig_in_nid)
23735d41762aSTakashi Iwai 		return;
23745d41762aSTakashi Iwai 	snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
23755d41762aSTakashi Iwai 			    AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
23765d41762aSTakashi Iwai }
23775d41762aSTakashi Iwai 
23784a918ffeSTakashi Iwai /* initialize the unsolicited events */
23794a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec)
23804a918ffeSTakashi Iwai {
23814a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
23824a918ffeSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->autocfg;
23834a918ffeSTakashi Iwai 	unsigned int ev;
23844a918ffeSTakashi Iwai 	int i;
23854a918ffeSTakashi Iwai 
23864a918ffeSTakashi Iwai 	if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
23874a918ffeSTakashi Iwai 		snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
23884a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
23894a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
23904a918ffeSTakashi Iwai 
23914a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
23924a918ffeSTakashi Iwai 		ev = VIA_LINE_EVENT;
23934a918ffeSTakashi Iwai 	else
23944a918ffeSTakashi Iwai 		ev = 0;
23954a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
23964a918ffeSTakashi Iwai 		if (cfg->line_out_pins[i] &&
23974a918ffeSTakashi Iwai 		    is_jack_detectable(codec, cfg->line_out_pins[i]))
239863f10d2cSLydia Wang 			snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
23994a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
24004a918ffeSTakashi Iwai 				AC_USRSP_EN | ev | VIA_JACK_EVENT);
24014a918ffeSTakashi Iwai 	}
24024a918ffeSTakashi Iwai 
24034a918ffeSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
24044a918ffeSTakashi Iwai 		if (is_jack_detectable(codec, cfg->inputs[i].pin))
24054a918ffeSTakashi Iwai 			snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
24064a918ffeSTakashi Iwai 				AC_VERB_SET_UNSOLICITED_ENABLE,
24074a918ffeSTakashi Iwai 				AC_USRSP_EN | VIA_JACK_EVENT);
24084a918ffeSTakashi Iwai 	}
24094a918ffeSTakashi Iwai }
24104a918ffeSTakashi Iwai 
24115d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
24125d41762aSTakashi Iwai {
24135d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
24145d41762aSTakashi Iwai 	int i;
24155d41762aSTakashi Iwai 
24165d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
24175d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
24185d41762aSTakashi Iwai 
2419c577b8a1SJoseph Chan 	via_auto_init_multi_out(codec);
2420c577b8a1SJoseph Chan 	via_auto_init_hp_out(codec);
24214a918ffeSTakashi Iwai 	via_auto_init_speaker_out(codec);
2422c577b8a1SJoseph Chan 	via_auto_init_analog_input(codec);
24235d41762aSTakashi Iwai 	via_auto_init_dig_outs(codec);
24245d41762aSTakashi Iwai 	via_auto_init_dig_in(codec);
242511890956SLydia Wang 
24264a918ffeSTakashi Iwai 	via_auto_init_unsol_event(codec);
24274a918ffeSTakashi Iwai 
242825eaba2fSLydia Wang 	via_hp_automute(codec);
24294a918ffeSTakashi Iwai 	via_line_automute(codec, false);
243025eaba2fSLydia Wang 
2431c577b8a1SJoseph Chan 	return 0;
2432c577b8a1SJoseph Chan }
2433c577b8a1SJoseph Chan 
24341f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work)
24351f2e99feSLydia Wang {
24361f2e99feSLydia Wang 	struct via_spec *spec = container_of(work, struct via_spec,
24371f2e99feSLydia Wang 					     vt1708_hp_work.work);
24381f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
24391f2e99feSLydia Wang 		return;
24401f2e99feSLydia Wang 	/* if jack state toggled */
24411f2e99feSLydia Wang 	if (spec->vt1708_hp_present
2442d56757abSTakashi Iwai 	    != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
24431f2e99feSLydia Wang 		spec->vt1708_hp_present ^= 1;
24441f2e99feSLydia Wang 		via_hp_automute(spec->codec);
24451f2e99feSLydia Wang 	}
24461f2e99feSLydia Wang 	vt1708_start_hp_work(spec);
24471f2e99feSLydia Wang }
24481f2e99feSLydia Wang 
2449337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec)
2450337b9d02STakashi Iwai {
2451337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
2452337b9d02STakashi Iwai 	hda_nid_t nid, conn[8];
2453337b9d02STakashi Iwai 	unsigned int type;
2454337b9d02STakashi Iwai 	int i, n;
2455337b9d02STakashi Iwai 
2456337b9d02STakashi Iwai 	for (i = 0; i < spec->num_adc_nids; i++) {
2457337b9d02STakashi Iwai 		nid = spec->adc_nids[i];
2458337b9d02STakashi Iwai 		while (nid) {
2459a22d543aSTakashi Iwai 			type = get_wcaps_type(get_wcaps(codec, nid));
24601c55d521STakashi Iwai 			if (type == AC_WID_PIN)
24611c55d521STakashi Iwai 				break;
2462337b9d02STakashi Iwai 			n = snd_hda_get_connections(codec, nid, conn,
2463337b9d02STakashi Iwai 						    ARRAY_SIZE(conn));
2464337b9d02STakashi Iwai 			if (n <= 0)
2465337b9d02STakashi Iwai 				break;
2466337b9d02STakashi Iwai 			if (n > 1) {
2467337b9d02STakashi Iwai 				spec->mux_nids[i] = nid;
2468337b9d02STakashi Iwai 				break;
2469337b9d02STakashi Iwai 			}
2470337b9d02STakashi Iwai 			nid = conn[0];
2471337b9d02STakashi Iwai 		}
2472337b9d02STakashi Iwai 	}
24731c55d521STakashi Iwai 	return 0;
2474337b9d02STakashi Iwai }
2475337b9d02STakashi Iwai 
2476c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
2477c577b8a1SJoseph Chan {
2478c577b8a1SJoseph Chan 	struct via_spec *spec;
2479c577b8a1SJoseph Chan 	int err;
2480c577b8a1SJoseph Chan 
2481c577b8a1SJoseph Chan 	/* create a codec specific record */
24825b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2483c577b8a1SJoseph Chan 	if (spec == NULL)
2484c577b8a1SJoseph Chan 		return -ENOMEM;
2485c577b8a1SJoseph Chan 
2486620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x17;
2487620e2b28STakashi Iwai 
248812daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
248912daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
249012daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
249112daef65STakashi Iwai 
2492c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
249312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2494c577b8a1SJoseph Chan 	if (err < 0) {
2495c577b8a1SJoseph Chan 		via_free(codec);
2496c577b8a1SJoseph Chan 		return err;
2497c577b8a1SJoseph Chan 	}
2498c577b8a1SJoseph Chan 
249912daef65STakashi Iwai 	/* add jack detect on/off control */
250012daef65STakashi Iwai 	if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
250112daef65STakashi Iwai 		return -ENOMEM;
250212daef65STakashi Iwai 
2503bc9b5623STakashi Iwai 	/* disable 32bit format on VT1708 */
2504bc9b5623STakashi Iwai 	if (codec->vendor_id == 0x11061708)
2505bc9b5623STakashi Iwai 		spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
2506c577b8a1SJoseph Chan 
2507c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2508c577b8a1SJoseph Chan 
25091f2e99feSLydia Wang 	INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
2510c577b8a1SJoseph Chan 	return 0;
2511c577b8a1SJoseph Chan }
2512c577b8a1SJoseph Chan 
2513ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
2514c577b8a1SJoseph Chan {
2515c577b8a1SJoseph Chan 	struct via_spec *spec;
2516c577b8a1SJoseph Chan 	int err;
2517c577b8a1SJoseph Chan 
2518c577b8a1SJoseph Chan 	/* create a codec specific record */
25195b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2520c577b8a1SJoseph Chan 	if (spec == NULL)
2521c577b8a1SJoseph Chan 		return -ENOMEM;
2522c577b8a1SJoseph Chan 
2523620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x18;
2524620e2b28STakashi Iwai 
252512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2526c577b8a1SJoseph Chan 	if (err < 0) {
2527c577b8a1SJoseph Chan 		via_free(codec);
2528c577b8a1SJoseph Chan 		return err;
2529c577b8a1SJoseph Chan 	}
2530c577b8a1SJoseph Chan 
2531c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
2532c577b8a1SJoseph Chan 
2533f7278fd0SJosepch Chan 	return 0;
2534f7278fd0SJosepch Chan }
2535f7278fd0SJosepch Chan 
25363e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
25373e95b9abSLydia Wang {
25383e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
25393e95b9abSLydia Wang 	int imux_is_smixer;
25403e95b9abSLydia Wang 	unsigned int parm;
25413e95b9abSLydia Wang 	int is_8ch = 0;
2542bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
2543bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
25443e95b9abSLydia Wang 		is_8ch = 1;
25453e95b9abSLydia Wang 
25463e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
25473e95b9abSLydia Wang 	imux_is_smixer =
25483e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
25493e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
25503e95b9abSLydia Wang 	/* inputs */
25513e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
25523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
25533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
25543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
25553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
25563e95b9abSLydia Wang 	if (imux_is_smixer)
25573e95b9abSLydia Wang 		parm = AC_PWRST_D0;
25583e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
25593e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
25603e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
25613e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
25623e95b9abSLydia Wang 
25633e95b9abSLydia Wang 	/* outputs */
25643e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
25653e95b9abSLydia Wang 	parm = AC_PWRST_D3;
25663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
25673e95b9abSLydia Wang 	if (spec->smart51_enabled)
25683e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
25693e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
25703e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
25713e95b9abSLydia Wang 
25723e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
25733e95b9abSLydia Wang 	if (is_8ch) {
25743e95b9abSLydia Wang 		parm = AC_PWRST_D3;
25753e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
25763e95b9abSLydia Wang 		if (spec->smart51_enabled)
25773e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
25783e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x26, 0,
25793e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
25803e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x24, 0,
25813e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2582bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
2583bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
2584bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
2585bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
2586bc92df7fSLydia Wang 		if (spec->smart51_enabled)
2587bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
2588bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
2589bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2590bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2591bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
25923e95b9abSLydia Wang 	}
25933e95b9abSLydia Wang 
25943e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
25953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
25963e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
25973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
25983e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
25993e95b9abSLydia Wang 	if (is_8ch)
26003e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
26013e95b9abSLydia Wang 
26023e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
26033e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
26043e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
26053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
26063e95b9abSLydia Wang 	if (is_8ch) {
26073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
26083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
26093e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x27, 0,
26103e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
2611bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2612bc92df7fSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
2613bc92df7fSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
26143e95b9abSLydia Wang }
26153e95b9abSLydia Wang 
2616518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
2617ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
2618f7278fd0SJosepch Chan {
2619f7278fd0SJosepch Chan 	struct via_spec *spec;
2620f7278fd0SJosepch Chan 	int err;
2621f7278fd0SJosepch Chan 
2622518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
2623518bf3baSLydia Wang 		return patch_vt1708S(codec);
2624ddd304d8STakashi Iwai 
2625f7278fd0SJosepch Chan 	/* create a codec specific record */
26265b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2627f7278fd0SJosepch Chan 	if (spec == NULL)
2628f7278fd0SJosepch Chan 		return -ENOMEM;
2629f7278fd0SJosepch Chan 
2630620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2631620e2b28STakashi Iwai 
2632f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
263312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2634f7278fd0SJosepch Chan 	if (err < 0) {
2635f7278fd0SJosepch Chan 		via_free(codec);
2636f7278fd0SJosepch Chan 		return err;
2637f7278fd0SJosepch Chan 	}
2638f7278fd0SJosepch Chan 
2639f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
2640f7278fd0SJosepch Chan 
26413e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
26423e95b9abSLydia Wang 
2643f7278fd0SJosepch Chan 	return 0;
2644f7278fd0SJosepch Chan }
2645f7278fd0SJosepch Chan 
2646d949cac1SHarald Welte /* Patch for VT1708S */
2647096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
2648d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
2649d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
2650bc7e7e5cSLydia Wang 	/* don't bybass mixer */
2651bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
2652d949cac1SHarald Welte 	{ }
2653d949cac1SHarald Welte };
2654d949cac1SHarald Welte 
26559da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */
26569da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec)
26579da29271STakashi Iwai {
26589da29271STakashi Iwai 	struct via_spec *spec = codec->spec;
26599da29271STakashi Iwai 	int i;
26609da29271STakashi Iwai 
26619da29271STakashi Iwai 	for (i = 0; i < spec->autocfg.dig_outs; i++) {
26629da29271STakashi Iwai 		hda_nid_t nid;
26639da29271STakashi Iwai 		int conn;
26649da29271STakashi Iwai 
26659da29271STakashi Iwai 		nid = spec->autocfg.dig_out_pins[i];
26669da29271STakashi Iwai 		if (!nid)
26679da29271STakashi Iwai 			continue;
26689da29271STakashi Iwai 		conn = snd_hda_get_connections(codec, nid, &nid, 1);
26699da29271STakashi Iwai 		if (conn < 1)
26709da29271STakashi Iwai 			continue;
26719da29271STakashi Iwai 		if (!spec->multiout.dig_out_nid)
26729da29271STakashi Iwai 			spec->multiout.dig_out_nid = nid;
26739da29271STakashi Iwai 		else {
26749da29271STakashi Iwai 			spec->slave_dig_outs[0] = nid;
26759da29271STakashi Iwai 			break; /* at most two dig outs */
26769da29271STakashi Iwai 		}
26779da29271STakashi Iwai 	}
26789da29271STakashi Iwai }
26799da29271STakashi Iwai 
268012daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec)
2681d949cac1SHarald Welte {
2682d949cac1SHarald Welte 	struct via_spec *spec = codec->spec;
268312daef65STakashi Iwai 	hda_nid_t dig_nid;
268412daef65STakashi Iwai 	int i, err;
2685d949cac1SHarald Welte 
268612daef65STakashi Iwai 	if (!spec->autocfg.dig_in_pin)
268712daef65STakashi Iwai 		return;
2688d949cac1SHarald Welte 
268912daef65STakashi Iwai 	dig_nid = codec->start_nid;
269012daef65STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
269112daef65STakashi Iwai 		unsigned int wcaps = get_wcaps(codec, dig_nid);
269212daef65STakashi Iwai 		if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
269312daef65STakashi Iwai 			continue;
269412daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_DIGITAL))
269512daef65STakashi Iwai 			continue;
269612daef65STakashi Iwai 		if (!(wcaps & AC_WCAP_CONN_LIST))
269712daef65STakashi Iwai 			continue;
269812daef65STakashi Iwai 		err = get_connection_index(codec, dig_nid,
269912daef65STakashi Iwai 					   spec->autocfg.dig_in_pin);
270012daef65STakashi Iwai 		if (err >= 0) {
270112daef65STakashi Iwai 			spec->dig_in_nid = dig_nid;
270212daef65STakashi Iwai 			break;
270312daef65STakashi Iwai 		}
270412daef65STakashi Iwai 	}
2705d949cac1SHarald Welte }
2706d949cac1SHarald Welte 
27076369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
27086369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
27096369bcfcSLydia Wang {
27106369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
27116369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
27126369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
27136369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
27146369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
27156369bcfcSLydia Wang }
27166369bcfcSLydia Wang 
2717d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
2718d949cac1SHarald Welte {
2719d949cac1SHarald Welte 	struct via_spec *spec;
2720d949cac1SHarald Welte 	int err;
2721d949cac1SHarald Welte 
2722d949cac1SHarald Welte 	/* create a codec specific record */
27235b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2724d949cac1SHarald Welte 	if (spec == NULL)
2725d949cac1SHarald Welte 		return -ENOMEM;
2726d949cac1SHarald Welte 
2727620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
2728d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
2729d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
2730620e2b28STakashi Iwai 
2731d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
273212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2733d949cac1SHarald Welte 	if (err < 0) {
2734d949cac1SHarald Welte 		via_free(codec);
2735d949cac1SHarald Welte 		return err;
2736d949cac1SHarald Welte 	}
2737d949cac1SHarald Welte 
2738096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
2739d949cac1SHarald Welte 
2740d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2741d949cac1SHarald Welte 
2742518bf3baSLydia Wang 	/* correct names for VT1708BCE */
2743518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
2744518bf3baSLydia Wang 		kfree(codec->chip_name);
2745518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2746518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
2747518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
2748518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2749970f630fSLydia Wang 	}
2750bc92df7fSLydia Wang 	/* correct names for VT1705 */
2751bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
2752bc92df7fSLydia Wang 		kfree(codec->chip_name);
2753bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2754bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
2755bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
2756bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
2757bc92df7fSLydia Wang 	}
27583e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
2759d949cac1SHarald Welte 	return 0;
2760d949cac1SHarald Welte }
2761d949cac1SHarald Welte 
2762d949cac1SHarald Welte /* Patch for VT1702 */
2763d949cac1SHarald Welte 
2764096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
2765bc7e7e5cSLydia Wang 	/* mixer enable */
2766bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
2767bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
2768bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
2769d949cac1SHarald Welte 	{ }
2770d949cac1SHarald Welte };
2771d949cac1SHarald Welte 
27723e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
27733e95b9abSLydia Wang {
27743e95b9abSLydia Wang 	int imux_is_smixer =
27753e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
27763e95b9abSLydia Wang 	unsigned int parm;
27773e95b9abSLydia Wang 	/* inputs */
27783e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
27793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
27813e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
27823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
27833e95b9abSLydia Wang 	if (imux_is_smixer)
27843e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
27853e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
27863e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
27873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
27883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
27893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
27903e95b9abSLydia Wang 
27913e95b9abSLydia Wang 	/* outputs */
27923e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
27933e95b9abSLydia Wang 	parm = AC_PWRST_D3;
27943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
27953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
27963e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
27973e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
27983e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
27993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
28003e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
28013e95b9abSLydia Wang }
28023e95b9abSLydia Wang 
2803d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
2804d949cac1SHarald Welte {
2805d949cac1SHarald Welte 	struct via_spec *spec;
2806d949cac1SHarald Welte 	int err;
2807d949cac1SHarald Welte 
2808d949cac1SHarald Welte 	/* create a codec specific record */
28095b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2810d949cac1SHarald Welte 	if (spec == NULL)
2811d949cac1SHarald Welte 		return -ENOMEM;
2812d949cac1SHarald Welte 
2813620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x1a;
2814620e2b28STakashi Iwai 
281512daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
281612daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
281712daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
281812daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
281912daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
282012daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
282112daef65STakashi Iwai 
2822d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
282312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2824d949cac1SHarald Welte 	if (err < 0) {
2825d949cac1SHarald Welte 		via_free(codec);
2826d949cac1SHarald Welte 		return err;
2827d949cac1SHarald Welte 	}
2828d949cac1SHarald Welte 
2829096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
2830d949cac1SHarald Welte 
2831d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
2832d949cac1SHarald Welte 
28333e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
2834d949cac1SHarald Welte 	return 0;
2835d949cac1SHarald Welte }
2836d949cac1SHarald Welte 
2837eb7188caSLydia Wang /* Patch for VT1718S */
2838eb7188caSLydia Wang 
2839096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
28404ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
28414ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
2842eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
2843eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
28445d41762aSTakashi Iwai 
2845eb7188caSLydia Wang 	{ }
2846eb7188caSLydia Wang };
2847eb7188caSLydia Wang 
28483e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
28493e95b9abSLydia Wang {
28503e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
28513e95b9abSLydia Wang 	int imux_is_smixer;
28523e95b9abSLydia Wang 	unsigned int parm;
28533e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
28543e95b9abSLydia Wang 	imux_is_smixer =
28553e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
28563e95b9abSLydia Wang 	/* inputs */
28573e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
28583e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
28603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
28613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
28623e95b9abSLydia Wang 	if (imux_is_smixer)
28633e95b9abSLydia Wang 		parm = AC_PWRST_D0;
28643e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
28653e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
28663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
28673e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
28683e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
28693e95b9abSLydia Wang 
28703e95b9abSLydia Wang 	/* outputs */
28713e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
28723e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28733e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
28743e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
28753e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
28763e95b9abSLydia Wang 
28773e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
28783e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
28803e95b9abSLydia Wang 	if (spec->smart51_enabled)
28813e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
28823e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
28833e95b9abSLydia Wang 
28843e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
28853e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
28873e95b9abSLydia Wang 	if (!spec->hp_independent_mode) /* check for redirected HP */
28883e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
28893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
28903e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
28913e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
28923e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
28933e95b9abSLydia Wang 
28943e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
28953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
28963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
28973e95b9abSLydia Wang 	if (spec->smart51_enabled)
28983e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
28993e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
29003e95b9abSLydia Wang 
29013e95b9abSLydia Wang 	if (spec->hp_independent_mode) {
29023e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
29033e95b9abSLydia Wang 		parm = AC_PWRST_D3;
29043e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
29053e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1b, 0,
29063e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29073e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
29083e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29093e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0xc, 0,
29103e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
29113e95b9abSLydia Wang 	}
29123e95b9abSLydia Wang }
29133e95b9abSLydia Wang 
2914eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
2915eb7188caSLydia Wang {
2916eb7188caSLydia Wang 	struct via_spec *spec;
2917eb7188caSLydia Wang 	int err;
2918eb7188caSLydia Wang 
2919eb7188caSLydia Wang 	/* create a codec specific record */
29205b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
2921eb7188caSLydia Wang 	if (spec == NULL)
2922eb7188caSLydia Wang 		return -ENOMEM;
2923eb7188caSLydia Wang 
2924620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
2925d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
2926d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
2927620e2b28STakashi Iwai 
2928eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
292912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
2930eb7188caSLydia Wang 	if (err < 0) {
2931eb7188caSLydia Wang 		via_free(codec);
2932eb7188caSLydia Wang 		return err;
2933eb7188caSLydia Wang 	}
2934eb7188caSLydia Wang 
2935096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
2936eb7188caSLydia Wang 
2937eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
2938eb7188caSLydia Wang 
29393e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
29403e95b9abSLydia Wang 
2941eb7188caSLydia Wang 	return 0;
2942eb7188caSLydia Wang }
2943f3db423dSLydia Wang 
2944f3db423dSLydia Wang /* Patch for VT1716S */
2945f3db423dSLydia Wang 
2946f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2947f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
2948f3db423dSLydia Wang {
2949f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2950f3db423dSLydia Wang 	uinfo->count = 1;
2951f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
2952f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
2953f3db423dSLydia Wang 	return 0;
2954f3db423dSLydia Wang }
2955f3db423dSLydia Wang 
2956f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2957f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2958f3db423dSLydia Wang {
2959f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2960f3db423dSLydia Wang 	int index = 0;
2961f3db423dSLydia Wang 
2962f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
2963f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
2964f3db423dSLydia Wang 	if (index != -1)
2965f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
2966f3db423dSLydia Wang 
2967f3db423dSLydia Wang 	return 0;
2968f3db423dSLydia Wang }
2969f3db423dSLydia Wang 
2970f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2971f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
2972f3db423dSLydia Wang {
2973f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2974f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
2975f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
2976f3db423dSLydia Wang 
2977f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
2978f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
2979f3db423dSLydia Wang 	spec->dmic_enabled = index;
29803e95b9abSLydia Wang 	set_widgets_power_state(codec);
2981f3db423dSLydia Wang 	return 1;
2982f3db423dSLydia Wang }
2983f3db423dSLydia Wang 
298490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
2985f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2986f3db423dSLydia Wang 	{
2987f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2988f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
29895b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
2990f3db423dSLydia Wang 	 .count = 1,
2991f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
2992f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
2993f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
2994f3db423dSLydia Wang 	 },
2995f3db423dSLydia Wang 	{}			/* end */
2996f3db423dSLydia Wang };
2997f3db423dSLydia Wang 
2998f3db423dSLydia Wang 
2999f3db423dSLydia Wang /* mono-out mixer elements */
300090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
3001f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3002f3db423dSLydia Wang 	{ } /* end */
3003f3db423dSLydia Wang };
3004f3db423dSLydia Wang 
3005096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
3006f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
3007f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
3008f3db423dSLydia Wang 	/* don't bybass mixer */
3009f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
3010f3db423dSLydia Wang 	/* Enable mono output */
3011f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
3012f3db423dSLydia Wang 	{ }
3013f3db423dSLydia Wang };
3014f3db423dSLydia Wang 
30153e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
30163e95b9abSLydia Wang {
30173e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
30183e95b9abSLydia Wang 	int imux_is_smixer;
30193e95b9abSLydia Wang 	unsigned int parm;
30203e95b9abSLydia Wang 	unsigned int mono_out, present;
30213e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
30223e95b9abSLydia Wang 	imux_is_smixer =
30233e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
30243e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
30253e95b9abSLydia Wang 	/* inputs */
30263e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
30273e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
30293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
30303e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
30313e95b9abSLydia Wang 	if (imux_is_smixer)
30323e95b9abSLydia Wang 		parm = AC_PWRST_D0;
30333e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
30343e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
30353e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
30363e95b9abSLydia Wang 
30373e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
30393e95b9abSLydia Wang 	/* PW11 (22h) */
30403e95b9abSLydia Wang 	if (spec->dmic_enabled)
30413e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
30423e95b9abSLydia Wang 	else
30433e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x22, 0,
30443e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
30453e95b9abSLydia Wang 
30463e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
30473e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
30483e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
30493e95b9abSLydia Wang 
30503e95b9abSLydia Wang 	/* outputs */
30513e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
30523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
30543e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
30553e95b9abSLydia Wang 	if (spec->smart51_enabled)
30563e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
30573e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
30583e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
30593e95b9abSLydia Wang 
30603e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
30613e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
30633e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
30643e95b9abSLydia Wang 	if (spec->smart51_enabled)
30653e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
30663e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
30673e95b9abSLydia Wang 
30683e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
30693e95b9abSLydia Wang 	if (spec->smart51_enabled)
30703e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
30713e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
30723e95b9abSLydia Wang 
30733e95b9abSLydia Wang 	/* Mono out */
30743e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
30753e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
30763e95b9abSLydia Wang 
30773e95b9abSLydia Wang 	if (present)
30783e95b9abSLydia Wang 		mono_out = 0;
30793e95b9abSLydia Wang 	else {
30803e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
30813e95b9abSLydia Wang 		if (!spec->hp_independent_mode && present)
30823e95b9abSLydia Wang 			mono_out = 0;
30833e95b9abSLydia Wang 		else
30843e95b9abSLydia Wang 			mono_out = 1;
30853e95b9abSLydia Wang 	}
30863e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
30873e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
30883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
30893e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
30903e95b9abSLydia Wang 
30913e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
30923e95b9abSLydia Wang 	parm = AC_PWRST_D3;
30933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
30943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
30953e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
30963e95b9abSLydia Wang 	if (spec->hp_independent_mode)
30973e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x25, 0,
30983e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
30993e95b9abSLydia Wang 
31003e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
31013e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
31023e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
31033e95b9abSLydia Wang 			    imux_is_smixer ? AC_PWRST_D0 : parm);
31043e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
31053e95b9abSLydia Wang 			    mono_out ? AC_PWRST_D0 : parm);
31063e95b9abSLydia Wang }
31073e95b9abSLydia Wang 
3108f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
3109f3db423dSLydia Wang {
3110f3db423dSLydia Wang 	struct via_spec *spec;
3111f3db423dSLydia Wang 	int err;
3112f3db423dSLydia Wang 
3113f3db423dSLydia Wang 	/* create a codec specific record */
31145b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3115f3db423dSLydia Wang 	if (spec == NULL)
3116f3db423dSLydia Wang 		return -ENOMEM;
3117f3db423dSLydia Wang 
3118620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x16;
3119d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
3120d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
3121620e2b28STakashi Iwai 
3122f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
312312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3124f3db423dSLydia Wang 	if (err < 0) {
3125f3db423dSLydia Wang 		via_free(codec);
3126f3db423dSLydia Wang 		return err;
3127f3db423dSLydia Wang 	}
3128f3db423dSLydia Wang 
3129096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
3130f3db423dSLydia Wang 
3131f3db423dSLydia Wang 	spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3132f3db423dSLydia Wang 	spec->num_mixers++;
3133f3db423dSLydia Wang 
3134f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3135f3db423dSLydia Wang 
3136f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
3137f3db423dSLydia Wang 
31383e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
3139f3db423dSLydia Wang 	return 0;
3140f3db423dSLydia Wang }
314125eaba2fSLydia Wang 
314225eaba2fSLydia Wang /* for vt2002P */
314325eaba2fSLydia Wang 
3144096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
3145eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
3146eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
3147eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
3148eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
314925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
315025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
315125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
315225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
315325eaba2fSLydia Wang 	{ }
315425eaba2fSLydia Wang };
31554a918ffeSTakashi Iwai 
3156096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
315711890956SLydia Wang 	/* Enable Boost Volume backdoor */
315811890956SLydia Wang 	{0x1, 0xfb9, 0x24},
315911890956SLydia Wang 	/* Enable AOW0 to MW9 */
316011890956SLydia Wang 	{0x1, 0xfb8, 0x88},
316111890956SLydia Wang 	{ }
316211890956SLydia Wang };
316325eaba2fSLydia Wang 
31643e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
31653e95b9abSLydia Wang {
31663e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
31673e95b9abSLydia Wang 	int imux_is_smixer;
31683e95b9abSLydia Wang 	unsigned int parm;
31693e95b9abSLydia Wang 	unsigned int present;
31703e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
31713e95b9abSLydia Wang 	imux_is_smixer =
31723e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
31733e95b9abSLydia Wang 	/* inputs */
31743e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
31753e95b9abSLydia Wang 	parm = AC_PWRST_D3;
31763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
31773e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
31783e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
31793e95b9abSLydia Wang 	parm = AC_PWRST_D0;
31803e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
31813e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
31823e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
31833e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
31843e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
31853e95b9abSLydia Wang 
31863e95b9abSLydia Wang 	/* outputs */
31873e95b9abSLydia Wang 	/* AOW0 (8h)*/
31883e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
31893e95b9abSLydia Wang 
319011890956SLydia Wang 	if (spec->codec_type == VT1802) {
319111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
319211890956SLydia Wang 		parm = AC_PWRST_D3;
319311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
319411890956SLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
319511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
319611890956SLydia Wang 		snd_hda_codec_write(codec, 0x38, 0,
319711890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
319811890956SLydia Wang 	} else {
31993e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
32003e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32013e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
32023e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
32033e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32043e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x37, 0,
32053e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
320611890956SLydia Wang 	}
32073e95b9abSLydia Wang 
320811890956SLydia Wang 	if (spec->codec_type == VT1802) {
320911890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
321011890956SLydia Wang 		parm = AC_PWRST_D3;
321111890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
321211890956SLydia Wang 		snd_hda_codec_write(codec, 0x15, 0,
321311890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
321411890956SLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
321511890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
321611890956SLydia Wang 	} else {
32173e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
32183e95b9abSLydia Wang 		parm = AC_PWRST_D3;
32193e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
32203e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x19, 0,
32213e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32223e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x35, 0,
32233e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
322411890956SLydia Wang 	}
32253e95b9abSLydia Wang 
32263e95b9abSLydia Wang 	if (spec->hp_independent_mode)
32273e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
32283e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
32293e95b9abSLydia Wang 
32303e95b9abSLydia Wang 	/* Class-D */
32313e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
32323e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
32333e95b9abSLydia Wang 
32343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
32353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
32363e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
323711890956SLydia Wang 	if (spec->codec_type == VT1802)
323811890956SLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
323911890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
324011890956SLydia Wang 	else
32413e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x18, 0,
32423e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
32443e95b9abSLydia Wang 
32453e95b9abSLydia Wang 	/* Mono Out */
32463e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
32473e95b9abSLydia Wang 
32483e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
324911890956SLydia Wang 	if (spec->codec_type == VT1802) {
325011890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
325111890956SLydia Wang 		snd_hda_codec_write(codec, 0x33, 0,
325211890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
325311890956SLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
325411890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
325511890956SLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
325611890956SLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
325711890956SLydia Wang 	} else {
32583e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
32593e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x31, 0,
32603e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32613e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x17, 0,
32623e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
32633e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3b, 0,
32643e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, parm);
326511890956SLydia Wang 	}
32663e95b9abSLydia Wang 	/* MW9 (21h) */
32673e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
32683e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
32693e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
32703e95b9abSLydia Wang 	else
32713e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x21, 0,
32723e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
32733e95b9abSLydia Wang }
327425eaba2fSLydia Wang 
327525eaba2fSLydia Wang /* patch for vt2002P */
327625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
327725eaba2fSLydia Wang {
327825eaba2fSLydia Wang 	struct via_spec *spec;
327925eaba2fSLydia Wang 	int err;
328025eaba2fSLydia Wang 
328125eaba2fSLydia Wang 	/* create a codec specific record */
32825b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
328325eaba2fSLydia Wang 	if (spec == NULL)
328425eaba2fSLydia Wang 		return -ENOMEM;
328525eaba2fSLydia Wang 
3286620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3287d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3288d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3289620e2b28STakashi Iwai 
329025eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
329112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
329225eaba2fSLydia Wang 	if (err < 0) {
329325eaba2fSLydia Wang 		via_free(codec);
329425eaba2fSLydia Wang 		return err;
329525eaba2fSLydia Wang 	}
329625eaba2fSLydia Wang 
329711890956SLydia Wang 	if (spec->codec_type == VT1802)
32984a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
329911890956SLydia Wang 	else
33004a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
330111890956SLydia Wang 
330225eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
330325eaba2fSLydia Wang 
33043e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
330525eaba2fSLydia Wang 	return 0;
330625eaba2fSLydia Wang }
3307ab6734e7SLydia Wang 
3308ab6734e7SLydia Wang /* for vt1812 */
3309ab6734e7SLydia Wang 
3310096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
3311ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
3312ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
3313ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
3314ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
3315ab6734e7SLydia Wang 	{ }
3316ab6734e7SLydia Wang };
3317ab6734e7SLydia Wang 
33183e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
33193e95b9abSLydia Wang {
33203e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
33213e95b9abSLydia Wang 	int imux_is_smixer =
33223e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
33233e95b9abSLydia Wang 	unsigned int parm;
33243e95b9abSLydia Wang 	unsigned int present;
33253e95b9abSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
33263e95b9abSLydia Wang 	imux_is_smixer =
33273e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
33283e95b9abSLydia Wang 	/* inputs */
33293e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
33303e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33313e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
33323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
33333e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
33343e95b9abSLydia Wang 	parm = AC_PWRST_D0;
33353e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
33363e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
33373e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
33383e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
33393e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
33403e95b9abSLydia Wang 
33413e95b9abSLydia Wang 	/* outputs */
33423e95b9abSLydia Wang 	/* AOW0 (8h)*/
33433e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x8, 0,
33443e95b9abSLydia Wang 			    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33453e95b9abSLydia Wang 
33463e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
33473e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
33493e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
33503e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
33513e95b9abSLydia Wang 
33523e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
33533e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
33553e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
33563e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
33573e95b9abSLydia Wang 	if (spec->hp_independent_mode)
33583e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x9, 0,
33593e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33603e95b9abSLydia Wang 
33613e95b9abSLydia Wang 	/* Internal Speaker */
33623e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
33633e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
33643e95b9abSLydia Wang 
33653e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
33673e95b9abSLydia Wang 	if (present) {
33683e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
33693e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33703e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
33713e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33723e95b9abSLydia Wang 	} else {
33733e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x14, 0,
33743e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33753e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x34, 0,
33763e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33773e95b9abSLydia Wang 	}
33783e95b9abSLydia Wang 
33793e95b9abSLydia Wang 
33803e95b9abSLydia Wang 	/* Mono Out */
33813e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
33823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
33833e95b9abSLydia Wang 
33843e95b9abSLydia Wang 	parm = AC_PWRST_D3;
33853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
33863e95b9abSLydia Wang 	if (present) {
33873e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
33883e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33893e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
33903e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33913e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
33923e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
33933e95b9abSLydia Wang 	} else {
33943e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x1c, 0,
33953e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33963e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3c, 0,
33973e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
33983e95b9abSLydia Wang 		snd_hda_codec_write(codec, 0x3e, 0,
33993e95b9abSLydia Wang 				    AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
34003e95b9abSLydia Wang 	}
34013e95b9abSLydia Wang 
34023e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
34033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
34043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
34053e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
34063e95b9abSLydia Wang 	snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
34073e95b9abSLydia Wang 
34083e95b9abSLydia Wang }
3409ab6734e7SLydia Wang 
3410ab6734e7SLydia Wang /* patch for vt1812 */
3411ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
3412ab6734e7SLydia Wang {
3413ab6734e7SLydia Wang 	struct via_spec *spec;
3414ab6734e7SLydia Wang 	int err;
3415ab6734e7SLydia Wang 
3416ab6734e7SLydia Wang 	/* create a codec specific record */
34175b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
3418ab6734e7SLydia Wang 	if (spec == NULL)
3419ab6734e7SLydia Wang 		return -ENOMEM;
3420ab6734e7SLydia Wang 
3421620e2b28STakashi Iwai 	spec->aa_mix_nid = 0x21;
3422d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
3423d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
3424620e2b28STakashi Iwai 
3425ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
342612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
3427ab6734e7SLydia Wang 	if (err < 0) {
3428ab6734e7SLydia Wang 		via_free(codec);
3429ab6734e7SLydia Wang 		return err;
3430ab6734e7SLydia Wang 	}
3431ab6734e7SLydia Wang 
3432096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
3433ab6734e7SLydia Wang 
3434ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
3435ab6734e7SLydia Wang 
34363e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
3437ab6734e7SLydia Wang 	return 0;
3438ab6734e7SLydia Wang }
3439ab6734e7SLydia Wang 
3440c577b8a1SJoseph Chan /*
3441c577b8a1SJoseph Chan  * patch entries
3442c577b8a1SJoseph Chan  */
344390dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
34443218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
34453218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
34463218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
34473218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
34483218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
3449ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34503218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
3451ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34523218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
3453ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34543218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
3455ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34563218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
3457ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34583218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
3459ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34603218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
3461ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34623218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
3463ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
34643218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
3465ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34663218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
3467ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34683218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
3469ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34703218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
3471ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34723218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
3473ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34743218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
3475ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34763218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
3477ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34783218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
3479ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
34803218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
3481d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34823218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
3483d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34843218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
3485d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34863218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
3487d949cac1SHarald Welte 	  .patch = patch_vt1708S},
3488bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
3489d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34903218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
3491d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34923218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
3493d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34943218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
3495d949cac1SHarald Welte 	  .patch = patch_vt1708S},
34963218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
3497d949cac1SHarald Welte 	  .patch = patch_vt1702},
34983218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
3499d949cac1SHarald Welte 	  .patch = patch_vt1702},
35003218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
3501d949cac1SHarald Welte 	  .patch = patch_vt1702},
35023218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
3503d949cac1SHarald Welte 	  .patch = patch_vt1702},
35043218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
3505d949cac1SHarald Welte 	  .patch = patch_vt1702},
35063218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
3507d949cac1SHarald Welte 	  .patch = patch_vt1702},
35083218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
3509d949cac1SHarald Welte 	  .patch = patch_vt1702},
35103218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
3511d949cac1SHarald Welte 	  .patch = patch_vt1702},
3512eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
3513eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3514eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
3515eb7188caSLydia Wang 	  .patch = patch_vt1718S},
3516bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
3517bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3518bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
3519bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
3520f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
3521f3db423dSLydia Wang 	  .patch = patch_vt1716S},
3522f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
3523f3db423dSLydia Wang 	  .patch = patch_vt1716S},
352425eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
352525eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
3526ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
352736dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
352836dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
352911890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
353011890956SLydia Wang 		.patch = patch_vt2002P},
353111890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
353211890956SLydia Wang 		.patch = patch_vt2002P},
3533c577b8a1SJoseph Chan 	{} /* terminator */
3534c577b8a1SJoseph Chan };
35351289e9e8STakashi Iwai 
35361289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
35371289e9e8STakashi Iwai 
35381289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
35391289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
35401289e9e8STakashi Iwai 	.owner = THIS_MODULE,
35411289e9e8STakashi Iwai };
35421289e9e8STakashi Iwai 
35431289e9e8STakashi Iwai MODULE_LICENSE("GPL");
35441289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
35451289e9e8STakashi Iwai 
35461289e9e8STakashi Iwai static int __init patch_via_init(void)
35471289e9e8STakashi Iwai {
35481289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
35491289e9e8STakashi Iwai }
35501289e9e8STakashi Iwai 
35511289e9e8STakashi Iwai static void __exit patch_via_exit(void)
35521289e9e8STakashi Iwai {
35531289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
35541289e9e8STakashi Iwai }
35551289e9e8STakashi Iwai 
35561289e9e8STakashi Iwai module_init(patch_via_init)
35571289e9e8STakashi Iwai module_exit(patch_via_exit)
3558