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