18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * HD audio interface patch for AD1882, AD1884, AD1981HD, AD1983, AD1984, 48c2ecf20Sopenharmony_ci * AD1986A, AD1988 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2005-2007 Takashi Iwai <tiwai@suse.de> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <sound/core.h> 148c2ecf20Sopenharmony_ci#include <sound/hda_codec.h> 158c2ecf20Sopenharmony_ci#include "hda_local.h" 168c2ecf20Sopenharmony_ci#include "hda_auto_parser.h" 178c2ecf20Sopenharmony_ci#include "hda_beep.h" 188c2ecf20Sopenharmony_ci#include "hda_jack.h" 198c2ecf20Sopenharmony_ci#include "hda_generic.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct ad198x_spec { 238c2ecf20Sopenharmony_ci struct hda_gen_spec gen; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci /* for auto parser */ 268c2ecf20Sopenharmony_ci int smux_paths[4]; 278c2ecf20Sopenharmony_ci unsigned int cur_smux; 288c2ecf20Sopenharmony_ci hda_nid_t eapd_nid; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci unsigned int beep_amp; /* beep amp value, set via set_beep_amp() */ 318c2ecf20Sopenharmony_ci int num_smux_conns; 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_INPUT_BEEP 368c2ecf20Sopenharmony_ci/* additional beep mixers; the actual parameters are overwritten at build */ 378c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new ad_beep_mixer[] = { 388c2ecf20Sopenharmony_ci HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), 398c2ecf20Sopenharmony_ci HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), 408c2ecf20Sopenharmony_ci { } /* end */ 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci#define set_beep_amp(spec, nid, idx, dir) \ 448c2ecf20Sopenharmony_ci ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ 458c2ecf20Sopenharmony_ci#else 468c2ecf20Sopenharmony_ci#define set_beep_amp(spec, nid, idx, dir) /* NOP */ 478c2ecf20Sopenharmony_ci#endif 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#ifdef CONFIG_SND_HDA_INPUT_BEEP 508c2ecf20Sopenharmony_cistatic int create_beep_ctls(struct hda_codec *codec) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 538c2ecf20Sopenharmony_ci const struct snd_kcontrol_new *knew; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci if (!spec->beep_amp) 568c2ecf20Sopenharmony_ci return 0; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci for (knew = ad_beep_mixer ; knew->name; knew++) { 598c2ecf20Sopenharmony_ci int err; 608c2ecf20Sopenharmony_ci struct snd_kcontrol *kctl; 618c2ecf20Sopenharmony_ci kctl = snd_ctl_new1(knew, codec); 628c2ecf20Sopenharmony_ci if (!kctl) 638c2ecf20Sopenharmony_ci return -ENOMEM; 648c2ecf20Sopenharmony_ci kctl->private_value = spec->beep_amp; 658c2ecf20Sopenharmony_ci err = snd_hda_ctl_add(codec, 0, kctl); 668c2ecf20Sopenharmony_ci if (err < 0) 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci return 0; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci#else 728c2ecf20Sopenharmony_ci#define create_beep_ctls(codec) 0 738c2ecf20Sopenharmony_ci#endif 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front, 778c2ecf20Sopenharmony_ci hda_nid_t hp) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci if (snd_hda_query_pin_caps(codec, front) & AC_PINCAP_EAPD) 808c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE, 818c2ecf20Sopenharmony_ci !codec->inv_eapd ? 0x00 : 0x02); 828c2ecf20Sopenharmony_ci if (snd_hda_query_pin_caps(codec, hp) & AC_PINCAP_EAPD) 838c2ecf20Sopenharmony_ci snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE, 848c2ecf20Sopenharmony_ci !codec->inv_eapd ? 0x00 : 0x02); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void ad198x_power_eapd(struct hda_codec *codec) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci /* We currently only handle front, HP */ 908c2ecf20Sopenharmony_ci switch (codec->core.vendor_id) { 918c2ecf20Sopenharmony_ci case 0x11d41882: 928c2ecf20Sopenharmony_ci case 0x11d4882a: 938c2ecf20Sopenharmony_ci case 0x11d41884: 948c2ecf20Sopenharmony_ci case 0x11d41984: 958c2ecf20Sopenharmony_ci case 0x11d41883: 968c2ecf20Sopenharmony_ci case 0x11d4184a: 978c2ecf20Sopenharmony_ci case 0x11d4194a: 988c2ecf20Sopenharmony_ci case 0x11d4194b: 998c2ecf20Sopenharmony_ci case 0x11d41988: 1008c2ecf20Sopenharmony_ci case 0x11d4198b: 1018c2ecf20Sopenharmony_ci case 0x11d4989a: 1028c2ecf20Sopenharmony_ci case 0x11d4989b: 1038c2ecf20Sopenharmony_ci ad198x_power_eapd_write(codec, 0x12, 0x11); 1048c2ecf20Sopenharmony_ci break; 1058c2ecf20Sopenharmony_ci case 0x11d41981: 1068c2ecf20Sopenharmony_ci case 0x11d41983: 1078c2ecf20Sopenharmony_ci ad198x_power_eapd_write(codec, 0x05, 0x06); 1088c2ecf20Sopenharmony_ci break; 1098c2ecf20Sopenharmony_ci case 0x11d41986: 1108c2ecf20Sopenharmony_ci ad198x_power_eapd_write(codec, 0x1b, 0x1a); 1118c2ecf20Sopenharmony_ci break; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic void ad198x_shutup(struct hda_codec *codec) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci snd_hda_shutup_pins(codec); 1188c2ecf20Sopenharmony_ci ad198x_power_eapd(codec); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1228c2ecf20Sopenharmony_cistatic int ad198x_suspend(struct hda_codec *codec) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci ad198x_shutup(codec); 1258c2ecf20Sopenharmony_ci return 0; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci#endif 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* follow EAPD via vmaster hook */ 1308c2ecf20Sopenharmony_cistatic void ad_vmaster_eapd_hook(void *private_data, int enabled) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct hda_codec *codec = private_data; 1338c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci if (!spec->eapd_nid) 1368c2ecf20Sopenharmony_ci return; 1378c2ecf20Sopenharmony_ci if (codec->inv_eapd) 1388c2ecf20Sopenharmony_ci enabled = !enabled; 1398c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, spec->eapd_nid, 0, 1408c2ecf20Sopenharmony_ci AC_VERB_SET_EAPD_BTLENABLE, 1418c2ecf20Sopenharmony_ci enabled ? 0x02 : 0x00); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * Automatic parse of I/O pins from the BIOS configuration 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int ad198x_auto_build_controls(struct hda_codec *codec) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int err; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci err = snd_hda_gen_build_controls(codec); 1538c2ecf20Sopenharmony_ci if (err < 0) 1548c2ecf20Sopenharmony_ci return err; 1558c2ecf20Sopenharmony_ci err = create_beep_ctls(codec); 1568c2ecf20Sopenharmony_ci if (err < 0) 1578c2ecf20Sopenharmony_ci return err; 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic const struct hda_codec_ops ad198x_auto_patch_ops = { 1628c2ecf20Sopenharmony_ci .build_controls = ad198x_auto_build_controls, 1638c2ecf20Sopenharmony_ci .build_pcms = snd_hda_gen_build_pcms, 1648c2ecf20Sopenharmony_ci .init = snd_hda_gen_init, 1658c2ecf20Sopenharmony_ci .free = snd_hda_gen_free, 1668c2ecf20Sopenharmony_ci .unsol_event = snd_hda_jack_unsol_event, 1678c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1688c2ecf20Sopenharmony_ci .check_power_status = snd_hda_gen_check_power_status, 1698c2ecf20Sopenharmony_ci .suspend = ad198x_suspend, 1708c2ecf20Sopenharmony_ci#endif 1718c2ecf20Sopenharmony_ci .reboot_notify = ad198x_shutup, 1728c2ecf20Sopenharmony_ci}; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 1788c2ecf20Sopenharmony_ci struct auto_pin_cfg *cfg = &spec->gen.autocfg; 1798c2ecf20Sopenharmony_ci int err; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci codec->spdif_status_reset = 1; 1828c2ecf20Sopenharmony_ci codec->no_trigger_sense = 1; 1838c2ecf20Sopenharmony_ci codec->no_sticky_stream = 1; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci spec->gen.indep_hp = indep_hp; 1868c2ecf20Sopenharmony_ci if (!spec->gen.add_stereo_mix_input) 1878c2ecf20Sopenharmony_ci spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0); 1908c2ecf20Sopenharmony_ci if (err < 0) 1918c2ecf20Sopenharmony_ci return err; 1928c2ecf20Sopenharmony_ci err = snd_hda_gen_parse_auto_config(codec, cfg); 1938c2ecf20Sopenharmony_ci if (err < 0) 1948c2ecf20Sopenharmony_ci return err; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci return 0; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* 2008c2ecf20Sopenharmony_ci * AD1986A specific 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int alloc_ad_spec(struct hda_codec *codec) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2088c2ecf20Sopenharmony_ci if (!spec) 2098c2ecf20Sopenharmony_ci return -ENOMEM; 2108c2ecf20Sopenharmony_ci codec->spec = spec; 2118c2ecf20Sopenharmony_ci snd_hda_gen_spec_init(&spec->gen); 2128c2ecf20Sopenharmony_ci codec->patch_ops = ad198x_auto_patch_ops; 2138c2ecf20Sopenharmony_ci return 0; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * AD1986A fixup codes 2188c2ecf20Sopenharmony_ci */ 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ 2218c2ecf20Sopenharmony_cistatic void ad_fixup_inv_jack_detect(struct hda_codec *codec, 2228c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) { 2278c2ecf20Sopenharmony_ci codec->inv_jack_detect = 1; 2288c2ecf20Sopenharmony_ci spec->gen.keep_eapd_on = 1; 2298c2ecf20Sopenharmony_ci spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; 2308c2ecf20Sopenharmony_ci spec->eapd_nid = 0x1b; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */ 2358c2ecf20Sopenharmony_cistatic void ad1986a_fixup_eapd(struct hda_codec *codec, 2368c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) { 2418c2ecf20Sopenharmony_ci codec->inv_eapd = 0; 2428c2ecf20Sopenharmony_ci spec->gen.keep_eapd_on = 1; 2438c2ecf20Sopenharmony_ci spec->eapd_nid = 0x1b; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* enable stereo-mix input for avoiding regression on KDE (bko#88251) */ 2488c2ecf20Sopenharmony_cistatic void ad1986a_fixup_eapd_mix_in(struct hda_codec *codec, 2498c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) { 2548c2ecf20Sopenharmony_ci ad1986a_fixup_eapd(codec, fix, action); 2558c2ecf20Sopenharmony_ci spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_ENABLE; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cienum { 2608c2ecf20Sopenharmony_ci AD1986A_FIXUP_INV_JACK_DETECT, 2618c2ecf20Sopenharmony_ci AD1986A_FIXUP_ULTRA, 2628c2ecf20Sopenharmony_ci AD1986A_FIXUP_SAMSUNG, 2638c2ecf20Sopenharmony_ci AD1986A_FIXUP_3STACK, 2648c2ecf20Sopenharmony_ci AD1986A_FIXUP_LAPTOP, 2658c2ecf20Sopenharmony_ci AD1986A_FIXUP_LAPTOP_IMIC, 2668c2ecf20Sopenharmony_ci AD1986A_FIXUP_EAPD, 2678c2ecf20Sopenharmony_ci AD1986A_FIXUP_EAPD_MIX_IN, 2688c2ecf20Sopenharmony_ci AD1986A_FIXUP_EASYNOTE, 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic const struct hda_fixup ad1986a_fixups[] = { 2728c2ecf20Sopenharmony_ci [AD1986A_FIXUP_INV_JACK_DETECT] = { 2738c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 2748c2ecf20Sopenharmony_ci .v.func = ad_fixup_inv_jack_detect, 2758c2ecf20Sopenharmony_ci }, 2768c2ecf20Sopenharmony_ci [AD1986A_FIXUP_ULTRA] = { 2778c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 2788c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 2798c2ecf20Sopenharmony_ci { 0x1b, 0x90170110 }, /* speaker */ 2808c2ecf20Sopenharmony_ci { 0x1d, 0x90a7013e }, /* int mic */ 2818c2ecf20Sopenharmony_ci {} 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci }, 2848c2ecf20Sopenharmony_ci [AD1986A_FIXUP_SAMSUNG] = { 2858c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 2868c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 2878c2ecf20Sopenharmony_ci { 0x1b, 0x90170110 }, /* speaker */ 2888c2ecf20Sopenharmony_ci { 0x1d, 0x90a7013e }, /* int mic */ 2898c2ecf20Sopenharmony_ci { 0x20, 0x411111f0 }, /* N/A */ 2908c2ecf20Sopenharmony_ci { 0x24, 0x411111f0 }, /* N/A */ 2918c2ecf20Sopenharmony_ci {} 2928c2ecf20Sopenharmony_ci }, 2938c2ecf20Sopenharmony_ci }, 2948c2ecf20Sopenharmony_ci [AD1986A_FIXUP_3STACK] = { 2958c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 2968c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 2978c2ecf20Sopenharmony_ci { 0x1a, 0x02214021 }, /* headphone */ 2988c2ecf20Sopenharmony_ci { 0x1b, 0x01014011 }, /* front */ 2998c2ecf20Sopenharmony_ci { 0x1c, 0x01813030 }, /* line-in */ 3008c2ecf20Sopenharmony_ci { 0x1d, 0x01a19020 }, /* rear mic */ 3018c2ecf20Sopenharmony_ci { 0x1e, 0x411111f0 }, /* N/A */ 3028c2ecf20Sopenharmony_ci { 0x1f, 0x02a190f0 }, /* mic */ 3038c2ecf20Sopenharmony_ci { 0x20, 0x411111f0 }, /* N/A */ 3048c2ecf20Sopenharmony_ci {} 3058c2ecf20Sopenharmony_ci }, 3068c2ecf20Sopenharmony_ci }, 3078c2ecf20Sopenharmony_ci [AD1986A_FIXUP_LAPTOP] = { 3088c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 3098c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 3108c2ecf20Sopenharmony_ci { 0x1a, 0x02214021 }, /* headphone */ 3118c2ecf20Sopenharmony_ci { 0x1b, 0x90170110 }, /* speaker */ 3128c2ecf20Sopenharmony_ci { 0x1c, 0x411111f0 }, /* N/A */ 3138c2ecf20Sopenharmony_ci { 0x1d, 0x411111f0 }, /* N/A */ 3148c2ecf20Sopenharmony_ci { 0x1e, 0x411111f0 }, /* N/A */ 3158c2ecf20Sopenharmony_ci { 0x1f, 0x02a191f0 }, /* mic */ 3168c2ecf20Sopenharmony_ci { 0x20, 0x411111f0 }, /* N/A */ 3178c2ecf20Sopenharmony_ci {} 3188c2ecf20Sopenharmony_ci }, 3198c2ecf20Sopenharmony_ci }, 3208c2ecf20Sopenharmony_ci [AD1986A_FIXUP_LAPTOP_IMIC] = { 3218c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 3228c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 3238c2ecf20Sopenharmony_ci { 0x1d, 0x90a7013e }, /* int mic */ 3248c2ecf20Sopenharmony_ci {} 3258c2ecf20Sopenharmony_ci }, 3268c2ecf20Sopenharmony_ci .chained_before = 1, 3278c2ecf20Sopenharmony_ci .chain_id = AD1986A_FIXUP_LAPTOP, 3288c2ecf20Sopenharmony_ci }, 3298c2ecf20Sopenharmony_ci [AD1986A_FIXUP_EAPD] = { 3308c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 3318c2ecf20Sopenharmony_ci .v.func = ad1986a_fixup_eapd, 3328c2ecf20Sopenharmony_ci }, 3338c2ecf20Sopenharmony_ci [AD1986A_FIXUP_EAPD_MIX_IN] = { 3348c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 3358c2ecf20Sopenharmony_ci .v.func = ad1986a_fixup_eapd_mix_in, 3368c2ecf20Sopenharmony_ci }, 3378c2ecf20Sopenharmony_ci [AD1986A_FIXUP_EASYNOTE] = { 3388c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 3398c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 3408c2ecf20Sopenharmony_ci { 0x1a, 0x0421402f }, /* headphone */ 3418c2ecf20Sopenharmony_ci { 0x1b, 0x90170110 }, /* speaker */ 3428c2ecf20Sopenharmony_ci { 0x1c, 0x411111f0 }, /* N/A */ 3438c2ecf20Sopenharmony_ci { 0x1d, 0x90a70130 }, /* int mic */ 3448c2ecf20Sopenharmony_ci { 0x1e, 0x411111f0 }, /* N/A */ 3458c2ecf20Sopenharmony_ci { 0x1f, 0x04a19040 }, /* mic */ 3468c2ecf20Sopenharmony_ci { 0x20, 0x411111f0 }, /* N/A */ 3478c2ecf20Sopenharmony_ci { 0x21, 0x411111f0 }, /* N/A */ 3488c2ecf20Sopenharmony_ci { 0x22, 0x411111f0 }, /* N/A */ 3498c2ecf20Sopenharmony_ci { 0x23, 0x411111f0 }, /* N/A */ 3508c2ecf20Sopenharmony_ci { 0x24, 0x411111f0 }, /* N/A */ 3518c2ecf20Sopenharmony_ci { 0x25, 0x411111f0 }, /* N/A */ 3528c2ecf20Sopenharmony_ci {} 3538c2ecf20Sopenharmony_ci }, 3548c2ecf20Sopenharmony_ci .chained = true, 3558c2ecf20Sopenharmony_ci .chain_id = AD1986A_FIXUP_EAPD_MIX_IN, 3568c2ecf20Sopenharmony_ci }, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk ad1986a_fixup_tbl[] = { 3608c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), 3618c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9V", AD1986A_FIXUP_LAPTOP_IMIC), 3628c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1043, 0x1443, "ASUS Z99He", AD1986A_FIXUP_EAPD), 3638c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD), 3648c2ecf20Sopenharmony_ci SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK), 3658c2ecf20Sopenharmony_ci SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK), 3668c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), 3678c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD), 3688c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP), 3698c2ecf20Sopenharmony_ci SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG), 3708c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), 3718c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x1631, 0xc022, "PackardBell EasyNote MX65", AD1986A_FIXUP_EASYNOTE), 3728c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT), 3738c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK), 3748c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK), 3758c2ecf20Sopenharmony_ci {} 3768c2ecf20Sopenharmony_ci}; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic const struct hda_model_fixup ad1986a_fixup_models[] = { 3798c2ecf20Sopenharmony_ci { .id = AD1986A_FIXUP_3STACK, .name = "3stack" }, 3808c2ecf20Sopenharmony_ci { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" }, 3818c2ecf20Sopenharmony_ci { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" }, 3828c2ecf20Sopenharmony_ci { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */ 3838c2ecf20Sopenharmony_ci { .id = AD1986A_FIXUP_EAPD, .name = "eapd" }, 3848c2ecf20Sopenharmony_ci {} 3858c2ecf20Sopenharmony_ci}; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci/* 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_cistatic int patch_ad1986a(struct hda_codec *codec) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int err; 3928c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 3938c2ecf20Sopenharmony_ci static const hda_nid_t preferred_pairs[] = { 3948c2ecf20Sopenharmony_ci 0x1a, 0x03, 3958c2ecf20Sopenharmony_ci 0x1b, 0x03, 3968c2ecf20Sopenharmony_ci 0x1c, 0x04, 3978c2ecf20Sopenharmony_ci 0x1d, 0x05, 3988c2ecf20Sopenharmony_ci 0x1e, 0x03, 3998c2ecf20Sopenharmony_ci 0 4008c2ecf20Sopenharmony_ci }; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 4038c2ecf20Sopenharmony_ci if (err < 0) 4048c2ecf20Sopenharmony_ci return err; 4058c2ecf20Sopenharmony_ci spec = codec->spec; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* AD1986A has the inverted EAPD implementation */ 4088c2ecf20Sopenharmony_ci codec->inv_eapd = 1; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x07; 4118c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x19; 4128c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x18, 0, HDA_OUTPUT); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* AD1986A has a hardware problem that it can't share a stream 4158c2ecf20Sopenharmony_ci * with multiple output pins. The copy of front to surrounds 4168c2ecf20Sopenharmony_ci * causes noisy or silent outputs at a certain timing, e.g. 4178c2ecf20Sopenharmony_ci * changing the volume. 4188c2ecf20Sopenharmony_ci * So, let's disable the shared stream. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci spec->gen.multiout.no_share_stream = 1; 4218c2ecf20Sopenharmony_ci /* give fixed DAC/pin pairs */ 4228c2ecf20Sopenharmony_ci spec->gen.preferred_dacs = preferred_pairs; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* AD1986A can't manage the dynamic pin on/off smoothly */ 4258c2ecf20Sopenharmony_ci spec->gen.auto_mute_via_amp = 1; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl, 4288c2ecf20Sopenharmony_ci ad1986a_fixups); 4298c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, false); 4328c2ecf20Sopenharmony_ci if (err < 0) { 4338c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 4348c2ecf20Sopenharmony_ci return err; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci return 0; 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci/* 4448c2ecf20Sopenharmony_ci * AD1983 specific 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci/* 4488c2ecf20Sopenharmony_ci * SPDIF mux control for AD1983 auto-parser 4498c2ecf20Sopenharmony_ci */ 4508c2ecf20Sopenharmony_cistatic int ad1983_auto_smux_enum_info(struct snd_kcontrol *kcontrol, 4518c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 4528c2ecf20Sopenharmony_ci{ 4538c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4548c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 4558c2ecf20Sopenharmony_ci static const char * const texts2[] = { "PCM", "ADC" }; 4568c2ecf20Sopenharmony_ci static const char * const texts3[] = { "PCM", "ADC1", "ADC2" }; 4578c2ecf20Sopenharmony_ci int num_conns = spec->num_smux_conns; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (num_conns == 2) 4608c2ecf20Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 2, texts2); 4618c2ecf20Sopenharmony_ci else if (num_conns == 3) 4628c2ecf20Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, 3, texts3); 4638c2ecf20Sopenharmony_ci else 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic int ad1983_auto_smux_enum_get(struct snd_kcontrol *kcontrol, 4688c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4718c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->cur_smux; 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int ad1983_auto_smux_enum_put(struct snd_kcontrol *kcontrol, 4788c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4818c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 4828c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.enumerated.item[0]; 4838c2ecf20Sopenharmony_ci hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; 4848c2ecf20Sopenharmony_ci int num_conns = spec->num_smux_conns; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (val >= num_conns) 4878c2ecf20Sopenharmony_ci return -EINVAL; 4888c2ecf20Sopenharmony_ci if (spec->cur_smux == val) 4898c2ecf20Sopenharmony_ci return 0; 4908c2ecf20Sopenharmony_ci spec->cur_smux = val; 4918c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, dig_out, 0, 4928c2ecf20Sopenharmony_ci AC_VERB_SET_CONNECT_SEL, val); 4938c2ecf20Sopenharmony_ci return 1; 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new ad1983_auto_smux_mixer = { 4978c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4988c2ecf20Sopenharmony_ci .name = "IEC958 Playback Source", 4998c2ecf20Sopenharmony_ci .info = ad1983_auto_smux_enum_info, 5008c2ecf20Sopenharmony_ci .get = ad1983_auto_smux_enum_get, 5018c2ecf20Sopenharmony_ci .put = ad1983_auto_smux_enum_put, 5028c2ecf20Sopenharmony_ci}; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cistatic int ad1983_add_spdif_mux_ctl(struct hda_codec *codec) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 5078c2ecf20Sopenharmony_ci hda_nid_t dig_out = spec->gen.multiout.dig_out_nid; 5088c2ecf20Sopenharmony_ci int num_conns; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!dig_out) 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci num_conns = snd_hda_get_num_conns(codec, dig_out); 5138c2ecf20Sopenharmony_ci if (num_conns != 2 && num_conns != 3) 5148c2ecf20Sopenharmony_ci return 0; 5158c2ecf20Sopenharmony_ci spec->num_smux_conns = num_conns; 5168c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1983_auto_smux_mixer)) 5178c2ecf20Sopenharmony_ci return -ENOMEM; 5188c2ecf20Sopenharmony_ci return 0; 5198c2ecf20Sopenharmony_ci} 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic int patch_ad1983(struct hda_codec *codec) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci static const hda_nid_t conn_0c[] = { 0x08 }; 5248c2ecf20Sopenharmony_ci static const hda_nid_t conn_0d[] = { 0x09 }; 5258c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 5268c2ecf20Sopenharmony_ci int err; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 5298c2ecf20Sopenharmony_ci if (err < 0) 5308c2ecf20Sopenharmony_ci return err; 5318c2ecf20Sopenharmony_ci spec = codec->spec; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x0e; 5348c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x10; 5358c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* limit the loopback routes not to confuse the parser */ 5388c2ecf20Sopenharmony_ci snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c); 5398c2ecf20Sopenharmony_ci snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, false); 5428c2ecf20Sopenharmony_ci if (err < 0) 5438c2ecf20Sopenharmony_ci goto error; 5448c2ecf20Sopenharmony_ci err = ad1983_add_spdif_mux_ctl(codec); 5458c2ecf20Sopenharmony_ci if (err < 0) 5468c2ecf20Sopenharmony_ci goto error; 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci error: 5508c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 5518c2ecf20Sopenharmony_ci return err; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/* 5568c2ecf20Sopenharmony_ci * AD1981 HD specific 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic void ad1981_fixup_hp_eapd(struct hda_codec *codec, 5608c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) { 5658c2ecf20Sopenharmony_ci spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; 5668c2ecf20Sopenharmony_ci spec->eapd_nid = 0x05; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci/* set the upper-limit for mixer amp to 0dB for avoiding the possible 5718c2ecf20Sopenharmony_ci * damage by overloading 5728c2ecf20Sopenharmony_ci */ 5738c2ecf20Sopenharmony_cistatic void ad1981_fixup_amp_override(struct hda_codec *codec, 5748c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) 5778c2ecf20Sopenharmony_ci snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, 5788c2ecf20Sopenharmony_ci (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 5798c2ecf20Sopenharmony_ci (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 5808c2ecf20Sopenharmony_ci (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | 5818c2ecf20Sopenharmony_ci (1 << AC_AMPCAP_MUTE_SHIFT)); 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cienum { 5858c2ecf20Sopenharmony_ci AD1981_FIXUP_AMP_OVERRIDE, 5868c2ecf20Sopenharmony_ci AD1981_FIXUP_HP_EAPD, 5878c2ecf20Sopenharmony_ci}; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic const struct hda_fixup ad1981_fixups[] = { 5908c2ecf20Sopenharmony_ci [AD1981_FIXUP_AMP_OVERRIDE] = { 5918c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 5928c2ecf20Sopenharmony_ci .v.func = ad1981_fixup_amp_override, 5938c2ecf20Sopenharmony_ci }, 5948c2ecf20Sopenharmony_ci [AD1981_FIXUP_HP_EAPD] = { 5958c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 5968c2ecf20Sopenharmony_ci .v.func = ad1981_fixup_hp_eapd, 5978c2ecf20Sopenharmony_ci .chained = true, 5988c2ecf20Sopenharmony_ci .chain_id = AD1981_FIXUP_AMP_OVERRIDE, 5998c2ecf20Sopenharmony_ci }, 6008c2ecf20Sopenharmony_ci}; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk ad1981_fixup_tbl[] = { 6038c2ecf20Sopenharmony_ci SND_PCI_QUIRK_VENDOR(0x1014, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), 6048c2ecf20Sopenharmony_ci SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1981_FIXUP_HP_EAPD), 6058c2ecf20Sopenharmony_ci SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo", AD1981_FIXUP_AMP_OVERRIDE), 6068c2ecf20Sopenharmony_ci /* HP nx6320 (reversed SSID, H/W bug) */ 6078c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_FIXUP_HP_EAPD), 6088c2ecf20Sopenharmony_ci {} 6098c2ecf20Sopenharmony_ci}; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic int patch_ad1981(struct hda_codec *codec) 6128c2ecf20Sopenharmony_ci{ 6138c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 6148c2ecf20Sopenharmony_ci int err; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 6178c2ecf20Sopenharmony_ci if (err < 0) 6188c2ecf20Sopenharmony_ci return -ENOMEM; 6198c2ecf20Sopenharmony_ci spec = codec->spec; 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x0e; 6228c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x10; 6238c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups); 6268c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, false); 6298c2ecf20Sopenharmony_ci if (err < 0) 6308c2ecf20Sopenharmony_ci goto error; 6318c2ecf20Sopenharmony_ci err = ad1983_add_spdif_mux_ctl(codec); 6328c2ecf20Sopenharmony_ci if (err < 0) 6338c2ecf20Sopenharmony_ci goto error; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci return 0; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci error: 6408c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 6418c2ecf20Sopenharmony_ci return err; 6428c2ecf20Sopenharmony_ci} 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* 6468c2ecf20Sopenharmony_ci * AD1988 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * Output pins and routes 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci * Pin Mix Sel DAC (*) 6518c2ecf20Sopenharmony_ci * port-A 0x11 (mute/hp) <- 0x22 <- 0x37 <- 03/04/06 6528c2ecf20Sopenharmony_ci * port-B 0x14 (mute/hp) <- 0x2b <- 0x30 <- 03/04/06 6538c2ecf20Sopenharmony_ci * port-C 0x15 (mute) <- 0x2c <- 0x31 <- 05/0a 6548c2ecf20Sopenharmony_ci * port-D 0x12 (mute/hp) <- 0x29 <- 04 6558c2ecf20Sopenharmony_ci * port-E 0x17 (mute/hp) <- 0x26 <- 0x32 <- 05/0a 6568c2ecf20Sopenharmony_ci * port-F 0x16 (mute) <- 0x2a <- 06 6578c2ecf20Sopenharmony_ci * port-G 0x24 (mute) <- 0x27 <- 05 6588c2ecf20Sopenharmony_ci * port-H 0x25 (mute) <- 0x28 <- 0a 6598c2ecf20Sopenharmony_ci * mono 0x13 (mute/amp)<- 0x1e <- 0x36 <- 03/04/06 6608c2ecf20Sopenharmony_ci * 6618c2ecf20Sopenharmony_ci * DAC0 = 03h, DAC1 = 04h, DAC2 = 05h, DAC3 = 06h, DAC4 = 0ah 6628c2ecf20Sopenharmony_ci * (*) DAC2/3/4 are swapped to DAC3/4/2 on AD198A rev.2 due to a h/w bug. 6638c2ecf20Sopenharmony_ci * 6648c2ecf20Sopenharmony_ci * Input pins and routes 6658c2ecf20Sopenharmony_ci * 6668c2ecf20Sopenharmony_ci * pin boost mix input # / adc input # 6678c2ecf20Sopenharmony_ci * port-A 0x11 -> 0x38 -> mix 2, ADC 0 6688c2ecf20Sopenharmony_ci * port-B 0x14 -> 0x39 -> mix 0, ADC 1 6698c2ecf20Sopenharmony_ci * port-C 0x15 -> 0x3a -> 33:0 - mix 1, ADC 2 6708c2ecf20Sopenharmony_ci * port-D 0x12 -> 0x3d -> mix 3, ADC 8 6718c2ecf20Sopenharmony_ci * port-E 0x17 -> 0x3c -> 34:0 - mix 4, ADC 4 6728c2ecf20Sopenharmony_ci * port-F 0x16 -> 0x3b -> mix 5, ADC 3 6738c2ecf20Sopenharmony_ci * port-G 0x24 -> N/A -> 33:1 - mix 1, 34:1 - mix 4, ADC 6 6748c2ecf20Sopenharmony_ci * port-H 0x25 -> N/A -> 33:2 - mix 1, 34:2 - mix 4, ADC 7 6758c2ecf20Sopenharmony_ci * 6768c2ecf20Sopenharmony_ci * 6778c2ecf20Sopenharmony_ci * DAC assignment 6788c2ecf20Sopenharmony_ci * 6stack - front/surr/CLFE/side/opt DACs - 04/06/05/0a/03 6798c2ecf20Sopenharmony_ci * 3stack - front/surr/CLFE/opt DACs - 04/05/0a/03 6808c2ecf20Sopenharmony_ci * 6818c2ecf20Sopenharmony_ci * Inputs of Analog Mix (0x20) 6828c2ecf20Sopenharmony_ci * 0:Port-B (front mic) 6838c2ecf20Sopenharmony_ci * 1:Port-C/G/H (line-in) 6848c2ecf20Sopenharmony_ci * 2:Port-A 6858c2ecf20Sopenharmony_ci * 3:Port-D (line-in/2) 6868c2ecf20Sopenharmony_ci * 4:Port-E/G/H (mic-in) 6878c2ecf20Sopenharmony_ci * 5:Port-F (mic2-in) 6888c2ecf20Sopenharmony_ci * 6:CD 6898c2ecf20Sopenharmony_ci * 7:Beep 6908c2ecf20Sopenharmony_ci * 6918c2ecf20Sopenharmony_ci * ADC selection 6928c2ecf20Sopenharmony_ci * 0:Port-A 6938c2ecf20Sopenharmony_ci * 1:Port-B (front mic-in) 6948c2ecf20Sopenharmony_ci * 2:Port-C (line-in) 6958c2ecf20Sopenharmony_ci * 3:Port-F (mic2-in) 6968c2ecf20Sopenharmony_ci * 4:Port-E (mic-in) 6978c2ecf20Sopenharmony_ci * 5:CD 6988c2ecf20Sopenharmony_ci * 6:Port-G 6998c2ecf20Sopenharmony_ci * 7:Port-H 7008c2ecf20Sopenharmony_ci * 8:Port-D (line-in/2) 7018c2ecf20Sopenharmony_ci * 9:Mix 7028c2ecf20Sopenharmony_ci * 7038c2ecf20Sopenharmony_ci * Proposed pin assignments by the datasheet 7048c2ecf20Sopenharmony_ci * 7058c2ecf20Sopenharmony_ci * 6-stack 7068c2ecf20Sopenharmony_ci * Port-A front headphone 7078c2ecf20Sopenharmony_ci * B front mic-in 7088c2ecf20Sopenharmony_ci * C rear line-in 7098c2ecf20Sopenharmony_ci * D rear front-out 7108c2ecf20Sopenharmony_ci * E rear mic-in 7118c2ecf20Sopenharmony_ci * F rear surround 7128c2ecf20Sopenharmony_ci * G rear CLFE 7138c2ecf20Sopenharmony_ci * H rear side 7148c2ecf20Sopenharmony_ci * 7158c2ecf20Sopenharmony_ci * 3-stack 7168c2ecf20Sopenharmony_ci * Port-A front headphone 7178c2ecf20Sopenharmony_ci * B front mic 7188c2ecf20Sopenharmony_ci * C rear line-in/surround 7198c2ecf20Sopenharmony_ci * D rear front-out 7208c2ecf20Sopenharmony_ci * E rear mic-in/CLFE 7218c2ecf20Sopenharmony_ci * 7228c2ecf20Sopenharmony_ci * laptop 7238c2ecf20Sopenharmony_ci * Port-A headphone 7248c2ecf20Sopenharmony_ci * B mic-in 7258c2ecf20Sopenharmony_ci * C docking station 7268c2ecf20Sopenharmony_ci * D internal speaker (with EAPD) 7278c2ecf20Sopenharmony_ci * E/F quad mic array 7288c2ecf20Sopenharmony_ci */ 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol, 7318c2ecf20Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7348c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 7358c2ecf20Sopenharmony_ci static const char * const texts[] = { 7368c2ecf20Sopenharmony_ci "PCM", "ADC1", "ADC2", "ADC3", 7378c2ecf20Sopenharmony_ci }; 7388c2ecf20Sopenharmony_ci int num_conns = spec->num_smux_conns; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (num_conns > 4) 7418c2ecf20Sopenharmony_ci num_conns = 4; 7428c2ecf20Sopenharmony_ci return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol, 7468c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7478c2ecf20Sopenharmony_ci{ 7488c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7498c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ucontrol->value.enumerated.item[0] = spec->cur_smux; 7528c2ecf20Sopenharmony_ci return 0; 7538c2ecf20Sopenharmony_ci} 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_cistatic int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol, 7568c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7598c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 7608c2ecf20Sopenharmony_ci unsigned int val = ucontrol->value.enumerated.item[0]; 7618c2ecf20Sopenharmony_ci struct nid_path *path; 7628c2ecf20Sopenharmony_ci int num_conns = spec->num_smux_conns; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci if (val >= num_conns) 7658c2ecf20Sopenharmony_ci return -EINVAL; 7668c2ecf20Sopenharmony_ci if (spec->cur_smux == val) 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci mutex_lock(&codec->control_mutex); 7708c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, 7718c2ecf20Sopenharmony_ci spec->smux_paths[spec->cur_smux]); 7728c2ecf20Sopenharmony_ci if (path) 7738c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, false, true); 7748c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]); 7758c2ecf20Sopenharmony_ci if (path) 7768c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, true, true); 7778c2ecf20Sopenharmony_ci spec->cur_smux = val; 7788c2ecf20Sopenharmony_ci mutex_unlock(&codec->control_mutex); 7798c2ecf20Sopenharmony_ci return 1; 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new ad1988_auto_smux_mixer = { 7838c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7848c2ecf20Sopenharmony_ci .name = "IEC958 Playback Source", 7858c2ecf20Sopenharmony_ci .info = ad1988_auto_smux_enum_info, 7868c2ecf20Sopenharmony_ci .get = ad1988_auto_smux_enum_get, 7878c2ecf20Sopenharmony_ci .put = ad1988_auto_smux_enum_put, 7888c2ecf20Sopenharmony_ci}; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic int ad1988_auto_init(struct hda_codec *codec) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 7938c2ecf20Sopenharmony_ci int i, err; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci err = snd_hda_gen_init(codec); 7968c2ecf20Sopenharmony_ci if (err < 0) 7978c2ecf20Sopenharmony_ci return err; 7988c2ecf20Sopenharmony_ci if (!spec->gen.autocfg.dig_outs) 7998c2ecf20Sopenharmony_ci return 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 8028c2ecf20Sopenharmony_ci struct nid_path *path; 8038c2ecf20Sopenharmony_ci path = snd_hda_get_path_from_idx(codec, spec->smux_paths[i]); 8048c2ecf20Sopenharmony_ci if (path) 8058c2ecf20Sopenharmony_ci snd_hda_activate_path(codec, path, path->active, false); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci return 0; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic int ad1988_add_spdif_mux_ctl(struct hda_codec *codec) 8128c2ecf20Sopenharmony_ci{ 8138c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 8148c2ecf20Sopenharmony_ci int i, num_conns; 8158c2ecf20Sopenharmony_ci /* we create four static faked paths, since AD codecs have odd 8168c2ecf20Sopenharmony_ci * widget connections regarding the SPDIF out source 8178c2ecf20Sopenharmony_ci */ 8188c2ecf20Sopenharmony_ci static const struct nid_path fake_paths[4] = { 8198c2ecf20Sopenharmony_ci { 8208c2ecf20Sopenharmony_ci .depth = 3, 8218c2ecf20Sopenharmony_ci .path = { 0x02, 0x1d, 0x1b }, 8228c2ecf20Sopenharmony_ci .idx = { 0, 0, 0 }, 8238c2ecf20Sopenharmony_ci .multi = { 0, 0, 0 }, 8248c2ecf20Sopenharmony_ci }, 8258c2ecf20Sopenharmony_ci { 8268c2ecf20Sopenharmony_ci .depth = 4, 8278c2ecf20Sopenharmony_ci .path = { 0x08, 0x0b, 0x1d, 0x1b }, 8288c2ecf20Sopenharmony_ci .idx = { 0, 0, 1, 0 }, 8298c2ecf20Sopenharmony_ci .multi = { 0, 1, 0, 0 }, 8308c2ecf20Sopenharmony_ci }, 8318c2ecf20Sopenharmony_ci { 8328c2ecf20Sopenharmony_ci .depth = 4, 8338c2ecf20Sopenharmony_ci .path = { 0x09, 0x0b, 0x1d, 0x1b }, 8348c2ecf20Sopenharmony_ci .idx = { 0, 1, 1, 0 }, 8358c2ecf20Sopenharmony_ci .multi = { 0, 1, 0, 0 }, 8368c2ecf20Sopenharmony_ci }, 8378c2ecf20Sopenharmony_ci { 8388c2ecf20Sopenharmony_ci .depth = 4, 8398c2ecf20Sopenharmony_ci .path = { 0x0f, 0x0b, 0x1d, 0x1b }, 8408c2ecf20Sopenharmony_ci .idx = { 0, 2, 1, 0 }, 8418c2ecf20Sopenharmony_ci .multi = { 0, 1, 0, 0 }, 8428c2ecf20Sopenharmony_ci }, 8438c2ecf20Sopenharmony_ci }; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci /* SPDIF source mux appears to be present only on AD1988A */ 8468c2ecf20Sopenharmony_ci if (!spec->gen.autocfg.dig_outs || 8478c2ecf20Sopenharmony_ci get_wcaps_type(get_wcaps(codec, 0x1d)) != AC_WID_AUD_MIX) 8488c2ecf20Sopenharmony_ci return 0; 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1; 8518c2ecf20Sopenharmony_ci if (num_conns != 3 && num_conns != 4) 8528c2ecf20Sopenharmony_ci return 0; 8538c2ecf20Sopenharmony_ci spec->num_smux_conns = num_conns; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci for (i = 0; i < num_conns; i++) { 8568c2ecf20Sopenharmony_ci struct nid_path *path = snd_array_new(&spec->gen.paths); 8578c2ecf20Sopenharmony_ci if (!path) 8588c2ecf20Sopenharmony_ci return -ENOMEM; 8598c2ecf20Sopenharmony_ci *path = fake_paths[i]; 8608c2ecf20Sopenharmony_ci if (!i) 8618c2ecf20Sopenharmony_ci path->active = 1; 8628c2ecf20Sopenharmony_ci spec->smux_paths[i] = snd_hda_get_path_idx(codec, path); 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &ad1988_auto_smux_mixer)) 8668c2ecf20Sopenharmony_ci return -ENOMEM; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci codec->patch_ops.init = ad1988_auto_init; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci return 0; 8718c2ecf20Sopenharmony_ci} 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci/* 8748c2ecf20Sopenharmony_ci */ 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cienum { 8778c2ecf20Sopenharmony_ci AD1988_FIXUP_6STACK_DIG, 8788c2ecf20Sopenharmony_ci}; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_cistatic const struct hda_fixup ad1988_fixups[] = { 8818c2ecf20Sopenharmony_ci [AD1988_FIXUP_6STACK_DIG] = { 8828c2ecf20Sopenharmony_ci .type = HDA_FIXUP_PINS, 8838c2ecf20Sopenharmony_ci .v.pins = (const struct hda_pintbl[]) { 8848c2ecf20Sopenharmony_ci { 0x11, 0x02214130 }, /* front-hp */ 8858c2ecf20Sopenharmony_ci { 0x12, 0x01014010 }, /* line-out */ 8868c2ecf20Sopenharmony_ci { 0x14, 0x02a19122 }, /* front-mic */ 8878c2ecf20Sopenharmony_ci { 0x15, 0x01813021 }, /* line-in */ 8888c2ecf20Sopenharmony_ci { 0x16, 0x01011012 }, /* line-out */ 8898c2ecf20Sopenharmony_ci { 0x17, 0x01a19020 }, /* mic */ 8908c2ecf20Sopenharmony_ci { 0x1b, 0x0145f1f0 }, /* SPDIF */ 8918c2ecf20Sopenharmony_ci { 0x24, 0x01016011 }, /* line-out */ 8928c2ecf20Sopenharmony_ci { 0x25, 0x01012013 }, /* line-out */ 8938c2ecf20Sopenharmony_ci { } 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci }, 8968c2ecf20Sopenharmony_ci}; 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic const struct hda_model_fixup ad1988_fixup_models[] = { 8998c2ecf20Sopenharmony_ci { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" }, 9008c2ecf20Sopenharmony_ci {} 9018c2ecf20Sopenharmony_ci}; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_cistatic int patch_ad1988(struct hda_codec *codec) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 9068c2ecf20Sopenharmony_ci int err; 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 9098c2ecf20Sopenharmony_ci if (err < 0) 9108c2ecf20Sopenharmony_ci return err; 9118c2ecf20Sopenharmony_ci spec = codec->spec; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x20; 9148c2ecf20Sopenharmony_ci spec->gen.mixer_merge_nid = 0x21; 9158c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x10; 9168c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups); 9198c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, true); 9228c2ecf20Sopenharmony_ci if (err < 0) 9238c2ecf20Sopenharmony_ci goto error; 9248c2ecf20Sopenharmony_ci err = ad1988_add_spdif_mux_ctl(codec); 9258c2ecf20Sopenharmony_ci if (err < 0) 9268c2ecf20Sopenharmony_ci goto error; 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci return 0; 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_ci error: 9338c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 9348c2ecf20Sopenharmony_ci return err; 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci/* 9398c2ecf20Sopenharmony_ci * AD1884 / AD1984 9408c2ecf20Sopenharmony_ci * 9418c2ecf20Sopenharmony_ci * port-B - front line/mic-in 9428c2ecf20Sopenharmony_ci * port-E - aux in/out 9438c2ecf20Sopenharmony_ci * port-F - aux in/out 9448c2ecf20Sopenharmony_ci * port-C - rear line/mic-in 9458c2ecf20Sopenharmony_ci * port-D - rear line/hp-out 9468c2ecf20Sopenharmony_ci * port-A - front line/hp-out 9478c2ecf20Sopenharmony_ci * 9488c2ecf20Sopenharmony_ci * AD1984 = AD1884 + two digital mic-ins 9498c2ecf20Sopenharmony_ci * 9508c2ecf20Sopenharmony_ci * AD1883 / AD1884A / AD1984A / AD1984B 9518c2ecf20Sopenharmony_ci * 9528c2ecf20Sopenharmony_ci * port-B (0x14) - front mic-in 9538c2ecf20Sopenharmony_ci * port-E (0x1c) - rear mic-in 9548c2ecf20Sopenharmony_ci * port-F (0x16) - CD / ext out 9558c2ecf20Sopenharmony_ci * port-C (0x15) - rear line-in 9568c2ecf20Sopenharmony_ci * port-D (0x12) - rear line-out 9578c2ecf20Sopenharmony_ci * port-A (0x11) - front hp-out 9588c2ecf20Sopenharmony_ci * 9598c2ecf20Sopenharmony_ci * AD1984A = AD1884A + digital-mic 9608c2ecf20Sopenharmony_ci * AD1883 = equivalent with AD1984A 9618c2ecf20Sopenharmony_ci * AD1984B = AD1984A + extra SPDIF-out 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci/* set the upper-limit for mixer amp to 0dB for avoiding the possible 9658c2ecf20Sopenharmony_ci * damage by overloading 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_cistatic void ad1884_fixup_amp_override(struct hda_codec *codec, 9688c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) 9718c2ecf20Sopenharmony_ci snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, 9728c2ecf20Sopenharmony_ci (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 9738c2ecf20Sopenharmony_ci (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 9748c2ecf20Sopenharmony_ci (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | 9758c2ecf20Sopenharmony_ci (1 << AC_AMPCAP_MUTE_SHIFT)); 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci/* toggle GPIO1 according to the mute state */ 9798c2ecf20Sopenharmony_cistatic void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled) 9808c2ecf20Sopenharmony_ci{ 9818c2ecf20Sopenharmony_ci struct hda_codec *codec = private_data; 9828c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci if (spec->eapd_nid) 9858c2ecf20Sopenharmony_ci ad_vmaster_eapd_hook(private_data, enabled); 9868c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, 0x01, 0, 9878c2ecf20Sopenharmony_ci AC_VERB_SET_GPIO_DATA, 9888c2ecf20Sopenharmony_ci enabled ? 0x00 : 0x02); 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_cistatic void ad1884_fixup_hp_eapd(struct hda_codec *codec, 9928c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci switch (action) { 9978c2ecf20Sopenharmony_ci case HDA_FIXUP_ACT_PRE_PROBE: 9988c2ecf20Sopenharmony_ci spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; 9998c2ecf20Sopenharmony_ci spec->gen.own_eapd_ctl = 1; 10008c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, 0x01, 0, 10018c2ecf20Sopenharmony_ci AC_VERB_SET_GPIO_MASK, 0x02); 10028c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, 0x01, 0, 10038c2ecf20Sopenharmony_ci AC_VERB_SET_GPIO_DIRECTION, 0x02); 10048c2ecf20Sopenharmony_ci snd_hda_codec_write_cache(codec, 0x01, 0, 10058c2ecf20Sopenharmony_ci AC_VERB_SET_GPIO_DATA, 0x02); 10068c2ecf20Sopenharmony_ci break; 10078c2ecf20Sopenharmony_ci case HDA_FIXUP_ACT_PROBE: 10088c2ecf20Sopenharmony_ci if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT) 10098c2ecf20Sopenharmony_ci spec->eapd_nid = spec->gen.autocfg.line_out_pins[0]; 10108c2ecf20Sopenharmony_ci else 10118c2ecf20Sopenharmony_ci spec->eapd_nid = spec->gen.autocfg.speaker_pins[0]; 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic void ad1884_fixup_thinkpad(struct hda_codec *codec, 10178c2ecf20Sopenharmony_ci const struct hda_fixup *fix, int action) 10188c2ecf20Sopenharmony_ci{ 10198c2ecf20Sopenharmony_ci struct ad198x_spec *spec = codec->spec; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci if (action == HDA_FIXUP_ACT_PRE_PROBE) { 10228c2ecf20Sopenharmony_ci spec->gen.keep_eapd_on = 1; 10238c2ecf20Sopenharmony_ci spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; 10248c2ecf20Sopenharmony_ci spec->eapd_nid = 0x12; 10258c2ecf20Sopenharmony_ci /* Analog PC Beeper - allow firmware/ACPI beeps */ 10268c2ecf20Sopenharmony_ci spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT); 10278c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0; /* no digital beep */ 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci/* set magic COEFs for dmic */ 10328c2ecf20Sopenharmony_cistatic const struct hda_verb ad1884_dmic_init_verbs[] = { 10338c2ecf20Sopenharmony_ci {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, 10348c2ecf20Sopenharmony_ci {0x01, AC_VERB_SET_PROC_COEF, 0x08}, 10358c2ecf20Sopenharmony_ci {} 10368c2ecf20Sopenharmony_ci}; 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cienum { 10398c2ecf20Sopenharmony_ci AD1884_FIXUP_AMP_OVERRIDE, 10408c2ecf20Sopenharmony_ci AD1884_FIXUP_HP_EAPD, 10418c2ecf20Sopenharmony_ci AD1884_FIXUP_DMIC_COEF, 10428c2ecf20Sopenharmony_ci AD1884_FIXUP_THINKPAD, 10438c2ecf20Sopenharmony_ci AD1884_FIXUP_HP_TOUCHSMART, 10448c2ecf20Sopenharmony_ci}; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_cistatic const struct hda_fixup ad1884_fixups[] = { 10478c2ecf20Sopenharmony_ci [AD1884_FIXUP_AMP_OVERRIDE] = { 10488c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 10498c2ecf20Sopenharmony_ci .v.func = ad1884_fixup_amp_override, 10508c2ecf20Sopenharmony_ci }, 10518c2ecf20Sopenharmony_ci [AD1884_FIXUP_HP_EAPD] = { 10528c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 10538c2ecf20Sopenharmony_ci .v.func = ad1884_fixup_hp_eapd, 10548c2ecf20Sopenharmony_ci .chained = true, 10558c2ecf20Sopenharmony_ci .chain_id = AD1884_FIXUP_AMP_OVERRIDE, 10568c2ecf20Sopenharmony_ci }, 10578c2ecf20Sopenharmony_ci [AD1884_FIXUP_DMIC_COEF] = { 10588c2ecf20Sopenharmony_ci .type = HDA_FIXUP_VERBS, 10598c2ecf20Sopenharmony_ci .v.verbs = ad1884_dmic_init_verbs, 10608c2ecf20Sopenharmony_ci }, 10618c2ecf20Sopenharmony_ci [AD1884_FIXUP_THINKPAD] = { 10628c2ecf20Sopenharmony_ci .type = HDA_FIXUP_FUNC, 10638c2ecf20Sopenharmony_ci .v.func = ad1884_fixup_thinkpad, 10648c2ecf20Sopenharmony_ci .chained = true, 10658c2ecf20Sopenharmony_ci .chain_id = AD1884_FIXUP_DMIC_COEF, 10668c2ecf20Sopenharmony_ci }, 10678c2ecf20Sopenharmony_ci [AD1884_FIXUP_HP_TOUCHSMART] = { 10688c2ecf20Sopenharmony_ci .type = HDA_FIXUP_VERBS, 10698c2ecf20Sopenharmony_ci .v.verbs = ad1884_dmic_init_verbs, 10708c2ecf20Sopenharmony_ci .chained = true, 10718c2ecf20Sopenharmony_ci .chain_id = AD1884_FIXUP_HP_EAPD, 10728c2ecf20Sopenharmony_ci }, 10738c2ecf20Sopenharmony_ci}; 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_cistatic const struct snd_pci_quirk ad1884_fixup_tbl[] = { 10768c2ecf20Sopenharmony_ci SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART), 10778c2ecf20Sopenharmony_ci SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), 10788c2ecf20Sopenharmony_ci SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD), 10798c2ecf20Sopenharmony_ci {} 10808c2ecf20Sopenharmony_ci}; 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_cistatic int patch_ad1884(struct hda_codec *codec) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 10868c2ecf20Sopenharmony_ci int err; 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 10898c2ecf20Sopenharmony_ci if (err < 0) 10908c2ecf20Sopenharmony_ci return err; 10918c2ecf20Sopenharmony_ci spec = codec->spec; 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x20; 10948c2ecf20Sopenharmony_ci spec->gen.mixer_merge_nid = 0x21; 10958c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x10; 10968c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups); 10998c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, true); 11028c2ecf20Sopenharmony_ci if (err < 0) 11038c2ecf20Sopenharmony_ci goto error; 11048c2ecf20Sopenharmony_ci err = ad1983_add_spdif_mux_ctl(codec); 11058c2ecf20Sopenharmony_ci if (err < 0) 11068c2ecf20Sopenharmony_ci goto error; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci error: 11138c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 11148c2ecf20Sopenharmony_ci return err; 11158c2ecf20Sopenharmony_ci} 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci/* 11188c2ecf20Sopenharmony_ci * AD1882 / AD1882A 11198c2ecf20Sopenharmony_ci * 11208c2ecf20Sopenharmony_ci * port-A - front hp-out 11218c2ecf20Sopenharmony_ci * port-B - front mic-in 11228c2ecf20Sopenharmony_ci * port-C - rear line-in, shared surr-out (3stack) 11238c2ecf20Sopenharmony_ci * port-D - rear line-out 11248c2ecf20Sopenharmony_ci * port-E - rear mic-in, shared clfe-out (3stack) 11258c2ecf20Sopenharmony_ci * port-F - rear surr-out (6stack) 11268c2ecf20Sopenharmony_ci * port-G - rear clfe-out (6stack) 11278c2ecf20Sopenharmony_ci */ 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int patch_ad1882(struct hda_codec *codec) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct ad198x_spec *spec; 11328c2ecf20Sopenharmony_ci int err; 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci err = alloc_ad_spec(codec); 11358c2ecf20Sopenharmony_ci if (err < 0) 11368c2ecf20Sopenharmony_ci return err; 11378c2ecf20Sopenharmony_ci spec = codec->spec; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci spec->gen.mixer_nid = 0x20; 11408c2ecf20Sopenharmony_ci spec->gen.mixer_merge_nid = 0x21; 11418c2ecf20Sopenharmony_ci spec->gen.beep_nid = 0x10; 11428c2ecf20Sopenharmony_ci set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); 11438c2ecf20Sopenharmony_ci err = ad198x_parse_auto_config(codec, true); 11448c2ecf20Sopenharmony_ci if (err < 0) 11458c2ecf20Sopenharmony_ci goto error; 11468c2ecf20Sopenharmony_ci err = ad1988_add_spdif_mux_ctl(codec); 11478c2ecf20Sopenharmony_ci if (err < 0) 11488c2ecf20Sopenharmony_ci goto error; 11498c2ecf20Sopenharmony_ci return 0; 11508c2ecf20Sopenharmony_ci 11518c2ecf20Sopenharmony_ci error: 11528c2ecf20Sopenharmony_ci snd_hda_gen_free(codec); 11538c2ecf20Sopenharmony_ci return err; 11548c2ecf20Sopenharmony_ci} 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci/* 11588c2ecf20Sopenharmony_ci * patch entries 11598c2ecf20Sopenharmony_ci */ 11608c2ecf20Sopenharmony_cistatic const struct hda_device_id snd_hda_id_analog[] = { 11618c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884), 11628c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882), 11638c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884), 11648c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884), 11658c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884), 11668c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884), 11678c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981), 11688c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983), 11698c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884), 11708c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a), 11718c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988), 11728c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988), 11738c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882), 11748c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988), 11758c2ecf20Sopenharmony_ci HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988), 11768c2ecf20Sopenharmony_ci {} /* terminator */ 11778c2ecf20Sopenharmony_ci}; 11788c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 11818c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices HD-audio codec"); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_cistatic struct hda_codec_driver analog_driver = { 11848c2ecf20Sopenharmony_ci .id = snd_hda_id_analog, 11858c2ecf20Sopenharmony_ci}; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cimodule_hda_codec_driver(analog_driver); 1188