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> 55c577b8a1SJoseph Chan #include "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 /* codec parameterization */ 9490dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 951f2e99feSLydia Wang unsigned int num_mixers; 961f2e99feSLydia Wang 9790dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 981f2e99feSLydia Wang unsigned int num_iverbs; 991f2e99feSLydia Wang 1001f2e99feSLydia Wang /* HP mode source */ 101f3db423dSLydia Wang unsigned int dmic_enabled; 1021f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1031f2e99feSLydia Wang 104e9d010c2STakashi Iwai /* analog low-power control */ 105e9d010c2STakashi Iwai bool alc_mode; 106e9d010c2STakashi Iwai 1071f2e99feSLydia Wang /* work to check hp jack state */ 108187d333eSTakashi Iwai int hp_work_active; 109e06e5a29STakashi Iwai int vt1708_jack_detect; 1104738465cSW. Trevor King 1114738465cSW. Trevor King unsigned int beep_amp; 1121f2e99feSLydia Wang }; 1131f2e99feSLydia Wang 1140341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 115b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 116b3f6008fSTakashi Iwai struct hda_codec *codec, 117b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 118b3f6008fSTakashi Iwai int action); 119b3f6008fSTakashi Iwai 120225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */ 121225068abSTakashi Iwai 1225b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1235b0cb1d8SJaroslav Kysela { 1245b0cb1d8SJaroslav Kysela struct via_spec *spec; 1255b0cb1d8SJaroslav Kysela 1265b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1275b0cb1d8SJaroslav Kysela if (spec == NULL) 1285b0cb1d8SJaroslav Kysela return NULL; 1295b0cb1d8SJaroslav Kysela 1305b0cb1d8SJaroslav Kysela codec->spec = spec; 131b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1320341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1330341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1340341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1350341ccd7SLydia Wang spec->codec_type = VT1708S; 13613961170STakashi Iwai spec->gen.indep_hp = 1; 13705909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1; 138b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 13974f14b36STakashi Iwai spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; 140967b1307STakashi Iwai codec->power_save_node = 1; 141688b12ccSTakashi Iwai spec->gen.power_down_unused = 1; 142225068abSTakashi Iwai codec->patch_ops = via_patch_ops; 1435b0cb1d8SJaroslav Kysela return spec; 1445b0cb1d8SJaroslav Kysela } 1455b0cb1d8SJaroslav Kysela 146744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 147d7426329SHarald Welte { 1487639a06cSTakashi Iwai u32 vendor_id = codec->core.vendor_id; 149d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 150d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 151d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 152d7426329SHarald Welte 153d7426329SHarald Welte /* get codec type */ 154d7426329SHarald Welte if (ven_id != 0x1106) 155d7426329SHarald Welte codec_type = UNKNOWN; 156d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 157d7426329SHarald Welte codec_type = VT1708; 158d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 159d7426329SHarald Welte codec_type = VT1709_10CH; 160d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 161d7426329SHarald Welte codec_type = VT1709_6CH; 162518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 163d7426329SHarald Welte codec_type = VT1708B_8CH; 164518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 165518bf3baSLydia Wang codec_type = VT1708BCE; 166518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 167d7426329SHarald Welte codec_type = VT1708B_4CH; 168d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 169d7426329SHarald Welte && (dev_id >> 12) < 8) 170d7426329SHarald Welte codec_type = VT1708S; 171d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 172d7426329SHarald Welte && (dev_id >> 12) < 8) 173d7426329SHarald Welte codec_type = VT1702; 174eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 175eb7188caSLydia Wang && (dev_id >> 12) < 8) 176eb7188caSLydia Wang codec_type = VT1718S; 177f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 178f3db423dSLydia Wang codec_type = VT1716S; 179bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 180bb3c6bfcSLydia Wang codec_type = VT1718S; 18125eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 18225eaba2fSLydia Wang codec_type = VT2002P; 183ab6734e7SLydia Wang else if (dev_id == 0x0448) 184ab6734e7SLydia Wang codec_type = VT1812; 18536dd5c4aSLydia Wang else if (dev_id == 0x0440) 18636dd5c4aSLydia Wang codec_type = VT1708S; 18711890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 18811890956SLydia Wang codec_type = VT1802; 18943737e0aSLydia Wang else if (dev_id == 0x4760) 19043737e0aSLydia Wang codec_type = VT1705CF; 1916121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762) 1926121b84aSLydia Wang codec_type = VT1808; 193d7426329SHarald Welte else 194d7426329SHarald Welte codec_type = UNKNOWN; 195d7426329SHarald Welte return codec_type; 196d7426329SHarald Welte }; 197d7426329SHarald Welte 198ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 199ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 2001f2e99feSLydia Wang 201187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \ 202187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ 203187d333eSTakashi Iwai !is_aa_path_mute(codec)) 2041f2e99feSLydia Wang 205b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec) 2061f2e99feSLydia Wang { 207b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 208b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 2091f2e99feSLydia Wang return; 210187d333eSTakashi Iwai if (spec->hp_work_active) { 211b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); 2127eaa9161SWang Xingchao codec->jackpoll_interval = 0; 213b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work); 214b3f6008fSTakashi Iwai spec->hp_work_active = false; 215187d333eSTakashi Iwai } 216187d333eSTakashi Iwai } 217187d333eSTakashi Iwai 218b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec) 219187d333eSTakashi Iwai { 220b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 221b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 222187d333eSTakashi Iwai return; 22305dc0fc9SDavid Henningsson if (spec->vt1708_jack_detect) { 224187d333eSTakashi Iwai if (!spec->hp_work_active) { 225b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 226b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); 2272f35c630STakashi Iwai schedule_delayed_work(&codec->jackpoll_work, 0); 228b3f6008fSTakashi Iwai spec->hp_work_active = true; 229187d333eSTakashi Iwai } 230b3f6008fSTakashi Iwai } else if (!hp_detect_with_aa(codec)) 231b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 2321f2e99feSLydia Wang } 233f5271101SLydia Wang 23424088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 23524088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 23624088a58STakashi Iwai { 237dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 23824088a58STakashi Iwai } 23924088a58STakashi Iwai 24024088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 24124088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 24224088a58STakashi Iwai { 24324088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 244735c75cfSTakashi Iwai struct via_spec *spec = codec->spec; 245735c75cfSTakashi Iwai 246735c75cfSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused; 24724088a58STakashi Iwai return 0; 24824088a58STakashi Iwai } 24924088a58STakashi Iwai 25024088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 25124088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 25224088a58STakashi Iwai { 25324088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25424088a58STakashi Iwai struct via_spec *spec = codec->spec; 255688b12ccSTakashi Iwai bool val = !!ucontrol->value.enumerated.item[0]; 25624088a58STakashi Iwai 257735c75cfSTakashi Iwai if (val == spec->gen.power_down_unused) 25824088a58STakashi Iwai return 0; 259735c75cfSTakashi Iwai /* codec->power_save_node = val; */ /* widget PM seems yet broken */ 260688b12ccSTakashi Iwai spec->gen.power_down_unused = val; 261e9d010c2STakashi Iwai analog_low_current_mode(codec); 26224088a58STakashi Iwai return 1; 26324088a58STakashi Iwai } 26424088a58STakashi Iwai 265b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { 266b3f6008fSTakashi Iwai { 26724088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26824088a58STakashi Iwai .name = "Dynamic Power-Control", 26924088a58STakashi Iwai .info = via_pin_power_ctl_info, 27024088a58STakashi Iwai .get = via_pin_power_ctl_get, 27124088a58STakashi Iwai .put = via_pin_power_ctl_put, 272b3f6008fSTakashi Iwai }, 273b3f6008fSTakashi Iwai {} /* terminator */ 27424088a58STakashi Iwai }; 27524088a58STakashi Iwai 2764738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP 2774738465cSW. Trevor King static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid, 2784738465cSW. Trevor King int idx, int dir) 2794738465cSW. Trevor King { 2804738465cSW. Trevor King spec->gen.beep_nid = nid; 2814738465cSW. Trevor King spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); 2824738465cSW. Trevor King } 2834738465cSW. Trevor King 2844738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */ 2854738465cSW. Trevor King static const struct snd_kcontrol_new cxt_beep_mixer[] = { 2864738465cSW. Trevor King HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), 2874738465cSW. Trevor King HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), 2884738465cSW. Trevor King { } /* end */ 2894738465cSW. Trevor King }; 2904738465cSW. Trevor King 2914738465cSW. Trevor King /* create beep controls if needed */ 2924738465cSW. Trevor King static int add_beep_ctls(struct hda_codec *codec) 2934738465cSW. Trevor King { 2944738465cSW. Trevor King struct via_spec *spec = codec->spec; 2954738465cSW. Trevor King int err; 2964738465cSW. Trevor King 2974738465cSW. Trevor King if (spec->beep_amp) { 2984738465cSW. Trevor King const struct snd_kcontrol_new *knew; 2994738465cSW. Trevor King for (knew = cxt_beep_mixer; knew->name; knew++) { 3004738465cSW. Trevor King struct snd_kcontrol *kctl; 3014738465cSW. Trevor King kctl = snd_ctl_new1(knew, codec); 3024738465cSW. Trevor King if (!kctl) 3034738465cSW. Trevor King return -ENOMEM; 3044738465cSW. Trevor King kctl->private_value = spec->beep_amp; 3054738465cSW. Trevor King err = snd_hda_ctl_add(codec, 0, kctl); 3064738465cSW. Trevor King if (err < 0) 3074738465cSW. Trevor King return err; 3084738465cSW. Trevor King } 3094738465cSW. Trevor King } 3104738465cSW. Trevor King return 0; 3114738465cSW. Trevor King } 3124738465cSW. Trevor King 3134738465cSW. Trevor King static void auto_parse_beep(struct hda_codec *codec) 3144738465cSW. Trevor King { 3154738465cSW. Trevor King struct via_spec *spec = codec->spec; 3164738465cSW. Trevor King hda_nid_t nid; 3174738465cSW. Trevor King 3184738465cSW. Trevor King for_each_hda_codec_node(nid, codec) 3194738465cSW. Trevor King if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { 3204738465cSW. Trevor King set_beep_amp(spec, nid, 0, HDA_OUTPUT); 3214738465cSW. Trevor King break; 3224738465cSW. Trevor King } 3234738465cSW. Trevor King } 3244738465cSW. Trevor King #else 3254738465cSW. Trevor King #define set_beep_amp(spec, nid, idx, dir) /* NOP */ 3264738465cSW. Trevor King #define add_beep_ctls(codec) 0 3274738465cSW. Trevor King #define auto_parse_beep(codec) 3284738465cSW. Trevor King #endif 32924088a58STakashi Iwai 330f5271101SLydia Wang /* check AA path's mute status */ 331ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 332ada509ecSTakashi Iwai { 333ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 334ada509ecSTakashi Iwai const struct hda_amp_list *p; 3350186f4f4STakashi Iwai int ch, v; 336ada509ecSTakashi Iwai 3370186f4f4STakashi Iwai p = spec->gen.loopback.amplist; 3380186f4f4STakashi Iwai if (!p) 3390186f4f4STakashi Iwai return true; 3400186f4f4STakashi Iwai for (; p->nid; p++) { 341ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 342ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 343ada509ecSTakashi Iwai p->idx); 344ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 345ada509ecSTakashi Iwai return false; 346f5271101SLydia Wang } 347f5271101SLydia Wang } 348ada509ecSTakashi Iwai return true; 349f5271101SLydia Wang } 350f5271101SLydia Wang 351f5271101SLydia Wang /* enter/exit analog low-current mode */ 352e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 353f5271101SLydia Wang { 354f5271101SLydia Wang struct via_spec *spec = codec->spec; 355ada509ecSTakashi Iwai bool enable; 356ada509ecSTakashi Iwai unsigned int verb, parm; 357f5271101SLydia Wang 358967b1307STakashi Iwai if (!codec->power_save_node) 359e9d010c2STakashi Iwai enable = false; 360e9d010c2STakashi Iwai else 361b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 362e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 363e9d010c2STakashi Iwai return; 364e9d010c2STakashi Iwai spec->alc_mode = enable; 365f5271101SLydia Wang 366f5271101SLydia Wang /* decide low current mode's verb & parameter */ 367f5271101SLydia Wang switch (spec->codec_type) { 368f5271101SLydia Wang case VT1708B_8CH: 369f5271101SLydia Wang case VT1708B_4CH: 370f5271101SLydia Wang verb = 0xf70; 371f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 372f5271101SLydia Wang break; 373f5271101SLydia Wang case VT1708S: 374eb7188caSLydia Wang case VT1718S: 375f3db423dSLydia Wang case VT1716S: 376f5271101SLydia Wang verb = 0xf73; 377f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 378f5271101SLydia Wang break; 379f5271101SLydia Wang case VT1702: 380f5271101SLydia Wang verb = 0xf73; 381f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 382f5271101SLydia Wang break; 38325eaba2fSLydia Wang case VT2002P: 384ab6734e7SLydia Wang case VT1812: 38511890956SLydia Wang case VT1802: 38625eaba2fSLydia Wang verb = 0xf93; 38725eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 38825eaba2fSLydia Wang break; 38943737e0aSLydia Wang case VT1705CF: 3906121b84aSLydia Wang case VT1808: 39143737e0aSLydia Wang verb = 0xf82; 39243737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 39343737e0aSLydia Wang break; 394f5271101SLydia Wang default: 395f5271101SLydia Wang return; /* other codecs are not supported */ 396f5271101SLydia Wang } 397f5271101SLydia Wang /* send verb */ 3987639a06cSTakashi Iwai snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); 399f5271101SLydia Wang } 400f5271101SLydia Wang 401e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 402e9d010c2STakashi Iwai { 403e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 404e9d010c2STakashi Iwai } 405e9d010c2STakashi Iwai 406c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 407c577b8a1SJoseph Chan { 408c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 4095b0cb1d8SJaroslav Kysela int err, i; 410c577b8a1SJoseph Chan 411b3f6008fSTakashi Iwai err = snd_hda_gen_build_controls(codec); 412b3f6008fSTakashi Iwai if (err < 0) 413b3f6008fSTakashi Iwai return err; 414b3f6008fSTakashi Iwai 4154738465cSW. Trevor King err = add_beep_ctls(codec); 4164738465cSW. Trevor King if (err < 0) 4174738465cSW. Trevor King return err; 4184738465cSW. Trevor King 419b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; 42024088a58STakashi Iwai 421c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 422c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 423c577b8a1SJoseph Chan if (err < 0) 424c577b8a1SJoseph Chan return err; 425c577b8a1SJoseph Chan } 426c577b8a1SJoseph Chan 427c577b8a1SJoseph Chan return 0; 428c577b8a1SJoseph Chan } 429c577b8a1SJoseph Chan 430b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 431b3f6008fSTakashi Iwai struct hda_codec *codec, 432b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 433b3f6008fSTakashi Iwai int action) 434c577b8a1SJoseph Chan { 435b3f6008fSTakashi Iwai analog_low_current_mode(codec); 436b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 437c577b8a1SJoseph Chan } 438c577b8a1SJoseph Chan 439c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 440c577b8a1SJoseph Chan { 441b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 442a8dca460STakashi Iwai snd_hda_gen_free(codec); 443c577b8a1SJoseph Chan } 444c577b8a1SJoseph Chan 4452a43952aSTakashi Iwai #ifdef CONFIG_PM 44668cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 4471f2e99feSLydia Wang { 4481f2e99feSLydia Wang struct via_spec *spec = codec->spec; 449b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 45094c142a1SDavid Henningsson 45194c142a1SDavid Henningsson /* Fix pop noise on headphones */ 4522c38d990STakashi Iwai if (spec->codec_type == VT1802) 4532c38d990STakashi Iwai snd_hda_shutup_pins(codec); 45494c142a1SDavid Henningsson 4551f2e99feSLydia Wang return 0; 4561f2e99feSLydia Wang } 4576b6d0007STakashi Iwai 4586b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec) 4596b6d0007STakashi Iwai { 4606b6d0007STakashi Iwai /* some delay here to make jack detection working (bko#98921) */ 4616b6d0007STakashi Iwai msleep(10); 4626b6d0007STakashi Iwai codec->patch_ops.init(codec); 4636b6d0007STakashi Iwai regcache_sync(codec->core.regmap); 4646b6d0007STakashi Iwai return 0; 4656b6d0007STakashi Iwai } 4661f2e99feSLydia Wang #endif 4671f2e99feSLydia Wang 46883012a7cSTakashi Iwai #ifdef CONFIG_PM 469cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 470cb53c626STakashi Iwai { 471cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 472b3f6008fSTakashi Iwai analog_low_current_mode(codec); 473b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 474b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 475cb53c626STakashi Iwai } 476cb53c626STakashi Iwai #endif 477cb53c626STakashi Iwai 478c577b8a1SJoseph Chan /* 479c577b8a1SJoseph Chan */ 4805d41762aSTakashi Iwai 4815d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 4825d41762aSTakashi Iwai 48390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 484c577b8a1SJoseph Chan .build_controls = via_build_controls, 485b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 486c577b8a1SJoseph Chan .init = via_init, 487c577b8a1SJoseph Chan .free = via_free, 4884e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 4892a43952aSTakashi Iwai #ifdef CONFIG_PM 4901f2e99feSLydia Wang .suspend = via_suspend, 4916b6d0007STakashi Iwai .resume = via_resume, 492cb53c626STakashi Iwai .check_power_status = via_check_power_status, 493cb53c626STakashi Iwai #endif 494c577b8a1SJoseph Chan }; 495c577b8a1SJoseph Chan 4964a79616dSTakashi Iwai 497b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 498b3f6008fSTakashi Iwai /* power down jack detect function */ 499b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 500b3f6008fSTakashi Iwai { } 5014a79616dSTakashi Iwai }; 50276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 50376d9b0ddSHarald Welte { 50476d9b0ddSHarald Welte unsigned int def_conf; 50576d9b0ddSHarald Welte unsigned char seqassoc; 50676d9b0ddSHarald Welte 5072f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 50876d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 50976d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 51082ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 51182ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 51276d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 5132f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 51476d9b0ddSHarald Welte } 51576d9b0ddSHarald Welte 51676d9b0ddSHarald Welte return; 51776d9b0ddSHarald Welte } 51876d9b0ddSHarald Welte 519e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 5201f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5211f2e99feSLydia Wang { 5221f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5231f2e99feSLydia Wang struct via_spec *spec = codec->spec; 5241f2e99feSLydia Wang 5251f2e99feSLydia Wang if (spec->codec_type != VT1708) 5261f2e99feSLydia Wang return 0; 527e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 5281f2e99feSLydia Wang return 0; 5291f2e99feSLydia Wang } 5301f2e99feSLydia Wang 531e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 5321f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5331f2e99feSLydia Wang { 5341f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5351f2e99feSLydia Wang struct via_spec *spec = codec->spec; 536187d333eSTakashi Iwai int val; 5371f2e99feSLydia Wang 5381f2e99feSLydia Wang if (spec->codec_type != VT1708) 5391f2e99feSLydia Wang return 0; 540187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 541187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 542187d333eSTakashi Iwai return 0; 543187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 544b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 545187d333eSTakashi Iwai return 1; 5461f2e99feSLydia Wang } 5471f2e99feSLydia Wang 548b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 549b3f6008fSTakashi Iwai { 5501f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5511f2e99feSLydia Wang .name = "Jack Detect", 5521f2e99feSLydia Wang .count = 1, 5531f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 554e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 555e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 556b3f6008fSTakashi Iwai }, 557b3f6008fSTakashi Iwai {} /* terminator */ 5581f2e99feSLydia Wang }; 5591f2e99feSLydia Wang 5604abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 5614abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 5624abdbd1cSTakashi Iwai .no_dac = 0x4000, 5634abdbd1cSTakashi Iwai .shared_primary = 0x10000, 5644abdbd1cSTakashi Iwai .shared_surr = 0x20, 5654abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5664abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 5674abdbd1cSTakashi Iwai }; 5684abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 5694abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 5704abdbd1cSTakashi Iwai .no_dac = 0x4000, 5714abdbd1cSTakashi Iwai .shared_primary = 0x12, 5724abdbd1cSTakashi Iwai .shared_surr = 0x20, 5734abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5744abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 5754abdbd1cSTakashi Iwai }; 5764abdbd1cSTakashi Iwai 577b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 578b3f6008fSTakashi Iwai { 579b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 580b3f6008fSTakashi Iwai int err; 581b3f6008fSTakashi Iwai 5824abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 5834abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 5844abdbd1cSTakashi Iwai 585b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 586b3f6008fSTakashi Iwai if (err < 0) 587b3f6008fSTakashi Iwai return err; 588b3f6008fSTakashi Iwai 5894738465cSW. Trevor King auto_parse_beep(codec); 5904738465cSW. Trevor King 591b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 592b3f6008fSTakashi Iwai if (err < 0) 593b3f6008fSTakashi Iwai return err; 594b3f6008fSTakashi Iwai 595688b12ccSTakashi Iwai /* disable widget PM at start for compatibility */ 596967b1307STakashi Iwai codec->power_save_node = 0; 597688b12ccSTakashi Iwai spec->gen.power_down_unused = 0; 598b3f6008fSTakashi Iwai return 0; 5994a918ffeSTakashi Iwai } 6004a918ffeSTakashi Iwai 6015d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 6025d41762aSTakashi Iwai { 6035d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 6045d41762aSTakashi Iwai int i; 6055d41762aSTakashi Iwai 6065d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 6075d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 6085d41762aSTakashi Iwai 609e9d010c2STakashi Iwai /* init power states */ 610e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 611e9d010c2STakashi Iwai 612b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 61311890956SLydia Wang 614b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 61525eaba2fSLydia Wang 616c577b8a1SJoseph Chan return 0; 617c577b8a1SJoseph Chan } 618c577b8a1SJoseph Chan 619f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 620f672f65aSDavid Henningsson { 621f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 622f672f65aSDavid Henningsson temporary enable jackpoll */ 623f672f65aSDavid Henningsson int err; 624f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 625f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 626f672f65aSDavid Henningsson err = via_build_controls(codec); 627f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 628f672f65aSDavid Henningsson return err; 629f672f65aSDavid Henningsson } 630f672f65aSDavid Henningsson 631b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 632337b9d02STakashi Iwai { 633337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 634b3f6008fSTakashi Iwai int i, err; 635337b9d02STakashi Iwai 636b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 6377639a06cSTakashi Iwai if (err < 0 || codec->core.vendor_id != 0x11061708) 638b3f6008fSTakashi Iwai return err; 639b3f6008fSTakashi Iwai 640b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 641b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 642b3f6008fSTakashi Iwai * disable the 24bit format, so far. 643b3f6008fSTakashi Iwai */ 644bbbc7e85STakashi Iwai for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { 645bbbc7e85STakashi Iwai struct hda_pcm *info = spec->gen.pcm_rec[i]; 646bbbc7e85STakashi Iwai if (!info) 647bbbc7e85STakashi Iwai continue; 648b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 649b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 650b3f6008fSTakashi Iwai continue; 651b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 652b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 653337b9d02STakashi Iwai } 654b3f6008fSTakashi Iwai 6551c55d521STakashi Iwai return 0; 656337b9d02STakashi Iwai } 657337b9d02STakashi Iwai 658c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 659c577b8a1SJoseph Chan { 660c577b8a1SJoseph Chan struct via_spec *spec; 661c577b8a1SJoseph Chan int err; 662c577b8a1SJoseph Chan 663c577b8a1SJoseph Chan /* create a codec specific record */ 6645b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 665c577b8a1SJoseph Chan if (spec == NULL) 666c577b8a1SJoseph Chan return -ENOMEM; 667c577b8a1SJoseph Chan 668225068abSTakashi Iwai /* override some patch_ops */ 669225068abSTakashi Iwai codec->patch_ops.build_controls = vt1708_build_controls; 670225068abSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 671b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 672b3f6008fSTakashi Iwai 673b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 674b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 675b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 676b3f6008fSTakashi Iwai 677b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 678b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 679b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 680eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 681eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 682620e2b28STakashi Iwai 68312daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 68412daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 68512daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 68612daef65STakashi Iwai 687c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 68812daef65STakashi Iwai err = via_parse_auto_config(codec); 689*fcbdcc1aSTakashi Iwai if (err < 0) 690*fcbdcc1aSTakashi Iwai goto error; 691c577b8a1SJoseph Chan 69212daef65STakashi Iwai /* add jack detect on/off control */ 693b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 694c577b8a1SJoseph Chan 695e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 696e322a36dSLydia Wang 697b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 698b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 699b3f6008fSTakashi Iwai 700c577b8a1SJoseph Chan return 0; 701*fcbdcc1aSTakashi Iwai 702*fcbdcc1aSTakashi Iwai error: 703*fcbdcc1aSTakashi Iwai via_free(codec); 704*fcbdcc1aSTakashi Iwai return err; 705c577b8a1SJoseph Chan } 706c577b8a1SJoseph Chan 707ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 708c577b8a1SJoseph Chan { 709c577b8a1SJoseph Chan struct via_spec *spec; 710c577b8a1SJoseph Chan int err; 711c577b8a1SJoseph Chan 712c577b8a1SJoseph Chan /* create a codec specific record */ 7135b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 714c577b8a1SJoseph Chan if (spec == NULL) 715c577b8a1SJoseph Chan return -ENOMEM; 716c577b8a1SJoseph Chan 717b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 718620e2b28STakashi Iwai 71912daef65STakashi Iwai err = via_parse_auto_config(codec); 720*fcbdcc1aSTakashi Iwai if (err < 0) 721*fcbdcc1aSTakashi Iwai goto error; 722c577b8a1SJoseph Chan 723f7278fd0SJosepch Chan return 0; 724*fcbdcc1aSTakashi Iwai 725*fcbdcc1aSTakashi Iwai error: 726*fcbdcc1aSTakashi Iwai via_free(codec); 727*fcbdcc1aSTakashi Iwai return err; 728f7278fd0SJosepch Chan } 729f7278fd0SJosepch Chan 730518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 731ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 732f7278fd0SJosepch Chan { 733f7278fd0SJosepch Chan struct via_spec *spec; 734f7278fd0SJosepch Chan int err; 735f7278fd0SJosepch Chan 736518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 737518bf3baSLydia Wang return patch_vt1708S(codec); 738ddd304d8STakashi Iwai 739f7278fd0SJosepch Chan /* create a codec specific record */ 7405b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 741f7278fd0SJosepch Chan if (spec == NULL) 742f7278fd0SJosepch Chan return -ENOMEM; 743f7278fd0SJosepch Chan 744b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 745620e2b28STakashi Iwai 746f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 74712daef65STakashi Iwai err = via_parse_auto_config(codec); 748*fcbdcc1aSTakashi Iwai if (err < 0) 749*fcbdcc1aSTakashi Iwai goto error; 750f7278fd0SJosepch Chan 751f7278fd0SJosepch Chan return 0; 752*fcbdcc1aSTakashi Iwai 753*fcbdcc1aSTakashi Iwai error: 754*fcbdcc1aSTakashi Iwai via_free(codec); 755*fcbdcc1aSTakashi Iwai return err; 756f7278fd0SJosepch Chan } 757f7278fd0SJosepch Chan 758d949cac1SHarald Welte /* Patch for VT1708S */ 759096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 760d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 761d7426329SHarald Welte {0x1, 0xf98, 0x1}, 762bc7e7e5cSLydia Wang /* don't bybass mixer */ 763bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 764d949cac1SHarald Welte { } 765d949cac1SHarald Welte }; 766d949cac1SHarald Welte 7676369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 7686369bcfcSLydia Wang int offset, int num_steps, int step_size) 7696369bcfcSLydia Wang { 770d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 771d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 7726369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 7736369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 7746369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 7756369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 7766369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 7776369bcfcSLydia Wang } 7786369bcfcSLydia Wang 779d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 780d949cac1SHarald Welte { 781d949cac1SHarald Welte struct via_spec *spec; 782d949cac1SHarald Welte int err; 783d949cac1SHarald Welte 784d949cac1SHarald Welte /* create a codec specific record */ 7855b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 786d949cac1SHarald Welte if (spec == NULL) 787d949cac1SHarald Welte return -ENOMEM; 788d949cac1SHarald Welte 789b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 790d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 791d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 792620e2b28STakashi Iwai 793518bf3baSLydia Wang /* correct names for VT1708BCE */ 794ded255beSTakashi Iwai if (get_codec_type(codec) == VT1708BCE) 795ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1708BCE"); 796bc92df7fSLydia Wang /* correct names for VT1705 */ 797ded255beSTakashi Iwai if (codec->core.vendor_id == 0x11064397) 798ded255beSTakashi Iwai snd_hda_codec_set_name(codec, "VT1705"); 799b3f6008fSTakashi Iwai 800b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 801b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 802*fcbdcc1aSTakashi Iwai if (err < 0) 803*fcbdcc1aSTakashi Iwai goto error; 804b3f6008fSTakashi Iwai 805b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 806b3f6008fSTakashi Iwai 807d949cac1SHarald Welte return 0; 808*fcbdcc1aSTakashi Iwai 809*fcbdcc1aSTakashi Iwai error: 810*fcbdcc1aSTakashi Iwai via_free(codec); 811*fcbdcc1aSTakashi Iwai return err; 812d949cac1SHarald Welte } 813d949cac1SHarald Welte 814d949cac1SHarald Welte /* Patch for VT1702 */ 815d949cac1SHarald Welte 816096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 817bc7e7e5cSLydia Wang /* mixer enable */ 818bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 819bc7e7e5cSLydia Wang /* GPIO 0~2 */ 820bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 821d949cac1SHarald Welte { } 822d949cac1SHarald Welte }; 823d949cac1SHarald Welte 824d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 825d949cac1SHarald Welte { 826d949cac1SHarald Welte struct via_spec *spec; 827d949cac1SHarald Welte int err; 828d949cac1SHarald Welte 829d949cac1SHarald Welte /* create a codec specific record */ 8305b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 831d949cac1SHarald Welte if (spec == NULL) 832d949cac1SHarald Welte return -ENOMEM; 833d949cac1SHarald Welte 834b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 835620e2b28STakashi Iwai 83612daef65STakashi Iwai /* limit AA path volume to 0 dB */ 83712daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 83812daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 83912daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 84012daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 84112daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 84212daef65STakashi Iwai 843d949cac1SHarald Welte /* automatic parse from the BIOS config */ 84412daef65STakashi Iwai err = via_parse_auto_config(codec); 845*fcbdcc1aSTakashi Iwai if (err < 0) 846*fcbdcc1aSTakashi Iwai goto error; 847d949cac1SHarald Welte 848096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 849d949cac1SHarald Welte 850d949cac1SHarald Welte return 0; 851*fcbdcc1aSTakashi Iwai 852*fcbdcc1aSTakashi Iwai error: 853*fcbdcc1aSTakashi Iwai via_free(codec); 854*fcbdcc1aSTakashi Iwai return err; 855d949cac1SHarald Welte } 856d949cac1SHarald Welte 857eb7188caSLydia Wang /* Patch for VT1718S */ 858eb7188caSLydia Wang 859096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 8604ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 8614ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 862eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 863eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 8645d41762aSTakashi Iwai 865eb7188caSLydia Wang { } 866eb7188caSLydia Wang }; 867eb7188caSLydia Wang 86830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 86930b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 87030b45033STakashi Iwai */ 87130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 87230b45033STakashi Iwai { 87330b45033STakashi Iwai struct via_spec *spec = codec->spec; 87430b45033STakashi Iwai int i, nums; 87530b45033STakashi Iwai hda_nid_t conn[8]; 87630b45033STakashi Iwai hda_nid_t nid; 87730b45033STakashi Iwai 878b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 87930b45033STakashi Iwai return 0; 880b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 88130b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 88230b45033STakashi Iwai for (i = 0; i < nums; i++) { 88330b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 88430b45033STakashi Iwai return 0; 88530b45033STakashi Iwai } 88630b45033STakashi Iwai 88730b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 8887639a06cSTakashi Iwai for_each_hda_codec_node(nid, codec) { 88930b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 89030b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 89130b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 89230b45033STakashi Iwai conn[nums++] = nid; 89330b45033STakashi Iwai return snd_hda_override_conn_list(codec, 894b3f6008fSTakashi Iwai spec->gen.mixer_nid, 89530b45033STakashi Iwai nums, conn); 89630b45033STakashi Iwai } 89730b45033STakashi Iwai } 89830b45033STakashi Iwai return 0; 89930b45033STakashi Iwai } 90030b45033STakashi Iwai 90130b45033STakashi Iwai 902eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 903eb7188caSLydia Wang { 904eb7188caSLydia Wang struct via_spec *spec; 905eb7188caSLydia Wang int err; 906eb7188caSLydia Wang 907eb7188caSLydia Wang /* create a codec specific record */ 9085b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 909eb7188caSLydia Wang if (spec == NULL) 910eb7188caSLydia Wang return -ENOMEM; 911eb7188caSLydia Wang 912b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 913d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 914d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 91530b45033STakashi Iwai add_secret_dac_path(codec); 916620e2b28STakashi Iwai 917eb7188caSLydia Wang /* automatic parse from the BIOS config */ 91812daef65STakashi Iwai err = via_parse_auto_config(codec); 919*fcbdcc1aSTakashi Iwai if (err < 0) 920*fcbdcc1aSTakashi Iwai goto error; 921eb7188caSLydia Wang 922096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 923eb7188caSLydia Wang 924eb7188caSLydia Wang return 0; 925*fcbdcc1aSTakashi Iwai 926*fcbdcc1aSTakashi Iwai error: 927*fcbdcc1aSTakashi Iwai via_free(codec); 928*fcbdcc1aSTakashi Iwai return err; 929eb7188caSLydia Wang } 930f3db423dSLydia Wang 931f3db423dSLydia Wang /* Patch for VT1716S */ 932f3db423dSLydia Wang 933f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 934f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 935f3db423dSLydia Wang { 936f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 937f3db423dSLydia Wang uinfo->count = 1; 938f3db423dSLydia Wang uinfo->value.integer.min = 0; 939f3db423dSLydia Wang uinfo->value.integer.max = 1; 940f3db423dSLydia Wang return 0; 941f3db423dSLydia Wang } 942f3db423dSLydia Wang 943f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 944f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 945f3db423dSLydia Wang { 946f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 947f3db423dSLydia Wang int index = 0; 948f3db423dSLydia Wang 949f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 950f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 951f3db423dSLydia Wang if (index != -1) 952f3db423dSLydia Wang *ucontrol->value.integer.value = index; 953f3db423dSLydia Wang 954f3db423dSLydia Wang return 0; 955f3db423dSLydia Wang } 956f3db423dSLydia Wang 957f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 958f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 959f3db423dSLydia Wang { 960f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 961f3db423dSLydia Wang struct via_spec *spec = codec->spec; 962f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 963f3db423dSLydia Wang 964f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 965f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 966f3db423dSLydia Wang spec->dmic_enabled = index; 967f3db423dSLydia Wang return 1; 968f3db423dSLydia Wang } 969f3db423dSLydia Wang 97090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 971f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 972f3db423dSLydia Wang { 973f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 974f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 9755b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 976f3db423dSLydia Wang .count = 1, 977f3db423dSLydia Wang .info = vt1716s_dmic_info, 978f3db423dSLydia Wang .get = vt1716s_dmic_get, 979f3db423dSLydia Wang .put = vt1716s_dmic_put, 980f3db423dSLydia Wang }, 981f3db423dSLydia Wang {} /* end */ 982f3db423dSLydia Wang }; 983f3db423dSLydia Wang 984f3db423dSLydia Wang 985f3db423dSLydia Wang /* mono-out mixer elements */ 98690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 987f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 988f3db423dSLydia Wang { } /* end */ 989f3db423dSLydia Wang }; 990f3db423dSLydia Wang 991096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 992f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 993f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 994f3db423dSLydia Wang /* don't bybass mixer */ 995f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 996f3db423dSLydia Wang /* Enable mono output */ 997f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 998f3db423dSLydia Wang { } 999f3db423dSLydia Wang }; 1000f3db423dSLydia Wang 1001f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 1002f3db423dSLydia Wang { 1003f3db423dSLydia Wang struct via_spec *spec; 1004f3db423dSLydia Wang int err; 1005f3db423dSLydia Wang 1006f3db423dSLydia Wang /* create a codec specific record */ 10075b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1008f3db423dSLydia Wang if (spec == NULL) 1009f3db423dSLydia Wang return -ENOMEM; 1010f3db423dSLydia Wang 1011b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 1012d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 1013d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 1014620e2b28STakashi Iwai 1015f3db423dSLydia Wang /* automatic parse from the BIOS config */ 101612daef65STakashi Iwai err = via_parse_auto_config(codec); 1017*fcbdcc1aSTakashi Iwai if (err < 0) 1018*fcbdcc1aSTakashi Iwai goto error; 1019f3db423dSLydia Wang 1020096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 1021f3db423dSLydia Wang 1022b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 1023f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 1024f3db423dSLydia Wang 1025f3db423dSLydia Wang return 0; 1026*fcbdcc1aSTakashi Iwai 1027*fcbdcc1aSTakashi Iwai error: 1028*fcbdcc1aSTakashi Iwai via_free(codec); 1029*fcbdcc1aSTakashi Iwai return err; 1030f3db423dSLydia Wang } 103125eaba2fSLydia Wang 103225eaba2fSLydia Wang /* for vt2002P */ 103325eaba2fSLydia Wang 1034096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 1035eadb9a80SLydia Wang /* Class-D speaker related verbs */ 1036eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 1037eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 1038eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 103925eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 104025eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 104125eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 104225eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 104325eaba2fSLydia Wang { } 104425eaba2fSLydia Wang }; 10454a918ffeSTakashi Iwai 1046096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 104711890956SLydia Wang /* Enable Boost Volume backdoor */ 104811890956SLydia Wang {0x1, 0xfb9, 0x24}, 104911890956SLydia Wang /* Enable AOW0 to MW9 */ 105011890956SLydia Wang {0x1, 0xfb8, 0x88}, 105111890956SLydia Wang { } 105211890956SLydia Wang }; 105325eaba2fSLydia Wang 10544b527b65SDavid Henningsson /* 10554b527b65SDavid Henningsson * pin fix-up 10564b527b65SDavid Henningsson */ 10574b527b65SDavid Henningsson enum { 10584b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1059d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 10604b527b65SDavid Henningsson }; 10614b527b65SDavid Henningsson 10624b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 10634b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 10644b527b65SDavid Henningsson { 10654b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 10664b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 10674b527b65SDavid Henningsson } 10684b527b65SDavid Henningsson 10694b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 10704b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 10714b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 10724b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 10734b527b65SDavid Henningsson }, 1074d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1075d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1076d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1077d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1078d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1079d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1080d5266125STakashi Iwai { } 1081d5266125STakashi Iwai } 1082d5266125STakashi Iwai }, 10834b527b65SDavid Henningsson }; 10844b527b65SDavid Henningsson 10854b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1086d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 10874b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 10884b527b65SDavid Henningsson {} 10894b527b65SDavid Henningsson }; 10904b527b65SDavid Henningsson 1091ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1092ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1093ef4da458STakashi Iwai */ 1094ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1095ef4da458STakashi Iwai { 1096ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1097ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1098ef4da458STakashi Iwai 1099ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1100ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1101ef4da458STakashi Iwai } 1102ef4da458STakashi Iwai 110325eaba2fSLydia Wang /* patch for vt2002P */ 110425eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 110525eaba2fSLydia Wang { 110625eaba2fSLydia Wang struct via_spec *spec; 110725eaba2fSLydia Wang int err; 110825eaba2fSLydia Wang 110925eaba2fSLydia Wang /* create a codec specific record */ 11105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 111125eaba2fSLydia Wang if (spec == NULL) 111225eaba2fSLydia Wang return -ENOMEM; 111325eaba2fSLydia Wang 1114b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1115d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1116d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1117ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1118ef4da458STakashi Iwai fix_vt1802_connections(codec); 111930b45033STakashi Iwai add_secret_dac_path(codec); 1120620e2b28STakashi Iwai 11214b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 11224b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 11234b527b65SDavid Henningsson 112425eaba2fSLydia Wang /* automatic parse from the BIOS config */ 112512daef65STakashi Iwai err = via_parse_auto_config(codec); 1126*fcbdcc1aSTakashi Iwai if (err < 0) 1127*fcbdcc1aSTakashi Iwai goto error; 112825eaba2fSLydia Wang 112911890956SLydia Wang if (spec->codec_type == VT1802) 11304a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 113111890956SLydia Wang else 11324a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 113311890956SLydia Wang 113425eaba2fSLydia Wang return 0; 1135*fcbdcc1aSTakashi Iwai 1136*fcbdcc1aSTakashi Iwai error: 1137*fcbdcc1aSTakashi Iwai via_free(codec); 1138*fcbdcc1aSTakashi Iwai return err; 113925eaba2fSLydia Wang } 1140ab6734e7SLydia Wang 1141ab6734e7SLydia Wang /* for vt1812 */ 1142ab6734e7SLydia Wang 1143096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1144ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1145ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1146ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1147ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1148ab6734e7SLydia Wang { } 1149ab6734e7SLydia Wang }; 1150ab6734e7SLydia Wang 1151ab6734e7SLydia Wang /* patch for vt1812 */ 1152ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1153ab6734e7SLydia Wang { 1154ab6734e7SLydia Wang struct via_spec *spec; 1155ab6734e7SLydia Wang int err; 1156ab6734e7SLydia Wang 1157ab6734e7SLydia Wang /* create a codec specific record */ 11585b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1159ab6734e7SLydia Wang if (spec == NULL) 1160ab6734e7SLydia Wang return -ENOMEM; 1161ab6734e7SLydia Wang 1162b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1163d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1164d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 116530b45033STakashi Iwai add_secret_dac_path(codec); 1166620e2b28STakashi Iwai 1167ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 116812daef65STakashi Iwai err = via_parse_auto_config(codec); 1169*fcbdcc1aSTakashi Iwai if (err < 0) 1170*fcbdcc1aSTakashi Iwai goto error; 1171ab6734e7SLydia Wang 1172096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1173ab6734e7SLydia Wang 1174ab6734e7SLydia Wang return 0; 1175*fcbdcc1aSTakashi Iwai 1176*fcbdcc1aSTakashi Iwai error: 1177*fcbdcc1aSTakashi Iwai via_free(codec); 1178*fcbdcc1aSTakashi Iwai return err; 1179ab6734e7SLydia Wang } 1180ab6734e7SLydia Wang 118143737e0aSLydia Wang /* patch for vt3476 */ 118243737e0aSLydia Wang 118343737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 118443737e0aSLydia Wang /* Enable DMic 8/16/32K */ 118543737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 118643737e0aSLydia Wang /* Enable Boost Volume backdoor */ 118743737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 118843737e0aSLydia Wang /* Enable AOW-MW9 path */ 118943737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 119043737e0aSLydia Wang { } 119143737e0aSLydia Wang }; 119243737e0aSLydia Wang 119343737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 119443737e0aSLydia Wang { 119543737e0aSLydia Wang struct via_spec *spec; 119643737e0aSLydia Wang int err; 119743737e0aSLydia Wang 119843737e0aSLydia Wang /* create a codec specific record */ 119943737e0aSLydia Wang spec = via_new_spec(codec); 120043737e0aSLydia Wang if (spec == NULL) 120143737e0aSLydia Wang return -ENOMEM; 120243737e0aSLydia Wang 1203b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 120443737e0aSLydia Wang add_secret_dac_path(codec); 120543737e0aSLydia Wang 120643737e0aSLydia Wang /* automatic parse from the BIOS config */ 120743737e0aSLydia Wang err = via_parse_auto_config(codec); 1208*fcbdcc1aSTakashi Iwai if (err < 0) 1209*fcbdcc1aSTakashi Iwai goto error; 121043737e0aSLydia Wang 121143737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 121243737e0aSLydia Wang 121343737e0aSLydia Wang return 0; 1214*fcbdcc1aSTakashi Iwai 1215*fcbdcc1aSTakashi Iwai error: 1216*fcbdcc1aSTakashi Iwai via_free(codec); 1217*fcbdcc1aSTakashi Iwai return err; 121843737e0aSLydia Wang } 121943737e0aSLydia Wang 1220c577b8a1SJoseph Chan /* 1221c577b8a1SJoseph Chan * patch entries 1222c577b8a1SJoseph Chan */ 1223b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = { 1224b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708), 1225b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708), 1226b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708), 1227b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708), 1228b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709), 1229b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709), 1230b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709), 1231b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709), 1232b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709), 1233b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709), 1234b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709), 1235b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709), 1236b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B), 1237b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B), 1238b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B), 1239b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B), 1240b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B), 1241b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B), 1242b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B), 1243b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B), 1244b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S), 1245b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S), 1246b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S), 1247b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S), 1248b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S), 1249b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S), 1250b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S), 1251b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S), 1252b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702), 1253b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702), 1254b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702), 1255b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702), 1256b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702), 1257b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702), 1258b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702), 1259b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702), 1260b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S), 1261b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S), 1262b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S), 1263b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S), 1264b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S), 1265b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S), 1266b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P), 1267b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P), 1268b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812), 1269b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S), 1270b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P), 1271b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P), 1272b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476), 1273b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476), 1274b9a94a9cSTakashi Iwai HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476), 1275c577b8a1SJoseph Chan {} /* terminator */ 1276c577b8a1SJoseph Chan }; 1277b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via); 12781289e9e8STakashi Iwai 1279d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = { 1280b9a94a9cSTakashi Iwai .id = snd_hda_id_via, 12811289e9e8STakashi Iwai }; 12821289e9e8STakashi Iwai 12831289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 12841289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 12851289e9e8STakashi Iwai 1286d8a766a1STakashi Iwai module_hda_codec_driver(via_driver); 1287