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