xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision b9a94a9c787d053e8d3bb7e7dff9648e723a4533)
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;
1021f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1031f2e99feSLydia Wang 
104e9d010c2STakashi Iwai 	/* analog low-power control */
105e9d010c2STakashi Iwai 	bool alc_mode;
106e9d010c2STakashi Iwai 
1071f2e99feSLydia Wang 	/* work to check hp jack state */
108187d333eSTakashi Iwai 	int hp_work_active;
109e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1104738465cSW. Trevor King 
1114738465cSW. Trevor King 	unsigned int beep_amp;
1121f2e99feSLydia Wang };
1131f2e99feSLydia Wang 
1140341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
115b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
116b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
117b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
118b3f6008fSTakashi Iwai 				  int action);
119b3f6008fSTakashi Iwai 
120225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */
121225068abSTakashi Iwai 
1225b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1235b0cb1d8SJaroslav Kysela {
1245b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1255b0cb1d8SJaroslav Kysela 
1265b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1275b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1285b0cb1d8SJaroslav Kysela 		return NULL;
1295b0cb1d8SJaroslav Kysela 
1305b0cb1d8SJaroslav Kysela 	codec->spec = spec;
131b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1320341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1330341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1340341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1350341ccd7SLydia Wang 		spec->codec_type = VT1708S;
13613961170STakashi Iwai 	spec->gen.indep_hp = 1;
13705909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
138b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
13974f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
140967b1307STakashi Iwai 	codec->power_save_node = 1;
141688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 1;
142225068abSTakashi Iwai 	codec->patch_ops = via_patch_ops;
1435b0cb1d8SJaroslav Kysela 	return spec;
1445b0cb1d8SJaroslav Kysela }
1455b0cb1d8SJaroslav Kysela 
146744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
147d7426329SHarald Welte {
1487639a06cSTakashi Iwai 	u32 vendor_id = codec->core.vendor_id;
149d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
150d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
151d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
152d7426329SHarald Welte 
153d7426329SHarald Welte 	/* get codec type */
154d7426329SHarald Welte 	if (ven_id != 0x1106)
155d7426329SHarald Welte 		codec_type = UNKNOWN;
156d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
157d7426329SHarald Welte 		codec_type = VT1708;
158d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
159d7426329SHarald Welte 		codec_type = VT1709_10CH;
160d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
161d7426329SHarald Welte 		codec_type = VT1709_6CH;
162518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
163d7426329SHarald Welte 		codec_type = VT1708B_8CH;
164518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
165518bf3baSLydia Wang 			codec_type = VT1708BCE;
166518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
167d7426329SHarald Welte 		codec_type = VT1708B_4CH;
168d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
169d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
170d7426329SHarald Welte 		codec_type = VT1708S;
171d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
172d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
173d7426329SHarald Welte 		codec_type = VT1702;
174eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
175eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
176eb7188caSLydia Wang 		codec_type = VT1718S;
177f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
178f3db423dSLydia Wang 		codec_type = VT1716S;
179bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
180bb3c6bfcSLydia Wang 		codec_type = VT1718S;
18125eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
18225eaba2fSLydia Wang 		codec_type = VT2002P;
183ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
184ab6734e7SLydia Wang 		codec_type = VT1812;
18536dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18636dd5c4aSLydia Wang 		codec_type = VT1708S;
18711890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18811890956SLydia Wang 		codec_type = VT1802;
18943737e0aSLydia Wang 	else if (dev_id == 0x4760)
19043737e0aSLydia Wang 		codec_type = VT1705CF;
1916121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1926121b84aSLydia Wang 		codec_type = VT1808;
193d7426329SHarald Welte 	else
194d7426329SHarald Welte 		codec_type = UNKNOWN;
195d7426329SHarald Welte 	return codec_type;
196d7426329SHarald Welte };
197d7426329SHarald Welte 
198ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
199ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
2001f2e99feSLydia Wang 
201187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
202187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
203187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2041f2e99feSLydia Wang 
205b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2061f2e99feSLydia Wang {
207b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
208b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2091f2e99feSLydia Wang 		return;
210187d333eSTakashi Iwai 	if (spec->hp_work_active) {
211b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
2127eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
213b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
214b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
215187d333eSTakashi Iwai 	}
216187d333eSTakashi Iwai }
217187d333eSTakashi Iwai 
218b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
219187d333eSTakashi Iwai {
220b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
221b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
222187d333eSTakashi Iwai 		return;
22305dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
224187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
225b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
226b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2272f35c630STakashi Iwai 			schedule_delayed_work(&codec->jackpoll_work, 0);
228b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
229187d333eSTakashi Iwai 		}
230b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
231b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2321f2e99feSLydia Wang }
233f5271101SLydia Wang 
23424088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
23524088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
23624088a58STakashi Iwai {
237dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
23824088a58STakashi Iwai }
23924088a58STakashi Iwai 
24024088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
24124088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
24224088a58STakashi Iwai {
24324088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
244735c75cfSTakashi Iwai 	struct via_spec *spec = codec->spec;
245735c75cfSTakashi Iwai 
246735c75cfSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
24724088a58STakashi Iwai 	return 0;
24824088a58STakashi Iwai }
24924088a58STakashi Iwai 
25024088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
25124088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
25224088a58STakashi Iwai {
25324088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
25424088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
255688b12ccSTakashi Iwai 	bool val = !!ucontrol->value.enumerated.item[0];
25624088a58STakashi Iwai 
257735c75cfSTakashi Iwai 	if (val == spec->gen.power_down_unused)
25824088a58STakashi Iwai 		return 0;
259735c75cfSTakashi Iwai 	/* codec->power_save_node = val; */ /* widget PM seems yet broken */
260688b12ccSTakashi Iwai 	spec->gen.power_down_unused = val;
261e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
26224088a58STakashi Iwai 	return 1;
26324088a58STakashi Iwai }
26424088a58STakashi Iwai 
265b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
266b3f6008fSTakashi Iwai 	{
26724088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26824088a58STakashi Iwai 	.name = "Dynamic Power-Control",
26924088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
27024088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
27124088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
272b3f6008fSTakashi Iwai 	},
273b3f6008fSTakashi Iwai 	{} /* terminator */
27424088a58STakashi Iwai };
27524088a58STakashi Iwai 
2764738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP
2774738465cSW. Trevor King static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid,
2784738465cSW. Trevor King 				int idx, int dir)
2794738465cSW. Trevor King {
2804738465cSW. Trevor King 	spec->gen.beep_nid = nid;
2814738465cSW. Trevor King 	spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
2824738465cSW. Trevor King }
2834738465cSW. Trevor King 
2844738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */
2854738465cSW. Trevor King static const struct snd_kcontrol_new cxt_beep_mixer[] = {
2864738465cSW. Trevor King 	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
2874738465cSW. Trevor King 	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
2884738465cSW. Trevor King 	{ } /* end */
2894738465cSW. Trevor King };
2904738465cSW. Trevor King 
2914738465cSW. Trevor King /* create beep controls if needed */
2924738465cSW. Trevor King static int add_beep_ctls(struct hda_codec *codec)
2934738465cSW. Trevor King {
2944738465cSW. Trevor King 	struct via_spec *spec = codec->spec;
2954738465cSW. Trevor King 	int err;
2964738465cSW. Trevor King 
2974738465cSW. Trevor King 	if (spec->beep_amp) {
2984738465cSW. Trevor King 		const struct snd_kcontrol_new *knew;
2994738465cSW. Trevor King 		for (knew = cxt_beep_mixer; knew->name; knew++) {
3004738465cSW. Trevor King 			struct snd_kcontrol *kctl;
3014738465cSW. Trevor King 			kctl = snd_ctl_new1(knew, codec);
3024738465cSW. Trevor King 			if (!kctl)
3034738465cSW. Trevor King 				return -ENOMEM;
3044738465cSW. Trevor King 			kctl->private_value = spec->beep_amp;
3054738465cSW. Trevor King 			err = snd_hda_ctl_add(codec, 0, kctl);
3064738465cSW. Trevor King 			if (err < 0)
3074738465cSW. Trevor King 				return err;
3084738465cSW. Trevor King 		}
3094738465cSW. Trevor King 	}
3104738465cSW. Trevor King 	return 0;
3114738465cSW. Trevor King }
3124738465cSW. Trevor King 
3134738465cSW. Trevor King static void auto_parse_beep(struct hda_codec *codec)
3144738465cSW. Trevor King {
3154738465cSW. Trevor King 	struct via_spec *spec = codec->spec;
3164738465cSW. Trevor King 	hda_nid_t nid;
3174738465cSW. Trevor King 
3184738465cSW. Trevor King 	for_each_hda_codec_node(nid, codec)
3194738465cSW. Trevor King 		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) {
3204738465cSW. Trevor King 			set_beep_amp(spec, nid, 0, HDA_OUTPUT);
3214738465cSW. Trevor King 			break;
3224738465cSW. Trevor King 		}
3234738465cSW. Trevor King }
3244738465cSW. Trevor King #else
3254738465cSW. Trevor King #define set_beep_amp(spec, nid, idx, dir) /* NOP */
3264738465cSW. Trevor King #define add_beep_ctls(codec)	0
3274738465cSW. Trevor King #define auto_parse_beep(codec)
3284738465cSW. Trevor King #endif
32924088a58STakashi Iwai 
330f5271101SLydia Wang /* check AA path's mute status */
331ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
332ada509ecSTakashi Iwai {
333ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
334ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
3350186f4f4STakashi Iwai 	int ch, v;
336ada509ecSTakashi Iwai 
3370186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
3380186f4f4STakashi Iwai 	if (!p)
3390186f4f4STakashi Iwai 		return true;
3400186f4f4STakashi Iwai 	for (; p->nid; p++) {
341ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
342ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
343ada509ecSTakashi Iwai 						   p->idx);
344ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
345ada509ecSTakashi Iwai 				return false;
346f5271101SLydia Wang 		}
347f5271101SLydia Wang 	}
348ada509ecSTakashi Iwai 	return true;
349f5271101SLydia Wang }
350f5271101SLydia Wang 
351f5271101SLydia Wang /* enter/exit analog low-current mode */
352e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
353f5271101SLydia Wang {
354f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
355ada509ecSTakashi Iwai 	bool enable;
356ada509ecSTakashi Iwai 	unsigned int verb, parm;
357f5271101SLydia Wang 
358967b1307STakashi Iwai 	if (!codec->power_save_node)
359e9d010c2STakashi Iwai 		enable = false;
360e9d010c2STakashi Iwai 	else
361b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
362e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
363e9d010c2STakashi Iwai 		return;
364e9d010c2STakashi Iwai 	spec->alc_mode = enable;
365f5271101SLydia Wang 
366f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
367f5271101SLydia Wang 	switch (spec->codec_type) {
368f5271101SLydia Wang 	case VT1708B_8CH:
369f5271101SLydia Wang 	case VT1708B_4CH:
370f5271101SLydia Wang 		verb = 0xf70;
371f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
372f5271101SLydia Wang 		break;
373f5271101SLydia Wang 	case VT1708S:
374eb7188caSLydia Wang 	case VT1718S:
375f3db423dSLydia Wang 	case VT1716S:
376f5271101SLydia Wang 		verb = 0xf73;
377f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
378f5271101SLydia Wang 		break;
379f5271101SLydia Wang 	case VT1702:
380f5271101SLydia Wang 		verb = 0xf73;
381f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
382f5271101SLydia Wang 		break;
38325eaba2fSLydia Wang 	case VT2002P:
384ab6734e7SLydia Wang 	case VT1812:
38511890956SLydia Wang 	case VT1802:
38625eaba2fSLydia Wang 		verb = 0xf93;
38725eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
38825eaba2fSLydia Wang 		break;
38943737e0aSLydia Wang 	case VT1705CF:
3906121b84aSLydia Wang 	case VT1808:
39143737e0aSLydia Wang 		verb = 0xf82;
39243737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
39343737e0aSLydia Wang 		break;
394f5271101SLydia Wang 	default:
395f5271101SLydia Wang 		return;		/* other codecs are not supported */
396f5271101SLydia Wang 	}
397f5271101SLydia Wang 	/* send verb */
3987639a06cSTakashi Iwai 	snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
399f5271101SLydia Wang }
400f5271101SLydia Wang 
401e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
402e9d010c2STakashi Iwai {
403e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
404e9d010c2STakashi Iwai }
405e9d010c2STakashi Iwai 
406c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
407c577b8a1SJoseph Chan {
408c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4095b0cb1d8SJaroslav Kysela 	int err, i;
410c577b8a1SJoseph Chan 
411b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
412b3f6008fSTakashi Iwai 	if (err < 0)
413b3f6008fSTakashi Iwai 		return err;
414b3f6008fSTakashi Iwai 
4154738465cSW. Trevor King 	err = add_beep_ctls(codec);
4164738465cSW. Trevor King 	if (err < 0)
4174738465cSW. Trevor King 		return err;
4184738465cSW. Trevor King 
419b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
42024088a58STakashi Iwai 
421c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
422c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
423c577b8a1SJoseph Chan 		if (err < 0)
424c577b8a1SJoseph Chan 			return err;
425c577b8a1SJoseph Chan 	}
426c577b8a1SJoseph Chan 
427c577b8a1SJoseph Chan 	return 0;
428c577b8a1SJoseph Chan }
429c577b8a1SJoseph Chan 
430b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
431b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
432b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
433b3f6008fSTakashi Iwai 				  int action)
434c577b8a1SJoseph Chan {
435b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
436b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
437c577b8a1SJoseph Chan }
438c577b8a1SJoseph Chan 
439c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
440c577b8a1SJoseph Chan {
441b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
442a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
443c577b8a1SJoseph Chan }
444c577b8a1SJoseph Chan 
4452a43952aSTakashi Iwai #ifdef CONFIG_PM
44668cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4471f2e99feSLydia Wang {
4481f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
449b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
45094c142a1SDavid Henningsson 
45194c142a1SDavid Henningsson 	/* Fix pop noise on headphones */
4522c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
4532c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
45494c142a1SDavid Henningsson 
4551f2e99feSLydia Wang 	return 0;
4561f2e99feSLydia Wang }
4576b6d0007STakashi Iwai 
4586b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec)
4596b6d0007STakashi Iwai {
4606b6d0007STakashi Iwai 	/* some delay here to make jack detection working (bko#98921) */
4616b6d0007STakashi Iwai 	msleep(10);
4626b6d0007STakashi Iwai 	codec->patch_ops.init(codec);
4636b6d0007STakashi Iwai 	regcache_sync(codec->core.regmap);
4646b6d0007STakashi Iwai 	return 0;
4656b6d0007STakashi Iwai }
4661f2e99feSLydia Wang #endif
4671f2e99feSLydia Wang 
46883012a7cSTakashi Iwai #ifdef CONFIG_PM
469cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
470cb53c626STakashi Iwai {
471cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
472b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
473b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
474b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
475cb53c626STakashi Iwai }
476cb53c626STakashi Iwai #endif
477cb53c626STakashi Iwai 
478c577b8a1SJoseph Chan /*
479c577b8a1SJoseph Chan  */
4805d41762aSTakashi Iwai 
4815d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4825d41762aSTakashi Iwai 
48390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
484c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
485b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
486c577b8a1SJoseph Chan 	.init = via_init,
487c577b8a1SJoseph Chan 	.free = via_free,
4884e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
4892a43952aSTakashi Iwai #ifdef CONFIG_PM
4901f2e99feSLydia Wang 	.suspend = via_suspend,
4916b6d0007STakashi Iwai 	.resume = via_resume,
492cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
493cb53c626STakashi Iwai #endif
494c577b8a1SJoseph Chan };
495c577b8a1SJoseph Chan 
4964a79616dSTakashi Iwai 
497b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
498b3f6008fSTakashi Iwai 	/* power down jack detect function */
499b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
500b3f6008fSTakashi Iwai 	{ }
5014a79616dSTakashi Iwai };
50276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
50376d9b0ddSHarald Welte {
50476d9b0ddSHarald Welte 	unsigned int def_conf;
50576d9b0ddSHarald Welte 	unsigned char seqassoc;
50676d9b0ddSHarald Welte 
5072f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
50876d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
50976d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
51082ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
51182ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
51276d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5132f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
51476d9b0ddSHarald Welte 	}
51576d9b0ddSHarald Welte 
51676d9b0ddSHarald Welte 	return;
51776d9b0ddSHarald Welte }
51876d9b0ddSHarald Welte 
519e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5201f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5211f2e99feSLydia Wang {
5221f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5231f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5241f2e99feSLydia Wang 
5251f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5261f2e99feSLydia Wang 		return 0;
527e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5281f2e99feSLydia Wang 	return 0;
5291f2e99feSLydia Wang }
5301f2e99feSLydia Wang 
531e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5321f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5331f2e99feSLydia Wang {
5341f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5351f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
536187d333eSTakashi Iwai 	int val;
5371f2e99feSLydia Wang 
5381f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5391f2e99feSLydia Wang 		return 0;
540187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
541187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
542187d333eSTakashi Iwai 		return 0;
543187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
544b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
545187d333eSTakashi Iwai 	return 1;
5461f2e99feSLydia Wang }
5471f2e99feSLydia Wang 
548b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
549b3f6008fSTakashi Iwai 	{
5501f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5511f2e99feSLydia Wang 	.name = "Jack Detect",
5521f2e99feSLydia Wang 	.count = 1,
5531f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
554e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
555e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
556b3f6008fSTakashi Iwai 	},
557b3f6008fSTakashi Iwai 	{} /* terminator */
5581f2e99feSLydia Wang };
5591f2e99feSLydia Wang 
5604abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
5614abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
5624abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5634abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
5644abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5654abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5664abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
5674abdbd1cSTakashi Iwai };
5684abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
5694abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
5704abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5714abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
5724abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5734abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5744abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
5754abdbd1cSTakashi Iwai };
5764abdbd1cSTakashi Iwai 
577b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
578b3f6008fSTakashi Iwai {
579b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
580b3f6008fSTakashi Iwai 	int err;
581b3f6008fSTakashi Iwai 
5824abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
5834abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
5844abdbd1cSTakashi Iwai 
585b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
586b3f6008fSTakashi Iwai 	if (err < 0)
587b3f6008fSTakashi Iwai 		return err;
588b3f6008fSTakashi Iwai 
5894738465cSW. Trevor King 	auto_parse_beep(codec);
5904738465cSW. Trevor King 
591b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
592b3f6008fSTakashi Iwai 	if (err < 0)
593b3f6008fSTakashi Iwai 		return err;
594b3f6008fSTakashi Iwai 
595688b12ccSTakashi Iwai 	/* disable widget PM at start for compatibility */
596967b1307STakashi Iwai 	codec->power_save_node = 0;
597688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 0;
598b3f6008fSTakashi Iwai 	return 0;
5994a918ffeSTakashi Iwai }
6004a918ffeSTakashi Iwai 
6015d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6025d41762aSTakashi Iwai {
6035d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6045d41762aSTakashi Iwai 	int i;
6055d41762aSTakashi Iwai 
6065d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6075d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6085d41762aSTakashi Iwai 
609e9d010c2STakashi Iwai 	/* init power states */
610e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
611e9d010c2STakashi Iwai 
612b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
61311890956SLydia Wang 
614b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
61525eaba2fSLydia Wang 
616c577b8a1SJoseph Chan 	return 0;
617c577b8a1SJoseph Chan }
618c577b8a1SJoseph Chan 
619f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
620f672f65aSDavid Henningsson {
621f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
622f672f65aSDavid Henningsson 	   temporary enable jackpoll */
623f672f65aSDavid Henningsson 	int err;
624f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
625f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
626f672f65aSDavid Henningsson 	err = via_build_controls(codec);
627f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
628f672f65aSDavid Henningsson 	return err;
629f672f65aSDavid Henningsson }
630f672f65aSDavid Henningsson 
631b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
632337b9d02STakashi Iwai {
633337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
634b3f6008fSTakashi Iwai 	int i, err;
635337b9d02STakashi Iwai 
636b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
6377639a06cSTakashi Iwai 	if (err < 0 || codec->core.vendor_id != 0x11061708)
638b3f6008fSTakashi Iwai 		return err;
639b3f6008fSTakashi Iwai 
640b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
641b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
642b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
643b3f6008fSTakashi Iwai 	 */
644bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
645bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
646bbbc7e85STakashi Iwai 		if (!info)
647bbbc7e85STakashi Iwai 			continue;
648b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
649b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
650b3f6008fSTakashi Iwai 			continue;
651b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
652b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
653337b9d02STakashi Iwai 	}
654b3f6008fSTakashi Iwai 
6551c55d521STakashi Iwai 	return 0;
656337b9d02STakashi Iwai }
657337b9d02STakashi Iwai 
658c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
659c577b8a1SJoseph Chan {
660c577b8a1SJoseph Chan 	struct via_spec *spec;
661c577b8a1SJoseph Chan 	int err;
662c577b8a1SJoseph Chan 
663c577b8a1SJoseph Chan 	/* create a codec specific record */
6645b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
665c577b8a1SJoseph Chan 	if (spec == NULL)
666c577b8a1SJoseph Chan 		return -ENOMEM;
667c577b8a1SJoseph Chan 
668225068abSTakashi Iwai 	/* override some patch_ops */
669225068abSTakashi Iwai 	codec->patch_ops.build_controls = vt1708_build_controls;
670225068abSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
671b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
672b3f6008fSTakashi Iwai 
673b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
674b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
675b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
676b3f6008fSTakashi Iwai 
677b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
678b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
679b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
680eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
681eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
682620e2b28STakashi Iwai 
68312daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
68412daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
68512daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
68612daef65STakashi Iwai 
687c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
68812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
689c577b8a1SJoseph Chan 	if (err < 0) {
690c577b8a1SJoseph Chan 		via_free(codec);
691c577b8a1SJoseph Chan 		return err;
692c577b8a1SJoseph Chan 	}
693c577b8a1SJoseph Chan 
69412daef65STakashi Iwai 	/* add jack detect on/off control */
695b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
696c577b8a1SJoseph Chan 
697e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
698e322a36dSLydia Wang 
699b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
700b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
701b3f6008fSTakashi Iwai 
702c577b8a1SJoseph Chan 	return 0;
703c577b8a1SJoseph Chan }
704c577b8a1SJoseph Chan 
705ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
706c577b8a1SJoseph Chan {
707c577b8a1SJoseph Chan 	struct via_spec *spec;
708c577b8a1SJoseph Chan 	int err;
709c577b8a1SJoseph Chan 
710c577b8a1SJoseph Chan 	/* create a codec specific record */
7115b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
712c577b8a1SJoseph Chan 	if (spec == NULL)
713c577b8a1SJoseph Chan 		return -ENOMEM;
714c577b8a1SJoseph Chan 
715b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
716620e2b28STakashi Iwai 
71712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
718c577b8a1SJoseph Chan 	if (err < 0) {
719c577b8a1SJoseph Chan 		via_free(codec);
720c577b8a1SJoseph Chan 		return err;
721c577b8a1SJoseph Chan 	}
722c577b8a1SJoseph Chan 
723f7278fd0SJosepch Chan 	return 0;
724f7278fd0SJosepch Chan }
725f7278fd0SJosepch Chan 
726518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
727ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
728f7278fd0SJosepch Chan {
729f7278fd0SJosepch Chan 	struct via_spec *spec;
730f7278fd0SJosepch Chan 	int err;
731f7278fd0SJosepch Chan 
732518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
733518bf3baSLydia Wang 		return patch_vt1708S(codec);
734ddd304d8STakashi Iwai 
735f7278fd0SJosepch Chan 	/* create a codec specific record */
7365b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
737f7278fd0SJosepch Chan 	if (spec == NULL)
738f7278fd0SJosepch Chan 		return -ENOMEM;
739f7278fd0SJosepch Chan 
740b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
741620e2b28STakashi Iwai 
742f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
74312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
744f7278fd0SJosepch Chan 	if (err < 0) {
745f7278fd0SJosepch Chan 		via_free(codec);
746f7278fd0SJosepch Chan 		return err;
747f7278fd0SJosepch Chan 	}
748f7278fd0SJosepch Chan 
749f7278fd0SJosepch Chan 	return 0;
750f7278fd0SJosepch Chan }
751f7278fd0SJosepch Chan 
752d949cac1SHarald Welte /* Patch for VT1708S */
753096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
754d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
755d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
756bc7e7e5cSLydia Wang 	/* don't bybass mixer */
757bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
758d949cac1SHarald Welte 	{ }
759d949cac1SHarald Welte };
760d949cac1SHarald Welte 
7616369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
7626369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
7636369bcfcSLydia Wang {
764d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
765d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
7666369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
7676369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
7686369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
7696369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
7706369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
7716369bcfcSLydia Wang }
7726369bcfcSLydia Wang 
773d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
774d949cac1SHarald Welte {
775d949cac1SHarald Welte 	struct via_spec *spec;
776d949cac1SHarald Welte 	int err;
777d949cac1SHarald Welte 
778d949cac1SHarald Welte 	/* create a codec specific record */
7795b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
780d949cac1SHarald Welte 	if (spec == NULL)
781d949cac1SHarald Welte 		return -ENOMEM;
782d949cac1SHarald Welte 
783b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
784d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
785d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
786620e2b28STakashi Iwai 
787518bf3baSLydia Wang 	/* correct names for VT1708BCE */
788ded255beSTakashi Iwai 	if (get_codec_type(codec) == VT1708BCE)
789ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1708BCE");
790bc92df7fSLydia Wang 	/* correct names for VT1705 */
791ded255beSTakashi Iwai 	if (codec->core.vendor_id == 0x11064397)
792ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1705");
793b3f6008fSTakashi Iwai 
794b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
795b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
796b3f6008fSTakashi Iwai 	if (err < 0) {
797b3f6008fSTakashi Iwai 		via_free(codec);
798b3f6008fSTakashi Iwai 		return err;
799b3f6008fSTakashi Iwai 	}
800b3f6008fSTakashi Iwai 
801b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
802b3f6008fSTakashi Iwai 
803d949cac1SHarald Welte 	return 0;
804d949cac1SHarald Welte }
805d949cac1SHarald Welte 
806d949cac1SHarald Welte /* Patch for VT1702 */
807d949cac1SHarald Welte 
808096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
809bc7e7e5cSLydia Wang 	/* mixer enable */
810bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
811bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
812bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
813d949cac1SHarald Welte 	{ }
814d949cac1SHarald Welte };
815d949cac1SHarald Welte 
816d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
817d949cac1SHarald Welte {
818d949cac1SHarald Welte 	struct via_spec *spec;
819d949cac1SHarald Welte 	int err;
820d949cac1SHarald Welte 
821d949cac1SHarald Welte 	/* create a codec specific record */
8225b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
823d949cac1SHarald Welte 	if (spec == NULL)
824d949cac1SHarald Welte 		return -ENOMEM;
825d949cac1SHarald Welte 
826b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
827620e2b28STakashi Iwai 
82812daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
82912daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
83012daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
83112daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
83212daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
83312daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
83412daef65STakashi Iwai 
835d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
83612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
837d949cac1SHarald Welte 	if (err < 0) {
838d949cac1SHarald Welte 		via_free(codec);
839d949cac1SHarald Welte 		return err;
840d949cac1SHarald Welte 	}
841d949cac1SHarald Welte 
842096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
843d949cac1SHarald Welte 
844d949cac1SHarald Welte 	return 0;
845d949cac1SHarald Welte }
846d949cac1SHarald Welte 
847eb7188caSLydia Wang /* Patch for VT1718S */
848eb7188caSLydia Wang 
849096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
8504ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
8514ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
852eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
853eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
8545d41762aSTakashi Iwai 
855eb7188caSLydia Wang 	{ }
856eb7188caSLydia Wang };
857eb7188caSLydia Wang 
85830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
85930b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
86030b45033STakashi Iwai  */
86130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
86230b45033STakashi Iwai {
86330b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
86430b45033STakashi Iwai 	int i, nums;
86530b45033STakashi Iwai 	hda_nid_t conn[8];
86630b45033STakashi Iwai 	hda_nid_t nid;
86730b45033STakashi Iwai 
868b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
86930b45033STakashi Iwai 		return 0;
870b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
87130b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
87230b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
87330b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
87430b45033STakashi Iwai 			return 0;
87530b45033STakashi Iwai 	}
87630b45033STakashi Iwai 
87730b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
8787639a06cSTakashi Iwai 	for_each_hda_codec_node(nid, codec) {
87930b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
88030b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
88130b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
88230b45033STakashi Iwai 			conn[nums++] = nid;
88330b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
884b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
88530b45033STakashi Iwai 							  nums, conn);
88630b45033STakashi Iwai 		}
88730b45033STakashi Iwai 	}
88830b45033STakashi Iwai 	return 0;
88930b45033STakashi Iwai }
89030b45033STakashi Iwai 
89130b45033STakashi Iwai 
892eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
893eb7188caSLydia Wang {
894eb7188caSLydia Wang 	struct via_spec *spec;
895eb7188caSLydia Wang 	int err;
896eb7188caSLydia Wang 
897eb7188caSLydia Wang 	/* create a codec specific record */
8985b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
899eb7188caSLydia Wang 	if (spec == NULL)
900eb7188caSLydia Wang 		return -ENOMEM;
901eb7188caSLydia Wang 
902b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
903d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
904d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
90530b45033STakashi Iwai 	add_secret_dac_path(codec);
906620e2b28STakashi Iwai 
907eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
90812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
909eb7188caSLydia Wang 	if (err < 0) {
910eb7188caSLydia Wang 		via_free(codec);
911eb7188caSLydia Wang 		return err;
912eb7188caSLydia Wang 	}
913eb7188caSLydia Wang 
914096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
915eb7188caSLydia Wang 
916eb7188caSLydia Wang 	return 0;
917eb7188caSLydia Wang }
918f3db423dSLydia Wang 
919f3db423dSLydia Wang /* Patch for VT1716S */
920f3db423dSLydia Wang 
921f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
922f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
923f3db423dSLydia Wang {
924f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
925f3db423dSLydia Wang 	uinfo->count = 1;
926f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
927f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
928f3db423dSLydia Wang 	return 0;
929f3db423dSLydia Wang }
930f3db423dSLydia Wang 
931f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
932f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
933f3db423dSLydia Wang {
934f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
935f3db423dSLydia Wang 	int index = 0;
936f3db423dSLydia Wang 
937f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
938f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
939f3db423dSLydia Wang 	if (index != -1)
940f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
941f3db423dSLydia Wang 
942f3db423dSLydia Wang 	return 0;
943f3db423dSLydia Wang }
944f3db423dSLydia Wang 
945f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
946f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
947f3db423dSLydia Wang {
948f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
949f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
950f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
951f3db423dSLydia Wang 
952f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
953f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
954f3db423dSLydia Wang 	spec->dmic_enabled = index;
955f3db423dSLydia Wang 	return 1;
956f3db423dSLydia Wang }
957f3db423dSLydia Wang 
95890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
959f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
960f3db423dSLydia Wang 	{
961f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
962f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
9635b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
964f3db423dSLydia Wang 	 .count = 1,
965f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
966f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
967f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
968f3db423dSLydia Wang 	 },
969f3db423dSLydia Wang 	{}			/* end */
970f3db423dSLydia Wang };
971f3db423dSLydia Wang 
972f3db423dSLydia Wang 
973f3db423dSLydia Wang /* mono-out mixer elements */
97490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
975f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
976f3db423dSLydia Wang 	{ } /* end */
977f3db423dSLydia Wang };
978f3db423dSLydia Wang 
979096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
980f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
981f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
982f3db423dSLydia Wang 	/* don't bybass mixer */
983f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
984f3db423dSLydia Wang 	/* Enable mono output */
985f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
986f3db423dSLydia Wang 	{ }
987f3db423dSLydia Wang };
988f3db423dSLydia Wang 
989f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
990f3db423dSLydia Wang {
991f3db423dSLydia Wang 	struct via_spec *spec;
992f3db423dSLydia Wang 	int err;
993f3db423dSLydia Wang 
994f3db423dSLydia Wang 	/* create a codec specific record */
9955b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
996f3db423dSLydia Wang 	if (spec == NULL)
997f3db423dSLydia Wang 		return -ENOMEM;
998f3db423dSLydia Wang 
999b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1000d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1001d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1002620e2b28STakashi Iwai 
1003f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
100412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1005f3db423dSLydia Wang 	if (err < 0) {
1006f3db423dSLydia Wang 		via_free(codec);
1007f3db423dSLydia Wang 		return err;
1008f3db423dSLydia Wang 	}
1009f3db423dSLydia Wang 
1010096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1011f3db423dSLydia Wang 
1012b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1013f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1014f3db423dSLydia Wang 
1015f3db423dSLydia Wang 	return 0;
1016f3db423dSLydia Wang }
101725eaba2fSLydia Wang 
101825eaba2fSLydia Wang /* for vt2002P */
101925eaba2fSLydia Wang 
1020096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1021eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1022eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1023eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1024eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
102525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
102625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
102725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
102825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
102925eaba2fSLydia Wang 	{ }
103025eaba2fSLydia Wang };
10314a918ffeSTakashi Iwai 
1032096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
103311890956SLydia Wang 	/* Enable Boost Volume backdoor */
103411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
103511890956SLydia Wang 	/* Enable AOW0 to MW9 */
103611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
103711890956SLydia Wang 	{ }
103811890956SLydia Wang };
103925eaba2fSLydia Wang 
10404b527b65SDavid Henningsson /*
10414b527b65SDavid Henningsson  * pin fix-up
10424b527b65SDavid Henningsson  */
10434b527b65SDavid Henningsson enum {
10444b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1045d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
10464b527b65SDavid Henningsson };
10474b527b65SDavid Henningsson 
10484b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
10494b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
10504b527b65SDavid Henningsson {
10514b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
10524b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
10534b527b65SDavid Henningsson }
10544b527b65SDavid Henningsson 
10554b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
10564b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
10574b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
10584b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
10594b527b65SDavid Henningsson 	},
1060d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1061d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1062d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1063d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1064d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1065d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1066d5266125STakashi Iwai 			{ }
1067d5266125STakashi Iwai 		}
1068d5266125STakashi Iwai 	},
10694b527b65SDavid Henningsson };
10704b527b65SDavid Henningsson 
10714b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1072d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10734b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
10744b527b65SDavid Henningsson 	{}
10754b527b65SDavid Henningsson };
10764b527b65SDavid Henningsson 
1077ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1078ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1079ef4da458STakashi Iwai  */
1080ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1081ef4da458STakashi Iwai {
1082ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1083ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1084ef4da458STakashi Iwai 
1085ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1086ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1087ef4da458STakashi Iwai }
1088ef4da458STakashi Iwai 
108925eaba2fSLydia Wang /* patch for vt2002P */
109025eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
109125eaba2fSLydia Wang {
109225eaba2fSLydia Wang 	struct via_spec *spec;
109325eaba2fSLydia Wang 	int err;
109425eaba2fSLydia Wang 
109525eaba2fSLydia Wang 	/* create a codec specific record */
10965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
109725eaba2fSLydia Wang 	if (spec == NULL)
109825eaba2fSLydia Wang 		return -ENOMEM;
109925eaba2fSLydia Wang 
1100b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1101d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1102d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1103ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1104ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
110530b45033STakashi Iwai 	add_secret_dac_path(codec);
1106620e2b28STakashi Iwai 
11074b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
11084b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
11094b527b65SDavid Henningsson 
111025eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
111112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
111225eaba2fSLydia Wang 	if (err < 0) {
111325eaba2fSLydia Wang 		via_free(codec);
111425eaba2fSLydia Wang 		return err;
111525eaba2fSLydia Wang 	}
111625eaba2fSLydia Wang 
111711890956SLydia Wang 	if (spec->codec_type == VT1802)
11184a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
111911890956SLydia Wang 	else
11204a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
112111890956SLydia Wang 
112225eaba2fSLydia Wang 	return 0;
112325eaba2fSLydia Wang }
1124ab6734e7SLydia Wang 
1125ab6734e7SLydia Wang /* for vt1812 */
1126ab6734e7SLydia Wang 
1127096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1128ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1129ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1130ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1131ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1132ab6734e7SLydia Wang 	{ }
1133ab6734e7SLydia Wang };
1134ab6734e7SLydia Wang 
1135ab6734e7SLydia Wang /* patch for vt1812 */
1136ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1137ab6734e7SLydia Wang {
1138ab6734e7SLydia Wang 	struct via_spec *spec;
1139ab6734e7SLydia Wang 	int err;
1140ab6734e7SLydia Wang 
1141ab6734e7SLydia Wang 	/* create a codec specific record */
11425b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1143ab6734e7SLydia Wang 	if (spec == NULL)
1144ab6734e7SLydia Wang 		return -ENOMEM;
1145ab6734e7SLydia Wang 
1146b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1147d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1148d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
114930b45033STakashi Iwai 	add_secret_dac_path(codec);
1150620e2b28STakashi Iwai 
1151ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
115212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1153ab6734e7SLydia Wang 	if (err < 0) {
1154ab6734e7SLydia Wang 		via_free(codec);
1155ab6734e7SLydia Wang 		return err;
1156ab6734e7SLydia Wang 	}
1157ab6734e7SLydia Wang 
1158096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1159ab6734e7SLydia Wang 
1160ab6734e7SLydia Wang 	return 0;
1161ab6734e7SLydia Wang }
1162ab6734e7SLydia Wang 
116343737e0aSLydia Wang /* patch for vt3476 */
116443737e0aSLydia Wang 
116543737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
116643737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
116743737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
116843737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
116943737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
117043737e0aSLydia Wang 	/* Enable AOW-MW9 path */
117143737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
117243737e0aSLydia Wang 	{ }
117343737e0aSLydia Wang };
117443737e0aSLydia Wang 
117543737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
117643737e0aSLydia Wang {
117743737e0aSLydia Wang 	struct via_spec *spec;
117843737e0aSLydia Wang 	int err;
117943737e0aSLydia Wang 
118043737e0aSLydia Wang 	/* create a codec specific record */
118143737e0aSLydia Wang 	spec = via_new_spec(codec);
118243737e0aSLydia Wang 	if (spec == NULL)
118343737e0aSLydia Wang 		return -ENOMEM;
118443737e0aSLydia Wang 
1185b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
118643737e0aSLydia Wang 	add_secret_dac_path(codec);
118743737e0aSLydia Wang 
118843737e0aSLydia Wang 	/* automatic parse from the BIOS config */
118943737e0aSLydia Wang 	err = via_parse_auto_config(codec);
119043737e0aSLydia Wang 	if (err < 0) {
119143737e0aSLydia Wang 		via_free(codec);
119243737e0aSLydia Wang 		return err;
119343737e0aSLydia Wang 	}
119443737e0aSLydia Wang 
119543737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
119643737e0aSLydia Wang 
119743737e0aSLydia Wang 	return 0;
119843737e0aSLydia Wang }
119943737e0aSLydia Wang 
1200c577b8a1SJoseph Chan /*
1201c577b8a1SJoseph Chan  * patch entries
1202c577b8a1SJoseph Chan  */
1203*b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = {
1204*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
1205*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
1206*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
1207*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
1208*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
1209*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
1210*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
1211*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
1212*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
1213*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
1214*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
1215*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
1216*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
1217*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
1218*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
1219*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
1220*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
1221*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
1222*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
1223*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
1224*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
1225*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
1226*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
1227*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
1228*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
1229*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
1230*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
1231*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
1232*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
1233*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
1234*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
1235*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
1236*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
1237*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
1238*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
1239*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
1240*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
1241*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
1242*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
1243*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
1244*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
1245*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
1246*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
1247*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
1248*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
1249*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
1250*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
1251*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
1252*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
1253*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
1254*b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
1255c577b8a1SJoseph Chan 	{} /* terminator */
1256c577b8a1SJoseph Chan };
1257*b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
12581289e9e8STakashi Iwai 
1259d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
1260*b9a94a9cSTakashi Iwai 	.id = snd_hda_id_via,
12611289e9e8STakashi Iwai };
12621289e9e8STakashi Iwai 
12631289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12641289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12651289e9e8STakashi Iwai 
1266d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1267