xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 05909d5c679cf7c9a8a5bc663677c066a546894f)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52da155d5bSPaul Gortmaker #include <linux/module.h>
53c577b8a1SJoseph Chan #include <sound/core.h>
540aa62aefSHarald Welte #include <sound/asoundef.h>
55c577b8a1SJoseph Chan #include "hda_codec.h"
56c577b8a1SJoseph Chan #include "hda_local.h"
57128bc4baSTakashi Iwai #include "hda_auto_parser.h"
581835a0f9STakashi Iwai #include "hda_jack.h"
59b3f6008fSTakashi Iwai #include "hda_generic.h"
60c577b8a1SJoseph Chan 
61c577b8a1SJoseph Chan /* Pin Widget NID */
6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
64c577b8a1SJoseph Chan 
65d7426329SHarald Welte enum VIA_HDA_CODEC {
66d7426329SHarald Welte 	UNKNOWN = -1,
67d7426329SHarald Welte 	VT1708,
68d7426329SHarald Welte 	VT1709_10CH,
69d7426329SHarald Welte 	VT1709_6CH,
70d7426329SHarald Welte 	VT1708B_8CH,
71d7426329SHarald Welte 	VT1708B_4CH,
72d7426329SHarald Welte 	VT1708S,
73518bf3baSLydia Wang 	VT1708BCE,
74d7426329SHarald Welte 	VT1702,
75eb7188caSLydia Wang 	VT1718S,
76f3db423dSLydia Wang 	VT1716S,
7725eaba2fSLydia Wang 	VT2002P,
78ab6734e7SLydia Wang 	VT1812,
7911890956SLydia Wang 	VT1802,
8043737e0aSLydia Wang 	VT1705CF,
816121b84aSLydia Wang 	VT1808,
82d7426329SHarald Welte 	CODEC_TYPES,
83d7426329SHarald Welte };
84d7426329SHarald Welte 
8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8811890956SLydia Wang 	 (spec)->codec_type == VT1802)
8911890956SLydia Wang 
901f2e99feSLydia Wang struct via_spec {
91b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
92b3f6008fSTakashi Iwai 
931f2e99feSLydia Wang 	/* codec parameterization */
9490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
951f2e99feSLydia Wang 	unsigned int num_mixers;
961f2e99feSLydia Wang 
9790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
981f2e99feSLydia Wang 	unsigned int num_iverbs;
991f2e99feSLydia Wang 
1001f2e99feSLydia Wang 	/* HP mode source */
101f3db423dSLydia Wang 	unsigned int dmic_enabled;
10224088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1031f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1041f2e99feSLydia Wang 
105e9d010c2STakashi Iwai 	/* analog low-power control */
106e9d010c2STakashi Iwai 	bool alc_mode;
107e9d010c2STakashi Iwai 
1081f2e99feSLydia Wang 	/* work to check hp jack state */
109187d333eSTakashi Iwai 	int hp_work_active;
110e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1113e95b9abSLydia Wang 
1123e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
11343737e0aSLydia Wang 	unsigned int dac_stream_tag[4];
1141f2e99feSLydia Wang };
1151f2e99feSLydia Wang 
1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
118b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
119b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
120b3f6008fSTakashi Iwai 				  int action);
121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
122b3f6008fSTakashi Iwai 
1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1245b0cb1d8SJaroslav Kysela {
1255b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1265b0cb1d8SJaroslav Kysela 
1275b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1285b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1295b0cb1d8SJaroslav Kysela 		return NULL;
1305b0cb1d8SJaroslav Kysela 
1315b0cb1d8SJaroslav Kysela 	codec->spec = spec;
132b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1330341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1340341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1350341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1360341ccd7SLydia Wang 		spec->codec_type = VT1708S;
137b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
13813961170STakashi Iwai 	spec->gen.indep_hp = 1;
139*05909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
140b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
1415b0cb1d8SJaroslav Kysela 	return spec;
1425b0cb1d8SJaroslav Kysela }
1435b0cb1d8SJaroslav Kysela 
144744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
145d7426329SHarald Welte {
146744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
147d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
148d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
149d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
150d7426329SHarald Welte 
151d7426329SHarald Welte 	/* get codec type */
152d7426329SHarald Welte 	if (ven_id != 0x1106)
153d7426329SHarald Welte 		codec_type = UNKNOWN;
154d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
155d7426329SHarald Welte 		codec_type = VT1708;
156d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
157d7426329SHarald Welte 		codec_type = VT1709_10CH;
158d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
159d7426329SHarald Welte 		codec_type = VT1709_6CH;
160518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
161d7426329SHarald Welte 		codec_type = VT1708B_8CH;
162518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
163518bf3baSLydia Wang 			codec_type = VT1708BCE;
164518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
165d7426329SHarald Welte 		codec_type = VT1708B_4CH;
166d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
167d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
168d7426329SHarald Welte 		codec_type = VT1708S;
169d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
170d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
171d7426329SHarald Welte 		codec_type = VT1702;
172eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
173eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
174eb7188caSLydia Wang 		codec_type = VT1718S;
175f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
176f3db423dSLydia Wang 		codec_type = VT1716S;
177bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
178bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17925eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
18025eaba2fSLydia Wang 		codec_type = VT2002P;
181ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
182ab6734e7SLydia Wang 		codec_type = VT1812;
18336dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18436dd5c4aSLydia Wang 		codec_type = VT1708S;
18511890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18611890956SLydia Wang 		codec_type = VT1802;
18743737e0aSLydia Wang 	else if (dev_id == 0x4760)
18843737e0aSLydia Wang 		codec_type = VT1705CF;
1896121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1906121b84aSLydia Wang 		codec_type = VT1808;
191d7426329SHarald Welte 	else
192d7426329SHarald Welte 		codec_type = UNKNOWN;
193d7426329SHarald Welte 	return codec_type;
194d7426329SHarald Welte };
195d7426329SHarald Welte 
196ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
197ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1981f2e99feSLydia Wang 
199187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
200187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
201187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2021f2e99feSLydia Wang 
203b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2041f2e99feSLydia Wang {
205b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
206b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2071f2e99feSLydia Wang 		return;
208187d333eSTakashi Iwai 	if (spec->hp_work_active) {
209b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
210b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
211b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
212b3f6008fSTakashi Iwai 		codec->jackpoll_interval = 0;
213187d333eSTakashi Iwai 	}
214187d333eSTakashi Iwai }
215187d333eSTakashi Iwai 
216b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
217187d333eSTakashi Iwai {
218b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
219b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
220187d333eSTakashi Iwai 		return;
22105dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
222187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
223b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
224b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
225b3f6008fSTakashi Iwai 			queue_delayed_work(codec->bus->workq,
226b3f6008fSTakashi Iwai 					   &codec->jackpoll_work, 0);
227b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
228187d333eSTakashi Iwai 		}
229b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
230b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2311f2e99feSLydia Wang }
232f5271101SLydia Wang 
2333e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2343e95b9abSLydia Wang {
235087c2e3bSTakashi Iwai #if 0 /* FIXME: the assumed connections don't match always with the
236087c2e3bSTakashi Iwai        * actual routes by the generic parser, so better to disable
237087c2e3bSTakashi Iwai        * the control for safety.
238087c2e3bSTakashi Iwai        */
2393e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2403e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2413e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
242087c2e3bSTakashi Iwai #endif
2433e95b9abSLydia Wang }
24425eaba2fSLydia Wang 
245054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
246054d867eSTakashi Iwai 			       unsigned int parm)
247054d867eSTakashi Iwai {
2489040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
249054d867eSTakashi Iwai 		return;
250054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
251054d867eSTakashi Iwai }
252054d867eSTakashi Iwai 
25343737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
25443737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
25543737e0aSLydia Wang {
25643737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25743737e0aSLydia Wang 	unsigned int format;
2589040d102STakashi Iwai 
2599040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
26043737e0aSLydia Wang 		return;
26143737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26243737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
26343737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
26443737e0aSLydia Wang 
26543737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26643737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26743737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26843737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
26943737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
27043737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
27143737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
27243737e0aSLydia Wang 	}
27343737e0aSLydia Wang }
27443737e0aSLydia Wang 
275b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
276b3f6008fSTakashi Iwai {
277b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
278b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
279b3f6008fSTakashi Iwai }
280b3f6008fSTakashi Iwai 
281b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
282b3f6008fSTakashi Iwai {
283b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
284b3f6008fSTakashi Iwai 	int i;
285b3f6008fSTakashi Iwai 
286b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
287b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
288b3f6008fSTakashi Iwai 			return true;
289b3f6008fSTakashi Iwai 	return false;
290b3f6008fSTakashi Iwai }
291b3f6008fSTakashi Iwai 
292f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
293f5271101SLydia Wang 				unsigned int *affected_parm)
294f5271101SLydia Wang {
295f5271101SLydia Wang 	unsigned parm;
296f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
297f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
298f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
299f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
3001564b287SLydia Wang 	struct via_spec *spec = codec->spec;
30124088a58STakashi Iwai 	unsigned present = 0;
30224088a58STakashi Iwai 
30324088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
30424088a58STakashi Iwai 	if (!no_presence)
30524088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
306b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3071564b287SLydia Wang 	    || ((no_presence || present)
3081564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
309f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
310f5271101SLydia Wang 		parm = AC_PWRST_D0;
311f5271101SLydia Wang 	} else
312f5271101SLydia Wang 		parm = AC_PWRST_D3;
313f5271101SLydia Wang 
314054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
315f5271101SLydia Wang }
316f5271101SLydia Wang 
31724088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31824088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
31924088a58STakashi Iwai {
320dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
32124088a58STakashi Iwai }
32224088a58STakashi Iwai 
32324088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
32424088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32524088a58STakashi Iwai {
32624088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32724088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32824088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
32924088a58STakashi Iwai 	return 0;
33024088a58STakashi Iwai }
33124088a58STakashi Iwai 
33224088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
33324088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
33424088a58STakashi Iwai {
33524088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33624088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33724088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33824088a58STakashi Iwai 
33924088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
34024088a58STakashi Iwai 		return 0;
34124088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
34224088a58STakashi Iwai 	set_widgets_power_state(codec);
343e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
34424088a58STakashi Iwai 	return 1;
34524088a58STakashi Iwai }
34624088a58STakashi Iwai 
347b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
348b3f6008fSTakashi Iwai 	{
34924088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
35024088a58STakashi Iwai 	.name = "Dynamic Power-Control",
35124088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
35224088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
35324088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
354b3f6008fSTakashi Iwai 	},
355b3f6008fSTakashi Iwai 	{} /* terminator */
35624088a58STakashi Iwai };
35724088a58STakashi Iwai 
35824088a58STakashi Iwai 
359f5271101SLydia Wang /* check AA path's mute status */
360ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
361ada509ecSTakashi Iwai {
362ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
363ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
3640186f4f4STakashi Iwai 	int ch, v;
365ada509ecSTakashi Iwai 
3660186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
3670186f4f4STakashi Iwai 	if (!p)
3680186f4f4STakashi Iwai 		return true;
3690186f4f4STakashi Iwai 	for (; p->nid; p++) {
370ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
371ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
372ada509ecSTakashi Iwai 						   p->idx);
373ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
374ada509ecSTakashi Iwai 				return false;
375f5271101SLydia Wang 		}
376f5271101SLydia Wang 	}
377ada509ecSTakashi Iwai 	return true;
378f5271101SLydia Wang }
379f5271101SLydia Wang 
380f5271101SLydia Wang /* enter/exit analog low-current mode */
381e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
382f5271101SLydia Wang {
383f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
384ada509ecSTakashi Iwai 	bool enable;
385ada509ecSTakashi Iwai 	unsigned int verb, parm;
386f5271101SLydia Wang 
387e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
388e9d010c2STakashi Iwai 		enable = false;
389e9d010c2STakashi Iwai 	else
390b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
391e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
392e9d010c2STakashi Iwai 		return;
393e9d010c2STakashi Iwai 	spec->alc_mode = enable;
394f5271101SLydia Wang 
395f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
396f5271101SLydia Wang 	switch (spec->codec_type) {
397f5271101SLydia Wang 	case VT1708B_8CH:
398f5271101SLydia Wang 	case VT1708B_4CH:
399f5271101SLydia Wang 		verb = 0xf70;
400f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
401f5271101SLydia Wang 		break;
402f5271101SLydia Wang 	case VT1708S:
403eb7188caSLydia Wang 	case VT1718S:
404f3db423dSLydia Wang 	case VT1716S:
405f5271101SLydia Wang 		verb = 0xf73;
406f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
407f5271101SLydia Wang 		break;
408f5271101SLydia Wang 	case VT1702:
409f5271101SLydia Wang 		verb = 0xf73;
410f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
411f5271101SLydia Wang 		break;
41225eaba2fSLydia Wang 	case VT2002P:
413ab6734e7SLydia Wang 	case VT1812:
41411890956SLydia Wang 	case VT1802:
41525eaba2fSLydia Wang 		verb = 0xf93;
41625eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
41725eaba2fSLydia Wang 		break;
41843737e0aSLydia Wang 	case VT1705CF:
4196121b84aSLydia Wang 	case VT1808:
42043737e0aSLydia Wang 		verb = 0xf82;
42143737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
42243737e0aSLydia Wang 		break;
423f5271101SLydia Wang 	default:
424f5271101SLydia Wang 		return;		/* other codecs are not supported */
425f5271101SLydia Wang 	}
426f5271101SLydia Wang 	/* send verb */
427f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
428f5271101SLydia Wang }
429f5271101SLydia Wang 
430e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
431e9d010c2STakashi Iwai {
432e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
433e9d010c2STakashi Iwai }
434e9d010c2STakashi Iwai 
435c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
436c577b8a1SJoseph Chan {
437c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4385b0cb1d8SJaroslav Kysela 	int err, i;
439c577b8a1SJoseph Chan 
440b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
441b3f6008fSTakashi Iwai 	if (err < 0)
442b3f6008fSTakashi Iwai 		return err;
443b3f6008fSTakashi Iwai 
44424088a58STakashi Iwai 	if (spec->set_widgets_power_state)
445b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
44624088a58STakashi Iwai 
447c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
448c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
449c577b8a1SJoseph Chan 		if (err < 0)
450c577b8a1SJoseph Chan 			return err;
451c577b8a1SJoseph Chan 	}
452c577b8a1SJoseph Chan 
453c577b8a1SJoseph Chan 	return 0;
454c577b8a1SJoseph Chan }
455c577b8a1SJoseph Chan 
456b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
457b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
458b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
459b3f6008fSTakashi Iwai 				  int action)
460c577b8a1SJoseph Chan {
461b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
462b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
463c577b8a1SJoseph Chan }
464c577b8a1SJoseph Chan 
465c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
466c577b8a1SJoseph Chan {
467c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
468c577b8a1SJoseph Chan 
469c577b8a1SJoseph Chan 	if (!spec)
470c577b8a1SJoseph Chan 		return;
471c577b8a1SJoseph Chan 
472b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
473b3f6008fSTakashi Iwai 	snd_hda_gen_spec_free(&spec->gen);
474a86a88eaSTakashi Iwai 	kfree(spec);
475c577b8a1SJoseph Chan }
476c577b8a1SJoseph Chan 
4772a43952aSTakashi Iwai #ifdef CONFIG_PM
47868cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4791f2e99feSLydia Wang {
4801f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
481b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
48294c142a1SDavid Henningsson 
48394c142a1SDavid Henningsson 	if (spec->codec_type == VT1802) {
48494c142a1SDavid Henningsson 		/* Fix pop noise on headphones */
48594c142a1SDavid Henningsson 		int i;
486b3f6008fSTakashi Iwai 		for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
487b3f6008fSTakashi Iwai 			snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0);
48894c142a1SDavid Henningsson 	}
48994c142a1SDavid Henningsson 
4901f2e99feSLydia Wang 	return 0;
4911f2e99feSLydia Wang }
4921f2e99feSLydia Wang #endif
4931f2e99feSLydia Wang 
49483012a7cSTakashi Iwai #ifdef CONFIG_PM
495cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
496cb53c626STakashi Iwai {
497cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
498b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
499b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
500b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
501b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
502cb53c626STakashi Iwai }
503cb53c626STakashi Iwai #endif
504cb53c626STakashi Iwai 
505c577b8a1SJoseph Chan /*
506c577b8a1SJoseph Chan  */
5075d41762aSTakashi Iwai 
5085d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5095d41762aSTakashi Iwai 
51090dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
511c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
512b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
513c577b8a1SJoseph Chan 	.init = via_init,
514c577b8a1SJoseph Chan 	.free = via_free,
5154e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5162a43952aSTakashi Iwai #ifdef CONFIG_PM
5171f2e99feSLydia Wang 	.suspend = via_suspend,
518cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
519cb53c626STakashi Iwai #endif
520c577b8a1SJoseph Chan };
521c577b8a1SJoseph Chan 
5224a79616dSTakashi Iwai 
523b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
524b3f6008fSTakashi Iwai 	/* power down jack detect function */
525b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
526b3f6008fSTakashi Iwai 	{ }
5274a79616dSTakashi Iwai };
52876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52976d9b0ddSHarald Welte {
53076d9b0ddSHarald Welte 	unsigned int def_conf;
53176d9b0ddSHarald Welte 	unsigned char seqassoc;
53276d9b0ddSHarald Welte 
5332f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
53476d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
53576d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
53682ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
53782ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
53876d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5392f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
54076d9b0ddSHarald Welte 	}
54176d9b0ddSHarald Welte 
54276d9b0ddSHarald Welte 	return;
54376d9b0ddSHarald Welte }
54476d9b0ddSHarald Welte 
545e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5461f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5471f2e99feSLydia Wang {
5481f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5491f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5501f2e99feSLydia Wang 
5511f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5521f2e99feSLydia Wang 		return 0;
553e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5541f2e99feSLydia Wang 	return 0;
5551f2e99feSLydia Wang }
5561f2e99feSLydia Wang 
557e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5581f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5591f2e99feSLydia Wang {
5601f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5611f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
562187d333eSTakashi Iwai 	int val;
5631f2e99feSLydia Wang 
5641f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5651f2e99feSLydia Wang 		return 0;
566187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
567187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
568187d333eSTakashi Iwai 		return 0;
569187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
570b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
571187d333eSTakashi Iwai 	return 1;
5721f2e99feSLydia Wang }
5731f2e99feSLydia Wang 
574b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
575b3f6008fSTakashi Iwai 	{
5761f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5771f2e99feSLydia Wang 	.name = "Jack Detect",
5781f2e99feSLydia Wang 	.count = 1,
5791f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
580e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
581e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
582b3f6008fSTakashi Iwai 	},
583b3f6008fSTakashi Iwai 	{} /* terminator */
5841f2e99feSLydia Wang };
5851f2e99feSLydia Wang 
586b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5874e2d16d3SDavid Henningsson {
5884e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
589b3f6008fSTakashi Iwai 	snd_hda_gen_hp_automute(codec, tbl);
590b3f6008fSTakashi Iwai }
591b3f6008fSTakashi Iwai 
592b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
593b3f6008fSTakashi Iwai {
594b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
595b3f6008fSTakashi Iwai 	snd_hda_gen_line_automute(codec, tbl);
5964e2d16d3SDavid Henningsson }
5974e2d16d3SDavid Henningsson 
5984e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5994e2d16d3SDavid Henningsson {
6004e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
6014e2d16d3SDavid Henningsson }
6024e2d16d3SDavid Henningsson 
603b3f6008fSTakashi Iwai #define VIA_JACK_EVENT	(HDA_GEN_LAST_EVENT + 1)
604b3f6008fSTakashi Iwai 
605b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
6064a918ffeSTakashi Iwai {
6074a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
608b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
609b3f6008fSTakashi Iwai 	hda_nid_t pin;
6104a918ffeSTakashi Iwai 	int i;
6114a918ffeSTakashi Iwai 
612b3f6008fSTakashi Iwai 	spec->gen.hp_automute_hook = via_hp_automute;
6134a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
614b3f6008fSTakashi Iwai 		spec->gen.line_automute_hook = via_line_automute;
6154e2d16d3SDavid Henningsson 
6164a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
617b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
618b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
619b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
620b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
6214e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
6224e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
6234a918ffeSTakashi Iwai 	}
624b3f6008fSTakashi Iwai 
625b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
626b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
627b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
628b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
629b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
630b3f6008fSTakashi Iwai 							    VIA_JACK_EVENT,
631b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
632b3f6008fSTakashi Iwai 	}
633b3f6008fSTakashi Iwai }
634b3f6008fSTakashi Iwai 
6354abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
6364abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
6374abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6384abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
6394abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6404abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6414abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
6424abdbd1cSTakashi Iwai };
6434abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
6444abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
6454abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6464abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
6474abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6484abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6494abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
6504abdbd1cSTakashi Iwai };
6514abdbd1cSTakashi Iwai 
652b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
653b3f6008fSTakashi Iwai {
654b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
655b3f6008fSTakashi Iwai 	int err;
656b3f6008fSTakashi Iwai 
6574abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
6584abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
6594abdbd1cSTakashi Iwai 
660b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
661b3f6008fSTakashi Iwai 	if (err < 0)
662b3f6008fSTakashi Iwai 		return err;
663b3f6008fSTakashi Iwai 
664b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
665b3f6008fSTakashi Iwai 	if (err < 0)
666b3f6008fSTakashi Iwai 		return err;
667b3f6008fSTakashi Iwai 
668b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
669b3f6008fSTakashi Iwai 	return 0;
6704a918ffeSTakashi Iwai }
6714a918ffeSTakashi Iwai 
6725d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6735d41762aSTakashi Iwai {
6745d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6755d41762aSTakashi Iwai 	int i;
6765d41762aSTakashi Iwai 
6775d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6785d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6795d41762aSTakashi Iwai 
680e9d010c2STakashi Iwai 	/* init power states */
681e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
682e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
683e9d010c2STakashi Iwai 
684b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
68511890956SLydia Wang 
686b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
68725eaba2fSLydia Wang 
688c577b8a1SJoseph Chan 	return 0;
689c577b8a1SJoseph Chan }
690c577b8a1SJoseph Chan 
691f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
692f672f65aSDavid Henningsson {
693f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
694f672f65aSDavid Henningsson 	   temporary enable jackpoll */
695f672f65aSDavid Henningsson 	int err;
696f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
697f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
698f672f65aSDavid Henningsson 	err = via_build_controls(codec);
699f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
700f672f65aSDavid Henningsson 	return err;
701f672f65aSDavid Henningsson }
702f672f65aSDavid Henningsson 
703b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
704337b9d02STakashi Iwai {
705337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
706b3f6008fSTakashi Iwai 	int i, err;
707337b9d02STakashi Iwai 
708b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
709b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
710b3f6008fSTakashi Iwai 		return err;
711b3f6008fSTakashi Iwai 
712b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
713b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
714b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
715b3f6008fSTakashi Iwai 	 */
716b3f6008fSTakashi Iwai 	for (i = 0; i < codec->num_pcms; i++) {
717b3f6008fSTakashi Iwai 		struct hda_pcm *info = &spec->gen.pcm_rec[i];
718b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
719b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
720b3f6008fSTakashi Iwai 			continue;
721b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
722b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
723337b9d02STakashi Iwai 	}
724b3f6008fSTakashi Iwai 
7251c55d521STakashi Iwai 	return 0;
726337b9d02STakashi Iwai }
727337b9d02STakashi Iwai 
728c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
729c577b8a1SJoseph Chan {
730c577b8a1SJoseph Chan 	struct via_spec *spec;
731c577b8a1SJoseph Chan 	int err;
732c577b8a1SJoseph Chan 
733c577b8a1SJoseph Chan 	/* create a codec specific record */
7345b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
735c577b8a1SJoseph Chan 	if (spec == NULL)
736c577b8a1SJoseph Chan 		return -ENOMEM;
737c577b8a1SJoseph Chan 
738b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
739b3f6008fSTakashi Iwai 
740b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
741b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
742b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
743b3f6008fSTakashi Iwai 
744b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
745b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
746b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
747620e2b28STakashi Iwai 
74812daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
74912daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
75012daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
75112daef65STakashi Iwai 
752c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
75312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
754c577b8a1SJoseph Chan 	if (err < 0) {
755c577b8a1SJoseph Chan 		via_free(codec);
756c577b8a1SJoseph Chan 		return err;
757c577b8a1SJoseph Chan 	}
758c577b8a1SJoseph Chan 
75912daef65STakashi Iwai 	/* add jack detect on/off control */
760b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
761c577b8a1SJoseph Chan 
762e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
763e322a36dSLydia Wang 
764c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
765f672f65aSDavid Henningsson 	codec->patch_ops.build_controls = vt1708_build_controls;
766b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
767c577b8a1SJoseph Chan 
768b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
769b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
770b3f6008fSTakashi Iwai 
771c577b8a1SJoseph Chan 	return 0;
772c577b8a1SJoseph Chan }
773c577b8a1SJoseph Chan 
774ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
775c577b8a1SJoseph Chan {
776c577b8a1SJoseph Chan 	struct via_spec *spec;
777c577b8a1SJoseph Chan 	int err;
778c577b8a1SJoseph Chan 
779c577b8a1SJoseph Chan 	/* create a codec specific record */
7805b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
781c577b8a1SJoseph Chan 	if (spec == NULL)
782c577b8a1SJoseph Chan 		return -ENOMEM;
783c577b8a1SJoseph Chan 
784b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
785620e2b28STakashi Iwai 
78612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
787c577b8a1SJoseph Chan 	if (err < 0) {
788c577b8a1SJoseph Chan 		via_free(codec);
789c577b8a1SJoseph Chan 		return err;
790c577b8a1SJoseph Chan 	}
791c577b8a1SJoseph Chan 
792c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
793c577b8a1SJoseph Chan 
794f7278fd0SJosepch Chan 	return 0;
795f7278fd0SJosepch Chan }
796f7278fd0SJosepch Chan 
7973e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7983e95b9abSLydia Wang {
7993e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
8003e95b9abSLydia Wang 	int imux_is_smixer;
8013e95b9abSLydia Wang 	unsigned int parm;
8023e95b9abSLydia Wang 	int is_8ch = 0;
803bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
804bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
8053e95b9abSLydia Wang 		is_8ch = 1;
8063e95b9abSLydia Wang 
8073e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
8083e95b9abSLydia Wang 	imux_is_smixer =
8093e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
8103e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
8113e95b9abSLydia Wang 	/* inputs */
8123e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
8133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
8153e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
8163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
8173e95b9abSLydia Wang 	if (imux_is_smixer)
8183e95b9abSLydia Wang 		parm = AC_PWRST_D0;
8193e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
820054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
821054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
822054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
8233e95b9abSLydia Wang 
8243e95b9abSLydia Wang 	/* outputs */
8253e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
8263e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8273e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
828b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
8293e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
830054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
831054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
8323e95b9abSLydia Wang 
8333e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
8343e95b9abSLydia Wang 	if (is_8ch) {
8353e95b9abSLydia Wang 		parm = AC_PWRST_D3;
8363e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
837b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
8383e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
839054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
840054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
841bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
842bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
843bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
844bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
845b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
846bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
847054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
848054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8493e95b9abSLydia Wang 	}
8503e95b9abSLydia Wang 
8513e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8533e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8563e95b9abSLydia Wang 	if (is_8ch)
8573e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8583e95b9abSLydia Wang 
8593e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
860054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
861054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8623e95b9abSLydia Wang 	if (is_8ch) {
863054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
864054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
865b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
866054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8673e95b9abSLydia Wang }
8683e95b9abSLydia Wang 
869518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
870ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
871f7278fd0SJosepch Chan {
872f7278fd0SJosepch Chan 	struct via_spec *spec;
873f7278fd0SJosepch Chan 	int err;
874f7278fd0SJosepch Chan 
875518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
876518bf3baSLydia Wang 		return patch_vt1708S(codec);
877ddd304d8STakashi Iwai 
878f7278fd0SJosepch Chan 	/* create a codec specific record */
8795b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
880f7278fd0SJosepch Chan 	if (spec == NULL)
881f7278fd0SJosepch Chan 		return -ENOMEM;
882f7278fd0SJosepch Chan 
883b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
884620e2b28STakashi Iwai 
885f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
88612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
887f7278fd0SJosepch Chan 	if (err < 0) {
888f7278fd0SJosepch Chan 		via_free(codec);
889f7278fd0SJosepch Chan 		return err;
890f7278fd0SJosepch Chan 	}
891f7278fd0SJosepch Chan 
892f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
893f7278fd0SJosepch Chan 
8943e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8953e95b9abSLydia Wang 
896f7278fd0SJosepch Chan 	return 0;
897f7278fd0SJosepch Chan }
898f7278fd0SJosepch Chan 
899d949cac1SHarald Welte /* Patch for VT1708S */
900096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
901d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
902d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
903bc7e7e5cSLydia Wang 	/* don't bybass mixer */
904bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
905d949cac1SHarald Welte 	{ }
906d949cac1SHarald Welte };
907d949cac1SHarald Welte 
9086369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
9096369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
9106369bcfcSLydia Wang {
9116369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
9126369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
9136369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
9146369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
9156369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
9166369bcfcSLydia Wang }
9176369bcfcSLydia Wang 
918d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
919d949cac1SHarald Welte {
920d949cac1SHarald Welte 	struct via_spec *spec;
921d949cac1SHarald Welte 	int err;
922d949cac1SHarald Welte 
923d949cac1SHarald Welte 	/* create a codec specific record */
9245b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
925d949cac1SHarald Welte 	if (spec == NULL)
926d949cac1SHarald Welte 		return -ENOMEM;
927d949cac1SHarald Welte 
928b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
929d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
930d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
931620e2b28STakashi Iwai 
932518bf3baSLydia Wang 	/* correct names for VT1708BCE */
933518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
934518bf3baSLydia Wang 		kfree(codec->chip_name);
935518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
936518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
937518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
938518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
939970f630fSLydia Wang 	}
940bc92df7fSLydia Wang 	/* correct names for VT1705 */
941bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
942bc92df7fSLydia Wang 		kfree(codec->chip_name);
943bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
944bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
945bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
946bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
947bc92df7fSLydia Wang 	}
948b3f6008fSTakashi Iwai 
949b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
950b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
951b3f6008fSTakashi Iwai 	if (err < 0) {
952b3f6008fSTakashi Iwai 		via_free(codec);
953b3f6008fSTakashi Iwai 		return err;
954b3f6008fSTakashi Iwai 	}
955b3f6008fSTakashi Iwai 
956b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
957b3f6008fSTakashi Iwai 
958b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
959b3f6008fSTakashi Iwai 
9603e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
961d949cac1SHarald Welte 	return 0;
962d949cac1SHarald Welte }
963d949cac1SHarald Welte 
964d949cac1SHarald Welte /* Patch for VT1702 */
965d949cac1SHarald Welte 
966096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
967bc7e7e5cSLydia Wang 	/* mixer enable */
968bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
969bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
970bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
971d949cac1SHarald Welte 	{ }
972d949cac1SHarald Welte };
973d949cac1SHarald Welte 
9743e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9753e95b9abSLydia Wang {
9763e95b9abSLydia Wang 	int imux_is_smixer =
9773e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9783e95b9abSLydia Wang 	unsigned int parm;
9793e95b9abSLydia Wang 	/* inputs */
9803e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9813e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9853e95b9abSLydia Wang 	if (imux_is_smixer)
9863e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9873e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
988054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
989054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
990054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
991054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9923e95b9abSLydia Wang 
9933e95b9abSLydia Wang 	/* outputs */
9943e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9953e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9963e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9983e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
999054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
1000054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1001054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
10023e95b9abSLydia Wang }
10033e95b9abSLydia Wang 
1004d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
1005d949cac1SHarald Welte {
1006d949cac1SHarald Welte 	struct via_spec *spec;
1007d949cac1SHarald Welte 	int err;
1008d949cac1SHarald Welte 
1009d949cac1SHarald Welte 	/* create a codec specific record */
10105b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1011d949cac1SHarald Welte 	if (spec == NULL)
1012d949cac1SHarald Welte 		return -ENOMEM;
1013d949cac1SHarald Welte 
1014b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
1015620e2b28STakashi Iwai 
101612daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
101712daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
101812daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
101912daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
102012daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
102112daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
102212daef65STakashi Iwai 
1023d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
102412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1025d949cac1SHarald Welte 	if (err < 0) {
1026d949cac1SHarald Welte 		via_free(codec);
1027d949cac1SHarald Welte 		return err;
1028d949cac1SHarald Welte 	}
1029d949cac1SHarald Welte 
1030096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
1031d949cac1SHarald Welte 
1032d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
1033d949cac1SHarald Welte 
10343e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
1035d949cac1SHarald Welte 	return 0;
1036d949cac1SHarald Welte }
1037d949cac1SHarald Welte 
1038eb7188caSLydia Wang /* Patch for VT1718S */
1039eb7188caSLydia Wang 
1040096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10414ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10424ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1043eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1044eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10455d41762aSTakashi Iwai 
1046eb7188caSLydia Wang 	{ }
1047eb7188caSLydia Wang };
1048eb7188caSLydia Wang 
10493e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10503e95b9abSLydia Wang {
10513e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10523e95b9abSLydia Wang 	int imux_is_smixer;
10536162552bSTakashi Iwai 	unsigned int parm, parm2;
10543e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10553e95b9abSLydia Wang 	imux_is_smixer =
10563e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10573e95b9abSLydia Wang 	/* inputs */
10583e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10633e95b9abSLydia Wang 	if (imux_is_smixer)
10643e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10653e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1066054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1067054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1068054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1069054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10703e95b9abSLydia Wang 
10713e95b9abSLydia Wang 	/* outputs */
10723e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1075054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10766162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10773e95b9abSLydia Wang 
10783e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1081b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10823e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1083054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10843e95b9abSLydia Wang 
10853e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10863e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1088b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1090054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1091b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10926162552bSTakashi Iwai 		parm = parm2;
10936162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10943e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1095054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10963e95b9abSLydia Wang 
10973e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10983e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10993e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1100b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
11013e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1102054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
11033e95b9abSLydia Wang 
1104b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
11053e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
11063e95b9abSLydia Wang 		parm = AC_PWRST_D3;
11073e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1108054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1109054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1110054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
11113e95b9abSLydia Wang 	}
11123e95b9abSLydia Wang }
11133e95b9abSLydia Wang 
111430b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
111530b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
111630b45033STakashi Iwai  */
111730b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
111830b45033STakashi Iwai {
111930b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
112030b45033STakashi Iwai 	int i, nums;
112130b45033STakashi Iwai 	hda_nid_t conn[8];
112230b45033STakashi Iwai 	hda_nid_t nid;
112330b45033STakashi Iwai 
1124b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
112530b45033STakashi Iwai 		return 0;
1126b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
112730b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
112830b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
112930b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
113030b45033STakashi Iwai 			return 0;
113130b45033STakashi Iwai 	}
113230b45033STakashi Iwai 
113330b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
113430b45033STakashi Iwai 	nid = codec->start_nid;
113530b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
113630b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
113730b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
113830b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
113930b45033STakashi Iwai 			conn[nums++] = nid;
114030b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1141b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
114230b45033STakashi Iwai 							  nums, conn);
114330b45033STakashi Iwai 		}
114430b45033STakashi Iwai 	}
114530b45033STakashi Iwai 	return 0;
114630b45033STakashi Iwai }
114730b45033STakashi Iwai 
114830b45033STakashi Iwai 
1149eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1150eb7188caSLydia Wang {
1151eb7188caSLydia Wang 	struct via_spec *spec;
1152eb7188caSLydia Wang 	int err;
1153eb7188caSLydia Wang 
1154eb7188caSLydia Wang 	/* create a codec specific record */
11555b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1156eb7188caSLydia Wang 	if (spec == NULL)
1157eb7188caSLydia Wang 		return -ENOMEM;
1158eb7188caSLydia Wang 
1159b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1160d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1161d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
116230b45033STakashi Iwai 	add_secret_dac_path(codec);
1163620e2b28STakashi Iwai 
1164eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
116512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1166eb7188caSLydia Wang 	if (err < 0) {
1167eb7188caSLydia Wang 		via_free(codec);
1168eb7188caSLydia Wang 		return err;
1169eb7188caSLydia Wang 	}
1170eb7188caSLydia Wang 
1171096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1172eb7188caSLydia Wang 
1173eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1174eb7188caSLydia Wang 
11753e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11763e95b9abSLydia Wang 
1177eb7188caSLydia Wang 	return 0;
1178eb7188caSLydia Wang }
1179f3db423dSLydia Wang 
1180f3db423dSLydia Wang /* Patch for VT1716S */
1181f3db423dSLydia Wang 
1182f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1183f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1184f3db423dSLydia Wang {
1185f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1186f3db423dSLydia Wang 	uinfo->count = 1;
1187f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1188f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1189f3db423dSLydia Wang 	return 0;
1190f3db423dSLydia Wang }
1191f3db423dSLydia Wang 
1192f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1193f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1194f3db423dSLydia Wang {
1195f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1196f3db423dSLydia Wang 	int index = 0;
1197f3db423dSLydia Wang 
1198f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1199f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1200f3db423dSLydia Wang 	if (index != -1)
1201f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1202f3db423dSLydia Wang 
1203f3db423dSLydia Wang 	return 0;
1204f3db423dSLydia Wang }
1205f3db423dSLydia Wang 
1206f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1207f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1208f3db423dSLydia Wang {
1209f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1210f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1211f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1212f3db423dSLydia Wang 
1213f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1214f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1215f3db423dSLydia Wang 	spec->dmic_enabled = index;
12163e95b9abSLydia Wang 	set_widgets_power_state(codec);
1217f3db423dSLydia Wang 	return 1;
1218f3db423dSLydia Wang }
1219f3db423dSLydia Wang 
122090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1221f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1222f3db423dSLydia Wang 	{
1223f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1224f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
12255b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1226f3db423dSLydia Wang 	 .count = 1,
1227f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1228f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1229f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1230f3db423dSLydia Wang 	 },
1231f3db423dSLydia Wang 	{}			/* end */
1232f3db423dSLydia Wang };
1233f3db423dSLydia Wang 
1234f3db423dSLydia Wang 
1235f3db423dSLydia Wang /* mono-out mixer elements */
123690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1237f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1238f3db423dSLydia Wang 	{ } /* end */
1239f3db423dSLydia Wang };
1240f3db423dSLydia Wang 
1241096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1242f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1243f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1244f3db423dSLydia Wang 	/* don't bybass mixer */
1245f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1246f3db423dSLydia Wang 	/* Enable mono output */
1247f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1248f3db423dSLydia Wang 	{ }
1249f3db423dSLydia Wang };
1250f3db423dSLydia Wang 
12513e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12523e95b9abSLydia Wang {
12533e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12543e95b9abSLydia Wang 	int imux_is_smixer;
12553e95b9abSLydia Wang 	unsigned int parm;
12563e95b9abSLydia Wang 	unsigned int mono_out, present;
12573e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12583e95b9abSLydia Wang 	imux_is_smixer =
12593e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12603e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12613e95b9abSLydia Wang 	/* inputs */
12623e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12653e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12673e95b9abSLydia Wang 	if (imux_is_smixer)
12683e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12693e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1270054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1271054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12723e95b9abSLydia Wang 
12733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12753e95b9abSLydia Wang 	/* PW11 (22h) */
12763e95b9abSLydia Wang 	if (spec->dmic_enabled)
12773e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12783e95b9abSLydia Wang 	else
1279054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12803e95b9abSLydia Wang 
12813e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1282054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1283054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12843e95b9abSLydia Wang 
12853e95b9abSLydia Wang 	/* outputs */
12863e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12893e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1290b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12913e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1292054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1293054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12943e95b9abSLydia Wang 
12953e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12963e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12983e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1299b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
13003e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1301054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
13023e95b9abSLydia Wang 
13033e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1304b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
13053e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1306054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
13073e95b9abSLydia Wang 
13083e95b9abSLydia Wang 	/* Mono out */
13093e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
13103e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
13113e95b9abSLydia Wang 
13123e95b9abSLydia Wang 	if (present)
13133e95b9abSLydia Wang 		mono_out = 0;
13143e95b9abSLydia Wang 	else {
13153e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1316b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
13173e95b9abSLydia Wang 			mono_out = 0;
13183e95b9abSLydia Wang 		else
13193e95b9abSLydia Wang 			mono_out = 1;
13203e95b9abSLydia Wang 	}
13213e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1322054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1323054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1324054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
13253e95b9abSLydia Wang 
13263e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
13273e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
13293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
13303e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1331b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1332054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
13333e95b9abSLydia Wang 
13343e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
13353e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1336054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1337054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
13383e95b9abSLydia Wang }
13393e95b9abSLydia Wang 
1340f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1341f3db423dSLydia Wang {
1342f3db423dSLydia Wang 	struct via_spec *spec;
1343f3db423dSLydia Wang 	int err;
1344f3db423dSLydia Wang 
1345f3db423dSLydia Wang 	/* create a codec specific record */
13465b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1347f3db423dSLydia Wang 	if (spec == NULL)
1348f3db423dSLydia Wang 		return -ENOMEM;
1349f3db423dSLydia Wang 
1350b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1351d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1352d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1353620e2b28STakashi Iwai 
1354f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
135512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1356f3db423dSLydia Wang 	if (err < 0) {
1357f3db423dSLydia Wang 		via_free(codec);
1358f3db423dSLydia Wang 		return err;
1359f3db423dSLydia Wang 	}
1360f3db423dSLydia Wang 
1361096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1362f3db423dSLydia Wang 
1363b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1364f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1365f3db423dSLydia Wang 
1366f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1367f3db423dSLydia Wang 
13683e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1369f3db423dSLydia Wang 	return 0;
1370f3db423dSLydia Wang }
137125eaba2fSLydia Wang 
137225eaba2fSLydia Wang /* for vt2002P */
137325eaba2fSLydia Wang 
1374096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1375eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1376eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1377eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1378eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
137925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
138025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
138125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
138225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
138325eaba2fSLydia Wang 	{ }
138425eaba2fSLydia Wang };
13854a918ffeSTakashi Iwai 
1386096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
138711890956SLydia Wang 	/* Enable Boost Volume backdoor */
138811890956SLydia Wang 	{0x1, 0xfb9, 0x24},
138911890956SLydia Wang 	/* Enable AOW0 to MW9 */
139011890956SLydia Wang 	{0x1, 0xfb8, 0x88},
139111890956SLydia Wang 	{ }
139211890956SLydia Wang };
139325eaba2fSLydia Wang 
13943e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13953e95b9abSLydia Wang {
13963e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13973e95b9abSLydia Wang 	int imux_is_smixer;
13983e95b9abSLydia Wang 	unsigned int parm;
13993e95b9abSLydia Wang 	unsigned int present;
14003e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
14013e95b9abSLydia Wang 	imux_is_smixer =
14023e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
14033e95b9abSLydia Wang 	/* inputs */
14043e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
14053e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14063e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
14073e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
14083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
14093e95b9abSLydia Wang 	parm = AC_PWRST_D0;
14103e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1411054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1412054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1413054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1414054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
14153e95b9abSLydia Wang 
14163e95b9abSLydia Wang 	/* outputs */
14173e95b9abSLydia Wang 	/* AOW0 (8h)*/
1418054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
14193e95b9abSLydia Wang 
142011890956SLydia Wang 	if (spec->codec_type == VT1802) {
142111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
142211890956SLydia Wang 		parm = AC_PWRST_D3;
142311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1424054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1425054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
142611890956SLydia Wang 	} else {
14273e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
14283e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14293e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1430054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1431054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
143211890956SLydia Wang 	}
14333e95b9abSLydia Wang 
143411890956SLydia Wang 	if (spec->codec_type == VT1802) {
143511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
143611890956SLydia Wang 		parm = AC_PWRST_D3;
143711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1438054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1439054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
144011890956SLydia Wang 	} else {
14413e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14423e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14433e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1444054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1445054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
144611890956SLydia Wang 	}
14473e95b9abSLydia Wang 
1448b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1449054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14503e95b9abSLydia Wang 
14513e95b9abSLydia Wang 	/* Class-D */
14523e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14533e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14543e95b9abSLydia Wang 
14553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14573e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
145811890956SLydia Wang 	if (spec->codec_type == VT1802)
1459054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
146011890956SLydia Wang 	else
1461054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1462054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14633e95b9abSLydia Wang 
14643e95b9abSLydia Wang 	/* Mono Out */
14653e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14663e95b9abSLydia Wang 
14673e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
146811890956SLydia Wang 	if (spec->codec_type == VT1802) {
146911890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1470054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1471054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1472054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
147311890956SLydia Wang 	} else {
14743e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1475054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1476054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1477054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
147811890956SLydia Wang 	}
14793e95b9abSLydia Wang 	/* MW9 (21h) */
14803e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1481054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14823e95b9abSLydia Wang 	else
1483054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14843e95b9abSLydia Wang }
148525eaba2fSLydia Wang 
14864b527b65SDavid Henningsson /*
14874b527b65SDavid Henningsson  * pin fix-up
14884b527b65SDavid Henningsson  */
14894b527b65SDavid Henningsson enum {
14904b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1491d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14924b527b65SDavid Henningsson };
14934b527b65SDavid Henningsson 
14944b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14954b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14964b527b65SDavid Henningsson {
14974b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14984b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14994b527b65SDavid Henningsson }
15004b527b65SDavid Henningsson 
15014b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
15024b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
15034b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
15044b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
15054b527b65SDavid Henningsson 	},
1506d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1507d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1508d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1509d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1510d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1511d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1512d5266125STakashi Iwai 			{ }
1513d5266125STakashi Iwai 		}
1514d5266125STakashi Iwai 	},
15154b527b65SDavid Henningsson };
15164b527b65SDavid Henningsson 
15174b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1518d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
15194b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
15204b527b65SDavid Henningsson 	{}
15214b527b65SDavid Henningsson };
15224b527b65SDavid Henningsson 
1523ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1524ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1525ef4da458STakashi Iwai  */
1526ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1527ef4da458STakashi Iwai {
1528ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1529ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1530ef4da458STakashi Iwai 
1531ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1532ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1533ef4da458STakashi Iwai }
1534ef4da458STakashi Iwai 
153525eaba2fSLydia Wang /* patch for vt2002P */
153625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
153725eaba2fSLydia Wang {
153825eaba2fSLydia Wang 	struct via_spec *spec;
153925eaba2fSLydia Wang 	int err;
154025eaba2fSLydia Wang 
154125eaba2fSLydia Wang 	/* create a codec specific record */
15425b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
154325eaba2fSLydia Wang 	if (spec == NULL)
154425eaba2fSLydia Wang 		return -ENOMEM;
154525eaba2fSLydia Wang 
1546b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1547d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1548d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1549ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1550ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
155130b45033STakashi Iwai 	add_secret_dac_path(codec);
1552620e2b28STakashi Iwai 
15534b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15544b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15554b527b65SDavid Henningsson 
155625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
155712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
155825eaba2fSLydia Wang 	if (err < 0) {
155925eaba2fSLydia Wang 		via_free(codec);
156025eaba2fSLydia Wang 		return err;
156125eaba2fSLydia Wang 	}
156225eaba2fSLydia Wang 
156311890956SLydia Wang 	if (spec->codec_type == VT1802)
15644a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
156511890956SLydia Wang 	else
15664a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
156711890956SLydia Wang 
156825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
156925eaba2fSLydia Wang 
15703e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
157125eaba2fSLydia Wang 	return 0;
157225eaba2fSLydia Wang }
1573ab6734e7SLydia Wang 
1574ab6734e7SLydia Wang /* for vt1812 */
1575ab6734e7SLydia Wang 
1576096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1577ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1578ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1579ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1580ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1581ab6734e7SLydia Wang 	{ }
1582ab6734e7SLydia Wang };
1583ab6734e7SLydia Wang 
15843e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15853e95b9abSLydia Wang {
15863e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15873e95b9abSLydia Wang 	unsigned int parm;
15883e95b9abSLydia Wang 	unsigned int present;
15893e95b9abSLydia Wang 	/* inputs */
15903e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15913e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15923e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15943e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15953e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15963e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1597054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1598054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1599054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1600054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
16013e95b9abSLydia Wang 
16023e95b9abSLydia Wang 	/* outputs */
16033e95b9abSLydia Wang 	/* AOW0 (8h)*/
1604054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
16053e95b9abSLydia Wang 
16063e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
16073e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1609054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1610054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
16113e95b9abSLydia Wang 
16123e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
16133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1615054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1616054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1617b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1618054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
16193e95b9abSLydia Wang 
16203e95b9abSLydia Wang 	/* Internal Speaker */
16213e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
16223e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
16233e95b9abSLydia Wang 
16243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
16263e95b9abSLydia Wang 	if (present) {
1627054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1628054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
16293e95b9abSLydia Wang 	} else {
1630054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1631054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
16323e95b9abSLydia Wang 	}
16333e95b9abSLydia Wang 
16343e95b9abSLydia Wang 
16353e95b9abSLydia Wang 	/* Mono Out */
16363e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
16373e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
16383e95b9abSLydia Wang 
16393e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16413e95b9abSLydia Wang 	if (present) {
1642054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1643054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1644054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16453e95b9abSLydia Wang 	} else {
1646054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1647054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1648054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16493e95b9abSLydia Wang 	}
16503e95b9abSLydia Wang 
16513e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1654054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1655054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16563e95b9abSLydia Wang 
16573e95b9abSLydia Wang }
1658ab6734e7SLydia Wang 
1659ab6734e7SLydia Wang /* patch for vt1812 */
1660ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1661ab6734e7SLydia Wang {
1662ab6734e7SLydia Wang 	struct via_spec *spec;
1663ab6734e7SLydia Wang 	int err;
1664ab6734e7SLydia Wang 
1665ab6734e7SLydia Wang 	/* create a codec specific record */
16665b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1667ab6734e7SLydia Wang 	if (spec == NULL)
1668ab6734e7SLydia Wang 		return -ENOMEM;
1669ab6734e7SLydia Wang 
1670b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1671d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1672d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
167330b45033STakashi Iwai 	add_secret_dac_path(codec);
1674620e2b28STakashi Iwai 
1675ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
167612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1677ab6734e7SLydia Wang 	if (err < 0) {
1678ab6734e7SLydia Wang 		via_free(codec);
1679ab6734e7SLydia Wang 		return err;
1680ab6734e7SLydia Wang 	}
1681ab6734e7SLydia Wang 
1682096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1683ab6734e7SLydia Wang 
1684ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1685ab6734e7SLydia Wang 
16863e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1687ab6734e7SLydia Wang 	return 0;
1688ab6734e7SLydia Wang }
1689ab6734e7SLydia Wang 
169043737e0aSLydia Wang /* patch for vt3476 */
169143737e0aSLydia Wang 
169243737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
169343737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
169443737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
169543737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
169643737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
169743737e0aSLydia Wang 	/* Enable AOW-MW9 path */
169843737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
169943737e0aSLydia Wang 	{ }
170043737e0aSLydia Wang };
170143737e0aSLydia Wang 
170243737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
170343737e0aSLydia Wang {
170443737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
170543737e0aSLydia Wang 	int imux_is_smixer;
170643737e0aSLydia Wang 	unsigned int parm, parm2;
170743737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
170843737e0aSLydia Wang 	imux_is_smixer =
170943737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
171043737e0aSLydia Wang 	/* inputs */
171143737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
171243737e0aSLydia Wang 	parm = AC_PWRST_D3;
171343737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
171443737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
171543737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
171643737e0aSLydia Wang 	if (imux_is_smixer)
171743737e0aSLydia Wang 		parm = AC_PWRST_D0;
171843737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
171943737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
172043737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
172143737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
172243737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
172343737e0aSLydia Wang 
172443737e0aSLydia Wang 	/* outputs */
172543737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
172643737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
172743737e0aSLydia Wang 		parm = AC_PWRST_D3;
172843737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
172943737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
173043737e0aSLydia Wang 	}	else {
173143737e0aSLydia Wang 		parm = AC_PWRST_D3;
173243737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
173343737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
173443737e0aSLydia Wang 	}
173543737e0aSLydia Wang 
173643737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
173743737e0aSLydia Wang 	parm = AC_PWRST_D3;
173843737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
173943737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1740b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
174143737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
174243737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
174343737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
174443737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
174543737e0aSLydia Wang 	}
174643737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
174743737e0aSLydia Wang 
174843737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
174943737e0aSLydia Wang 	parm = AC_PWRST_D3;
175043737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
175143737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1752b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
175343737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
175443737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
175543737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
175643737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
175743737e0aSLydia Wang 	}
175843737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
175943737e0aSLydia Wang 
176043737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
176143737e0aSLydia Wang 	parm = AC_PWRST_D3;
176243737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
176343737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
176443737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1765b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
176643737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
176743737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
176843737e0aSLydia Wang 
176943737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
177043737e0aSLydia Wang 	parm = AC_PWRST_D3;
177143737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
177243737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1773b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
177443737e0aSLydia Wang 		parm = parm2;
177543737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
177643737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
177743737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
177843737e0aSLydia Wang }
177943737e0aSLydia Wang 
178043737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
178143737e0aSLydia Wang {
178243737e0aSLydia Wang 	struct via_spec *spec;
178343737e0aSLydia Wang 	int err;
178443737e0aSLydia Wang 
178543737e0aSLydia Wang 	/* create a codec specific record */
178643737e0aSLydia Wang 	spec = via_new_spec(codec);
178743737e0aSLydia Wang 	if (spec == NULL)
178843737e0aSLydia Wang 		return -ENOMEM;
178943737e0aSLydia Wang 
1790b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
179143737e0aSLydia Wang 	add_secret_dac_path(codec);
179243737e0aSLydia Wang 
179343737e0aSLydia Wang 	/* automatic parse from the BIOS config */
179443737e0aSLydia Wang 	err = via_parse_auto_config(codec);
179543737e0aSLydia Wang 	if (err < 0) {
179643737e0aSLydia Wang 		via_free(codec);
179743737e0aSLydia Wang 		return err;
179843737e0aSLydia Wang 	}
179943737e0aSLydia Wang 
180043737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
180143737e0aSLydia Wang 
180243737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
180343737e0aSLydia Wang 
180443737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
180543737e0aSLydia Wang 
180643737e0aSLydia Wang 	return 0;
180743737e0aSLydia Wang }
180843737e0aSLydia Wang 
1809c577b8a1SJoseph Chan /*
1810c577b8a1SJoseph Chan  * patch entries
1811c577b8a1SJoseph Chan  */
181290dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
18133218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
18143218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
18153218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
18163218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
18173218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1818ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18193218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1820ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18213218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1822ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18233218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1824ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18253218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1826ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18273218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1828ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18293218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1830ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18313218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1832ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18333218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1834ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18353218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1836ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18373218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1838ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18393218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1840ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18413218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1842ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18433218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1844ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18453218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1846ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18473218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1848ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18493218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1850d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18513218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1852d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18533218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1854d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18553218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1856d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1857bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1858d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18593218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1860d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18613218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1862d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18633218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1864d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18653218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1866d949cac1SHarald Welte 	  .patch = patch_vt1702},
18673218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1868d949cac1SHarald Welte 	  .patch = patch_vt1702},
18693218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1870d949cac1SHarald Welte 	  .patch = patch_vt1702},
18713218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1872d949cac1SHarald Welte 	  .patch = patch_vt1702},
18733218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1874d949cac1SHarald Welte 	  .patch = patch_vt1702},
18753218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1876d949cac1SHarald Welte 	  .patch = patch_vt1702},
18773218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1878d949cac1SHarald Welte 	  .patch = patch_vt1702},
18793218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1880d949cac1SHarald Welte 	  .patch = patch_vt1702},
1881eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1882eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1883eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1884eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1885bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1886bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1887bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1888bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1889f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1890f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1891f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1892f3db423dSLydia Wang 	  .patch = patch_vt1716S},
189325eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
189425eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1895ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
189636dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
189736dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
189811890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
189911890956SLydia Wang 		.patch = patch_vt2002P},
190011890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
190111890956SLydia Wang 		.patch = patch_vt2002P},
190243737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
190343737e0aSLydia Wang 		.patch = patch_vt3476},
19046121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
19056121b84aSLydia Wang 		.patch = patch_vt3476},
19066121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
19076121b84aSLydia Wang 		.patch = patch_vt3476},
1908c577b8a1SJoseph Chan 	{} /* terminator */
1909c577b8a1SJoseph Chan };
19101289e9e8STakashi Iwai 
19111289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
19121289e9e8STakashi Iwai 
19131289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
19141289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
19151289e9e8STakashi Iwai 	.owner = THIS_MODULE,
19161289e9e8STakashi Iwai };
19171289e9e8STakashi Iwai 
19181289e9e8STakashi Iwai MODULE_LICENSE("GPL");
19191289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
19201289e9e8STakashi Iwai 
19211289e9e8STakashi Iwai static int __init patch_via_init(void)
19221289e9e8STakashi Iwai {
19231289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
19241289e9e8STakashi Iwai }
19251289e9e8STakashi Iwai 
19261289e9e8STakashi Iwai static void __exit patch_via_exit(void)
19271289e9e8STakashi Iwai {
19281289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
19291289e9e8STakashi Iwai }
19301289e9e8STakashi Iwai 
19311289e9e8STakashi Iwai module_init(patch_via_init)
19321289e9e8STakashi Iwai module_exit(patch_via_exit)
1933