xref: /kernel/linux/linux-6.6/sound/pci/hda/patch_via.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Universal Interface for Intel High Definition Audio Codec
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  (C) 2006-2009 VIA Technology, Inc.
862306a36Sopenharmony_ci *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
1262306a36Sopenharmony_ci/*									     */
1362306a36Sopenharmony_ci/* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
1462306a36Sopenharmony_ci/* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
1562306a36Sopenharmony_ci/* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
1662306a36Sopenharmony_ci/* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
1762306a36Sopenharmony_ci/* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
1862306a36Sopenharmony_ci/* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
1962306a36Sopenharmony_ci/* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
2062306a36Sopenharmony_ci/* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
2162306a36Sopenharmony_ci/* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
2262306a36Sopenharmony_ci/* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
2362306a36Sopenharmony_ci/* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
2462306a36Sopenharmony_ci/* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
2562306a36Sopenharmony_ci/* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
2662306a36Sopenharmony_ci/* 2009-02-16  Logan Li	   Add support for VT1718S			     */
2762306a36Sopenharmony_ci/* 2009-03-13  Logan Li	   Add support for VT1716S			     */
2862306a36Sopenharmony_ci/* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
2962306a36Sopenharmony_ci/* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
3062306a36Sopenharmony_ci/* 2009-07-21  Lydia Wang  Add support for VT1812			     */
3162306a36Sopenharmony_ci/* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
3262306a36Sopenharmony_ci/*									     */
3362306a36Sopenharmony_ci/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include <linux/init.h>
3762306a36Sopenharmony_ci#include <linux/delay.h>
3862306a36Sopenharmony_ci#include <linux/slab.h>
3962306a36Sopenharmony_ci#include <linux/module.h>
4062306a36Sopenharmony_ci#include <sound/core.h>
4162306a36Sopenharmony_ci#include <sound/asoundef.h>
4262306a36Sopenharmony_ci#include <sound/hda_codec.h>
4362306a36Sopenharmony_ci#include "hda_local.h"
4462306a36Sopenharmony_ci#include "hda_auto_parser.h"
4562306a36Sopenharmony_ci#include "hda_jack.h"
4662306a36Sopenharmony_ci#include "hda_generic.h"
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Pin Widget NID */
4962306a36Sopenharmony_ci#define VT1708_HP_PIN_NID	0x20
5062306a36Sopenharmony_ci#define VT1708_CD_PIN_NID	0x24
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cienum VIA_HDA_CODEC {
5362306a36Sopenharmony_ci	UNKNOWN = -1,
5462306a36Sopenharmony_ci	VT1708,
5562306a36Sopenharmony_ci	VT1709_10CH,
5662306a36Sopenharmony_ci	VT1709_6CH,
5762306a36Sopenharmony_ci	VT1708B_8CH,
5862306a36Sopenharmony_ci	VT1708B_4CH,
5962306a36Sopenharmony_ci	VT1708S,
6062306a36Sopenharmony_ci	VT1708BCE,
6162306a36Sopenharmony_ci	VT1702,
6262306a36Sopenharmony_ci	VT1718S,
6362306a36Sopenharmony_ci	VT1716S,
6462306a36Sopenharmony_ci	VT2002P,
6562306a36Sopenharmony_ci	VT1812,
6662306a36Sopenharmony_ci	VT1802,
6762306a36Sopenharmony_ci	VT1705CF,
6862306a36Sopenharmony_ci	VT1808,
6962306a36Sopenharmony_ci	CODEC_TYPES,
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define VT2002P_COMPATIBLE(spec) \
7362306a36Sopenharmony_ci	((spec)->codec_type == VT2002P ||\
7462306a36Sopenharmony_ci	 (spec)->codec_type == VT1812 ||\
7562306a36Sopenharmony_ci	 (spec)->codec_type == VT1802)
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct via_spec {
7862306a36Sopenharmony_ci	struct hda_gen_spec gen;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* HP mode source */
8162306a36Sopenharmony_ci	unsigned int dmic_enabled;
8262306a36Sopenharmony_ci	enum VIA_HDA_CODEC codec_type;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* analog low-power control */
8562306a36Sopenharmony_ci	bool alc_mode;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/* work to check hp jack state */
8862306a36Sopenharmony_ci	int hp_work_active;
8962306a36Sopenharmony_ci	int vt1708_jack_detect;
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
9362306a36Sopenharmony_cistatic void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
9462306a36Sopenharmony_ci				  struct hda_codec *codec,
9562306a36Sopenharmony_ci				  struct snd_pcm_substream *substream,
9662306a36Sopenharmony_ci				  int action);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic const struct hda_codec_ops via_patch_ops; /* defined below */
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic struct via_spec *via_new_spec(struct hda_codec *codec)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct via_spec *spec;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
10562306a36Sopenharmony_ci	if (spec == NULL)
10662306a36Sopenharmony_ci		return NULL;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	codec->spec = spec;
10962306a36Sopenharmony_ci	snd_hda_gen_spec_init(&spec->gen);
11062306a36Sopenharmony_ci	spec->codec_type = get_codec_type(codec);
11162306a36Sopenharmony_ci	/* VT1708BCE & VT1708S are almost same */
11262306a36Sopenharmony_ci	if (spec->codec_type == VT1708BCE)
11362306a36Sopenharmony_ci		spec->codec_type = VT1708S;
11462306a36Sopenharmony_ci	spec->gen.indep_hp = 1;
11562306a36Sopenharmony_ci	spec->gen.keep_eapd_on = 1;
11662306a36Sopenharmony_ci	spec->gen.dac_min_mute = 1;
11762306a36Sopenharmony_ci	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
11862306a36Sopenharmony_ci	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
11962306a36Sopenharmony_ci	codec->power_save_node = 1;
12062306a36Sopenharmony_ci	spec->gen.power_down_unused = 1;
12162306a36Sopenharmony_ci	codec->patch_ops = via_patch_ops;
12262306a36Sopenharmony_ci	return spec;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	u32 vendor_id = codec->core.vendor_id;
12862306a36Sopenharmony_ci	u16 ven_id = vendor_id >> 16;
12962306a36Sopenharmony_ci	u16 dev_id = vendor_id & 0xffff;
13062306a36Sopenharmony_ci	enum VIA_HDA_CODEC codec_type;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* get codec type */
13362306a36Sopenharmony_ci	if (ven_id != 0x1106)
13462306a36Sopenharmony_ci		codec_type = UNKNOWN;
13562306a36Sopenharmony_ci	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
13662306a36Sopenharmony_ci		codec_type = VT1708;
13762306a36Sopenharmony_ci	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
13862306a36Sopenharmony_ci		codec_type = VT1709_10CH;
13962306a36Sopenharmony_ci	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
14062306a36Sopenharmony_ci		codec_type = VT1709_6CH;
14162306a36Sopenharmony_ci	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
14262306a36Sopenharmony_ci		codec_type = VT1708B_8CH;
14362306a36Sopenharmony_ci		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
14462306a36Sopenharmony_ci			codec_type = VT1708BCE;
14562306a36Sopenharmony_ci	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
14662306a36Sopenharmony_ci		codec_type = VT1708B_4CH;
14762306a36Sopenharmony_ci	else if ((dev_id & 0xfff) == 0x397
14862306a36Sopenharmony_ci		 && (dev_id >> 12) < 8)
14962306a36Sopenharmony_ci		codec_type = VT1708S;
15062306a36Sopenharmony_ci	else if ((dev_id & 0xfff) == 0x398
15162306a36Sopenharmony_ci		 && (dev_id >> 12) < 8)
15262306a36Sopenharmony_ci		codec_type = VT1702;
15362306a36Sopenharmony_ci	else if ((dev_id & 0xfff) == 0x428
15462306a36Sopenharmony_ci		 && (dev_id >> 12) < 8)
15562306a36Sopenharmony_ci		codec_type = VT1718S;
15662306a36Sopenharmony_ci	else if (dev_id == 0x0433 || dev_id == 0xa721)
15762306a36Sopenharmony_ci		codec_type = VT1716S;
15862306a36Sopenharmony_ci	else if (dev_id == 0x0441 || dev_id == 0x4441)
15962306a36Sopenharmony_ci		codec_type = VT1718S;
16062306a36Sopenharmony_ci	else if (dev_id == 0x0438 || dev_id == 0x4438)
16162306a36Sopenharmony_ci		codec_type = VT2002P;
16262306a36Sopenharmony_ci	else if (dev_id == 0x0448)
16362306a36Sopenharmony_ci		codec_type = VT1812;
16462306a36Sopenharmony_ci	else if (dev_id == 0x0440)
16562306a36Sopenharmony_ci		codec_type = VT1708S;
16662306a36Sopenharmony_ci	else if ((dev_id & 0xfff) == 0x446)
16762306a36Sopenharmony_ci		codec_type = VT1802;
16862306a36Sopenharmony_ci	else if (dev_id == 0x4760)
16962306a36Sopenharmony_ci		codec_type = VT1705CF;
17062306a36Sopenharmony_ci	else if (dev_id == 0x4761 || dev_id == 0x4762)
17162306a36Sopenharmony_ci		codec_type = VT1808;
17262306a36Sopenharmony_ci	else
17362306a36Sopenharmony_ci		codec_type = UNKNOWN;
17462306a36Sopenharmony_ci	return codec_type;
17562306a36Sopenharmony_ci};
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic void analog_low_current_mode(struct hda_codec *codec);
17862306a36Sopenharmony_cistatic bool is_aa_path_mute(struct hda_codec *codec);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci#define hp_detect_with_aa(codec) \
18162306a36Sopenharmony_ci	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
18262306a36Sopenharmony_ci	 !is_aa_path_mute(codec))
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void vt1708_stop_hp_work(struct hda_codec *codec)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
18762306a36Sopenharmony_ci	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
18862306a36Sopenharmony_ci		return;
18962306a36Sopenharmony_ci	if (spec->hp_work_active) {
19062306a36Sopenharmony_ci		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
19162306a36Sopenharmony_ci		codec->jackpoll_interval = 0;
19262306a36Sopenharmony_ci		cancel_delayed_work_sync(&codec->jackpoll_work);
19362306a36Sopenharmony_ci		spec->hp_work_active = false;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void vt1708_update_hp_work(struct hda_codec *codec)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
20062306a36Sopenharmony_ci	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
20162306a36Sopenharmony_ci		return;
20262306a36Sopenharmony_ci	if (spec->vt1708_jack_detect) {
20362306a36Sopenharmony_ci		if (!spec->hp_work_active) {
20462306a36Sopenharmony_ci			codec->jackpoll_interval = msecs_to_jiffies(100);
20562306a36Sopenharmony_ci			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
20662306a36Sopenharmony_ci			schedule_delayed_work(&codec->jackpoll_work, 0);
20762306a36Sopenharmony_ci			spec->hp_work_active = true;
20862306a36Sopenharmony_ci		}
20962306a36Sopenharmony_ci	} else if (!hp_detect_with_aa(codec))
21062306a36Sopenharmony_ci		vt1708_stop_hp_work(codec);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
21462306a36Sopenharmony_ci				  struct snd_ctl_elem_info *uinfo)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
22062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
22362306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
23062306a36Sopenharmony_ci				 struct snd_ctl_elem_value *ucontrol)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
23362306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
23462306a36Sopenharmony_ci	bool val = !!ucontrol->value.enumerated.item[0];
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if (val == spec->gen.power_down_unused)
23762306a36Sopenharmony_ci		return 0;
23862306a36Sopenharmony_ci	/* codec->power_save_node = val; */ /* widget PM seems yet broken */
23962306a36Sopenharmony_ci	spec->gen.power_down_unused = val;
24062306a36Sopenharmony_ci	analog_low_current_mode(codec);
24162306a36Sopenharmony_ci	return 1;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic const struct snd_kcontrol_new via_pin_power_ctl_enum = {
24562306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24662306a36Sopenharmony_ci	.name = "Dynamic Power-Control",
24762306a36Sopenharmony_ci	.info = via_pin_power_ctl_info,
24862306a36Sopenharmony_ci	.get = via_pin_power_ctl_get,
24962306a36Sopenharmony_ci	.put = via_pin_power_ctl_put,
25062306a36Sopenharmony_ci};
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_INPUT_BEEP
25362306a36Sopenharmony_ci/* additional beep mixers; the actual parameters are overwritten at build */
25462306a36Sopenharmony_cistatic const struct snd_kcontrol_new via_beep_mixer[] = {
25562306a36Sopenharmony_ci	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
25662306a36Sopenharmony_ci	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
25762306a36Sopenharmony_ci};
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_cistatic int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
26062306a36Sopenharmony_ci			int idx, int dir)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct snd_kcontrol_new *knew;
26362306a36Sopenharmony_ci	unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
26462306a36Sopenharmony_ci	int i;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	spec->gen.beep_nid = nid;
26762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
26862306a36Sopenharmony_ci		knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
26962306a36Sopenharmony_ci					    &via_beep_mixer[i]);
27062306a36Sopenharmony_ci		if (!knew)
27162306a36Sopenharmony_ci			return -ENOMEM;
27262306a36Sopenharmony_ci		knew->private_value = beep_amp;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci	return 0;
27562306a36Sopenharmony_ci}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic int auto_parse_beep(struct hda_codec *codec)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
28062306a36Sopenharmony_ci	hda_nid_t nid;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	for_each_hda_codec_node(nid, codec)
28362306a36Sopenharmony_ci		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
28462306a36Sopenharmony_ci			return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci#else
28862306a36Sopenharmony_ci#define auto_parse_beep(codec)	0
28962306a36Sopenharmony_ci#endif
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci/* check AA path's mute status */
29262306a36Sopenharmony_cistatic bool is_aa_path_mute(struct hda_codec *codec)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
29562306a36Sopenharmony_ci	const struct hda_amp_list *p;
29662306a36Sopenharmony_ci	int ch, v;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	p = spec->gen.loopback.amplist;
29962306a36Sopenharmony_ci	if (!p)
30062306a36Sopenharmony_ci		return true;
30162306a36Sopenharmony_ci	for (; p->nid; p++) {
30262306a36Sopenharmony_ci		for (ch = 0; ch < 2; ch++) {
30362306a36Sopenharmony_ci			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
30462306a36Sopenharmony_ci						   p->idx);
30562306a36Sopenharmony_ci			if (!(v & HDA_AMP_MUTE) && v > 0)
30662306a36Sopenharmony_ci				return false;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci	return true;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/* enter/exit analog low-current mode */
31362306a36Sopenharmony_cistatic void __analog_low_current_mode(struct hda_codec *codec, bool force)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
31662306a36Sopenharmony_ci	bool enable;
31762306a36Sopenharmony_ci	unsigned int verb, parm;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (!codec->power_save_node)
32062306a36Sopenharmony_ci		enable = false;
32162306a36Sopenharmony_ci	else
32262306a36Sopenharmony_ci		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
32362306a36Sopenharmony_ci	if (enable == spec->alc_mode && !force)
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci	spec->alc_mode = enable;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* decide low current mode's verb & parameter */
32862306a36Sopenharmony_ci	switch (spec->codec_type) {
32962306a36Sopenharmony_ci	case VT1708B_8CH:
33062306a36Sopenharmony_ci	case VT1708B_4CH:
33162306a36Sopenharmony_ci		verb = 0xf70;
33262306a36Sopenharmony_ci		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
33362306a36Sopenharmony_ci		break;
33462306a36Sopenharmony_ci	case VT1708S:
33562306a36Sopenharmony_ci	case VT1718S:
33662306a36Sopenharmony_ci	case VT1716S:
33762306a36Sopenharmony_ci		verb = 0xf73;
33862306a36Sopenharmony_ci		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
33962306a36Sopenharmony_ci		break;
34062306a36Sopenharmony_ci	case VT1702:
34162306a36Sopenharmony_ci		verb = 0xf73;
34262306a36Sopenharmony_ci		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
34362306a36Sopenharmony_ci		break;
34462306a36Sopenharmony_ci	case VT2002P:
34562306a36Sopenharmony_ci	case VT1812:
34662306a36Sopenharmony_ci	case VT1802:
34762306a36Sopenharmony_ci		verb = 0xf93;
34862306a36Sopenharmony_ci		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
34962306a36Sopenharmony_ci		break;
35062306a36Sopenharmony_ci	case VT1705CF:
35162306a36Sopenharmony_ci	case VT1808:
35262306a36Sopenharmony_ci		verb = 0xf82;
35362306a36Sopenharmony_ci		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
35462306a36Sopenharmony_ci		break;
35562306a36Sopenharmony_ci	default:
35662306a36Sopenharmony_ci		return;		/* other codecs are not supported */
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci	/* send verb */
35962306a36Sopenharmony_ci	snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic void analog_low_current_mode(struct hda_codec *codec)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	return __analog_low_current_mode(codec, false);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
36862306a36Sopenharmony_ci				  struct hda_codec *codec,
36962306a36Sopenharmony_ci				  struct snd_pcm_substream *substream,
37062306a36Sopenharmony_ci				  int action)
37162306a36Sopenharmony_ci{
37262306a36Sopenharmony_ci	analog_low_current_mode(codec);
37362306a36Sopenharmony_ci	vt1708_update_hp_work(codec);
37462306a36Sopenharmony_ci}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void via_free(struct hda_codec *codec)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	vt1708_stop_hp_work(codec);
37962306a36Sopenharmony_ci	snd_hda_gen_free(codec);
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci#ifdef CONFIG_PM
38362306a36Sopenharmony_cistatic int via_suspend(struct hda_codec *codec)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
38662306a36Sopenharmony_ci	vt1708_stop_hp_work(codec);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* Fix pop noise on headphones */
38962306a36Sopenharmony_ci	if (spec->codec_type == VT1802)
39062306a36Sopenharmony_ci		snd_hda_shutup_pins(codec);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int via_resume(struct hda_codec *codec)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	/* some delay here to make jack detection working (bko#98921) */
39862306a36Sopenharmony_ci	msleep(10);
39962306a36Sopenharmony_ci	codec->patch_ops.init(codec);
40062306a36Sopenharmony_ci	snd_hda_regmap_sync(codec);
40162306a36Sopenharmony_ci	return 0;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci#endif
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci#ifdef CONFIG_PM
40662306a36Sopenharmony_cistatic int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
40962306a36Sopenharmony_ci	analog_low_current_mode(codec);
41062306a36Sopenharmony_ci	vt1708_update_hp_work(codec);
41162306a36Sopenharmony_ci	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
41262306a36Sopenharmony_ci}
41362306a36Sopenharmony_ci#endif
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/*
41662306a36Sopenharmony_ci */
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_cistatic int via_init(struct hda_codec *codec);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic const struct hda_codec_ops via_patch_ops = {
42162306a36Sopenharmony_ci	.build_controls = snd_hda_gen_build_controls,
42262306a36Sopenharmony_ci	.build_pcms = snd_hda_gen_build_pcms,
42362306a36Sopenharmony_ci	.init = via_init,
42462306a36Sopenharmony_ci	.free = via_free,
42562306a36Sopenharmony_ci	.unsol_event = snd_hda_jack_unsol_event,
42662306a36Sopenharmony_ci#ifdef CONFIG_PM
42762306a36Sopenharmony_ci	.suspend = via_suspend,
42862306a36Sopenharmony_ci	.resume = via_resume,
42962306a36Sopenharmony_ci	.check_power_status = via_check_power_status,
43062306a36Sopenharmony_ci#endif
43162306a36Sopenharmony_ci};
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_cistatic const struct hda_verb vt1708_init_verbs[] = {
43562306a36Sopenharmony_ci	/* power down jack detect function */
43662306a36Sopenharmony_ci	{0x1, 0xf81, 0x1},
43762306a36Sopenharmony_ci	{ }
43862306a36Sopenharmony_ci};
43962306a36Sopenharmony_cistatic void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	unsigned int def_conf;
44262306a36Sopenharmony_ci	unsigned char seqassoc;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	def_conf = snd_hda_codec_get_pincfg(codec, nid);
44562306a36Sopenharmony_ci	seqassoc = (unsigned char) get_defcfg_association(def_conf);
44662306a36Sopenharmony_ci	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
44762306a36Sopenharmony_ci	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
44862306a36Sopenharmony_ci	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
44962306a36Sopenharmony_ci		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
45062306a36Sopenharmony_ci		snd_hda_codec_set_pincfg(codec, nid, def_conf);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
45562306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
45862306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (spec->codec_type != VT1708)
46162306a36Sopenharmony_ci		return 0;
46262306a36Sopenharmony_ci	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
46762306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
46862306a36Sopenharmony_ci{
46962306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
47062306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
47162306a36Sopenharmony_ci	int val;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	if (spec->codec_type != VT1708)
47462306a36Sopenharmony_ci		return 0;
47562306a36Sopenharmony_ci	val = !!ucontrol->value.integer.value[0];
47662306a36Sopenharmony_ci	if (spec->vt1708_jack_detect == val)
47762306a36Sopenharmony_ci		return 0;
47862306a36Sopenharmony_ci	spec->vt1708_jack_detect = val;
47962306a36Sopenharmony_ci	vt1708_update_hp_work(codec);
48062306a36Sopenharmony_ci	return 1;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
48462306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
48562306a36Sopenharmony_ci	.name = "Jack Detect",
48662306a36Sopenharmony_ci	.count = 1,
48762306a36Sopenharmony_ci	.info = snd_ctl_boolean_mono_info,
48862306a36Sopenharmony_ci	.get = vt1708_jack_detect_get,
48962306a36Sopenharmony_ci	.put = vt1708_jack_detect_put,
49062306a36Sopenharmony_ci};
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic const struct badness_table via_main_out_badness = {
49362306a36Sopenharmony_ci	.no_primary_dac = 0x10000,
49462306a36Sopenharmony_ci	.no_dac = 0x4000,
49562306a36Sopenharmony_ci	.shared_primary = 0x10000,
49662306a36Sopenharmony_ci	.shared_surr = 0x20,
49762306a36Sopenharmony_ci	.shared_clfe = 0x20,
49862306a36Sopenharmony_ci	.shared_surr_main = 0x20,
49962306a36Sopenharmony_ci};
50062306a36Sopenharmony_cistatic const struct badness_table via_extra_out_badness = {
50162306a36Sopenharmony_ci	.no_primary_dac = 0x4000,
50262306a36Sopenharmony_ci	.no_dac = 0x4000,
50362306a36Sopenharmony_ci	.shared_primary = 0x12,
50462306a36Sopenharmony_ci	.shared_surr = 0x20,
50562306a36Sopenharmony_ci	.shared_clfe = 0x20,
50662306a36Sopenharmony_ci	.shared_surr_main = 0x10,
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_cistatic int via_parse_auto_config(struct hda_codec *codec)
51062306a36Sopenharmony_ci{
51162306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
51262306a36Sopenharmony_ci	int err;
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	spec->gen.main_out_badness = &via_main_out_badness;
51562306a36Sopenharmony_ci	spec->gen.extra_out_badness = &via_extra_out_badness;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
51862306a36Sopenharmony_ci	if (err < 0)
51962306a36Sopenharmony_ci		return err;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	err = auto_parse_beep(codec);
52262306a36Sopenharmony_ci	if (err < 0)
52362306a36Sopenharmony_ci		return err;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
52662306a36Sopenharmony_ci	if (err < 0)
52762306a36Sopenharmony_ci		return err;
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
53062306a36Sopenharmony_ci		return -ENOMEM;
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	/* disable widget PM at start for compatibility */
53362306a36Sopenharmony_ci	codec->power_save_node = 0;
53462306a36Sopenharmony_ci	spec->gen.power_down_unused = 0;
53562306a36Sopenharmony_ci	return 0;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic int via_init(struct hda_codec *codec)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	/* init power states */
54162306a36Sopenharmony_ci	__analog_low_current_mode(codec, true);
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	snd_hda_gen_init(codec);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	vt1708_update_hp_work(codec);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	return 0;
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic int vt1708_build_controls(struct hda_codec *codec)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	/* In order not to create "Phantom Jack" controls,
55362306a36Sopenharmony_ci	   temporary enable jackpoll */
55462306a36Sopenharmony_ci	int err;
55562306a36Sopenharmony_ci	int old_interval = codec->jackpoll_interval;
55662306a36Sopenharmony_ci	codec->jackpoll_interval = msecs_to_jiffies(100);
55762306a36Sopenharmony_ci	err = snd_hda_gen_build_controls(codec);
55862306a36Sopenharmony_ci	codec->jackpoll_interval = old_interval;
55962306a36Sopenharmony_ci	return err;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_cistatic int vt1708_build_pcms(struct hda_codec *codec)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
56562306a36Sopenharmony_ci	int i, err;
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	err = snd_hda_gen_build_pcms(codec);
56862306a36Sopenharmony_ci	if (err < 0 || codec->core.vendor_id != 0x11061708)
56962306a36Sopenharmony_ci		return err;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	/* We got noisy outputs on the right channel on VT1708 when
57262306a36Sopenharmony_ci	 * 24bit samples are used.  Until any workaround is found,
57362306a36Sopenharmony_ci	 * disable the 24bit format, so far.
57462306a36Sopenharmony_ci	 */
57562306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
57662306a36Sopenharmony_ci		struct hda_pcm *info = spec->gen.pcm_rec[i];
57762306a36Sopenharmony_ci		if (!info)
57862306a36Sopenharmony_ci			continue;
57962306a36Sopenharmony_ci		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
58062306a36Sopenharmony_ci		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
58162306a36Sopenharmony_ci			continue;
58262306a36Sopenharmony_ci		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
58362306a36Sopenharmony_ci			SNDRV_PCM_FMTBIT_S16_LE;
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_cistatic int patch_vt1708(struct hda_codec *codec)
59062306a36Sopenharmony_ci{
59162306a36Sopenharmony_ci	struct via_spec *spec;
59262306a36Sopenharmony_ci	int err;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	/* create a codec specific record */
59562306a36Sopenharmony_ci	spec = via_new_spec(codec);
59662306a36Sopenharmony_ci	if (spec == NULL)
59762306a36Sopenharmony_ci		return -ENOMEM;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	/* override some patch_ops */
60062306a36Sopenharmony_ci	codec->patch_ops.build_controls = vt1708_build_controls;
60162306a36Sopenharmony_ci	codec->patch_ops.build_pcms = vt1708_build_pcms;
60262306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x17;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	/* set jackpoll_interval while parsing the codec */
60562306a36Sopenharmony_ci	codec->jackpoll_interval = msecs_to_jiffies(100);
60662306a36Sopenharmony_ci	spec->vt1708_jack_detect = 1;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	/* don't support the input jack switching due to lack of unsol event */
60962306a36Sopenharmony_ci	/* (it may work with polling, though, but it needs testing) */
61062306a36Sopenharmony_ci	spec->gen.suppress_auto_mic = 1;
61162306a36Sopenharmony_ci	/* Some machines show the broken speaker mute */
61262306a36Sopenharmony_ci	spec->gen.auto_mute_via_amp = 1;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	/* Add HP and CD pin config connect bit re-config action */
61562306a36Sopenharmony_ci	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
61662306a36Sopenharmony_ci	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1708_init_verbs);
61962306a36Sopenharmony_ci	if (err < 0)
62062306a36Sopenharmony_ci		goto error;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
62362306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
62462306a36Sopenharmony_ci	if (err < 0)
62562306a36Sopenharmony_ci		goto error;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	/* add jack detect on/off control */
62862306a36Sopenharmony_ci	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
62962306a36Sopenharmony_ci		err = -ENOMEM;
63062306a36Sopenharmony_ci		goto error;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	/* clear jackpoll_interval again; it's set dynamically */
63462306a36Sopenharmony_ci	codec->jackpoll_interval = 0;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	return 0;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci error:
63962306a36Sopenharmony_ci	via_free(codec);
64062306a36Sopenharmony_ci	return err;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int patch_vt1709(struct hda_codec *codec)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	struct via_spec *spec;
64662306a36Sopenharmony_ci	int err;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* create a codec specific record */
64962306a36Sopenharmony_ci	spec = via_new_spec(codec);
65062306a36Sopenharmony_ci	if (spec == NULL)
65162306a36Sopenharmony_ci		return -ENOMEM;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x18;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
65662306a36Sopenharmony_ci	if (err < 0)
65762306a36Sopenharmony_ci		goto error;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci error:
66262306a36Sopenharmony_ci	via_free(codec);
66362306a36Sopenharmony_ci	return err;
66462306a36Sopenharmony_ci}
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_cistatic int patch_vt1708S(struct hda_codec *codec);
66762306a36Sopenharmony_cistatic int patch_vt1708B(struct hda_codec *codec)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct via_spec *spec;
67062306a36Sopenharmony_ci	int err;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (get_codec_type(codec) == VT1708BCE)
67362306a36Sopenharmony_ci		return patch_vt1708S(codec);
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	/* create a codec specific record */
67662306a36Sopenharmony_ci	spec = via_new_spec(codec);
67762306a36Sopenharmony_ci	if (spec == NULL)
67862306a36Sopenharmony_ci		return -ENOMEM;
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x16;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
68362306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
68462306a36Sopenharmony_ci	if (err < 0)
68562306a36Sopenharmony_ci		goto error;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	return 0;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci error:
69062306a36Sopenharmony_ci	via_free(codec);
69162306a36Sopenharmony_ci	return err;
69262306a36Sopenharmony_ci}
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci/* Patch for VT1708S */
69562306a36Sopenharmony_cistatic const struct hda_verb vt1708S_init_verbs[] = {
69662306a36Sopenharmony_ci	/* Enable Mic Boost Volume backdoor */
69762306a36Sopenharmony_ci	{0x1, 0xf98, 0x1},
69862306a36Sopenharmony_ci	/* don't bybass mixer */
69962306a36Sopenharmony_ci	{0x1, 0xf88, 0xc0},
70062306a36Sopenharmony_ci	{ }
70162306a36Sopenharmony_ci};
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cistatic void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
70462306a36Sopenharmony_ci			       int offset, int num_steps, int step_size)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	snd_hda_override_wcaps(codec, pin,
70762306a36Sopenharmony_ci			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
70862306a36Sopenharmony_ci	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
70962306a36Sopenharmony_ci				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
71062306a36Sopenharmony_ci				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
71162306a36Sopenharmony_ci				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
71262306a36Sopenharmony_ci				  (0 << AC_AMPCAP_MUTE_SHIFT));
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic int patch_vt1708S(struct hda_codec *codec)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	struct via_spec *spec;
71862306a36Sopenharmony_ci	int err;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	/* create a codec specific record */
72162306a36Sopenharmony_ci	spec = via_new_spec(codec);
72262306a36Sopenharmony_ci	if (spec == NULL)
72362306a36Sopenharmony_ci		return -ENOMEM;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x16;
72662306a36Sopenharmony_ci	override_mic_boost(codec, 0x1a, 0, 3, 40);
72762306a36Sopenharmony_ci	override_mic_boost(codec, 0x1e, 0, 3, 40);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/* correct names for VT1708BCE */
73062306a36Sopenharmony_ci	if (get_codec_type(codec) == VT1708BCE)
73162306a36Sopenharmony_ci		snd_hda_codec_set_name(codec, "VT1708BCE");
73262306a36Sopenharmony_ci	/* correct names for VT1705 */
73362306a36Sopenharmony_ci	if (codec->core.vendor_id == 0x11064397)
73462306a36Sopenharmony_ci		snd_hda_codec_set_name(codec, "VT1705");
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
73762306a36Sopenharmony_ci	if (err < 0)
73862306a36Sopenharmony_ci		goto error;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
74162306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
74262306a36Sopenharmony_ci	if (err < 0)
74362306a36Sopenharmony_ci		goto error;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci error:
74862306a36Sopenharmony_ci	via_free(codec);
74962306a36Sopenharmony_ci	return err;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci/* Patch for VT1702 */
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_cistatic const struct hda_verb vt1702_init_verbs[] = {
75562306a36Sopenharmony_ci	/* mixer enable */
75662306a36Sopenharmony_ci	{0x1, 0xF88, 0x3},
75762306a36Sopenharmony_ci	/* GPIO 0~2 */
75862306a36Sopenharmony_ci	{0x1, 0xF82, 0x3F},
75962306a36Sopenharmony_ci	{ }
76062306a36Sopenharmony_ci};
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_cistatic int patch_vt1702(struct hda_codec *codec)
76362306a36Sopenharmony_ci{
76462306a36Sopenharmony_ci	struct via_spec *spec;
76562306a36Sopenharmony_ci	int err;
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	/* create a codec specific record */
76862306a36Sopenharmony_ci	spec = via_new_spec(codec);
76962306a36Sopenharmony_ci	if (spec == NULL)
77062306a36Sopenharmony_ci		return -ENOMEM;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x1a;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	/* limit AA path volume to 0 dB */
77562306a36Sopenharmony_ci	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
77662306a36Sopenharmony_ci				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
77762306a36Sopenharmony_ci				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
77862306a36Sopenharmony_ci				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
77962306a36Sopenharmony_ci				  (1 << AC_AMPCAP_MUTE_SHIFT));
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1702_init_verbs);
78262306a36Sopenharmony_ci	if (err < 0)
78362306a36Sopenharmony_ci		goto error;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
78662306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
78762306a36Sopenharmony_ci	if (err < 0)
78862306a36Sopenharmony_ci		goto error;
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	return 0;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci error:
79362306a36Sopenharmony_ci	via_free(codec);
79462306a36Sopenharmony_ci	return err;
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci/* Patch for VT1718S */
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic const struct hda_verb vt1718S_init_verbs[] = {
80062306a36Sopenharmony_ci	/* Enable MW0 adjust Gain 5 */
80162306a36Sopenharmony_ci	{0x1, 0xfb2, 0x10},
80262306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
80362306a36Sopenharmony_ci	{0x1, 0xf88, 0x8},
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	{ }
80662306a36Sopenharmony_ci};
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci/* Add a connection to the primary DAC from AA-mixer for some codecs
80962306a36Sopenharmony_ci * This isn't listed from the raw info, but the chip has a secret connection.
81062306a36Sopenharmony_ci */
81162306a36Sopenharmony_cistatic int add_secret_dac_path(struct hda_codec *codec)
81262306a36Sopenharmony_ci{
81362306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
81462306a36Sopenharmony_ci	int i, nums;
81562306a36Sopenharmony_ci	hda_nid_t conn[8];
81662306a36Sopenharmony_ci	hda_nid_t nid;
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	if (!spec->gen.mixer_nid)
81962306a36Sopenharmony_ci		return 0;
82062306a36Sopenharmony_ci	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
82162306a36Sopenharmony_ci				       ARRAY_SIZE(conn) - 1);
82262306a36Sopenharmony_ci	if (nums < 0)
82362306a36Sopenharmony_ci		return nums;
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	for (i = 0; i < nums; i++) {
82662306a36Sopenharmony_ci		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
82762306a36Sopenharmony_ci			return 0;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	/* find the primary DAC and add to the connection list */
83162306a36Sopenharmony_ci	for_each_hda_codec_node(nid, codec) {
83262306a36Sopenharmony_ci		unsigned int caps = get_wcaps(codec, nid);
83362306a36Sopenharmony_ci		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
83462306a36Sopenharmony_ci		    !(caps & AC_WCAP_DIGITAL)) {
83562306a36Sopenharmony_ci			conn[nums++] = nid;
83662306a36Sopenharmony_ci			return snd_hda_override_conn_list(codec,
83762306a36Sopenharmony_ci							  spec->gen.mixer_nid,
83862306a36Sopenharmony_ci							  nums, conn);
83962306a36Sopenharmony_ci		}
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci	return 0;
84262306a36Sopenharmony_ci}
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_cistatic int patch_vt1718S(struct hda_codec *codec)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct via_spec *spec;
84862306a36Sopenharmony_ci	int err;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* create a codec specific record */
85162306a36Sopenharmony_ci	spec = via_new_spec(codec);
85262306a36Sopenharmony_ci	if (spec == NULL)
85362306a36Sopenharmony_ci		return -ENOMEM;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x21;
85662306a36Sopenharmony_ci	override_mic_boost(codec, 0x2b, 0, 3, 40);
85762306a36Sopenharmony_ci	override_mic_boost(codec, 0x29, 0, 3, 40);
85862306a36Sopenharmony_ci	add_secret_dac_path(codec);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
86162306a36Sopenharmony_ci	if (err < 0)
86262306a36Sopenharmony_ci		goto error;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
86562306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
86662306a36Sopenharmony_ci	if (err < 0)
86762306a36Sopenharmony_ci		goto error;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	return 0;
87062306a36Sopenharmony_ci
87162306a36Sopenharmony_ci error:
87262306a36Sopenharmony_ci	via_free(codec);
87362306a36Sopenharmony_ci	return err;
87462306a36Sopenharmony_ci}
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci/* Patch for VT1716S */
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
87962306a36Sopenharmony_ci			    struct snd_ctl_elem_info *uinfo)
88062306a36Sopenharmony_ci{
88162306a36Sopenharmony_ci	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
88262306a36Sopenharmony_ci	uinfo->count = 1;
88362306a36Sopenharmony_ci	uinfo->value.integer.min = 0;
88462306a36Sopenharmony_ci	uinfo->value.integer.max = 1;
88562306a36Sopenharmony_ci	return 0;
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_cistatic int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
88962306a36Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
89062306a36Sopenharmony_ci{
89162306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
89262306a36Sopenharmony_ci	int index = 0;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	index = snd_hda_codec_read(codec, 0x26, 0,
89562306a36Sopenharmony_ci					       AC_VERB_GET_CONNECT_SEL, 0);
89662306a36Sopenharmony_ci	if (index != -1)
89762306a36Sopenharmony_ci		*ucontrol->value.integer.value = index;
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	return 0;
90062306a36Sopenharmony_ci}
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_cistatic int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
90362306a36Sopenharmony_ci			   struct snd_ctl_elem_value *ucontrol)
90462306a36Sopenharmony_ci{
90562306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
90662306a36Sopenharmony_ci	struct via_spec *spec = codec->spec;
90762306a36Sopenharmony_ci	int index = *ucontrol->value.integer.value;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	snd_hda_codec_write(codec, 0x26, 0,
91062306a36Sopenharmony_ci					       AC_VERB_SET_CONNECT_SEL, index);
91162306a36Sopenharmony_ci	spec->dmic_enabled = index;
91262306a36Sopenharmony_ci	return 1;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_cistatic const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
91662306a36Sopenharmony_ci	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
91762306a36Sopenharmony_cistatic const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
91862306a36Sopenharmony_ci	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
91962306a36Sopenharmony_ci	 .name = "Digital Mic Capture Switch",
92062306a36Sopenharmony_ci	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
92162306a36Sopenharmony_ci	 .count = 1,
92262306a36Sopenharmony_ci	 .info = vt1716s_dmic_info,
92362306a36Sopenharmony_ci	 .get = vt1716s_dmic_get,
92462306a36Sopenharmony_ci	 .put = vt1716s_dmic_put,
92562306a36Sopenharmony_ci};
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci/* mono-out mixer elements */
92962306a36Sopenharmony_cistatic const struct snd_kcontrol_new vt1716S_mono_out_mixer =
93062306a36Sopenharmony_ci	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic const struct hda_verb vt1716S_init_verbs[] = {
93362306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
93462306a36Sopenharmony_ci	{0x1, 0xf8a, 0x80},
93562306a36Sopenharmony_ci	/* don't bybass mixer */
93662306a36Sopenharmony_ci	{0x1, 0xf88, 0xc0},
93762306a36Sopenharmony_ci	/* Enable mono output */
93862306a36Sopenharmony_ci	{0x1, 0xf90, 0x08},
93962306a36Sopenharmony_ci	{ }
94062306a36Sopenharmony_ci};
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic int patch_vt1716S(struct hda_codec *codec)
94362306a36Sopenharmony_ci{
94462306a36Sopenharmony_ci	struct via_spec *spec;
94562306a36Sopenharmony_ci	int err;
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	/* create a codec specific record */
94862306a36Sopenharmony_ci	spec = via_new_spec(codec);
94962306a36Sopenharmony_ci	if (spec == NULL)
95062306a36Sopenharmony_ci		return -ENOMEM;
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x16;
95362306a36Sopenharmony_ci	override_mic_boost(codec, 0x1a, 0, 3, 40);
95462306a36Sopenharmony_ci	override_mic_boost(codec, 0x1e, 0, 3, 40);
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
95762306a36Sopenharmony_ci	if (err < 0)
95862306a36Sopenharmony_ci		goto error;
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
96162306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
96262306a36Sopenharmony_ci	if (err < 0)
96362306a36Sopenharmony_ci		goto error;
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
96662306a36Sopenharmony_ci	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
96762306a36Sopenharmony_ci	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
96862306a36Sopenharmony_ci		err = -ENOMEM;
96962306a36Sopenharmony_ci		goto error;
97062306a36Sopenharmony_ci	}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci	return 0;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci error:
97562306a36Sopenharmony_ci	via_free(codec);
97662306a36Sopenharmony_ci	return err;
97762306a36Sopenharmony_ci}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci/* for vt2002P */
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_cistatic const struct hda_verb vt2002P_init_verbs[] = {
98262306a36Sopenharmony_ci	/* Class-D speaker related verbs */
98362306a36Sopenharmony_ci	{0x1, 0xfe0, 0x4},
98462306a36Sopenharmony_ci	{0x1, 0xfe9, 0x80},
98562306a36Sopenharmony_ci	{0x1, 0xfe2, 0x22},
98662306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
98762306a36Sopenharmony_ci	{0x1, 0xfb9, 0x24},
98862306a36Sopenharmony_ci	/* Enable AOW0 to MW9 */
98962306a36Sopenharmony_ci	{0x1, 0xfb8, 0x88},
99062306a36Sopenharmony_ci	{ }
99162306a36Sopenharmony_ci};
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_cistatic const struct hda_verb vt1802_init_verbs[] = {
99462306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
99562306a36Sopenharmony_ci	{0x1, 0xfb9, 0x24},
99662306a36Sopenharmony_ci	/* Enable AOW0 to MW9 */
99762306a36Sopenharmony_ci	{0x1, 0xfb8, 0x88},
99862306a36Sopenharmony_ci	{ }
99962306a36Sopenharmony_ci};
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci/*
100262306a36Sopenharmony_ci * pin fix-up
100362306a36Sopenharmony_ci */
100462306a36Sopenharmony_cienum {
100562306a36Sopenharmony_ci	VIA_FIXUP_INTMIC_BOOST,
100662306a36Sopenharmony_ci	VIA_FIXUP_ASUS_G75,
100762306a36Sopenharmony_ci	VIA_FIXUP_POWER_SAVE,
100862306a36Sopenharmony_ci};
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_cistatic void via_fixup_intmic_boost(struct hda_codec *codec,
101162306a36Sopenharmony_ci				  const struct hda_fixup *fix, int action)
101262306a36Sopenharmony_ci{
101362306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE)
101462306a36Sopenharmony_ci		override_mic_boost(codec, 0x30, 0, 2, 40);
101562306a36Sopenharmony_ci}
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_cistatic void via_fixup_power_save(struct hda_codec *codec,
101862306a36Sopenharmony_ci				 const struct hda_fixup *fix, int action)
101962306a36Sopenharmony_ci{
102062306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE)
102162306a36Sopenharmony_ci		codec->power_save_node = 0;
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic const struct hda_fixup via_fixups[] = {
102562306a36Sopenharmony_ci	[VIA_FIXUP_INTMIC_BOOST] = {
102662306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
102762306a36Sopenharmony_ci		.v.func = via_fixup_intmic_boost,
102862306a36Sopenharmony_ci	},
102962306a36Sopenharmony_ci	[VIA_FIXUP_ASUS_G75] = {
103062306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
103162306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
103262306a36Sopenharmony_ci			/* set 0x24 and 0x33 as speakers */
103362306a36Sopenharmony_ci			{ 0x24, 0x991301f0 },
103462306a36Sopenharmony_ci			{ 0x33, 0x991301f1 }, /* subwoofer */
103562306a36Sopenharmony_ci			{ }
103662306a36Sopenharmony_ci		}
103762306a36Sopenharmony_ci	},
103862306a36Sopenharmony_ci	[VIA_FIXUP_POWER_SAVE] = {
103962306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
104062306a36Sopenharmony_ci		.v.func = via_fixup_power_save,
104162306a36Sopenharmony_ci	},
104262306a36Sopenharmony_ci};
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_cistatic const struct snd_pci_quirk vt2002p_fixups[] = {
104562306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x13f7, "Asus B23E", VIA_FIXUP_POWER_SAVE),
104662306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
104762306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
104862306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
104962306a36Sopenharmony_ci	{}
105062306a36Sopenharmony_ci};
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
105362306a36Sopenharmony_ci * Replace this with mixer NID 0x1c
105462306a36Sopenharmony_ci */
105562306a36Sopenharmony_cistatic void fix_vt1802_connections(struct hda_codec *codec)
105662306a36Sopenharmony_ci{
105762306a36Sopenharmony_ci	static const hda_nid_t conn_24[] = { 0x14, 0x1c };
105862306a36Sopenharmony_ci	static const hda_nid_t conn_33[] = { 0x1c };
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
106162306a36Sopenharmony_ci	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
106262306a36Sopenharmony_ci}
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci/* patch for vt2002P */
106562306a36Sopenharmony_cistatic int patch_vt2002P(struct hda_codec *codec)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	struct via_spec *spec;
106862306a36Sopenharmony_ci	int err;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* create a codec specific record */
107162306a36Sopenharmony_ci	spec = via_new_spec(codec);
107262306a36Sopenharmony_ci	if (spec == NULL)
107362306a36Sopenharmony_ci		return -ENOMEM;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x21;
107662306a36Sopenharmony_ci	override_mic_boost(codec, 0x2b, 0, 3, 40);
107762306a36Sopenharmony_ci	override_mic_boost(codec, 0x29, 0, 3, 40);
107862306a36Sopenharmony_ci	if (spec->codec_type == VT1802)
107962306a36Sopenharmony_ci		fix_vt1802_connections(codec);
108062306a36Sopenharmony_ci	add_secret_dac_path(codec);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
108362306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	if (spec->codec_type == VT1802)
108662306a36Sopenharmony_ci		err = snd_hda_add_verbs(codec, vt1802_init_verbs);
108762306a36Sopenharmony_ci	else
108862306a36Sopenharmony_ci		err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
108962306a36Sopenharmony_ci	if (err < 0)
109062306a36Sopenharmony_ci		goto error;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
109362306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
109462306a36Sopenharmony_ci	if (err < 0)
109562306a36Sopenharmony_ci		goto error;
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return 0;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci error:
110062306a36Sopenharmony_ci	via_free(codec);
110162306a36Sopenharmony_ci	return err;
110262306a36Sopenharmony_ci}
110362306a36Sopenharmony_ci
110462306a36Sopenharmony_ci/* for vt1812 */
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_cistatic const struct hda_verb vt1812_init_verbs[] = {
110762306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
110862306a36Sopenharmony_ci	{0x1, 0xfb9, 0x24},
110962306a36Sopenharmony_ci	/* Enable AOW0 to MW9 */
111062306a36Sopenharmony_ci	{0x1, 0xfb8, 0xa8},
111162306a36Sopenharmony_ci	{ }
111262306a36Sopenharmony_ci};
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci/* patch for vt1812 */
111562306a36Sopenharmony_cistatic int patch_vt1812(struct hda_codec *codec)
111662306a36Sopenharmony_ci{
111762306a36Sopenharmony_ci	struct via_spec *spec;
111862306a36Sopenharmony_ci	int err;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/* create a codec specific record */
112162306a36Sopenharmony_ci	spec = via_new_spec(codec);
112262306a36Sopenharmony_ci	if (spec == NULL)
112362306a36Sopenharmony_ci		return -ENOMEM;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x21;
112662306a36Sopenharmony_ci	override_mic_boost(codec, 0x2b, 0, 3, 40);
112762306a36Sopenharmony_ci	override_mic_boost(codec, 0x29, 0, 3, 40);
112862306a36Sopenharmony_ci	add_secret_dac_path(codec);
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt1812_init_verbs);
113162306a36Sopenharmony_ci	if (err < 0)
113262306a36Sopenharmony_ci		goto error;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
113562306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
113662306a36Sopenharmony_ci	if (err < 0)
113762306a36Sopenharmony_ci		goto error;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci	return 0;
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci error:
114262306a36Sopenharmony_ci	via_free(codec);
114362306a36Sopenharmony_ci	return err;
114462306a36Sopenharmony_ci}
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci/* patch for vt3476 */
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_cistatic const struct hda_verb vt3476_init_verbs[] = {
114962306a36Sopenharmony_ci	/* Enable DMic 8/16/32K */
115062306a36Sopenharmony_ci	{0x1, 0xF7B, 0x30},
115162306a36Sopenharmony_ci	/* Enable Boost Volume backdoor */
115262306a36Sopenharmony_ci	{0x1, 0xFB9, 0x20},
115362306a36Sopenharmony_ci	/* Enable AOW-MW9 path */
115462306a36Sopenharmony_ci	{0x1, 0xFB8, 0x10},
115562306a36Sopenharmony_ci	{ }
115662306a36Sopenharmony_ci};
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_cistatic int patch_vt3476(struct hda_codec *codec)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	struct via_spec *spec;
116162306a36Sopenharmony_ci	int err;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	/* create a codec specific record */
116462306a36Sopenharmony_ci	spec = via_new_spec(codec);
116562306a36Sopenharmony_ci	if (spec == NULL)
116662306a36Sopenharmony_ci		return -ENOMEM;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x3f;
116962306a36Sopenharmony_ci	add_secret_dac_path(codec);
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci	err = snd_hda_add_verbs(codec, vt3476_init_verbs);
117262306a36Sopenharmony_ci	if (err < 0)
117362306a36Sopenharmony_ci		goto error;
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	/* automatic parse from the BIOS config */
117662306a36Sopenharmony_ci	err = via_parse_auto_config(codec);
117762306a36Sopenharmony_ci	if (err < 0)
117862306a36Sopenharmony_ci		goto error;
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci error:
118362306a36Sopenharmony_ci	via_free(codec);
118462306a36Sopenharmony_ci	return err;
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci/*
118862306a36Sopenharmony_ci * patch entries
118962306a36Sopenharmony_ci */
119062306a36Sopenharmony_cistatic const struct hda_device_id snd_hda_id_via[] = {
119162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
119262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
119362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
119462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
119562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
119662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
119762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
119862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
119962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
120062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
120162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
120262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
120362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
120462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
120562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
120662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
120762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
120862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
120962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
121062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
121162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
121262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
121362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
121462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
121562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
121662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
121762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
121862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
121962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
122062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
122162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
122262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
122362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
122462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
122562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
122662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
122762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
122862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
122962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
123062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
123162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
123262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
123362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
123462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
123562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
123662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
123762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
123862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
123962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
124062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
124162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
124262306a36Sopenharmony_ci	{} /* terminator */
124362306a36Sopenharmony_ci};
124462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_cistatic struct hda_codec_driver via_driver = {
124762306a36Sopenharmony_ci	.id = snd_hda_id_via,
124862306a36Sopenharmony_ci};
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
125162306a36Sopenharmony_ciMODULE_DESCRIPTION("VIA HD-audio codec");
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_cimodule_hda_codec_driver(via_driver);
1254