1c577b8a1SJoseph Chan /* 2c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec 3c577b8a1SJoseph Chan * 48e86597fSLydia Wang * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec 5c577b8a1SJoseph Chan * 68e86597fSLydia Wang * (C) 2006-2009 VIA Technology, Inc. 78e86597fSLydia Wang * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> 8c577b8a1SJoseph Chan * 9c577b8a1SJoseph Chan * This driver is free software; you can redistribute it and/or modify 10c577b8a1SJoseph Chan * it under the terms of the GNU General Public License as published by 11c577b8a1SJoseph Chan * the Free Software Foundation; either version 2 of the License, or 12c577b8a1SJoseph Chan * (at your option) any later version. 13c577b8a1SJoseph Chan * 14c577b8a1SJoseph Chan * This driver is distributed in the hope that it will be useful, 15c577b8a1SJoseph Chan * but WITHOUT ANY WARRANTY; without even the implied warranty of 16c577b8a1SJoseph Chan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17c577b8a1SJoseph Chan * GNU General Public License for more details. 18c577b8a1SJoseph Chan * 19c577b8a1SJoseph Chan * You should have received a copy of the GNU General Public License 20c577b8a1SJoseph Chan * along with this program; if not, write to the Free Software 21c577b8a1SJoseph Chan * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22c577b8a1SJoseph Chan */ 23c577b8a1SJoseph Chan 24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ 25c577b8a1SJoseph Chan /* */ 26c577b8a1SJoseph Chan /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ 27c577b8a1SJoseph Chan /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ 28c577b8a1SJoseph Chan /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ 29c577b8a1SJoseph Chan /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ 30f7278fd0SJosepch Chan /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ 31f7278fd0SJosepch Chan /* 2007-09-17 Lydia Wang Add VT1708B codec support */ 3276d9b0ddSHarald Welte /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ 33fb4cb772SHarald Welte /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ 34d949cac1SHarald Welte /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ 3569e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ 360aa62aefSHarald Welte /* 2008-04-09 Lydia Wang Add Independent HP feature */ 3798aa34c0SHarald Welte /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ 38d7426329SHarald Welte /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ 398e86597fSLydia Wang /* 2009-02-16 Logan Li Add support for VT1718S */ 408e86597fSLydia Wang /* 2009-03-13 Logan Li Add support for VT1716S */ 418e86597fSLydia Wang /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ 428e86597fSLydia Wang /* 2009-07-08 Lydia Wang Add support for VT2002P */ 438e86597fSLydia Wang /* 2009-07-21 Lydia Wang Add support for VT1812 */ 4436dd5c4aSLydia Wang /* 2009-09-19 Lydia Wang Add support for VT1818S */ 45c577b8a1SJoseph Chan /* */ 46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan 49c577b8a1SJoseph Chan #include <linux/init.h> 50c577b8a1SJoseph Chan #include <linux/delay.h> 51c577b8a1SJoseph Chan #include <linux/slab.h> 52da155d5bSPaul Gortmaker #include <linux/module.h> 53c577b8a1SJoseph Chan #include <sound/core.h> 540aa62aefSHarald Welte #include <sound/asoundef.h> 55*be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h> 56c577b8a1SJoseph Chan #include "hda_local.h" 57128bc4baSTakashi Iwai #include "hda_auto_parser.h" 581835a0f9STakashi Iwai #include "hda_jack.h" 59b3f6008fSTakashi Iwai #include "hda_generic.h" 60c577b8a1SJoseph Chan 61c577b8a1SJoseph Chan /* Pin Widget NID */ 6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 64c577b8a1SJoseph Chan 65d7426329SHarald Welte enum VIA_HDA_CODEC { 66d7426329SHarald Welte UNKNOWN = -1, 67d7426329SHarald Welte VT1708, 68d7426329SHarald Welte VT1709_10CH, 69d7426329SHarald Welte VT1709_6CH, 70d7426329SHarald Welte VT1708B_8CH, 71d7426329SHarald Welte VT1708B_4CH, 72d7426329SHarald Welte VT1708S, 73518bf3baSLydia Wang VT1708BCE, 74d7426329SHarald Welte VT1702, 75eb7188caSLydia Wang VT1718S, 76f3db423dSLydia Wang VT1716S, 7725eaba2fSLydia Wang VT2002P, 78ab6734e7SLydia Wang VT1812, 7911890956SLydia Wang VT1802, 8043737e0aSLydia Wang VT1705CF, 816121b84aSLydia Wang VT1808, 82d7426329SHarald Welte CODEC_TYPES, 83d7426329SHarald Welte }; 84d7426329SHarald Welte 8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 8611890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 8711890956SLydia Wang (spec)->codec_type == VT1812 ||\ 8811890956SLydia Wang (spec)->codec_type == VT1802) 8911890956SLydia Wang 901f2e99feSLydia Wang struct via_spec { 91b3f6008fSTakashi Iwai struct hda_gen_spec gen; 92b3f6008fSTakashi Iwai 931f2e99feSLydia Wang /* HP mode source */ 94f3db423dSLydia Wang unsigned int dmic_enabled; 951f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 961f2e99feSLydia Wang 97e9d010c2STakashi Iwai /* analog low-power control */ 98e9d010c2STakashi Iwai bool alc_mode; 99e9d010c2STakashi Iwai 1001f2e99feSLydia Wang /* work to check hp jack state */ 101187d333eSTakashi Iwai int hp_work_active; 102e06e5a29STakashi Iwai int vt1708_jack_detect; 1031f2e99feSLydia Wang }; 1041f2e99feSLydia Wang 1050341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 106b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 107b3f6008fSTakashi Iwai struct hda_codec *codec, 108b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 109b3f6008fSTakashi Iwai int action); 110b3f6008fSTakashi Iwai 111225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */ 112225068abSTakashi Iwai 1135b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1145b0cb1d8SJaroslav Kysela { 1155b0cb1d8SJaroslav Kysela struct via_spec *spec; 1165b0cb1d8SJaroslav Kysela 1175b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1185b0cb1d8SJaroslav Kysela if (spec == NULL) 1195b0cb1d8SJaroslav Kysela return NULL; 1205b0cb1d8SJaroslav Kysela 1215b0cb1d8SJaroslav Kysela codec->spec = spec; 122b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1230341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1240341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1250341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1260341ccd7SLydia Wang spec->codec_type = VT1708S; 12713961170STakashi Iwai spec->gen.indep_hp = 1; 12805909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1; 129b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 13074f14b36STakashi Iwai spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; 131967b1307STakashi Iwai codec->power_save_node = 1; 132688b12ccSTakashi Iwai spec->gen.power_down_unused = 1; 133225068abSTakashi Iwai codec->patch_ops = via_patch_ops; 1345b0cb1d8SJaroslav Kysela return spec; 1355b0cb1d8SJaroslav Kysela } 1365b0cb1d8SJaroslav Kysela 137744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 138d7426329SHarald Welte { 1397639a06cSTakashi Iwai u32 vendor_id = codec->core.vendor_id; 140d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 141d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 142d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 143d7426329SHarald Welte 144d7426329SHarald Welte /* get codec type */ 145d7426329SHarald Welte if (ven_id != 0x1106) 146d7426329SHarald Welte codec_type = UNKNOWN; 147d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 148d7426329SHarald Welte codec_type = VT1708; 149d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 150d7426329SHarald Welte codec_type = VT1709_10CH; 151d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 152d7426329SHarald Welte codec_type = VT1709_6CH; 153518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 154d7426329SHarald Welte codec_type = VT1708B_8CH; 155518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 156518bf3baSLydia Wang codec_type = VT1708BCE; 157518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 158d7426329SHarald Welte codec_type = VT1708B_4CH; 159d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 160d7426329SHarald Welte && (dev_id >> 12) < 8) 161d7426329SHarald Welte codec_type = VT1708S; 162d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 163d7426329SHarald Welte && (dev_id >> 12) < 8) 164d7426329SHarald Welte codec_type = VT1702; 165eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 166eb7188caSLydia Wang && (dev_id >> 12) < 8) 167eb7188caSLydia Wang codec_type = VT1718S; 168f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 169f3db423dSLydia Wang codec_type = VT1716S; 170bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 171bb3c6bfcSLydia Wang codec_type = VT1718S; 17225eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 17325eaba2fSLydia Wang codec_type = VT2002P; 174ab6734e7SLydia Wang else if (dev_id == 0x0448) 175ab6734e7SLydia Wang codec_type = VT1812; 17636dd5c4aSLydia Wang else if (dev_id == 0x0440) 17736dd5c4aSLydia Wang codec_type = VT1708S; 17811890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 17911890956SLydia Wang codec_type = VT1802; 18043737e0aSLydia Wang else if (dev_id == 0x4760) 18143737e0aSLydia Wang codec_type = VT1705CF; 1826121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762) 1836121b84aSLydia Wang codec_type = VT1808; 184d7426329SHarald Welte else 185d7426329SHarald Welte codec_type = UNKNOWN; 186d7426329SHarald Welte return codec_type; 187d7426329SHarald Welte }; 188d7426329SHarald Welte 189ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 190ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 1911f2e99feSLydia Wang 192187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \ 193187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ 194187d333eSTakashi Iwai !is_aa_path_mute(codec)) 1951f2e99feSLydia Wang 196b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec) 1971f2e99feSLydia Wang { 198b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 199b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 2001f2e99feSLydia Wang return; 201187d333eSTakashi Iwai if (spec->hp_work_active) { 202b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); 2037eaa9161SWang Xingchao codec->jackpoll_interval = 0; 204b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work); 205b3f6008fSTakashi Iwai spec->hp_work_active = false; 206187d333eSTakashi Iwai } 207187d333eSTakashi Iwai } 208187d333eSTakashi Iwai 209b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec) 210187d333eSTakashi Iwai { 211b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 212b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 213187d333eSTakashi Iwai return; 21405dc0fc9SDavid Henningsson if (spec->vt1708_jack_detect) { 215187d333eSTakashi Iwai if (!spec->hp_work_active) { 216b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 217b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); 2182f35c630STakashi Iwai schedule_delayed_work(&codec->jackpoll_work, 0); 219b3f6008fSTakashi Iwai spec->hp_work_active = true; 220187d333eSTakashi Iwai } 221b3f6008fSTakashi Iwai } else if (!hp_detect_with_aa(codec)) 222b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 2231f2e99feSLydia Wang } 224f5271101SLydia Wang 22524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 22624088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 22724088a58STakashi Iwai { 228dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 22924088a58STakashi Iwai } 23024088a58STakashi Iwai 23124088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 23224088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 23324088a58STakashi Iwai { 23424088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 235735c75cfSTakashi Iwai struct via_spec *spec = codec->spec; 236735c75cfSTakashi Iwai 237735c75cfSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused; 23824088a58STakashi Iwai return 0; 23924088a58STakashi Iwai } 24024088a58STakashi Iwai 24124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 24224088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 24324088a58STakashi Iwai { 24424088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24524088a58STakashi Iwai struct via_spec *spec = codec->spec; 246688b12ccSTakashi Iwai bool val = !!ucontrol->value.enumerated.item[0]; 24724088a58STakashi Iwai 248735c75cfSTakashi Iwai if (val == spec->gen.power_down_unused) 24924088a58STakashi Iwai return 0; 250735c75cfSTakashi Iwai /* codec->power_save_node = val; */ /* widget PM seems yet broken */ 251688b12ccSTakashi Iwai spec->gen.power_down_unused = val; 252e9d010c2STakashi Iwai analog_low_current_mode(codec); 25324088a58STakashi Iwai return 1; 25424088a58STakashi Iwai } 25524088a58STakashi Iwai 2560e8f9862STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 25724088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25824088a58STakashi Iwai .name = "Dynamic Power-Control", 25924088a58STakashi Iwai .info = via_pin_power_ctl_info, 26024088a58STakashi Iwai .get = via_pin_power_ctl_get, 26124088a58STakashi Iwai .put = via_pin_power_ctl_put, 26224088a58STakashi Iwai }; 26324088a58STakashi Iwai 2644738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP 2654738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */ 2660e8f9862STakashi Iwai static const struct snd_kcontrol_new via_beep_mixer[] = { 2674738465cSW. Trevor King HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), 2684738465cSW. Trevor King HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), 2694738465cSW. Trevor King }; 2704738465cSW. Trevor King 2710e8f9862STakashi Iwai static int set_beep_amp(struct via_spec *spec, hda_nid_t nid, 2720e8f9862STakashi Iwai int idx, int dir) 2734738465cSW. Trevor King { 2740e8f9862STakashi Iwai struct snd_kcontrol_new *knew; 2750e8f9862STakashi Iwai unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); 2760e8f9862STakashi Iwai int i; 2774738465cSW. Trevor King 2780e8f9862STakashi Iwai spec->gen.beep_nid = nid; 2790e8f9862STakashi Iwai for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) { 2800e8f9862STakashi Iwai knew = snd_hda_gen_add_kctl(&spec->gen, NULL, 2810e8f9862STakashi Iwai &via_beep_mixer[i]); 2820e8f9862STakashi Iwai if (!knew) 2834738465cSW. Trevor King return -ENOMEM; 2840e8f9862STakashi Iwai knew->private_value = beep_amp; 2854738465cSW. Trevor King } 2864738465cSW. Trevor King return 0; 2874738465cSW. Trevor King } 2884738465cSW. Trevor King 2890e8f9862STakashi Iwai static int auto_parse_beep(struct hda_codec *codec) 2904738465cSW. Trevor King { 2914738465cSW. Trevor King struct via_spec *spec = codec->spec; 2924738465cSW. Trevor King hda_nid_t nid; 2934738465cSW. Trevor King 2944738465cSW. Trevor King for_each_hda_codec_node(nid, codec) 2950e8f9862STakashi Iwai if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) 2960e8f9862STakashi Iwai return set_beep_amp(spec, nid, 0, HDA_OUTPUT); 2970e8f9862STakashi Iwai return 0; 2984738465cSW. Trevor King } 2994738465cSW. Trevor King #else 3000e8f9862STakashi Iwai #define auto_parse_beep(codec) 0 3014738465cSW. Trevor King #endif 30224088a58STakashi Iwai 303f5271101SLydia Wang /* check AA path's mute status */ 304ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 305ada509ecSTakashi Iwai { 306ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 307ada509ecSTakashi Iwai const struct hda_amp_list *p; 3080186f4f4STakashi Iwai int ch, v; 309ada509ecSTakashi Iwai 3100186f4f4STakashi Iwai p = spec->gen.loopback.amplist; 3110186f4f4STakashi Iwai if (!p) 3120186f4f4STakashi Iwai return true; 3130186f4f4STakashi Iwai for (; p->nid; p++) { 314ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 315ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 316ada509ecSTakashi Iwai p->idx); 317ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 318ada509ecSTakashi Iwai return false; 319f5271101SLydia Wang } 320f5271101SLydia Wang } 321ada509ecSTakashi Iwai return true; 322f5271101SLydia Wang } 323f5271101SLydia Wang 324f5271101SLydia Wang /* enter/exit analog low-current mode */ 325e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 326f5271101SLydia Wang { 327f5271101SLydia Wang struct via_spec *spec = codec->spec; 328ada509ecSTakashi Iwai bool enable; 329ada509ecSTakashi Iwai unsigned int verb, parm; 330f5271101SLydia Wang 331967b1307STakashi Iwai if (!codec->power_save_node) 332e9d010c2STakashi Iwai enable = false; 333e9d010c2STakashi Iwai else 334b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 335e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 336e9d010c2STakashi Iwai return; 337e9d010c2STakashi Iwai spec->alc_mode = enable; 338f5271101SLydia Wang 339f5271101SLydia Wang /* decide low current mode's verb & parameter */ 340f5271101SLydia Wang switch (spec->codec_type) { 341f5271101SLydia Wang case VT1708B_8CH: 342f5271101SLydia Wang case VT1708B_4CH: 343f5271101SLydia Wang verb = 0xf70; 344f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 345f5271101SLydia Wang break; 346f5271101SLydia Wang case VT1708S: 347eb7188caSLydia Wang case VT1718S: 348f3db423dSLydia Wang case VT1716S: 349f5271101SLydia Wang verb = 0xf73; 350f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 351f5271101SLydia Wang break; 352f5271101SLydia Wang case VT1702: 353f5271101SLydia Wang verb = 0xf73; 354f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 355f5271101SLydia Wang break; 35625eaba2fSLydia Wang case VT2002P: 357ab6734e7SLydia Wang case VT1812: 35811890956SLydia Wang case VT1802: 35925eaba2fSLydia Wang verb = 0xf93; 36025eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 36125eaba2fSLydia Wang break; 36243737e0aSLydia Wang case VT1705CF: 3636121b84aSLydia Wang case VT1808: 36443737e0aSLydia Wang verb = 0xf82; 36543737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 36643737e0aSLydia Wang break; 367f5271101SLydia Wang default: 368f5271101SLydia Wang return; /* other codecs are not supported */ 369f5271101SLydia Wang } 370f5271101SLydia Wang /* send verb */ 3717639a06cSTakashi Iwai snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); 372f5271101SLydia Wang } 373f5271101SLydia Wang 374e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 375e9d010c2STakashi Iwai { 376e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 377e9d010c2STakashi Iwai } 378e9d010c2STakashi Iwai 379b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 380b3f6008fSTakashi Iwai struct hda_codec *codec, 381b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 382b3f6008fSTakashi Iwai int action) 383c577b8a1SJoseph Chan { 384b3f6008fSTakashi Iwai analog_low_current_mode(codec); 385b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 386c577b8a1SJoseph Chan } 387c577b8a1SJoseph Chan 388c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 389c577b8a1SJoseph Chan { 390b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 391a8dca460STakashi Iwai snd_hda_gen_free(codec); 392c577b8a1SJoseph Chan } 393c577b8a1SJoseph Chan 3942a43952aSTakashi Iwai #ifdef CONFIG_PM 39568cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 3961f2e99feSLydia Wang { 3971f2e99feSLydia Wang struct via_spec *spec = codec->spec; 398b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 39994c142a1SDavid Henningsson 40094c142a1SDavid Henningsson /* Fix pop noise on headphones */ 4012c38d990STakashi Iwai if (spec->codec_type == VT1802) 4022c38d990STakashi Iwai snd_hda_shutup_pins(codec); 40394c142a1SDavid Henningsson 4041f2e99feSLydia Wang return 0; 4051f2e99feSLydia Wang } 4066b6d0007STakashi Iwai 4076b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec) 4086b6d0007STakashi Iwai { 4096b6d0007STakashi Iwai /* some delay here to make jack detection working (bko#98921) */ 4106b6d0007STakashi Iwai msleep(10); 4116b6d0007STakashi Iwai codec->patch_ops.init(codec); 4126b6d0007STakashi Iwai regcache_sync(codec->core.regmap); 4136b6d0007STakashi Iwai return 0; 4146b6d0007STakashi Iwai } 4151f2e99feSLydia Wang #endif 4161f2e99feSLydia Wang 41783012a7cSTakashi Iwai #ifdef CONFIG_PM 418cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 419cb53c626STakashi Iwai { 420cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 421b3f6008fSTakashi Iwai analog_low_current_mode(codec); 422b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 423b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 424cb53c626STakashi Iwai } 425cb53c626STakashi Iwai #endif 426cb53c626STakashi Iwai 427c577b8a1SJoseph Chan /* 428c577b8a1SJoseph Chan */ 4295d41762aSTakashi Iwai 4305d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 4315d41762aSTakashi Iwai 43290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 4330e8f9862STakashi Iwai .build_controls = snd_hda_gen_build_controls, 434b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 435c577b8a1SJoseph Chan .init = via_init, 436c577b8a1SJoseph Chan .free = via_free, 4374e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 4382a43952aSTakashi Iwai #ifdef CONFIG_PM 4391f2e99feSLydia Wang .suspend = via_suspend, 4406b6d0007STakashi Iwai .resume = via_resume, 441cb53c626STakashi Iwai .check_power_status = via_check_power_status, 442cb53c626STakashi Iwai #endif 443c577b8a1SJoseph Chan }; 444c577b8a1SJoseph Chan 4454a79616dSTakashi Iwai 446b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 447b3f6008fSTakashi Iwai /* power down jack detect function */ 448b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 449b3f6008fSTakashi Iwai { } 4504a79616dSTakashi Iwai }; 45176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 45276d9b0ddSHarald Welte { 45376d9b0ddSHarald Welte unsigned int def_conf; 45476d9b0ddSHarald Welte unsigned char seqassoc; 45576d9b0ddSHarald Welte 4562f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 45776d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 45876d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 45982ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 46082ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 46176d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 4622f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 46376d9b0ddSHarald Welte } 46476d9b0ddSHarald Welte 46576d9b0ddSHarald Welte return; 46676d9b0ddSHarald Welte } 46776d9b0ddSHarald Welte 468e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 4691f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 4701f2e99feSLydia Wang { 4711f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4721f2e99feSLydia Wang struct via_spec *spec = codec->spec; 4731f2e99feSLydia Wang 4741f2e99feSLydia Wang if (spec->codec_type != VT1708) 4751f2e99feSLydia Wang return 0; 476e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 4771f2e99feSLydia Wang return 0; 4781f2e99feSLydia Wang } 4791f2e99feSLydia Wang 480e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 4811f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 4821f2e99feSLydia Wang { 4831f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4841f2e99feSLydia Wang struct via_spec *spec = codec->spec; 485187d333eSTakashi Iwai int val; 4861f2e99feSLydia Wang 4871f2e99feSLydia Wang if (spec->codec_type != VT1708) 4881f2e99feSLydia Wang return 0; 489187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 490187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 491187d333eSTakashi Iwai return 0; 492187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 493b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 494187d333eSTakashi Iwai return 1; 4951f2e99feSLydia Wang } 4961f2e99feSLydia Wang 4970e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 4981f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4991f2e99feSLydia Wang .name = "Jack Detect", 5001f2e99feSLydia Wang .count = 1, 5011f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 502e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 503e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 5041f2e99feSLydia Wang }; 5051f2e99feSLydia Wang 5064abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 5074abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 5084abdbd1cSTakashi Iwai .no_dac = 0x4000, 5094abdbd1cSTakashi Iwai .shared_primary = 0x10000, 5104abdbd1cSTakashi Iwai .shared_surr = 0x20, 5114abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5124abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 5134abdbd1cSTakashi Iwai }; 5144abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 5154abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 5164abdbd1cSTakashi Iwai .no_dac = 0x4000, 5174abdbd1cSTakashi Iwai .shared_primary = 0x12, 5184abdbd1cSTakashi Iwai .shared_surr = 0x20, 5194abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5204abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 5214abdbd1cSTakashi Iwai }; 5224abdbd1cSTakashi Iwai 523b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 524b3f6008fSTakashi Iwai { 525b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 526b3f6008fSTakashi Iwai int err; 527b3f6008fSTakashi Iwai 5284abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 5294abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 5304abdbd1cSTakashi Iwai 531b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 532b3f6008fSTakashi Iwai if (err < 0) 533b3f6008fSTakashi Iwai return err; 534b3f6008fSTakashi Iwai 535b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 536b3f6008fSTakashi Iwai if (err < 0) 537b3f6008fSTakashi Iwai return err; 538b3f6008fSTakashi Iwai 5390e8f9862STakashi Iwai err = auto_parse_beep(codec); 5400e8f9862STakashi Iwai if (err < 0) 5410e8f9862STakashi Iwai return err; 5420e8f9862STakashi Iwai 5430e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum)) 5440e8f9862STakashi Iwai return -ENOMEM; 5450e8f9862STakashi Iwai 546688b12ccSTakashi Iwai /* disable widget PM at start for compatibility */ 547967b1307STakashi Iwai codec->power_save_node = 0; 548688b12ccSTakashi Iwai spec->gen.power_down_unused = 0; 549b3f6008fSTakashi Iwai return 0; 5504a918ffeSTakashi Iwai } 5514a918ffeSTakashi Iwai 5525d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 5535d41762aSTakashi Iwai { 554e9d010c2STakashi Iwai /* init power states */ 555e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 556e9d010c2STakashi Iwai 557b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 55811890956SLydia Wang 559b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 56025eaba2fSLydia Wang 561c577b8a1SJoseph Chan return 0; 562c577b8a1SJoseph Chan } 563c577b8a1SJoseph Chan 564f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 565f672f65aSDavid Henningsson { 566f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 567f672f65aSDavid Henningsson temporary enable jackpoll */ 568f672f65aSDavid Henningsson int err; 569f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 570f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 5710e8f9862STakashi Iwai err = snd_hda_gen_build_controls(codec); 572f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 573f672f65aSDavid Henningsson return err; 574f672f65aSDavid Henningsson } 575f672f65aSDavid Henningsson 576b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 577337b9d02STakashi Iwai { 578337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 579b3f6008fSTakashi Iwai int i, err; 580337b9d02STakashi Iwai 581b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 5827639a06cSTakashi Iwai if (err < 0 || codec->core.vendor_id != 0x11061708) 583b3f6008fSTakashi Iwai return err; 584b3f6008fSTakashi Iwai 585b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 586b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 587b3f6008fSTakashi Iwai * disable the 24bit format, so far. 588b3f6008fSTakashi Iwai */ 589bbbc7e85STakashi Iwai for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { 590bbbc7e85STakashi Iwai struct hda_pcm *info = spec->gen.pcm_rec[i]; 591bbbc7e85STakashi Iwai if (!info) 592bbbc7e85STakashi Iwai continue; 593b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 594b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 595b3f6008fSTakashi Iwai continue; 596b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 597b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 598337b9d02STakashi Iwai } 599b3f6008fSTakashi Iwai 6001c55d521STakashi Iwai return 0; 601337b9d02STakashi Iwai } 602337b9d02STakashi Iwai 603c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 604c577b8a1SJoseph Chan { 605c577b8a1SJoseph Chan struct via_spec *spec; 606c577b8a1SJoseph Chan int err; 607c577b8a1SJoseph Chan 608c577b8a1SJoseph Chan /* create a codec specific record */ 6095b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 610c577b8a1SJoseph Chan if (spec == NULL) 611c577b8a1SJoseph Chan return -ENOMEM; 612c577b8a1SJoseph Chan 613225068abSTakashi Iwai /* override some patch_ops */ 614225068abSTakashi Iwai codec->patch_ops.build_controls = vt1708_build_controls; 615225068abSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 616b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 617b3f6008fSTakashi Iwai 618b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 619b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 620b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 621b3f6008fSTakashi Iwai 622b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 623b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 624b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 625eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 626eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 627620e2b28STakashi Iwai 62812daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 62912daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 63012daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 63112daef65STakashi Iwai 632f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1708_init_verbs); 633f8bfc628STakashi Iwai if (err < 0) 634f8bfc628STakashi Iwai goto error; 635f8bfc628STakashi Iwai 636c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 63712daef65STakashi Iwai err = via_parse_auto_config(codec); 638fcbdcc1aSTakashi Iwai if (err < 0) 639fcbdcc1aSTakashi Iwai goto error; 640c577b8a1SJoseph Chan 64112daef65STakashi Iwai /* add jack detect on/off control */ 6420e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) { 6430e8f9862STakashi Iwai err = -ENOMEM; 6440e8f9862STakashi Iwai goto error; 6450e8f9862STakashi Iwai } 646c577b8a1SJoseph Chan 647b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 648b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 649b3f6008fSTakashi Iwai 650c577b8a1SJoseph Chan return 0; 651fcbdcc1aSTakashi Iwai 652fcbdcc1aSTakashi Iwai error: 653fcbdcc1aSTakashi Iwai via_free(codec); 654fcbdcc1aSTakashi Iwai return err; 655c577b8a1SJoseph Chan } 656c577b8a1SJoseph Chan 657ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 658c577b8a1SJoseph Chan { 659c577b8a1SJoseph Chan struct via_spec *spec; 660c577b8a1SJoseph Chan int err; 661c577b8a1SJoseph Chan 662c577b8a1SJoseph Chan /* create a codec specific record */ 6635b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 664c577b8a1SJoseph Chan if (spec == NULL) 665c577b8a1SJoseph Chan return -ENOMEM; 666c577b8a1SJoseph Chan 667b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 668620e2b28STakashi Iwai 66912daef65STakashi Iwai err = via_parse_auto_config(codec); 670fcbdcc1aSTakashi Iwai if (err < 0) 671fcbdcc1aSTakashi Iwai goto error; 672c577b8a1SJoseph Chan 673f7278fd0SJosepch Chan return 0; 674fcbdcc1aSTakashi Iwai 675fcbdcc1aSTakashi Iwai error: 676fcbdcc1aSTakashi Iwai via_free(codec); 677fcbdcc1aSTakashi Iwai return err; 678f7278fd0SJosepch Chan } 679f7278fd0SJosepch Chan 680518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 681ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 682f7278fd0SJosepch Chan { 683f7278fd0SJosepch Chan struct via_spec *spec; 684f7278fd0SJosepch Chan int err; 685f7278fd0SJosepch Chan 686518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 687518bf3baSLydia Wang return patch_vt1708S(codec); 688ddd304d8STakashi Iwai 689f7278fd0SJosepch Chan /* create a codec specific record */ 6905b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 691f7278fd0SJosepch Chan if (spec == NULL) 692f7278fd0SJosepch Chan return -ENOMEM; 693f7278fd0SJosepch Chan 694b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 695620e2b28STakashi Iwai 696f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 69712daef65STakashi Iwai err = via_parse_auto_config(codec); 698fcbdcc1aSTakashi Iwai if (err < 0) 699fcbdcc1aSTakashi Iwai goto error; 700f7278fd0SJosepch Chan 701f7278fd0SJosepch Chan return 0; 702fcbdcc1aSTakashi Iwai 703fcbdcc1aSTakashi Iwai error: 704fcbdcc1aSTakashi Iwai via_free(codec); 705fcbdcc1aSTakashi Iwai return err; 706f7278fd0SJosepch Chan } 707f7278fd0SJosepch Chan 708d949cac1SHarald Welte /* Patch for VT1708S */ 709096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 710d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 711d7426329SHarald Welte {0x1, 0xf98, 0x1}, 712bc7e7e5cSLydia Wang /* don't bybass mixer */ 713bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 714d949cac1SHarald Welte { } 715d949cac1SHarald Welte }; 716d949cac1SHarald Welte 7176369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 7186369bcfcSLydia Wang int offset, int num_steps, int step_size) 7196369bcfcSLydia Wang { 720d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 721d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 7226369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 7236369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 7246369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 7256369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 7266369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 7276369bcfcSLydia Wang } 7286369bcfcSLydia Wang 729d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 730d949cac1SHarald Welte { 731d949cac1SHarald Welte struct via_spec *spec; 732d949cac1SHarald Welte int err; 733d949cac1SHarald Welte 734d949cac1SHarald Welte /* create a codec specific record */ 7355b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 736d949cac1SHarald Welte if (spec == NULL) 737d949cac1SHarald Welte return -ENOMEM; 738d949cac1SHarald Welte 739b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 740d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 741d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 742620e2b28STakashi Iwai 743518bf3baSLydia Wang /* correct names for VT1708BCE */ 744ded255beSTakashi Iwai if (get_codec_type(codec) == VT1708BCE) 745ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1708BCE"); 746bc92df7fSLydia Wang /* correct names for VT1705 */ 747ded255beSTakashi Iwai if (codec->core.vendor_id == 0x11064397) 748ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1705"); 749b3f6008fSTakashi Iwai 750f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1708S_init_verbs); 751f8bfc628STakashi Iwai if (err < 0) 752f8bfc628STakashi Iwai goto error; 753f8bfc628STakashi Iwai 754b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 755b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 756fcbdcc1aSTakashi Iwai if (err < 0) 757fcbdcc1aSTakashi Iwai goto error; 758b3f6008fSTakashi Iwai 759d949cac1SHarald Welte return 0; 760fcbdcc1aSTakashi Iwai 761fcbdcc1aSTakashi Iwai error: 762fcbdcc1aSTakashi Iwai via_free(codec); 763fcbdcc1aSTakashi Iwai return err; 764d949cac1SHarald Welte } 765d949cac1SHarald Welte 766d949cac1SHarald Welte /* Patch for VT1702 */ 767d949cac1SHarald Welte 768096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 769bc7e7e5cSLydia Wang /* mixer enable */ 770bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 771bc7e7e5cSLydia Wang /* GPIO 0~2 */ 772bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 773d949cac1SHarald Welte { } 774d949cac1SHarald Welte }; 775d949cac1SHarald Welte 776d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 777d949cac1SHarald Welte { 778d949cac1SHarald Welte struct via_spec *spec; 779d949cac1SHarald Welte int err; 780d949cac1SHarald Welte 781d949cac1SHarald Welte /* create a codec specific record */ 7825b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 783d949cac1SHarald Welte if (spec == NULL) 784d949cac1SHarald Welte return -ENOMEM; 785d949cac1SHarald Welte 786b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 787620e2b28STakashi Iwai 78812daef65STakashi Iwai /* limit AA path volume to 0 dB */ 78912daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 79012daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 79112daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 79212daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 79312daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 79412daef65STakashi Iwai 795f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1702_init_verbs); 796f8bfc628STakashi Iwai if (err < 0) 797f8bfc628STakashi Iwai goto error; 798f8bfc628STakashi Iwai 799d949cac1SHarald Welte /* automatic parse from the BIOS config */ 80012daef65STakashi Iwai err = via_parse_auto_config(codec); 801fcbdcc1aSTakashi Iwai if (err < 0) 802fcbdcc1aSTakashi Iwai goto error; 803d949cac1SHarald Welte 804d949cac1SHarald Welte return 0; 805fcbdcc1aSTakashi Iwai 806fcbdcc1aSTakashi Iwai error: 807fcbdcc1aSTakashi Iwai via_free(codec); 808fcbdcc1aSTakashi Iwai return err; 809d949cac1SHarald Welte } 810d949cac1SHarald Welte 811eb7188caSLydia Wang /* Patch for VT1718S */ 812eb7188caSLydia Wang 813096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 8144ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 8154ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 816eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 817eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 8185d41762aSTakashi Iwai 819eb7188caSLydia Wang { } 820eb7188caSLydia Wang }; 821eb7188caSLydia Wang 82230b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 82330b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 82430b45033STakashi Iwai */ 82530b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 82630b45033STakashi Iwai { 82730b45033STakashi Iwai struct via_spec *spec = codec->spec; 82830b45033STakashi Iwai int i, nums; 82930b45033STakashi Iwai hda_nid_t conn[8]; 83030b45033STakashi Iwai hda_nid_t nid; 83130b45033STakashi Iwai 832b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 83330b45033STakashi Iwai return 0; 834b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 83530b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 83630b45033STakashi Iwai for (i = 0; i < nums; i++) { 83730b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 83830b45033STakashi Iwai return 0; 83930b45033STakashi Iwai } 84030b45033STakashi Iwai 84130b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 8427639a06cSTakashi Iwai for_each_hda_codec_node(nid, codec) { 84330b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 84430b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 84530b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 84630b45033STakashi Iwai conn[nums++] = nid; 84730b45033STakashi Iwai return snd_hda_override_conn_list(codec, 848b3f6008fSTakashi Iwai spec->gen.mixer_nid, 84930b45033STakashi Iwai nums, conn); 85030b45033STakashi Iwai } 85130b45033STakashi Iwai } 85230b45033STakashi Iwai return 0; 85330b45033STakashi Iwai } 85430b45033STakashi Iwai 85530b45033STakashi Iwai 856eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 857eb7188caSLydia Wang { 858eb7188caSLydia Wang struct via_spec *spec; 859eb7188caSLydia Wang int err; 860eb7188caSLydia Wang 861eb7188caSLydia Wang /* create a codec specific record */ 8625b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 863eb7188caSLydia Wang if (spec == NULL) 864eb7188caSLydia Wang return -ENOMEM; 865eb7188caSLydia Wang 866b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 867d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 868d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 86930b45033STakashi Iwai add_secret_dac_path(codec); 870620e2b28STakashi Iwai 871f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1718S_init_verbs); 872f8bfc628STakashi Iwai if (err < 0) 873f8bfc628STakashi Iwai goto error; 874f8bfc628STakashi Iwai 875eb7188caSLydia Wang /* automatic parse from the BIOS config */ 87612daef65STakashi Iwai err = via_parse_auto_config(codec); 877fcbdcc1aSTakashi Iwai if (err < 0) 878fcbdcc1aSTakashi Iwai goto error; 879eb7188caSLydia Wang 880eb7188caSLydia Wang return 0; 881fcbdcc1aSTakashi Iwai 882fcbdcc1aSTakashi Iwai error: 883fcbdcc1aSTakashi Iwai via_free(codec); 884fcbdcc1aSTakashi Iwai return err; 885eb7188caSLydia Wang } 886f3db423dSLydia Wang 887f3db423dSLydia Wang /* Patch for VT1716S */ 888f3db423dSLydia Wang 889f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 890f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 891f3db423dSLydia Wang { 892f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 893f3db423dSLydia Wang uinfo->count = 1; 894f3db423dSLydia Wang uinfo->value.integer.min = 0; 895f3db423dSLydia Wang uinfo->value.integer.max = 1; 896f3db423dSLydia Wang return 0; 897f3db423dSLydia Wang } 898f3db423dSLydia Wang 899f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 900f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 901f3db423dSLydia Wang { 902f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 903f3db423dSLydia Wang int index = 0; 904f3db423dSLydia Wang 905f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 906f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 907f3db423dSLydia Wang if (index != -1) 908f3db423dSLydia Wang *ucontrol->value.integer.value = index; 909f3db423dSLydia Wang 910f3db423dSLydia Wang return 0; 911f3db423dSLydia Wang } 912f3db423dSLydia Wang 913f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 914f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 915f3db423dSLydia Wang { 916f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 917f3db423dSLydia Wang struct via_spec *spec = codec->spec; 918f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 919f3db423dSLydia Wang 920f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 921f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 922f3db423dSLydia Wang spec->dmic_enabled = index; 923f3db423dSLydia Wang return 1; 924f3db423dSLydia Wang } 925f3db423dSLydia Wang 9260e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol = 9270e8f9862STakashi Iwai HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT); 9280e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = { 929f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 930f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 9315b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 932f3db423dSLydia Wang .count = 1, 933f3db423dSLydia Wang .info = vt1716s_dmic_info, 934f3db423dSLydia Wang .get = vt1716s_dmic_get, 935f3db423dSLydia Wang .put = vt1716s_dmic_put, 936f3db423dSLydia Wang }; 937f3db423dSLydia Wang 938f3db423dSLydia Wang 939f3db423dSLydia Wang /* mono-out mixer elements */ 9400e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer = 9410e8f9862STakashi Iwai HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT); 942f3db423dSLydia Wang 943096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 944f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 945f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 946f3db423dSLydia Wang /* don't bybass mixer */ 947f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 948f3db423dSLydia Wang /* Enable mono output */ 949f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 950f3db423dSLydia Wang { } 951f3db423dSLydia Wang }; 952f3db423dSLydia Wang 953f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 954f3db423dSLydia Wang { 955f3db423dSLydia Wang struct via_spec *spec; 956f3db423dSLydia Wang int err; 957f3db423dSLydia Wang 958f3db423dSLydia Wang /* create a codec specific record */ 9595b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 960f3db423dSLydia Wang if (spec == NULL) 961f3db423dSLydia Wang return -ENOMEM; 962f3db423dSLydia Wang 963b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 964d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 965d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 966620e2b28STakashi Iwai 967f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1716S_init_verbs); 968f8bfc628STakashi Iwai if (err < 0) 969f8bfc628STakashi Iwai goto error; 970f8bfc628STakashi Iwai 971f3db423dSLydia Wang /* automatic parse from the BIOS config */ 97212daef65STakashi Iwai err = via_parse_auto_config(codec); 973fcbdcc1aSTakashi Iwai if (err < 0) 974fcbdcc1aSTakashi Iwai goto error; 975f3db423dSLydia Wang 9760e8f9862STakashi Iwai if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) || 9770e8f9862STakashi Iwai !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) || 9780e8f9862STakashi Iwai !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) { 9790e8f9862STakashi Iwai err = -ENOMEM; 9800e8f9862STakashi Iwai goto error; 9810e8f9862STakashi Iwai } 982f3db423dSLydia Wang 983f3db423dSLydia Wang return 0; 984fcbdcc1aSTakashi Iwai 985fcbdcc1aSTakashi Iwai error: 986fcbdcc1aSTakashi Iwai via_free(codec); 987fcbdcc1aSTakashi Iwai return err; 988f3db423dSLydia Wang } 98925eaba2fSLydia Wang 99025eaba2fSLydia Wang /* for vt2002P */ 99125eaba2fSLydia Wang 992096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 993eadb9a80SLydia Wang /* Class-D speaker related verbs */ 994eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 995eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 996eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 99725eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 99825eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 99925eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 100025eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 100125eaba2fSLydia Wang { } 100225eaba2fSLydia Wang }; 10034a918ffeSTakashi Iwai 1004096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 100511890956SLydia Wang /* Enable Boost Volume backdoor */ 100611890956SLydia Wang {0x1, 0xfb9, 0x24}, 100711890956SLydia Wang /* Enable AOW0 to MW9 */ 100811890956SLydia Wang {0x1, 0xfb8, 0x88}, 100911890956SLydia Wang { } 101011890956SLydia Wang }; 101125eaba2fSLydia Wang 10124b527b65SDavid Henningsson /* 10134b527b65SDavid Henningsson * pin fix-up 10144b527b65SDavid Henningsson */ 10154b527b65SDavid Henningsson enum { 10164b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1017d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 10184b527b65SDavid Henningsson }; 10194b527b65SDavid Henningsson 10204b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 10214b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 10224b527b65SDavid Henningsson { 10234b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 10244b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 10254b527b65SDavid Henningsson } 10264b527b65SDavid Henningsson 10274b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 10284b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 10294b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 10304b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 10314b527b65SDavid Henningsson }, 1032d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1033d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1034d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1035d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1036d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1037d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1038d5266125STakashi Iwai { } 1039d5266125STakashi Iwai } 1040d5266125STakashi Iwai }, 10414b527b65SDavid Henningsson }; 10424b527b65SDavid Henningsson 10434b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1044d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 10454b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 10464b527b65SDavid Henningsson {} 10474b527b65SDavid Henningsson }; 10484b527b65SDavid Henningsson 1049ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1050ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1051ef4da458STakashi Iwai */ 1052ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1053ef4da458STakashi Iwai { 1054ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1055ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1056ef4da458STakashi Iwai 1057ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1058ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1059ef4da458STakashi Iwai } 1060ef4da458STakashi Iwai 106125eaba2fSLydia Wang /* patch for vt2002P */ 106225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 106325eaba2fSLydia Wang { 106425eaba2fSLydia Wang struct via_spec *spec; 106525eaba2fSLydia Wang int err; 106625eaba2fSLydia Wang 106725eaba2fSLydia Wang /* create a codec specific record */ 10685b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 106925eaba2fSLydia Wang if (spec == NULL) 107025eaba2fSLydia Wang return -ENOMEM; 107125eaba2fSLydia Wang 1072b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1073d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1074d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1075ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1076ef4da458STakashi Iwai fix_vt1802_connections(codec); 107730b45033STakashi Iwai add_secret_dac_path(codec); 1078620e2b28STakashi Iwai 10794b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 10804b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 10814b527b65SDavid Henningsson 1082f8bfc628STakashi Iwai if (spec->codec_type == VT1802) 1083f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1802_init_verbs); 1084f8bfc628STakashi Iwai else 1085f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt2002P_init_verbs); 1086f8bfc628STakashi Iwai if (err < 0) 1087f8bfc628STakashi Iwai goto error; 1088f8bfc628STakashi Iwai 108925eaba2fSLydia Wang /* automatic parse from the BIOS config */ 109012daef65STakashi Iwai err = via_parse_auto_config(codec); 1091fcbdcc1aSTakashi Iwai if (err < 0) 1092fcbdcc1aSTakashi Iwai goto error; 109325eaba2fSLydia Wang 109425eaba2fSLydia Wang return 0; 1095fcbdcc1aSTakashi Iwai 1096fcbdcc1aSTakashi Iwai error: 1097fcbdcc1aSTakashi Iwai via_free(codec); 1098fcbdcc1aSTakashi Iwai return err; 109925eaba2fSLydia Wang } 1100ab6734e7SLydia Wang 1101ab6734e7SLydia Wang /* for vt1812 */ 1102ab6734e7SLydia Wang 1103096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1104ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1105ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1106ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1107ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1108ab6734e7SLydia Wang { } 1109ab6734e7SLydia Wang }; 1110ab6734e7SLydia Wang 1111ab6734e7SLydia Wang /* patch for vt1812 */ 1112ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1113ab6734e7SLydia Wang { 1114ab6734e7SLydia Wang struct via_spec *spec; 1115ab6734e7SLydia Wang int err; 1116ab6734e7SLydia Wang 1117ab6734e7SLydia Wang /* create a codec specific record */ 11185b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1119ab6734e7SLydia Wang if (spec == NULL) 1120ab6734e7SLydia Wang return -ENOMEM; 1121ab6734e7SLydia Wang 1122b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1123d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1124d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 112530b45033STakashi Iwai add_secret_dac_path(codec); 1126620e2b28STakashi Iwai 1127f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt1812_init_verbs); 1128f8bfc628STakashi Iwai if (err < 0) 1129f8bfc628STakashi Iwai goto error; 1130f8bfc628STakashi Iwai 1131ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 113212daef65STakashi Iwai err = via_parse_auto_config(codec); 1133fcbdcc1aSTakashi Iwai if (err < 0) 1134fcbdcc1aSTakashi Iwai goto error; 1135ab6734e7SLydia Wang 1136ab6734e7SLydia Wang return 0; 1137fcbdcc1aSTakashi Iwai 1138fcbdcc1aSTakashi Iwai error: 1139fcbdcc1aSTakashi Iwai via_free(codec); 1140fcbdcc1aSTakashi Iwai return err; 1141ab6734e7SLydia Wang } 1142ab6734e7SLydia Wang 114343737e0aSLydia Wang /* patch for vt3476 */ 114443737e0aSLydia Wang 114543737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 114643737e0aSLydia Wang /* Enable DMic 8/16/32K */ 114743737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 114843737e0aSLydia Wang /* Enable Boost Volume backdoor */ 114943737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 115043737e0aSLydia Wang /* Enable AOW-MW9 path */ 115143737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 115243737e0aSLydia Wang { } 115343737e0aSLydia Wang }; 115443737e0aSLydia Wang 115543737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 115643737e0aSLydia Wang { 115743737e0aSLydia Wang struct via_spec *spec; 115843737e0aSLydia Wang int err; 115943737e0aSLydia Wang 116043737e0aSLydia Wang /* create a codec specific record */ 116143737e0aSLydia Wang spec = via_new_spec(codec); 116243737e0aSLydia Wang if (spec == NULL) 116343737e0aSLydia Wang return -ENOMEM; 116443737e0aSLydia Wang 1165b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 116643737e0aSLydia Wang add_secret_dac_path(codec); 116743737e0aSLydia Wang 1168f8bfc628STakashi Iwai err = snd_hda_add_verbs(codec, vt3476_init_verbs); 1169f8bfc628STakashi Iwai if (err < 0) 1170f8bfc628STakashi Iwai goto error; 1171f8bfc628STakashi Iwai 117243737e0aSLydia Wang /* automatic parse from the BIOS config */ 117343737e0aSLydia Wang err = via_parse_auto_config(codec); 1174fcbdcc1aSTakashi Iwai if (err < 0) 1175fcbdcc1aSTakashi Iwai goto error; 117643737e0aSLydia Wang 117743737e0aSLydia Wang return 0; 1178fcbdcc1aSTakashi Iwai 1179fcbdcc1aSTakashi Iwai error: 1180fcbdcc1aSTakashi Iwai via_free(codec); 1181fcbdcc1aSTakashi Iwai return err; 118243737e0aSLydia Wang } 118343737e0aSLydia Wang 1184c577b8a1SJoseph Chan /* 1185c577b8a1SJoseph Chan * patch entries 1186c577b8a1SJoseph Chan */ 1187b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = { 1188b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), 1189b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), 1190b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), 1191b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), 1192b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), 1193b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), 1194b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), 1195b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), 1196b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), 1197b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), 1198b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), 1199b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), 1200b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), 1201b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), 1202b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), 1203b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), 1204b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), 1205b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), 1206b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), 1207b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), 1208b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), 1209b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), 1210b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), 1211b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), 1212b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), 1213b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), 1214b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), 1215b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), 1216b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), 1217b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), 1218b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), 1219b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), 1220b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), 1221b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), 1222b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), 1223b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), 1224b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), 1225b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), 1226b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), 1227b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), 1228b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), 1229b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), 1230b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), 1231b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), 1232b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), 1233b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), 1234b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), 1235b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), 1236b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), 1237b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), 1238b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), 1239c577b8a1SJoseph Chan {} /* terminator */ 1240c577b8a1SJoseph Chan }; 1241b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); 12421289e9e8STakashi Iwai 1243d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = { 1244b9a94a9cSTakashi Iwai .id = snd_hda_id_via, 12451289e9e8STakashi Iwai }; 12461289e9e8STakashi Iwai 12471289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 12481289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 12491289e9e8STakashi Iwai 1250d8a766a1STakashi Iwai module_hda_codec_driver(via_driver); 1251