162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984,
462306a36Sopenharmony_ci *   AD1986A, AD1988
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/init.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <sound/core.h>
1462306a36Sopenharmony_ci#include <sound/hda_codec.h>
1562306a36Sopenharmony_ci#include "hda_local.h"
1662306a36Sopenharmony_ci#include "hda_auto_parser.h"
1762306a36Sopenharmony_ci#include "hda_beep.h"
1862306a36Sopenharmony_ci#include "hda_jack.h"
1962306a36Sopenharmony_ci#include "hda_generic.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct ad198x_spec {
2362306a36Sopenharmony_ci	struct hda_gen_spec gen;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	/* for auto parser */
2662306a36Sopenharmony_ci	int smux_paths[4];
2762306a36Sopenharmony_ci	unsigned int cur_smux;
2862306a36Sopenharmony_ci	hda_nid_t eapd_nid;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	unsigned int beep_amp;	/* beep amp value, set via set_beep_amp() */
3162306a36Sopenharmony_ci	int num_smux_conns;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_INPUT_BEEP
3662306a36Sopenharmony_ci/* additional beep mixers; the actual parameters are overwritten at build */
3762306a36Sopenharmony_cistatic const struct snd_kcontrol_new ad_beep_mixer[] = {
3862306a36Sopenharmony_ci	HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT),
3962306a36Sopenharmony_ci	HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT),
4062306a36Sopenharmony_ci	{ } /* end */
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define set_beep_amp(spec, nid, idx, dir) \
4462306a36Sopenharmony_ci	((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
4562306a36Sopenharmony_ci#else
4662306a36Sopenharmony_ci#define set_beep_amp(spec, nid, idx, dir) /* NOP */
4762306a36Sopenharmony_ci#endif
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#ifdef CONFIG_SND_HDA_INPUT_BEEP
5062306a36Sopenharmony_cistatic int create_beep_ctls(struct hda_codec *codec)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
5362306a36Sopenharmony_ci	const struct snd_kcontrol_new *knew;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (!spec->beep_amp)
5662306a36Sopenharmony_ci		return 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	for (knew = ad_beep_mixer ; knew->name; knew++) {
5962306a36Sopenharmony_ci		int err;
6062306a36Sopenharmony_ci		struct snd_kcontrol *kctl;
6162306a36Sopenharmony_ci		kctl = snd_ctl_new1(knew, codec);
6262306a36Sopenharmony_ci		if (!kctl)
6362306a36Sopenharmony_ci			return -ENOMEM;
6462306a36Sopenharmony_ci		kctl->private_value = spec->beep_amp;
6562306a36Sopenharmony_ci		err = snd_hda_ctl_add(codec, 0, kctl);
6662306a36Sopenharmony_ci		if (err < 0)
6762306a36Sopenharmony_ci			return err;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	return 0;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci#else
7262306a36Sopenharmony_ci#define create_beep_ctls(codec)		0
7362306a36Sopenharmony_ci#endif
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci#ifdef CONFIG_PM
7662306a36Sopenharmony_cistatic void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
7762306a36Sopenharmony_ci				hda_nid_t hp)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD)
8062306a36Sopenharmony_ci		snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
8162306a36Sopenharmony_ci			    !codec->inv_eapd ? 0x00 : 0x02);
8262306a36Sopenharmony_ci	if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD)
8362306a36Sopenharmony_ci		snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
8462306a36Sopenharmony_ci			    !codec->inv_eapd ? 0x00 : 0x02);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic void ad198x_power_eapd(struct hda_codec *codec)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	/* We currently only handle front, HP */
9062306a36Sopenharmony_ci	switch (codec->core.vendor_id) {
9162306a36Sopenharmony_ci	case 0x11d41882:
9262306a36Sopenharmony_ci	case 0x11d4882a:
9362306a36Sopenharmony_ci	case 0x11d41884:
9462306a36Sopenharmony_ci	case 0x11d41984:
9562306a36Sopenharmony_ci	case 0x11d41883:
9662306a36Sopenharmony_ci	case 0x11d4184a:
9762306a36Sopenharmony_ci	case 0x11d4194a:
9862306a36Sopenharmony_ci	case 0x11d4194b:
9962306a36Sopenharmony_ci	case 0x11d41988:
10062306a36Sopenharmony_ci	case 0x11d4198b:
10162306a36Sopenharmony_ci	case 0x11d4989a:
10262306a36Sopenharmony_ci	case 0x11d4989b:
10362306a36Sopenharmony_ci		ad198x_power_eapd_write(codec, 0x12, 0x11);
10462306a36Sopenharmony_ci		break;
10562306a36Sopenharmony_ci	case 0x11d41981:
10662306a36Sopenharmony_ci	case 0x11d41983:
10762306a36Sopenharmony_ci		ad198x_power_eapd_write(codec, 0x05, 0x06);
10862306a36Sopenharmony_ci		break;
10962306a36Sopenharmony_ci	case 0x11d41986:
11062306a36Sopenharmony_ci		ad198x_power_eapd_write(codec, 0x1b, 0x1a);
11162306a36Sopenharmony_ci		break;
11262306a36Sopenharmony_ci	}
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic int ad198x_suspend(struct hda_codec *codec)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	snd_hda_shutup_pins(codec);
11862306a36Sopenharmony_ci	ad198x_power_eapd(codec);
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci/* follow EAPD via vmaster hook */
12462306a36Sopenharmony_cistatic void ad_vmaster_eapd_hook(void *private_data, int enabled)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct hda_codec *codec = private_data;
12762306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	if (!spec->eapd_nid)
13062306a36Sopenharmony_ci		return;
13162306a36Sopenharmony_ci	if (codec->inv_eapd)
13262306a36Sopenharmony_ci		enabled = !enabled;
13362306a36Sopenharmony_ci	snd_hda_codec_write_cache(codec, spec->eapd_nid, 0,
13462306a36Sopenharmony_ci				   AC_VERB_SET_EAPD_BTLENABLE,
13562306a36Sopenharmony_ci				   enabled ? 0x02 : 0x00);
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/*
13962306a36Sopenharmony_ci * Automatic parse of I/O pins from the BIOS configuration
14062306a36Sopenharmony_ci */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int ad198x_auto_build_controls(struct hda_codec *codec)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	int err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	err = snd_hda_gen_build_controls(codec);
14762306a36Sopenharmony_ci	if (err < 0)
14862306a36Sopenharmony_ci		return err;
14962306a36Sopenharmony_ci	err = create_beep_ctls(codec);
15062306a36Sopenharmony_ci	if (err < 0)
15162306a36Sopenharmony_ci		return err;
15262306a36Sopenharmony_ci	return 0;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic const struct hda_codec_ops ad198x_auto_patch_ops = {
15662306a36Sopenharmony_ci	.build_controls = ad198x_auto_build_controls,
15762306a36Sopenharmony_ci	.build_pcms = snd_hda_gen_build_pcms,
15862306a36Sopenharmony_ci	.init = snd_hda_gen_init,
15962306a36Sopenharmony_ci	.free = snd_hda_gen_free,
16062306a36Sopenharmony_ci	.unsol_event = snd_hda_jack_unsol_event,
16162306a36Sopenharmony_ci#ifdef CONFIG_PM
16262306a36Sopenharmony_ci	.check_power_status = snd_hda_gen_check_power_status,
16362306a36Sopenharmony_ci	.suspend = ad198x_suspend,
16462306a36Sopenharmony_ci#endif
16562306a36Sopenharmony_ci};
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
17162306a36Sopenharmony_ci	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
17262306a36Sopenharmony_ci	int err;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	codec->spdif_status_reset = 1;
17562306a36Sopenharmony_ci	codec->no_trigger_sense = 1;
17662306a36Sopenharmony_ci	codec->no_sticky_stream = 1;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	spec->gen.indep_hp = indep_hp;
17962306a36Sopenharmony_ci	if (!spec->gen.add_stereo_mix_input)
18062306a36Sopenharmony_ci		spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);
18362306a36Sopenharmony_ci	if (err < 0)
18462306a36Sopenharmony_ci		return err;
18562306a36Sopenharmony_ci	err = snd_hda_gen_parse_auto_config(codec, cfg);
18662306a36Sopenharmony_ci	if (err < 0)
18762306a36Sopenharmony_ci		return err;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return 0;
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci/*
19362306a36Sopenharmony_ci * AD1986A specific
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int alloc_ad_spec(struct hda_codec *codec)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	struct ad198x_spec *spec;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
20162306a36Sopenharmony_ci	if (!spec)
20262306a36Sopenharmony_ci		return -ENOMEM;
20362306a36Sopenharmony_ci	codec->spec = spec;
20462306a36Sopenharmony_ci	snd_hda_gen_spec_init(&spec->gen);
20562306a36Sopenharmony_ci	codec->patch_ops = ad198x_auto_patch_ops;
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci/*
21062306a36Sopenharmony_ci * AD1986A fixup codes
21162306a36Sopenharmony_ci */
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
21462306a36Sopenharmony_cistatic void ad_fixup_inv_jack_detect(struct hda_codec *codec,
21562306a36Sopenharmony_ci				     const struct hda_fixup *fix, int action)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
22062306a36Sopenharmony_ci		codec->inv_jack_detect = 1;
22162306a36Sopenharmony_ci		spec->gen.keep_eapd_on = 1;
22262306a36Sopenharmony_ci		spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
22362306a36Sopenharmony_ci		spec->eapd_nid = 0x1b;
22462306a36Sopenharmony_ci	}
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */
22862306a36Sopenharmony_cistatic void ad1986a_fixup_eapd(struct hda_codec *codec,
22962306a36Sopenharmony_ci			       const struct hda_fixup *fix, int action)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
23462306a36Sopenharmony_ci		codec->inv_eapd = 0;
23562306a36Sopenharmony_ci		spec->gen.keep_eapd_on = 1;
23662306a36Sopenharmony_ci		spec->eapd_nid = 0x1b;
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci/* enable stereo-mix input for avoiding regression on KDE (bko#88251) */
24162306a36Sopenharmony_cistatic void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec,
24262306a36Sopenharmony_ci				      const struct hda_fixup *fix, int action)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
24762306a36Sopenharmony_ci		ad1986a_fixup_eapd(codec, fix, action);
24862306a36Sopenharmony_ci		spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cienum {
25362306a36Sopenharmony_ci	AD1986A_FIXUP_INV_JACK_DETECT,
25462306a36Sopenharmony_ci	AD1986A_FIXUP_ULTRA,
25562306a36Sopenharmony_ci	AD1986A_FIXUP_SAMSUNG,
25662306a36Sopenharmony_ci	AD1986A_FIXUP_3STACK,
25762306a36Sopenharmony_ci	AD1986A_FIXUP_LAPTOP,
25862306a36Sopenharmony_ci	AD1986A_FIXUP_LAPTOP_IMIC,
25962306a36Sopenharmony_ci	AD1986A_FIXUP_EAPD,
26062306a36Sopenharmony_ci	AD1986A_FIXUP_EAPD_MIX_IN,
26162306a36Sopenharmony_ci	AD1986A_FIXUP_EASYNOTE,
26262306a36Sopenharmony_ci};
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_cistatic const struct hda_fixup ad1986a_fixups[] = {
26562306a36Sopenharmony_ci	[AD1986A_FIXUP_INV_JACK_DETECT] = {
26662306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
26762306a36Sopenharmony_ci		.v.func = ad_fixup_inv_jack_detect,
26862306a36Sopenharmony_ci	},
26962306a36Sopenharmony_ci	[AD1986A_FIXUP_ULTRA] = {
27062306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
27162306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
27262306a36Sopenharmony_ci			{ 0x1b, 0x90170110 }, /* speaker */
27362306a36Sopenharmony_ci			{ 0x1d, 0x90a7013e }, /* int mic */
27462306a36Sopenharmony_ci			{}
27562306a36Sopenharmony_ci		},
27662306a36Sopenharmony_ci	},
27762306a36Sopenharmony_ci	[AD1986A_FIXUP_SAMSUNG] = {
27862306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
27962306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
28062306a36Sopenharmony_ci			{ 0x1b, 0x90170110 }, /* speaker */
28162306a36Sopenharmony_ci			{ 0x1d, 0x90a7013e }, /* int mic */
28262306a36Sopenharmony_ci			{ 0x20, 0x411111f0 }, /* N/A */
28362306a36Sopenharmony_ci			{ 0x24, 0x411111f0 }, /* N/A */
28462306a36Sopenharmony_ci			{}
28562306a36Sopenharmony_ci		},
28662306a36Sopenharmony_ci	},
28762306a36Sopenharmony_ci	[AD1986A_FIXUP_3STACK] = {
28862306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
28962306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
29062306a36Sopenharmony_ci			{ 0x1a, 0x02214021 }, /* headphone */
29162306a36Sopenharmony_ci			{ 0x1b, 0x01014011 }, /* front */
29262306a36Sopenharmony_ci			{ 0x1c, 0x01813030 }, /* line-in */
29362306a36Sopenharmony_ci			{ 0x1d, 0x01a19020 }, /* rear mic */
29462306a36Sopenharmony_ci			{ 0x1e, 0x411111f0 }, /* N/A */
29562306a36Sopenharmony_ci			{ 0x1f, 0x02a190f0 }, /* mic */
29662306a36Sopenharmony_ci			{ 0x20, 0x411111f0 }, /* N/A */
29762306a36Sopenharmony_ci			{}
29862306a36Sopenharmony_ci		},
29962306a36Sopenharmony_ci	},
30062306a36Sopenharmony_ci	[AD1986A_FIXUP_LAPTOP] = {
30162306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
30262306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
30362306a36Sopenharmony_ci			{ 0x1a, 0x02214021 }, /* headphone */
30462306a36Sopenharmony_ci			{ 0x1b, 0x90170110 }, /* speaker */
30562306a36Sopenharmony_ci			{ 0x1c, 0x411111f0 }, /* N/A */
30662306a36Sopenharmony_ci			{ 0x1d, 0x411111f0 }, /* N/A */
30762306a36Sopenharmony_ci			{ 0x1e, 0x411111f0 }, /* N/A */
30862306a36Sopenharmony_ci			{ 0x1f, 0x02a191f0 }, /* mic */
30962306a36Sopenharmony_ci			{ 0x20, 0x411111f0 }, /* N/A */
31062306a36Sopenharmony_ci			{}
31162306a36Sopenharmony_ci		},
31262306a36Sopenharmony_ci	},
31362306a36Sopenharmony_ci	[AD1986A_FIXUP_LAPTOP_IMIC] = {
31462306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
31562306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
31662306a36Sopenharmony_ci			{ 0x1d, 0x90a7013e }, /* int mic */
31762306a36Sopenharmony_ci			{}
31862306a36Sopenharmony_ci		},
31962306a36Sopenharmony_ci		.chained_before = 1,
32062306a36Sopenharmony_ci		.chain_id = AD1986A_FIXUP_LAPTOP,
32162306a36Sopenharmony_ci	},
32262306a36Sopenharmony_ci	[AD1986A_FIXUP_EAPD] = {
32362306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
32462306a36Sopenharmony_ci		.v.func = ad1986a_fixup_eapd,
32562306a36Sopenharmony_ci	},
32662306a36Sopenharmony_ci	[AD1986A_FIXUP_EAPD_MIX_IN] = {
32762306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
32862306a36Sopenharmony_ci		.v.func = ad1986a_fixup_eapd_mix_in,
32962306a36Sopenharmony_ci	},
33062306a36Sopenharmony_ci	[AD1986A_FIXUP_EASYNOTE] = {
33162306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
33262306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
33362306a36Sopenharmony_ci			{ 0x1a, 0x0421402f }, /* headphone */
33462306a36Sopenharmony_ci			{ 0x1b, 0x90170110 }, /* speaker */
33562306a36Sopenharmony_ci			{ 0x1c, 0x411111f0 }, /* N/A */
33662306a36Sopenharmony_ci			{ 0x1d, 0x90a70130 }, /* int mic */
33762306a36Sopenharmony_ci			{ 0x1e, 0x411111f0 }, /* N/A */
33862306a36Sopenharmony_ci			{ 0x1f, 0x04a19040 }, /* mic */
33962306a36Sopenharmony_ci			{ 0x20, 0x411111f0 }, /* N/A */
34062306a36Sopenharmony_ci			{ 0x21, 0x411111f0 }, /* N/A */
34162306a36Sopenharmony_ci			{ 0x22, 0x411111f0 }, /* N/A */
34262306a36Sopenharmony_ci			{ 0x23, 0x411111f0 }, /* N/A */
34362306a36Sopenharmony_ci			{ 0x24, 0x411111f0 }, /* N/A */
34462306a36Sopenharmony_ci			{ 0x25, 0x411111f0 }, /* N/A */
34562306a36Sopenharmony_ci			{}
34662306a36Sopenharmony_ci		},
34762306a36Sopenharmony_ci		.chained = true,
34862306a36Sopenharmony_ci		.chain_id = AD1986A_FIXUP_EAPD_MIX_IN,
34962306a36Sopenharmony_ci	},
35062306a36Sopenharmony_ci};
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
35362306a36Sopenharmony_ci	SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
35462306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC),
35562306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD),
35662306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),
35762306a36Sopenharmony_ci	SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
35862306a36Sopenharmony_ci	SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
35962306a36Sopenharmony_ci	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
36062306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD),
36162306a36Sopenharmony_ci	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
36262306a36Sopenharmony_ci	SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
36362306a36Sopenharmony_ci	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
36462306a36Sopenharmony_ci	SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65", AD1986A_FIXUP_EASYNOTE),
36562306a36Sopenharmony_ci	SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
36662306a36Sopenharmony_ci	SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
36762306a36Sopenharmony_ci	SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
36862306a36Sopenharmony_ci	{}
36962306a36Sopenharmony_ci};
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic const struct hda_model_fixup ad1986a_fixup_models[] = {
37262306a36Sopenharmony_ci	{ .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
37362306a36Sopenharmony_ci	{ .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
37462306a36Sopenharmony_ci	{ .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
37562306a36Sopenharmony_ci	{ .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
37662306a36Sopenharmony_ci	{ .id = AD1986A_FIXUP_EAPD, .name = "eapd" },
37762306a36Sopenharmony_ci	{}
37862306a36Sopenharmony_ci};
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/*
38162306a36Sopenharmony_ci */
38262306a36Sopenharmony_cistatic int patch_ad1986a(struct hda_codec *codec)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int err;
38562306a36Sopenharmony_ci	struct ad198x_spec *spec;
38662306a36Sopenharmony_ci	static const hda_nid_t preferred_pairs[] = {
38762306a36Sopenharmony_ci		0x1a, 0x03,
38862306a36Sopenharmony_ci		0x1b, 0x03,
38962306a36Sopenharmony_ci		0x1c, 0x04,
39062306a36Sopenharmony_ci		0x1d, 0x05,
39162306a36Sopenharmony_ci		0x1e, 0x03,
39262306a36Sopenharmony_ci		0
39362306a36Sopenharmony_ci	};
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
39662306a36Sopenharmony_ci	if (err < 0)
39762306a36Sopenharmony_ci		return err;
39862306a36Sopenharmony_ci	spec = codec->spec;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/* AD1986A has the inverted EAPD implementation */
40162306a36Sopenharmony_ci	codec->inv_eapd = 1;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x07;
40462306a36Sopenharmony_ci	spec->gen.beep_nid = 0x19;
40562306a36Sopenharmony_ci	set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* AD1986A has a hardware problem that it can't share a stream
40862306a36Sopenharmony_ci	 * with multiple output pins.  The copy of front to surrounds
40962306a36Sopenharmony_ci	 * causes noisy or silent outputs at a certain timing, e.g.
41062306a36Sopenharmony_ci	 * changing the volume.
41162306a36Sopenharmony_ci	 * So, let's disable the shared stream.
41262306a36Sopenharmony_ci	 */
41362306a36Sopenharmony_ci	spec->gen.multiout.no_share_stream = 1;
41462306a36Sopenharmony_ci	/* give fixed DAC/pin pairs */
41562306a36Sopenharmony_ci	spec->gen.preferred_dacs = preferred_pairs;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	/* AD1986A can't manage the dynamic pin on/off smoothly */
41862306a36Sopenharmony_ci	spec->gen.auto_mute_via_amp = 1;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
42162306a36Sopenharmony_ci			   ad1986a_fixups);
42262306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, false);
42562306a36Sopenharmony_ci	if (err < 0) {
42662306a36Sopenharmony_ci		snd_hda_gen_free(codec);
42762306a36Sopenharmony_ci		return err;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return 0;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/*
43762306a36Sopenharmony_ci * AD1983 specific
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * SPDIF mux control for AD1983 auto-parser
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_cistatic int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
44462306a36Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
44562306a36Sopenharmony_ci{
44662306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
44762306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
44862306a36Sopenharmony_ci	static const char * const texts2[] = { "PCM", "ADC" };
44962306a36Sopenharmony_ci	static const char * const texts3[] = { "PCM", "ADC1", "ADC2" };
45062306a36Sopenharmony_ci	int num_conns = spec->num_smux_conns;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (num_conns == 2)
45362306a36Sopenharmony_ci		return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2);
45462306a36Sopenharmony_ci	else if (num_conns == 3)
45562306a36Sopenharmony_ci		return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3);
45662306a36Sopenharmony_ci	else
45762306a36Sopenharmony_ci		return -EINVAL;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
46162306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
46462306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = spec->cur_smux;
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci}
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
47162306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
47462306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
47562306a36Sopenharmony_ci	unsigned int val = ucontrol->value.enumerated.item[0];
47662306a36Sopenharmony_ci	hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
47762306a36Sopenharmony_ci	int num_conns = spec->num_smux_conns;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	if (val >= num_conns)
48062306a36Sopenharmony_ci		return -EINVAL;
48162306a36Sopenharmony_ci	if (spec->cur_smux == val)
48262306a36Sopenharmony_ci		return 0;
48362306a36Sopenharmony_ci	spec->cur_smux = val;
48462306a36Sopenharmony_ci	snd_hda_codec_write_cache(codec, dig_out, 0,
48562306a36Sopenharmony_ci				  AC_VERB_SET_CONNECT_SEL, val);
48662306a36Sopenharmony_ci	return 1;
48762306a36Sopenharmony_ci}
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_cistatic const struct snd_kcontrol_new ad1983_auto_smux_mixer = {
49062306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
49162306a36Sopenharmony_ci	.name = "IEC958 Playback Source",
49262306a36Sopenharmony_ci	.info = ad1983_auto_smux_enum_info,
49362306a36Sopenharmony_ci	.get = ad1983_auto_smux_enum_get,
49462306a36Sopenharmony_ci	.put = ad1983_auto_smux_enum_put,
49562306a36Sopenharmony_ci};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
50062306a36Sopenharmony_ci	hda_nid_t dig_out = spec->gen.multiout.dig_out_nid;
50162306a36Sopenharmony_ci	int num_conns;
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	if (!dig_out)
50462306a36Sopenharmony_ci		return 0;
50562306a36Sopenharmony_ci	num_conns = snd_hda_get_num_conns(codec, dig_out);
50662306a36Sopenharmony_ci	if (num_conns != 2 && num_conns != 3)
50762306a36Sopenharmony_ci		return 0;
50862306a36Sopenharmony_ci	spec->num_smux_conns = num_conns;
50962306a36Sopenharmony_ci	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer))
51062306a36Sopenharmony_ci		return -ENOMEM;
51162306a36Sopenharmony_ci	return 0;
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int patch_ad1983(struct hda_codec *codec)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	static const hda_nid_t conn_0c[] = { 0x08 };
51762306a36Sopenharmony_ci	static const hda_nid_t conn_0d[] = { 0x09 };
51862306a36Sopenharmony_ci	struct ad198x_spec *spec;
51962306a36Sopenharmony_ci	int err;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
52262306a36Sopenharmony_ci	if (err < 0)
52362306a36Sopenharmony_ci		return err;
52462306a36Sopenharmony_ci	spec = codec->spec;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x0e;
52762306a36Sopenharmony_ci	spec->gen.beep_nid = 0x10;
52862306a36Sopenharmony_ci	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* limit the loopback routes not to confuse the parser */
53162306a36Sopenharmony_ci	snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c);
53262306a36Sopenharmony_ci	snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d);
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, false);
53562306a36Sopenharmony_ci	if (err < 0)
53662306a36Sopenharmony_ci		goto error;
53762306a36Sopenharmony_ci	err = ad1983_add_spdif_mux_ctl(codec);
53862306a36Sopenharmony_ci	if (err < 0)
53962306a36Sopenharmony_ci		goto error;
54062306a36Sopenharmony_ci	return 0;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci error:
54362306a36Sopenharmony_ci	snd_hda_gen_free(codec);
54462306a36Sopenharmony_ci	return err;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci/*
54962306a36Sopenharmony_ci * AD1981 HD specific
55062306a36Sopenharmony_ci */
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cistatic void ad1981_fixup_hp_eapd(struct hda_codec *codec,
55362306a36Sopenharmony_ci				 const struct hda_fixup *fix, int action)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
55862306a36Sopenharmony_ci		spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
55962306a36Sopenharmony_ci		spec->eapd_nid = 0x05;
56062306a36Sopenharmony_ci	}
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci/* set the upper-limit for mixer amp to 0dB for avoiding the possible
56462306a36Sopenharmony_ci * damage by overloading
56562306a36Sopenharmony_ci */
56662306a36Sopenharmony_cistatic void ad1981_fixup_amp_override(struct hda_codec *codec,
56762306a36Sopenharmony_ci				      const struct hda_fixup *fix, int action)
56862306a36Sopenharmony_ci{
56962306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE)
57062306a36Sopenharmony_ci		snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
57162306a36Sopenharmony_ci					  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
57262306a36Sopenharmony_ci					  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
57362306a36Sopenharmony_ci					  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
57462306a36Sopenharmony_ci					  (1 << AC_AMPCAP_MUTE_SHIFT));
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cienum {
57862306a36Sopenharmony_ci	AD1981_FIXUP_AMP_OVERRIDE,
57962306a36Sopenharmony_ci	AD1981_FIXUP_HP_EAPD,
58062306a36Sopenharmony_ci};
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic const struct hda_fixup ad1981_fixups[] = {
58362306a36Sopenharmony_ci	[AD1981_FIXUP_AMP_OVERRIDE] = {
58462306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
58562306a36Sopenharmony_ci		.v.func = ad1981_fixup_amp_override,
58662306a36Sopenharmony_ci	},
58762306a36Sopenharmony_ci	[AD1981_FIXUP_HP_EAPD] = {
58862306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
58962306a36Sopenharmony_ci		.v.func = ad1981_fixup_hp_eapd,
59062306a36Sopenharmony_ci		.chained = true,
59162306a36Sopenharmony_ci		.chain_id = AD1981_FIXUP_AMP_OVERRIDE,
59262306a36Sopenharmony_ci	},
59362306a36Sopenharmony_ci};
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic const struct snd_pci_quirk ad1981_fixup_tbl[] = {
59662306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
59762306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD),
59862306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE),
59962306a36Sopenharmony_ci	/* HP nx6320 (reversed SSID, H/W bug) */
60062306a36Sopenharmony_ci	SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD),
60162306a36Sopenharmony_ci	{}
60262306a36Sopenharmony_ci};
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_cistatic int patch_ad1981(struct hda_codec *codec)
60562306a36Sopenharmony_ci{
60662306a36Sopenharmony_ci	struct ad198x_spec *spec;
60762306a36Sopenharmony_ci	int err;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
61062306a36Sopenharmony_ci	if (err < 0)
61162306a36Sopenharmony_ci		return -ENOMEM;
61262306a36Sopenharmony_ci	spec = codec->spec;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x0e;
61562306a36Sopenharmony_ci	spec->gen.beep_nid = 0x10;
61662306a36Sopenharmony_ci	set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);
61962306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, false);
62262306a36Sopenharmony_ci	if (err < 0)
62362306a36Sopenharmony_ci		goto error;
62462306a36Sopenharmony_ci	err = ad1983_add_spdif_mux_ctl(codec);
62562306a36Sopenharmony_ci	if (err < 0)
62662306a36Sopenharmony_ci		goto error;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return 0;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci error:
63362306a36Sopenharmony_ci	snd_hda_gen_free(codec);
63462306a36Sopenharmony_ci	return err;
63562306a36Sopenharmony_ci}
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/*
63962306a36Sopenharmony_ci * AD1988
64062306a36Sopenharmony_ci *
64162306a36Sopenharmony_ci * Output pins and routes
64262306a36Sopenharmony_ci *
64362306a36Sopenharmony_ci *        Pin               Mix     Sel     DAC (*)
64462306a36Sopenharmony_ci * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06
64562306a36Sopenharmony_ci * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06
64662306a36Sopenharmony_ci * port-C 0x15 (mute)    <- 0x2c <- 0x31 <- 05/0a
64762306a36Sopenharmony_ci * port-D 0x12 (mute/hp) <- 0x29         <- 04
64862306a36Sopenharmony_ci * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a
64962306a36Sopenharmony_ci * port-F 0x16 (mute)    <- 0x2a         <- 06
65062306a36Sopenharmony_ci * port-G 0x24 (mute)    <- 0x27         <- 05
65162306a36Sopenharmony_ci * port-H 0x25 (mute)    <- 0x28         <- 0a
65262306a36Sopenharmony_ci * mono   0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06
65362306a36Sopenharmony_ci *
65462306a36Sopenharmony_ci * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah
65562306a36Sopenharmony_ci * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug.
65662306a36Sopenharmony_ci *
65762306a36Sopenharmony_ci * Input pins and routes
65862306a36Sopenharmony_ci *
65962306a36Sopenharmony_ci *        pin     boost   mix input # / adc input #
66062306a36Sopenharmony_ci * port-A 0x11 -> 0x38 -> mix 2, ADC 0
66162306a36Sopenharmony_ci * port-B 0x14 -> 0x39 -> mix 0, ADC 1
66262306a36Sopenharmony_ci * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2
66362306a36Sopenharmony_ci * port-D 0x12 -> 0x3d -> mix 3, ADC 8
66462306a36Sopenharmony_ci * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4
66562306a36Sopenharmony_ci * port-F 0x16 -> 0x3b -> mix 5, ADC 3
66662306a36Sopenharmony_ci * port-G 0x24 -> N/A  -> 33:1 - mix 1, 34:1 - mix 4, ADC 6
66762306a36Sopenharmony_ci * port-H 0x25 -> N/A  -> 33:2 - mix 1, 34:2 - mix 4, ADC 7
66862306a36Sopenharmony_ci *
66962306a36Sopenharmony_ci *
67062306a36Sopenharmony_ci * DAC assignment
67162306a36Sopenharmony_ci *   6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03
67262306a36Sopenharmony_ci *   3stack - front/surr/CLFE/opt DACs - 04/05/0a/03
67362306a36Sopenharmony_ci *
67462306a36Sopenharmony_ci * Inputs of Analog Mix (0x20)
67562306a36Sopenharmony_ci *   0:Port-B (front mic)
67662306a36Sopenharmony_ci *   1:Port-C/G/H (line-in)
67762306a36Sopenharmony_ci *   2:Port-A
67862306a36Sopenharmony_ci *   3:Port-D (line-in/2)
67962306a36Sopenharmony_ci *   4:Port-E/G/H (mic-in)
68062306a36Sopenharmony_ci *   5:Port-F (mic2-in)
68162306a36Sopenharmony_ci *   6:CD
68262306a36Sopenharmony_ci *   7:Beep
68362306a36Sopenharmony_ci *
68462306a36Sopenharmony_ci * ADC selection
68562306a36Sopenharmony_ci *   0:Port-A
68662306a36Sopenharmony_ci *   1:Port-B (front mic-in)
68762306a36Sopenharmony_ci *   2:Port-C (line-in)
68862306a36Sopenharmony_ci *   3:Port-F (mic2-in)
68962306a36Sopenharmony_ci *   4:Port-E (mic-in)
69062306a36Sopenharmony_ci *   5:CD
69162306a36Sopenharmony_ci *   6:Port-G
69262306a36Sopenharmony_ci *   7:Port-H
69362306a36Sopenharmony_ci *   8:Port-D (line-in/2)
69462306a36Sopenharmony_ci *   9:Mix
69562306a36Sopenharmony_ci *
69662306a36Sopenharmony_ci * Proposed pin assignments by the datasheet
69762306a36Sopenharmony_ci *
69862306a36Sopenharmony_ci * 6-stack
69962306a36Sopenharmony_ci * Port-A front headphone
70062306a36Sopenharmony_ci *      B front mic-in
70162306a36Sopenharmony_ci *      C rear line-in
70262306a36Sopenharmony_ci *      D rear front-out
70362306a36Sopenharmony_ci *      E rear mic-in
70462306a36Sopenharmony_ci *      F rear surround
70562306a36Sopenharmony_ci *      G rear CLFE
70662306a36Sopenharmony_ci *      H rear side
70762306a36Sopenharmony_ci *
70862306a36Sopenharmony_ci * 3-stack
70962306a36Sopenharmony_ci * Port-A front headphone
71062306a36Sopenharmony_ci *      B front mic
71162306a36Sopenharmony_ci *      C rear line-in/surround
71262306a36Sopenharmony_ci *      D rear front-out
71362306a36Sopenharmony_ci *      E rear mic-in/CLFE
71462306a36Sopenharmony_ci *
71562306a36Sopenharmony_ci * laptop
71662306a36Sopenharmony_ci * Port-A headphone
71762306a36Sopenharmony_ci *      B mic-in
71862306a36Sopenharmony_ci *      C docking station
71962306a36Sopenharmony_ci *      D internal speaker (with EAPD)
72062306a36Sopenharmony_ci *      E/F quad mic array
72162306a36Sopenharmony_ci */
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_cistatic int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
72462306a36Sopenharmony_ci				      struct snd_ctl_elem_info *uinfo)
72562306a36Sopenharmony_ci{
72662306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
72762306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
72862306a36Sopenharmony_ci	static const char * const texts[] = {
72962306a36Sopenharmony_ci		"PCM", "ADC1", "ADC2", "ADC3",
73062306a36Sopenharmony_ci	};
73162306a36Sopenharmony_ci	int num_conns = spec->num_smux_conns;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (num_conns > 4)
73462306a36Sopenharmony_ci		num_conns = 4;
73562306a36Sopenharmony_ci	return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
73962306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
74262306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	ucontrol->value.enumerated.item[0] = spec->cur_smux;
74562306a36Sopenharmony_ci	return 0;
74662306a36Sopenharmony_ci}
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_cistatic int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
74962306a36Sopenharmony_ci				     struct snd_ctl_elem_value *ucontrol)
75062306a36Sopenharmony_ci{
75162306a36Sopenharmony_ci	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
75262306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
75362306a36Sopenharmony_ci	unsigned int val = ucontrol->value.enumerated.item[0];
75462306a36Sopenharmony_ci	struct nid_path *path;
75562306a36Sopenharmony_ci	int num_conns = spec->num_smux_conns;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	if (val >= num_conns)
75862306a36Sopenharmony_ci		return -EINVAL;
75962306a36Sopenharmony_ci	if (spec->cur_smux == val)
76062306a36Sopenharmony_ci		return 0;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	mutex_lock(&codec->control_mutex);
76362306a36Sopenharmony_ci	path = snd_hda_get_path_from_idx(codec,
76462306a36Sopenharmony_ci					 spec->smux_paths[spec->cur_smux]);
76562306a36Sopenharmony_ci	if (path)
76662306a36Sopenharmony_ci		snd_hda_activate_path(codec, path, false, true);
76762306a36Sopenharmony_ci	path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
76862306a36Sopenharmony_ci	if (path)
76962306a36Sopenharmony_ci		snd_hda_activate_path(codec, path, true, true);
77062306a36Sopenharmony_ci	spec->cur_smux = val;
77162306a36Sopenharmony_ci	mutex_unlock(&codec->control_mutex);
77262306a36Sopenharmony_ci	return 1;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic const struct snd_kcontrol_new ad1988_auto_smux_mixer = {
77662306a36Sopenharmony_ci	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
77762306a36Sopenharmony_ci	.name = "IEC958 Playback Source",
77862306a36Sopenharmony_ci	.info = ad1988_auto_smux_enum_info,
77962306a36Sopenharmony_ci	.get = ad1988_auto_smux_enum_get,
78062306a36Sopenharmony_ci	.put = ad1988_auto_smux_enum_put,
78162306a36Sopenharmony_ci};
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_cistatic int ad1988_auto_init(struct hda_codec *codec)
78462306a36Sopenharmony_ci{
78562306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
78662306a36Sopenharmony_ci	int i, err;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	err = snd_hda_gen_init(codec);
78962306a36Sopenharmony_ci	if (err < 0)
79062306a36Sopenharmony_ci		return err;
79162306a36Sopenharmony_ci	if (!spec->gen.autocfg.dig_outs)
79262306a36Sopenharmony_ci		return 0;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	for (i = 0; i < 4; i++) {
79562306a36Sopenharmony_ci		struct nid_path *path;
79662306a36Sopenharmony_ci		path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]);
79762306a36Sopenharmony_ci		if (path)
79862306a36Sopenharmony_ci			snd_hda_activate_path(codec, path, path->active, false);
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	return 0;
80262306a36Sopenharmony_ci}
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_cistatic int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
80762306a36Sopenharmony_ci	int i, num_conns;
80862306a36Sopenharmony_ci	/* we create four static faked paths, since AD codecs have odd
80962306a36Sopenharmony_ci	 * widget connections regarding the SPDIF out source
81062306a36Sopenharmony_ci	 */
81162306a36Sopenharmony_ci	static const struct nid_path fake_paths[4] = {
81262306a36Sopenharmony_ci		{
81362306a36Sopenharmony_ci			.depth = 3,
81462306a36Sopenharmony_ci			.path = { 0x02, 0x1d, 0x1b },
81562306a36Sopenharmony_ci			.idx = { 0, 0, 0 },
81662306a36Sopenharmony_ci			.multi = { 0, 0, 0 },
81762306a36Sopenharmony_ci		},
81862306a36Sopenharmony_ci		{
81962306a36Sopenharmony_ci			.depth = 4,
82062306a36Sopenharmony_ci			.path = { 0x08, 0x0b, 0x1d, 0x1b },
82162306a36Sopenharmony_ci			.idx = { 0, 0, 1, 0 },
82262306a36Sopenharmony_ci			.multi = { 0, 1, 0, 0 },
82362306a36Sopenharmony_ci		},
82462306a36Sopenharmony_ci		{
82562306a36Sopenharmony_ci			.depth = 4,
82662306a36Sopenharmony_ci			.path = { 0x09, 0x0b, 0x1d, 0x1b },
82762306a36Sopenharmony_ci			.idx = { 0, 1, 1, 0 },
82862306a36Sopenharmony_ci			.multi = { 0, 1, 0, 0 },
82962306a36Sopenharmony_ci		},
83062306a36Sopenharmony_ci		{
83162306a36Sopenharmony_ci			.depth = 4,
83262306a36Sopenharmony_ci			.path = { 0x0f, 0x0b, 0x1d, 0x1b },
83362306a36Sopenharmony_ci			.idx = { 0, 2, 1, 0 },
83462306a36Sopenharmony_ci			.multi = { 0, 1, 0, 0 },
83562306a36Sopenharmony_ci		},
83662306a36Sopenharmony_ci	};
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	/* SPDIF source mux appears to be present only on AD1988A */
83962306a36Sopenharmony_ci	if (!spec->gen.autocfg.dig_outs ||
84062306a36Sopenharmony_ci	    get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX)
84162306a36Sopenharmony_ci		return 0;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
84462306a36Sopenharmony_ci	if (num_conns != 3 && num_conns != 4)
84562306a36Sopenharmony_ci		return 0;
84662306a36Sopenharmony_ci	spec->num_smux_conns = num_conns;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	for (i = 0; i < num_conns; i++) {
84962306a36Sopenharmony_ci		struct nid_path *path = snd_array_new(&spec->gen.paths);
85062306a36Sopenharmony_ci		if (!path)
85162306a36Sopenharmony_ci			return -ENOMEM;
85262306a36Sopenharmony_ci		*path = fake_paths[i];
85362306a36Sopenharmony_ci		if (!i)
85462306a36Sopenharmony_ci			path->active = 1;
85562306a36Sopenharmony_ci		spec->smux_paths[i] = snd_hda_get_path_idx(codec, path);
85662306a36Sopenharmony_ci	}
85762306a36Sopenharmony_ci
85862306a36Sopenharmony_ci	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer))
85962306a36Sopenharmony_ci		return -ENOMEM;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	codec->patch_ops.init = ad1988_auto_init;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	return 0;
86462306a36Sopenharmony_ci}
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci/*
86762306a36Sopenharmony_ci */
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_cienum {
87062306a36Sopenharmony_ci	AD1988_FIXUP_6STACK_DIG,
87162306a36Sopenharmony_ci};
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic const struct hda_fixup ad1988_fixups[] = {
87462306a36Sopenharmony_ci	[AD1988_FIXUP_6STACK_DIG] = {
87562306a36Sopenharmony_ci		.type = HDA_FIXUP_PINS,
87662306a36Sopenharmony_ci		.v.pins = (const struct hda_pintbl[]) {
87762306a36Sopenharmony_ci			{ 0x11, 0x02214130 }, /* front-hp */
87862306a36Sopenharmony_ci			{ 0x12, 0x01014010 }, /* line-out */
87962306a36Sopenharmony_ci			{ 0x14, 0x02a19122 }, /* front-mic */
88062306a36Sopenharmony_ci			{ 0x15, 0x01813021 }, /* line-in */
88162306a36Sopenharmony_ci			{ 0x16, 0x01011012 }, /* line-out */
88262306a36Sopenharmony_ci			{ 0x17, 0x01a19020 }, /* mic */
88362306a36Sopenharmony_ci			{ 0x1b, 0x0145f1f0 }, /* SPDIF */
88462306a36Sopenharmony_ci			{ 0x24, 0x01016011 }, /* line-out */
88562306a36Sopenharmony_ci			{ 0x25, 0x01012013 }, /* line-out */
88662306a36Sopenharmony_ci			{ }
88762306a36Sopenharmony_ci		}
88862306a36Sopenharmony_ci	},
88962306a36Sopenharmony_ci};
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic const struct hda_model_fixup ad1988_fixup_models[] = {
89262306a36Sopenharmony_ci	{ .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
89362306a36Sopenharmony_ci	{}
89462306a36Sopenharmony_ci};
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_cistatic int patch_ad1988(struct hda_codec *codec)
89762306a36Sopenharmony_ci{
89862306a36Sopenharmony_ci	struct ad198x_spec *spec;
89962306a36Sopenharmony_ci	int err;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
90262306a36Sopenharmony_ci	if (err < 0)
90362306a36Sopenharmony_ci		return err;
90462306a36Sopenharmony_ci	spec = codec->spec;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x20;
90762306a36Sopenharmony_ci	spec->gen.mixer_merge_nid = 0x21;
90862306a36Sopenharmony_ci	spec->gen.beep_nid = 0x10;
90962306a36Sopenharmony_ci	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
91262306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, true);
91562306a36Sopenharmony_ci	if (err < 0)
91662306a36Sopenharmony_ci		goto error;
91762306a36Sopenharmony_ci	err = ad1988_add_spdif_mux_ctl(codec);
91862306a36Sopenharmony_ci	if (err < 0)
91962306a36Sopenharmony_ci		goto error;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	return 0;
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci error:
92662306a36Sopenharmony_ci	snd_hda_gen_free(codec);
92762306a36Sopenharmony_ci	return err;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci/*
93262306a36Sopenharmony_ci * AD1884 / AD1984
93362306a36Sopenharmony_ci *
93462306a36Sopenharmony_ci * port-B - front line/mic-in
93562306a36Sopenharmony_ci * port-E - aux in/out
93662306a36Sopenharmony_ci * port-F - aux in/out
93762306a36Sopenharmony_ci * port-C - rear line/mic-in
93862306a36Sopenharmony_ci * port-D - rear line/hp-out
93962306a36Sopenharmony_ci * port-A - front line/hp-out
94062306a36Sopenharmony_ci *
94162306a36Sopenharmony_ci * AD1984 = AD1884 + two digital mic-ins
94262306a36Sopenharmony_ci *
94362306a36Sopenharmony_ci * AD1883 / AD1884A / AD1984A / AD1984B
94462306a36Sopenharmony_ci *
94562306a36Sopenharmony_ci * port-B (0x14) - front mic-in
94662306a36Sopenharmony_ci * port-E (0x1c) - rear mic-in
94762306a36Sopenharmony_ci * port-F (0x16) - CD / ext out
94862306a36Sopenharmony_ci * port-C (0x15) - rear line-in
94962306a36Sopenharmony_ci * port-D (0x12) - rear line-out
95062306a36Sopenharmony_ci * port-A (0x11) - front hp-out
95162306a36Sopenharmony_ci *
95262306a36Sopenharmony_ci * AD1984A = AD1884A + digital-mic
95362306a36Sopenharmony_ci * AD1883 = equivalent with AD1984A
95462306a36Sopenharmony_ci * AD1984B = AD1984A + extra SPDIF-out
95562306a36Sopenharmony_ci */
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci/* set the upper-limit for mixer amp to 0dB for avoiding the possible
95862306a36Sopenharmony_ci * damage by overloading
95962306a36Sopenharmony_ci */
96062306a36Sopenharmony_cistatic void ad1884_fixup_amp_override(struct hda_codec *codec,
96162306a36Sopenharmony_ci				      const struct hda_fixup *fix, int action)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE)
96462306a36Sopenharmony_ci		snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
96562306a36Sopenharmony_ci					  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
96662306a36Sopenharmony_ci					  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
96762306a36Sopenharmony_ci					  (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
96862306a36Sopenharmony_ci					  (1 << AC_AMPCAP_MUTE_SHIFT));
96962306a36Sopenharmony_ci}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci/* toggle GPIO1 according to the mute state */
97262306a36Sopenharmony_cistatic void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
97362306a36Sopenharmony_ci{
97462306a36Sopenharmony_ci	struct hda_codec *codec = private_data;
97562306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	if (spec->eapd_nid)
97862306a36Sopenharmony_ci		ad_vmaster_eapd_hook(private_data, enabled);
97962306a36Sopenharmony_ci	snd_hda_codec_write_cache(codec, 0x01, 0,
98062306a36Sopenharmony_ci				   AC_VERB_SET_GPIO_DATA,
98162306a36Sopenharmony_ci				   enabled ? 0x00 : 0x02);
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_cistatic void ad1884_fixup_hp_eapd(struct hda_codec *codec,
98562306a36Sopenharmony_ci				 const struct hda_fixup *fix, int action)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	switch (action) {
99062306a36Sopenharmony_ci	case HDA_FIXUP_ACT_PRE_PROBE:
99162306a36Sopenharmony_ci		spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
99262306a36Sopenharmony_ci		spec->gen.own_eapd_ctl = 1;
99362306a36Sopenharmony_ci		snd_hda_codec_write_cache(codec, 0x01, 0,
99462306a36Sopenharmony_ci					  AC_VERB_SET_GPIO_MASK, 0x02);
99562306a36Sopenharmony_ci		snd_hda_codec_write_cache(codec, 0x01, 0,
99662306a36Sopenharmony_ci					  AC_VERB_SET_GPIO_DIRECTION, 0x02);
99762306a36Sopenharmony_ci		snd_hda_codec_write_cache(codec, 0x01, 0,
99862306a36Sopenharmony_ci					  AC_VERB_SET_GPIO_DATA, 0x02);
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	case HDA_FIXUP_ACT_PROBE:
100162306a36Sopenharmony_ci		if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
100262306a36Sopenharmony_ci			spec->eapd_nid = spec->gen.autocfg.line_out_pins[0];
100362306a36Sopenharmony_ci		else
100462306a36Sopenharmony_ci			spec->eapd_nid = spec->gen.autocfg.speaker_pins[0];
100562306a36Sopenharmony_ci		break;
100662306a36Sopenharmony_ci	}
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic void ad1884_fixup_thinkpad(struct hda_codec *codec,
101062306a36Sopenharmony_ci				  const struct hda_fixup *fix, int action)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct ad198x_spec *spec = codec->spec;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	if (action == HDA_FIXUP_ACT_PRE_PROBE) {
101562306a36Sopenharmony_ci		spec->gen.keep_eapd_on = 1;
101662306a36Sopenharmony_ci		spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
101762306a36Sopenharmony_ci		spec->eapd_nid = 0x12;
101862306a36Sopenharmony_ci		/* Analog PC Beeper - allow firmware/ACPI beeps */
101962306a36Sopenharmony_ci		spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT);
102062306a36Sopenharmony_ci		spec->gen.beep_nid = 0; /* no digital beep */
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci/* set magic COEFs for dmic */
102562306a36Sopenharmony_cistatic const struct hda_verb ad1884_dmic_init_verbs[] = {
102662306a36Sopenharmony_ci	{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
102762306a36Sopenharmony_ci	{0x01, AC_VERB_SET_PROC_COEF, 0x08},
102862306a36Sopenharmony_ci	{}
102962306a36Sopenharmony_ci};
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_cienum {
103262306a36Sopenharmony_ci	AD1884_FIXUP_AMP_OVERRIDE,
103362306a36Sopenharmony_ci	AD1884_FIXUP_HP_EAPD,
103462306a36Sopenharmony_ci	AD1884_FIXUP_DMIC_COEF,
103562306a36Sopenharmony_ci	AD1884_FIXUP_THINKPAD,
103662306a36Sopenharmony_ci	AD1884_FIXUP_HP_TOUCHSMART,
103762306a36Sopenharmony_ci};
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_cistatic const struct hda_fixup ad1884_fixups[] = {
104062306a36Sopenharmony_ci	[AD1884_FIXUP_AMP_OVERRIDE] = {
104162306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
104262306a36Sopenharmony_ci		.v.func = ad1884_fixup_amp_override,
104362306a36Sopenharmony_ci	},
104462306a36Sopenharmony_ci	[AD1884_FIXUP_HP_EAPD] = {
104562306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
104662306a36Sopenharmony_ci		.v.func = ad1884_fixup_hp_eapd,
104762306a36Sopenharmony_ci		.chained = true,
104862306a36Sopenharmony_ci		.chain_id = AD1884_FIXUP_AMP_OVERRIDE,
104962306a36Sopenharmony_ci	},
105062306a36Sopenharmony_ci	[AD1884_FIXUP_DMIC_COEF] = {
105162306a36Sopenharmony_ci		.type = HDA_FIXUP_VERBS,
105262306a36Sopenharmony_ci		.v.verbs = ad1884_dmic_init_verbs,
105362306a36Sopenharmony_ci	},
105462306a36Sopenharmony_ci	[AD1884_FIXUP_THINKPAD] = {
105562306a36Sopenharmony_ci		.type = HDA_FIXUP_FUNC,
105662306a36Sopenharmony_ci		.v.func = ad1884_fixup_thinkpad,
105762306a36Sopenharmony_ci		.chained = true,
105862306a36Sopenharmony_ci		.chain_id = AD1884_FIXUP_DMIC_COEF,
105962306a36Sopenharmony_ci	},
106062306a36Sopenharmony_ci	[AD1884_FIXUP_HP_TOUCHSMART] = {
106162306a36Sopenharmony_ci		.type = HDA_FIXUP_VERBS,
106262306a36Sopenharmony_ci		.v.verbs = ad1884_dmic_init_verbs,
106362306a36Sopenharmony_ci		.chained = true,
106462306a36Sopenharmony_ci		.chain_id = AD1884_FIXUP_HP_EAPD,
106562306a36Sopenharmony_ci	},
106662306a36Sopenharmony_ci};
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_cistatic const struct snd_pci_quirk ad1884_fixup_tbl[] = {
106962306a36Sopenharmony_ci	SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
107062306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
107162306a36Sopenharmony_ci	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),
107262306a36Sopenharmony_ci	{}
107362306a36Sopenharmony_ci};
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_cistatic int patch_ad1884(struct hda_codec *codec)
107762306a36Sopenharmony_ci{
107862306a36Sopenharmony_ci	struct ad198x_spec *spec;
107962306a36Sopenharmony_ci	int err;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
108262306a36Sopenharmony_ci	if (err < 0)
108362306a36Sopenharmony_ci		return err;
108462306a36Sopenharmony_ci	spec = codec->spec;
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x20;
108762306a36Sopenharmony_ci	spec->gen.mixer_merge_nid = 0x21;
108862306a36Sopenharmony_ci	spec->gen.beep_nid = 0x10;
108962306a36Sopenharmony_ci	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);
109262306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, true);
109562306a36Sopenharmony_ci	if (err < 0)
109662306a36Sopenharmony_ci		goto error;
109762306a36Sopenharmony_ci	err = ad1983_add_spdif_mux_ctl(codec);
109862306a36Sopenharmony_ci	if (err < 0)
109962306a36Sopenharmony_ci		goto error;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return 0;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci error:
110662306a36Sopenharmony_ci	snd_hda_gen_free(codec);
110762306a36Sopenharmony_ci	return err;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci/*
111162306a36Sopenharmony_ci * AD1882 / AD1882A
111262306a36Sopenharmony_ci *
111362306a36Sopenharmony_ci * port-A - front hp-out
111462306a36Sopenharmony_ci * port-B - front mic-in
111562306a36Sopenharmony_ci * port-C - rear line-in, shared surr-out (3stack)
111662306a36Sopenharmony_ci * port-D - rear line-out
111762306a36Sopenharmony_ci * port-E - rear mic-in, shared clfe-out (3stack)
111862306a36Sopenharmony_ci * port-F - rear surr-out (6stack)
111962306a36Sopenharmony_ci * port-G - rear clfe-out (6stack)
112062306a36Sopenharmony_ci */
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_cistatic int patch_ad1882(struct hda_codec *codec)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct ad198x_spec *spec;
112562306a36Sopenharmony_ci	int err;
112662306a36Sopenharmony_ci
112762306a36Sopenharmony_ci	err = alloc_ad_spec(codec);
112862306a36Sopenharmony_ci	if (err < 0)
112962306a36Sopenharmony_ci		return err;
113062306a36Sopenharmony_ci	spec = codec->spec;
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	spec->gen.mixer_nid = 0x20;
113362306a36Sopenharmony_ci	spec->gen.mixer_merge_nid = 0x21;
113462306a36Sopenharmony_ci	spec->gen.beep_nid = 0x10;
113562306a36Sopenharmony_ci	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
113662306a36Sopenharmony_ci	err = ad198x_parse_auto_config(codec, true);
113762306a36Sopenharmony_ci	if (err < 0)
113862306a36Sopenharmony_ci		goto error;
113962306a36Sopenharmony_ci	err = ad1988_add_spdif_mux_ctl(codec);
114062306a36Sopenharmony_ci	if (err < 0)
114162306a36Sopenharmony_ci		goto error;
114262306a36Sopenharmony_ci	return 0;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci error:
114562306a36Sopenharmony_ci	snd_hda_gen_free(codec);
114662306a36Sopenharmony_ci	return err;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci/*
115162306a36Sopenharmony_ci * patch entries
115262306a36Sopenharmony_ci */
115362306a36Sopenharmony_cistatic const struct hda_device_id snd_hda_id_analog[] = {
115462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
115562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
115662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
115762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
115862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
115962306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
116062306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
116162306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
116262306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
116362306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
116462306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
116562306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
116662306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
116762306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
116862306a36Sopenharmony_ci	HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
116962306a36Sopenharmony_ci	{} /* terminator */
117062306a36Sopenharmony_ci};
117162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
117462306a36Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices HD-audio codec");
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_cistatic struct hda_codec_driver analog_driver = {
117762306a36Sopenharmony_ci	.id = snd_hda_id_analog,
117862306a36Sopenharmony_ci};
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_cimodule_hda_codec_driver(analog_driver);
1181