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 120*225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */ 121*225068abSTakashi 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; 142*225068abSTakashi 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); 244967b1307STakashi Iwai ucontrol->value.enumerated.item[0] = codec->power_save_node; 24524088a58STakashi Iwai return 0; 24624088a58STakashi Iwai } 24724088a58STakashi Iwai 24824088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 24924088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 25024088a58STakashi Iwai { 25124088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25224088a58STakashi Iwai struct via_spec *spec = codec->spec; 253688b12ccSTakashi Iwai bool val = !!ucontrol->value.enumerated.item[0]; 25424088a58STakashi Iwai 255967b1307STakashi Iwai if (val == codec->power_save_node) 25624088a58STakashi Iwai return 0; 257967b1307STakashi Iwai codec->power_save_node = val; 258688b12ccSTakashi Iwai spec->gen.power_down_unused = val; 259e9d010c2STakashi Iwai analog_low_current_mode(codec); 26024088a58STakashi Iwai return 1; 26124088a58STakashi Iwai } 26224088a58STakashi Iwai 263b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { 264b3f6008fSTakashi Iwai { 26524088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26624088a58STakashi Iwai .name = "Dynamic Power-Control", 26724088a58STakashi Iwai .info = via_pin_power_ctl_info, 26824088a58STakashi Iwai .get = via_pin_power_ctl_get, 26924088a58STakashi Iwai .put = via_pin_power_ctl_put, 270b3f6008fSTakashi Iwai }, 271b3f6008fSTakashi Iwai {} /* terminator */ 27224088a58STakashi Iwai }; 27324088a58STakashi Iwai 2744738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP 2754738465cSW. Trevor King static inline void set_beep_amp(struct via_spec *spec, hda_nid_t nid, 2764738465cSW. Trevor King int idx, int dir) 2774738465cSW. Trevor King { 2784738465cSW. Trevor King spec->gen.beep_nid = nid; 2794738465cSW. Trevor King spec->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir); 2804738465cSW. Trevor King } 2814738465cSW. Trevor King 2824738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */ 2834738465cSW. Trevor King static const struct snd_kcontrol_new cxt_beep_mixer[] = { 2844738465cSW. Trevor King HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT), 2854738465cSW. Trevor King HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT), 2864738465cSW. Trevor King { } /* end */ 2874738465cSW. Trevor King }; 2884738465cSW. Trevor King 2894738465cSW. Trevor King /* create beep controls if needed */ 2904738465cSW. Trevor King static int add_beep_ctls(struct hda_codec *codec) 2914738465cSW. Trevor King { 2924738465cSW. Trevor King struct via_spec *spec = codec->spec; 2934738465cSW. Trevor King int err; 2944738465cSW. Trevor King 2954738465cSW. Trevor King if (spec->beep_amp) { 2964738465cSW. Trevor King const struct snd_kcontrol_new *knew; 2974738465cSW. Trevor King for (knew = cxt_beep_mixer; knew->name; knew++) { 2984738465cSW. Trevor King struct snd_kcontrol *kctl; 2994738465cSW. Trevor King kctl = snd_ctl_new1(knew, codec); 3004738465cSW. Trevor King if (!kctl) 3014738465cSW. Trevor King return -ENOMEM; 3024738465cSW. Trevor King kctl->private_value = spec->beep_amp; 3034738465cSW. Trevor King err = snd_hda_ctl_add(codec, 0, kctl); 3044738465cSW. Trevor King if (err < 0) 3054738465cSW. Trevor King return err; 3064738465cSW. Trevor King } 3074738465cSW. Trevor King } 3084738465cSW. Trevor King return 0; 3094738465cSW. Trevor King } 3104738465cSW. Trevor King 3114738465cSW. Trevor King static void auto_parse_beep(struct hda_codec *codec) 3124738465cSW. Trevor King { 3134738465cSW. Trevor King struct via_spec *spec = codec->spec; 3144738465cSW. Trevor King hda_nid_t nid; 3154738465cSW. Trevor King 3164738465cSW. Trevor King for_each_hda_codec_node(nid, codec) 3174738465cSW. Trevor King if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP) { 3184738465cSW. Trevor King set_beep_amp(spec, nid, 0, HDA_OUTPUT); 3194738465cSW. Trevor King break; 3204738465cSW. Trevor King } 3214738465cSW. Trevor King } 3224738465cSW. Trevor King #else 3234738465cSW. Trevor King #define set_beep_amp(spec, nid, idx, dir) /* NOP */ 3244738465cSW. Trevor King #define add_beep_ctls(codec) 0 3254738465cSW. Trevor King #define auto_parse_beep(codec) 3264738465cSW. Trevor King #endif 32724088a58STakashi Iwai 328f5271101SLydia Wang /* check AA path's mute status */ 329ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 330ada509ecSTakashi Iwai { 331ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 332ada509ecSTakashi Iwai const struct hda_amp_list *p; 3330186f4f4STakashi Iwai int ch, v; 334ada509ecSTakashi Iwai 3350186f4f4STakashi Iwai p = spec->gen.loopback.amplist; 3360186f4f4STakashi Iwai if (!p) 3370186f4f4STakashi Iwai return true; 3380186f4f4STakashi Iwai for (; p->nid; p++) { 339ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 340ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 341ada509ecSTakashi Iwai p->idx); 342ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 343ada509ecSTakashi Iwai return false; 344f5271101SLydia Wang } 345f5271101SLydia Wang } 346ada509ecSTakashi Iwai return true; 347f5271101SLydia Wang } 348f5271101SLydia Wang 349f5271101SLydia Wang /* enter/exit analog low-current mode */ 350e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 351f5271101SLydia Wang { 352f5271101SLydia Wang struct via_spec *spec = codec->spec; 353ada509ecSTakashi Iwai bool enable; 354ada509ecSTakashi Iwai unsigned int verb, parm; 355f5271101SLydia Wang 356967b1307STakashi Iwai if (!codec->power_save_node) 357e9d010c2STakashi Iwai enable = false; 358e9d010c2STakashi Iwai else 359b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 360e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 361e9d010c2STakashi Iwai return; 362e9d010c2STakashi Iwai spec->alc_mode = enable; 363f5271101SLydia Wang 364f5271101SLydia Wang /* decide low current mode's verb & parameter */ 365f5271101SLydia Wang switch (spec->codec_type) { 366f5271101SLydia Wang case VT1708B_8CH: 367f5271101SLydia Wang case VT1708B_4CH: 368f5271101SLydia Wang verb = 0xf70; 369f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 370f5271101SLydia Wang break; 371f5271101SLydia Wang case VT1708S: 372eb7188caSLydia Wang case VT1718S: 373f3db423dSLydia Wang case VT1716S: 374f5271101SLydia Wang verb = 0xf73; 375f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 376f5271101SLydia Wang break; 377f5271101SLydia Wang case VT1702: 378f5271101SLydia Wang verb = 0xf73; 379f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 380f5271101SLydia Wang break; 38125eaba2fSLydia Wang case VT2002P: 382ab6734e7SLydia Wang case VT1812: 38311890956SLydia Wang case VT1802: 38425eaba2fSLydia Wang verb = 0xf93; 38525eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 38625eaba2fSLydia Wang break; 38743737e0aSLydia Wang case VT1705CF: 3886121b84aSLydia Wang case VT1808: 38943737e0aSLydia Wang verb = 0xf82; 39043737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 39143737e0aSLydia Wang break; 392f5271101SLydia Wang default: 393f5271101SLydia Wang return; /* other codecs are not supported */ 394f5271101SLydia Wang } 395f5271101SLydia Wang /* send verb */ 3967639a06cSTakashi Iwai snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); 397f5271101SLydia Wang } 398f5271101SLydia Wang 399e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 400e9d010c2STakashi Iwai { 401e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 402e9d010c2STakashi Iwai } 403e9d010c2STakashi Iwai 404c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 405c577b8a1SJoseph Chan { 406c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 4075b0cb1d8SJaroslav Kysela int err, i; 408c577b8a1SJoseph Chan 409b3f6008fSTakashi Iwai err = snd_hda_gen_build_controls(codec); 410b3f6008fSTakashi Iwai if (err < 0) 411b3f6008fSTakashi Iwai return err; 412b3f6008fSTakashi Iwai 4134738465cSW. Trevor King err = add_beep_ctls(codec); 4144738465cSW. Trevor King if (err < 0) 4154738465cSW. Trevor King return err; 4164738465cSW. Trevor King 417b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; 41824088a58STakashi Iwai 419c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 420c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 421c577b8a1SJoseph Chan if (err < 0) 422c577b8a1SJoseph Chan return err; 423c577b8a1SJoseph Chan } 424c577b8a1SJoseph Chan 425c577b8a1SJoseph Chan return 0; 426c577b8a1SJoseph Chan } 427c577b8a1SJoseph Chan 428b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 429b3f6008fSTakashi Iwai struct hda_codec *codec, 430b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 431b3f6008fSTakashi Iwai int action) 432c577b8a1SJoseph Chan { 433b3f6008fSTakashi Iwai analog_low_current_mode(codec); 434b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 435c577b8a1SJoseph Chan } 436c577b8a1SJoseph Chan 437c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 438c577b8a1SJoseph Chan { 439b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 440a8dca460STakashi Iwai snd_hda_gen_free(codec); 441c577b8a1SJoseph Chan } 442c577b8a1SJoseph Chan 4432a43952aSTakashi Iwai #ifdef CONFIG_PM 44468cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 4451f2e99feSLydia Wang { 4461f2e99feSLydia Wang struct via_spec *spec = codec->spec; 447b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 44894c142a1SDavid Henningsson 44994c142a1SDavid Henningsson /* Fix pop noise on headphones */ 4502c38d990STakashi Iwai if (spec->codec_type == VT1802) 4512c38d990STakashi Iwai snd_hda_shutup_pins(codec); 45294c142a1SDavid Henningsson 4531f2e99feSLydia Wang return 0; 4541f2e99feSLydia Wang } 4551f2e99feSLydia Wang #endif 4561f2e99feSLydia Wang 45783012a7cSTakashi Iwai #ifdef CONFIG_PM 458cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 459cb53c626STakashi Iwai { 460cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 461b3f6008fSTakashi Iwai analog_low_current_mode(codec); 462b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 463b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 464cb53c626STakashi Iwai } 465cb53c626STakashi Iwai #endif 466cb53c626STakashi Iwai 467c577b8a1SJoseph Chan /* 468c577b8a1SJoseph Chan */ 4695d41762aSTakashi Iwai 4705d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 4715d41762aSTakashi Iwai 47290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 473c577b8a1SJoseph Chan .build_controls = via_build_controls, 474b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 475c577b8a1SJoseph Chan .init = via_init, 476c577b8a1SJoseph Chan .free = via_free, 4774e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 4782a43952aSTakashi Iwai #ifdef CONFIG_PM 4791f2e99feSLydia Wang .suspend = via_suspend, 480cb53c626STakashi Iwai .check_power_status = via_check_power_status, 481cb53c626STakashi Iwai #endif 482c577b8a1SJoseph Chan }; 483c577b8a1SJoseph Chan 4844a79616dSTakashi Iwai 485b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 486b3f6008fSTakashi Iwai /* power down jack detect function */ 487b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 488b3f6008fSTakashi Iwai { } 4894a79616dSTakashi Iwai }; 49076d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 49176d9b0ddSHarald Welte { 49276d9b0ddSHarald Welte unsigned int def_conf; 49376d9b0ddSHarald Welte unsigned char seqassoc; 49476d9b0ddSHarald Welte 4952f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 49676d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 49776d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 49882ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 49982ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 50076d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 5012f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 50276d9b0ddSHarald Welte } 50376d9b0ddSHarald Welte 50476d9b0ddSHarald Welte return; 50576d9b0ddSHarald Welte } 50676d9b0ddSHarald Welte 507e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 5081f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5091f2e99feSLydia Wang { 5101f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5111f2e99feSLydia Wang struct via_spec *spec = codec->spec; 5121f2e99feSLydia Wang 5131f2e99feSLydia Wang if (spec->codec_type != VT1708) 5141f2e99feSLydia Wang return 0; 515e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 5161f2e99feSLydia Wang return 0; 5171f2e99feSLydia Wang } 5181f2e99feSLydia Wang 519e06e5a29STakashi Iwai static int vt1708_jack_detect_put(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; 524187d333eSTakashi Iwai int val; 5251f2e99feSLydia Wang 5261f2e99feSLydia Wang if (spec->codec_type != VT1708) 5271f2e99feSLydia Wang return 0; 528187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 529187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 530187d333eSTakashi Iwai return 0; 531187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 532b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 533187d333eSTakashi Iwai return 1; 5341f2e99feSLydia Wang } 5351f2e99feSLydia Wang 536b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 537b3f6008fSTakashi Iwai { 5381f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5391f2e99feSLydia Wang .name = "Jack Detect", 5401f2e99feSLydia Wang .count = 1, 5411f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 542e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 543e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 544b3f6008fSTakashi Iwai }, 545b3f6008fSTakashi Iwai {} /* terminator */ 5461f2e99feSLydia Wang }; 5471f2e99feSLydia Wang 5484abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 5494abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 5504abdbd1cSTakashi Iwai .no_dac = 0x4000, 5514abdbd1cSTakashi Iwai .shared_primary = 0x10000, 5524abdbd1cSTakashi Iwai .shared_surr = 0x20, 5534abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5544abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 5554abdbd1cSTakashi Iwai }; 5564abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 5574abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 5584abdbd1cSTakashi Iwai .no_dac = 0x4000, 5594abdbd1cSTakashi Iwai .shared_primary = 0x12, 5604abdbd1cSTakashi Iwai .shared_surr = 0x20, 5614abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5624abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 5634abdbd1cSTakashi Iwai }; 5644abdbd1cSTakashi Iwai 565b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 566b3f6008fSTakashi Iwai { 567b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 568b3f6008fSTakashi Iwai int err; 569b3f6008fSTakashi Iwai 5704abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 5714abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 5724abdbd1cSTakashi Iwai 573b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 574b3f6008fSTakashi Iwai if (err < 0) 575b3f6008fSTakashi Iwai return err; 576b3f6008fSTakashi Iwai 5774738465cSW. Trevor King auto_parse_beep(codec); 5784738465cSW. Trevor King 579b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 580b3f6008fSTakashi Iwai if (err < 0) 581b3f6008fSTakashi Iwai return err; 582b3f6008fSTakashi Iwai 583688b12ccSTakashi Iwai /* disable widget PM at start for compatibility */ 584967b1307STakashi Iwai codec->power_save_node = 0; 585688b12ccSTakashi Iwai spec->gen.power_down_unused = 0; 586b3f6008fSTakashi Iwai return 0; 5874a918ffeSTakashi Iwai } 5884a918ffeSTakashi Iwai 5895d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 5905d41762aSTakashi Iwai { 5915d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 5925d41762aSTakashi Iwai int i; 5935d41762aSTakashi Iwai 5945d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 5955d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 5965d41762aSTakashi Iwai 597e9d010c2STakashi Iwai /* init power states */ 598e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 599e9d010c2STakashi Iwai 600b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 60111890956SLydia Wang 602b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 60325eaba2fSLydia Wang 604c577b8a1SJoseph Chan return 0; 605c577b8a1SJoseph Chan } 606c577b8a1SJoseph Chan 607f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 608f672f65aSDavid Henningsson { 609f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 610f672f65aSDavid Henningsson temporary enable jackpoll */ 611f672f65aSDavid Henningsson int err; 612f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 613f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 614f672f65aSDavid Henningsson err = via_build_controls(codec); 615f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 616f672f65aSDavid Henningsson return err; 617f672f65aSDavid Henningsson } 618f672f65aSDavid Henningsson 619b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 620337b9d02STakashi Iwai { 621337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 622b3f6008fSTakashi Iwai int i, err; 623337b9d02STakashi Iwai 624b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 6257639a06cSTakashi Iwai if (err < 0 || codec->core.vendor_id != 0x11061708) 626b3f6008fSTakashi Iwai return err; 627b3f6008fSTakashi Iwai 628b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 629b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 630b3f6008fSTakashi Iwai * disable the 24bit format, so far. 631b3f6008fSTakashi Iwai */ 632bbbc7e85STakashi Iwai for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { 633bbbc7e85STakashi Iwai struct hda_pcm *info = spec->gen.pcm_rec[i]; 634bbbc7e85STakashi Iwai if (!info) 635bbbc7e85STakashi Iwai continue; 636b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 637b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 638b3f6008fSTakashi Iwai continue; 639b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 640b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 641337b9d02STakashi Iwai } 642b3f6008fSTakashi Iwai 6431c55d521STakashi Iwai return 0; 644337b9d02STakashi Iwai } 645337b9d02STakashi Iwai 646c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 647c577b8a1SJoseph Chan { 648c577b8a1SJoseph Chan struct via_spec *spec; 649c577b8a1SJoseph Chan int err; 650c577b8a1SJoseph Chan 651c577b8a1SJoseph Chan /* create a codec specific record */ 6525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 653c577b8a1SJoseph Chan if (spec == NULL) 654c577b8a1SJoseph Chan return -ENOMEM; 655c577b8a1SJoseph Chan 656*225068abSTakashi Iwai /* override some patch_ops */ 657*225068abSTakashi Iwai codec->patch_ops.build_controls = vt1708_build_controls; 658*225068abSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 659b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 660b3f6008fSTakashi Iwai 661b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 662b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 663b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 664b3f6008fSTakashi Iwai 665b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 666b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 667b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 668eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 669eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 670620e2b28STakashi Iwai 67112daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 67212daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 67312daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 67412daef65STakashi Iwai 675c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 67612daef65STakashi Iwai err = via_parse_auto_config(codec); 677c577b8a1SJoseph Chan if (err < 0) { 678c577b8a1SJoseph Chan via_free(codec); 679c577b8a1SJoseph Chan return err; 680c577b8a1SJoseph Chan } 681c577b8a1SJoseph Chan 68212daef65STakashi Iwai /* add jack detect on/off control */ 683b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 684c577b8a1SJoseph Chan 685e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 686e322a36dSLydia Wang 687b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 688b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 689b3f6008fSTakashi Iwai 690c577b8a1SJoseph Chan return 0; 691c577b8a1SJoseph Chan } 692c577b8a1SJoseph Chan 693ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 694c577b8a1SJoseph Chan { 695c577b8a1SJoseph Chan struct via_spec *spec; 696c577b8a1SJoseph Chan int err; 697c577b8a1SJoseph Chan 698c577b8a1SJoseph Chan /* create a codec specific record */ 6995b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 700c577b8a1SJoseph Chan if (spec == NULL) 701c577b8a1SJoseph Chan return -ENOMEM; 702c577b8a1SJoseph Chan 703b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 704620e2b28STakashi Iwai 70512daef65STakashi Iwai err = via_parse_auto_config(codec); 706c577b8a1SJoseph Chan if (err < 0) { 707c577b8a1SJoseph Chan via_free(codec); 708c577b8a1SJoseph Chan return err; 709c577b8a1SJoseph Chan } 710c577b8a1SJoseph Chan 711f7278fd0SJosepch Chan return 0; 712f7278fd0SJosepch Chan } 713f7278fd0SJosepch Chan 714518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 715ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 716f7278fd0SJosepch Chan { 717f7278fd0SJosepch Chan struct via_spec *spec; 718f7278fd0SJosepch Chan int err; 719f7278fd0SJosepch Chan 720518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 721518bf3baSLydia Wang return patch_vt1708S(codec); 722ddd304d8STakashi Iwai 723f7278fd0SJosepch Chan /* create a codec specific record */ 7245b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 725f7278fd0SJosepch Chan if (spec == NULL) 726f7278fd0SJosepch Chan return -ENOMEM; 727f7278fd0SJosepch Chan 728b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 729620e2b28STakashi Iwai 730f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 73112daef65STakashi Iwai err = via_parse_auto_config(codec); 732f7278fd0SJosepch Chan if (err < 0) { 733f7278fd0SJosepch Chan via_free(codec); 734f7278fd0SJosepch Chan return err; 735f7278fd0SJosepch Chan } 736f7278fd0SJosepch Chan 737f7278fd0SJosepch Chan return 0; 738f7278fd0SJosepch Chan } 739f7278fd0SJosepch Chan 740d949cac1SHarald Welte /* Patch for VT1708S */ 741096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 742d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 743d7426329SHarald Welte {0x1, 0xf98, 0x1}, 744bc7e7e5cSLydia Wang /* don't bybass mixer */ 745bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 746d949cac1SHarald Welte { } 747d949cac1SHarald Welte }; 748d949cac1SHarald Welte 7496369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 7506369bcfcSLydia Wang int offset, int num_steps, int step_size) 7516369bcfcSLydia Wang { 752d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 753d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 7546369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 7556369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 7566369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 7576369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 7586369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 7596369bcfcSLydia Wang } 7606369bcfcSLydia Wang 761d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 762d949cac1SHarald Welte { 763d949cac1SHarald Welte struct via_spec *spec; 764d949cac1SHarald Welte int err; 765d949cac1SHarald Welte 766d949cac1SHarald Welte /* create a codec specific record */ 7675b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 768d949cac1SHarald Welte if (spec == NULL) 769d949cac1SHarald Welte return -ENOMEM; 770d949cac1SHarald Welte 771b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 772d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 773d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 774620e2b28STakashi Iwai 775518bf3baSLydia Wang /* correct names for VT1708BCE */ 776518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 7777639a06cSTakashi Iwai kfree(codec->core.chip_name); 7787639a06cSTakashi Iwai codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 7796efdd851STakashi Iwai snprintf(codec->card->mixername, 7806efdd851STakashi Iwai sizeof(codec->card->mixername), 7817639a06cSTakashi Iwai "%s %s", codec->core.vendor_name, codec->core.chip_name); 782970f630fSLydia Wang } 783bc92df7fSLydia Wang /* correct names for VT1705 */ 7847639a06cSTakashi Iwai if (codec->core.vendor_id == 0x11064397) { 7857639a06cSTakashi Iwai kfree(codec->core.chip_name); 7867639a06cSTakashi Iwai codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); 7876efdd851STakashi Iwai snprintf(codec->card->mixername, 7886efdd851STakashi Iwai sizeof(codec->card->mixername), 7897639a06cSTakashi Iwai "%s %s", codec->core.vendor_name, codec->core.chip_name); 790bc92df7fSLydia Wang } 791b3f6008fSTakashi Iwai 792b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 793b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 794b3f6008fSTakashi Iwai if (err < 0) { 795b3f6008fSTakashi Iwai via_free(codec); 796b3f6008fSTakashi Iwai return err; 797b3f6008fSTakashi Iwai } 798b3f6008fSTakashi Iwai 799b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 800b3f6008fSTakashi Iwai 801d949cac1SHarald Welte return 0; 802d949cac1SHarald Welte } 803d949cac1SHarald Welte 804d949cac1SHarald Welte /* Patch for VT1702 */ 805d949cac1SHarald Welte 806096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 807bc7e7e5cSLydia Wang /* mixer enable */ 808bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 809bc7e7e5cSLydia Wang /* GPIO 0~2 */ 810bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 811d949cac1SHarald Welte { } 812d949cac1SHarald Welte }; 813d949cac1SHarald Welte 814d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 815d949cac1SHarald Welte { 816d949cac1SHarald Welte struct via_spec *spec; 817d949cac1SHarald Welte int err; 818d949cac1SHarald Welte 819d949cac1SHarald Welte /* create a codec specific record */ 8205b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 821d949cac1SHarald Welte if (spec == NULL) 822d949cac1SHarald Welte return -ENOMEM; 823d949cac1SHarald Welte 824b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 825620e2b28STakashi Iwai 82612daef65STakashi Iwai /* limit AA path volume to 0 dB */ 82712daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 82812daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 82912daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 83012daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 83112daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 83212daef65STakashi Iwai 833d949cac1SHarald Welte /* automatic parse from the BIOS config */ 83412daef65STakashi Iwai err = via_parse_auto_config(codec); 835d949cac1SHarald Welte if (err < 0) { 836d949cac1SHarald Welte via_free(codec); 837d949cac1SHarald Welte return err; 838d949cac1SHarald Welte } 839d949cac1SHarald Welte 840096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 841d949cac1SHarald Welte 842d949cac1SHarald Welte return 0; 843d949cac1SHarald Welte } 844d949cac1SHarald Welte 845eb7188caSLydia Wang /* Patch for VT1718S */ 846eb7188caSLydia Wang 847096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 8484ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 8494ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 850eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 851eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 8525d41762aSTakashi Iwai 853eb7188caSLydia Wang { } 854eb7188caSLydia Wang }; 855eb7188caSLydia Wang 85630b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 85730b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 85830b45033STakashi Iwai */ 85930b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 86030b45033STakashi Iwai { 86130b45033STakashi Iwai struct via_spec *spec = codec->spec; 86230b45033STakashi Iwai int i, nums; 86330b45033STakashi Iwai hda_nid_t conn[8]; 86430b45033STakashi Iwai hda_nid_t nid; 86530b45033STakashi Iwai 866b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 86730b45033STakashi Iwai return 0; 868b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 86930b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 87030b45033STakashi Iwai for (i = 0; i < nums; i++) { 87130b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 87230b45033STakashi Iwai return 0; 87330b45033STakashi Iwai } 87430b45033STakashi Iwai 87530b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 8767639a06cSTakashi Iwai for_each_hda_codec_node(nid, codec) { 87730b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 87830b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 87930b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 88030b45033STakashi Iwai conn[nums++] = nid; 88130b45033STakashi Iwai return snd_hda_override_conn_list(codec, 882b3f6008fSTakashi Iwai spec->gen.mixer_nid, 88330b45033STakashi Iwai nums, conn); 88430b45033STakashi Iwai } 88530b45033STakashi Iwai } 88630b45033STakashi Iwai return 0; 88730b45033STakashi Iwai } 88830b45033STakashi Iwai 88930b45033STakashi Iwai 890eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 891eb7188caSLydia Wang { 892eb7188caSLydia Wang struct via_spec *spec; 893eb7188caSLydia Wang int err; 894eb7188caSLydia Wang 895eb7188caSLydia Wang /* create a codec specific record */ 8965b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 897eb7188caSLydia Wang if (spec == NULL) 898eb7188caSLydia Wang return -ENOMEM; 899eb7188caSLydia Wang 900b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 901d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 902d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 90330b45033STakashi Iwai add_secret_dac_path(codec); 904620e2b28STakashi Iwai 905eb7188caSLydia Wang /* automatic parse from the BIOS config */ 90612daef65STakashi Iwai err = via_parse_auto_config(codec); 907eb7188caSLydia Wang if (err < 0) { 908eb7188caSLydia Wang via_free(codec); 909eb7188caSLydia Wang return err; 910eb7188caSLydia Wang } 911eb7188caSLydia Wang 912096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 913eb7188caSLydia Wang 914eb7188caSLydia Wang return 0; 915eb7188caSLydia Wang } 916f3db423dSLydia Wang 917f3db423dSLydia Wang /* Patch for VT1716S */ 918f3db423dSLydia Wang 919f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 920f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 921f3db423dSLydia Wang { 922f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 923f3db423dSLydia Wang uinfo->count = 1; 924f3db423dSLydia Wang uinfo->value.integer.min = 0; 925f3db423dSLydia Wang uinfo->value.integer.max = 1; 926f3db423dSLydia Wang return 0; 927f3db423dSLydia Wang } 928f3db423dSLydia Wang 929f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 930f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 931f3db423dSLydia Wang { 932f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 933f3db423dSLydia Wang int index = 0; 934f3db423dSLydia Wang 935f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 936f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 937f3db423dSLydia Wang if (index != -1) 938f3db423dSLydia Wang *ucontrol->value.integer.value = index; 939f3db423dSLydia Wang 940f3db423dSLydia Wang return 0; 941f3db423dSLydia Wang } 942f3db423dSLydia Wang 943f3db423dSLydia Wang static int vt1716s_dmic_put(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 struct via_spec *spec = codec->spec; 948f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 949f3db423dSLydia Wang 950f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 951f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 952f3db423dSLydia Wang spec->dmic_enabled = index; 953f3db423dSLydia Wang return 1; 954f3db423dSLydia Wang } 955f3db423dSLydia Wang 95690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 957f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 958f3db423dSLydia Wang { 959f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 960f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 9615b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 962f3db423dSLydia Wang .count = 1, 963f3db423dSLydia Wang .info = vt1716s_dmic_info, 964f3db423dSLydia Wang .get = vt1716s_dmic_get, 965f3db423dSLydia Wang .put = vt1716s_dmic_put, 966f3db423dSLydia Wang }, 967f3db423dSLydia Wang {} /* end */ 968f3db423dSLydia Wang }; 969f3db423dSLydia Wang 970f3db423dSLydia Wang 971f3db423dSLydia Wang /* mono-out mixer elements */ 97290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 973f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 974f3db423dSLydia Wang { } /* end */ 975f3db423dSLydia Wang }; 976f3db423dSLydia Wang 977096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 978f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 979f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 980f3db423dSLydia Wang /* don't bybass mixer */ 981f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 982f3db423dSLydia Wang /* Enable mono output */ 983f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 984f3db423dSLydia Wang { } 985f3db423dSLydia Wang }; 986f3db423dSLydia Wang 987f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 988f3db423dSLydia Wang { 989f3db423dSLydia Wang struct via_spec *spec; 990f3db423dSLydia Wang int err; 991f3db423dSLydia Wang 992f3db423dSLydia Wang /* create a codec specific record */ 9935b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 994f3db423dSLydia Wang if (spec == NULL) 995f3db423dSLydia Wang return -ENOMEM; 996f3db423dSLydia Wang 997b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 998d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 999d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 1000620e2b28STakashi Iwai 1001f3db423dSLydia Wang /* automatic parse from the BIOS config */ 100212daef65STakashi Iwai err = via_parse_auto_config(codec); 1003f3db423dSLydia Wang if (err < 0) { 1004f3db423dSLydia Wang via_free(codec); 1005f3db423dSLydia Wang return err; 1006f3db423dSLydia Wang } 1007f3db423dSLydia Wang 1008096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 1009f3db423dSLydia Wang 1010b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 1011f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 1012f3db423dSLydia Wang 1013f3db423dSLydia Wang return 0; 1014f3db423dSLydia Wang } 101525eaba2fSLydia Wang 101625eaba2fSLydia Wang /* for vt2002P */ 101725eaba2fSLydia Wang 1018096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 1019eadb9a80SLydia Wang /* Class-D speaker related verbs */ 1020eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 1021eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 1022eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 102325eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 102425eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 102525eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 102625eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 102725eaba2fSLydia Wang { } 102825eaba2fSLydia Wang }; 10294a918ffeSTakashi Iwai 1030096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 103111890956SLydia Wang /* Enable Boost Volume backdoor */ 103211890956SLydia Wang {0x1, 0xfb9, 0x24}, 103311890956SLydia Wang /* Enable AOW0 to MW9 */ 103411890956SLydia Wang {0x1, 0xfb8, 0x88}, 103511890956SLydia Wang { } 103611890956SLydia Wang }; 103725eaba2fSLydia Wang 10384b527b65SDavid Henningsson /* 10394b527b65SDavid Henningsson * pin fix-up 10404b527b65SDavid Henningsson */ 10414b527b65SDavid Henningsson enum { 10424b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1043d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 10444b527b65SDavid Henningsson }; 10454b527b65SDavid Henningsson 10464b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 10474b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 10484b527b65SDavid Henningsson { 10494b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 10504b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 10514b527b65SDavid Henningsson } 10524b527b65SDavid Henningsson 10534b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 10544b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 10554b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 10564b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 10574b527b65SDavid Henningsson }, 1058d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1059d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1060d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1061d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1062d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1063d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1064d5266125STakashi Iwai { } 1065d5266125STakashi Iwai } 1066d5266125STakashi Iwai }, 10674b527b65SDavid Henningsson }; 10684b527b65SDavid Henningsson 10694b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1070d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 10714b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 10724b527b65SDavid Henningsson {} 10734b527b65SDavid Henningsson }; 10744b527b65SDavid Henningsson 1075ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1076ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1077ef4da458STakashi Iwai */ 1078ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1079ef4da458STakashi Iwai { 1080ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1081ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1082ef4da458STakashi Iwai 1083ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1084ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1085ef4da458STakashi Iwai } 1086ef4da458STakashi Iwai 108725eaba2fSLydia Wang /* patch for vt2002P */ 108825eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 108925eaba2fSLydia Wang { 109025eaba2fSLydia Wang struct via_spec *spec; 109125eaba2fSLydia Wang int err; 109225eaba2fSLydia Wang 109325eaba2fSLydia Wang /* create a codec specific record */ 10945b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 109525eaba2fSLydia Wang if (spec == NULL) 109625eaba2fSLydia Wang return -ENOMEM; 109725eaba2fSLydia Wang 1098b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1099d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1100d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1101ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1102ef4da458STakashi Iwai fix_vt1802_connections(codec); 110330b45033STakashi Iwai add_secret_dac_path(codec); 1104620e2b28STakashi Iwai 11054b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 11064b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 11074b527b65SDavid Henningsson 110825eaba2fSLydia Wang /* automatic parse from the BIOS config */ 110912daef65STakashi Iwai err = via_parse_auto_config(codec); 111025eaba2fSLydia Wang if (err < 0) { 111125eaba2fSLydia Wang via_free(codec); 111225eaba2fSLydia Wang return err; 111325eaba2fSLydia Wang } 111425eaba2fSLydia Wang 111511890956SLydia Wang if (spec->codec_type == VT1802) 11164a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 111711890956SLydia Wang else 11184a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 111911890956SLydia Wang 112025eaba2fSLydia Wang return 0; 112125eaba2fSLydia Wang } 1122ab6734e7SLydia Wang 1123ab6734e7SLydia Wang /* for vt1812 */ 1124ab6734e7SLydia Wang 1125096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1126ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1127ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1128ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1129ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1130ab6734e7SLydia Wang { } 1131ab6734e7SLydia Wang }; 1132ab6734e7SLydia Wang 1133ab6734e7SLydia Wang /* patch for vt1812 */ 1134ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1135ab6734e7SLydia Wang { 1136ab6734e7SLydia Wang struct via_spec *spec; 1137ab6734e7SLydia Wang int err; 1138ab6734e7SLydia Wang 1139ab6734e7SLydia Wang /* create a codec specific record */ 11405b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1141ab6734e7SLydia Wang if (spec == NULL) 1142ab6734e7SLydia Wang return -ENOMEM; 1143ab6734e7SLydia Wang 1144b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1145d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1146d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 114730b45033STakashi Iwai add_secret_dac_path(codec); 1148620e2b28STakashi Iwai 1149ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 115012daef65STakashi Iwai err = via_parse_auto_config(codec); 1151ab6734e7SLydia Wang if (err < 0) { 1152ab6734e7SLydia Wang via_free(codec); 1153ab6734e7SLydia Wang return err; 1154ab6734e7SLydia Wang } 1155ab6734e7SLydia Wang 1156096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1157ab6734e7SLydia Wang 1158ab6734e7SLydia Wang return 0; 1159ab6734e7SLydia Wang } 1160ab6734e7SLydia Wang 116143737e0aSLydia Wang /* patch for vt3476 */ 116243737e0aSLydia Wang 116343737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 116443737e0aSLydia Wang /* Enable DMic 8/16/32K */ 116543737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 116643737e0aSLydia Wang /* Enable Boost Volume backdoor */ 116743737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 116843737e0aSLydia Wang /* Enable AOW-MW9 path */ 116943737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 117043737e0aSLydia Wang { } 117143737e0aSLydia Wang }; 117243737e0aSLydia Wang 117343737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 117443737e0aSLydia Wang { 117543737e0aSLydia Wang struct via_spec *spec; 117643737e0aSLydia Wang int err; 117743737e0aSLydia Wang 117843737e0aSLydia Wang /* create a codec specific record */ 117943737e0aSLydia Wang spec = via_new_spec(codec); 118043737e0aSLydia Wang if (spec == NULL) 118143737e0aSLydia Wang return -ENOMEM; 118243737e0aSLydia Wang 1183b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 118443737e0aSLydia Wang add_secret_dac_path(codec); 118543737e0aSLydia Wang 118643737e0aSLydia Wang /* automatic parse from the BIOS config */ 118743737e0aSLydia Wang err = via_parse_auto_config(codec); 118843737e0aSLydia Wang if (err < 0) { 118943737e0aSLydia Wang via_free(codec); 119043737e0aSLydia Wang return err; 119143737e0aSLydia Wang } 119243737e0aSLydia Wang 119343737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 119443737e0aSLydia Wang 119543737e0aSLydia Wang return 0; 119643737e0aSLydia Wang } 119743737e0aSLydia Wang 1198c577b8a1SJoseph Chan /* 1199c577b8a1SJoseph Chan * patch entries 1200c577b8a1SJoseph Chan */ 120190dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 12023218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 12033218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 12043218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 12053218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 12063218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 1207ddd304d8STakashi Iwai .patch = patch_vt1709}, 12083218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 1209ddd304d8STakashi Iwai .patch = patch_vt1709}, 12103218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 1211ddd304d8STakashi Iwai .patch = patch_vt1709}, 12123218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 1213ddd304d8STakashi Iwai .patch = patch_vt1709}, 12143218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 1215ddd304d8STakashi Iwai .patch = patch_vt1709}, 12163218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 1217ddd304d8STakashi Iwai .patch = patch_vt1709}, 12183218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 1219ddd304d8STakashi Iwai .patch = patch_vt1709}, 12203218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 1221ddd304d8STakashi Iwai .patch = patch_vt1709}, 12223218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 1223ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12243218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 1225ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12263218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 1227ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12283218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 1229ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12303218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 1231ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12323218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 1233ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12343218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 1235ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12363218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 1237ddd304d8STakashi Iwai .patch = patch_vt1708B}, 12383218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 1239d949cac1SHarald Welte .patch = patch_vt1708S}, 12403218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 1241d949cac1SHarald Welte .patch = patch_vt1708S}, 12423218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 1243d949cac1SHarald Welte .patch = patch_vt1708S}, 12443218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 1245d949cac1SHarald Welte .patch = patch_vt1708S}, 1246bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 1247d949cac1SHarald Welte .patch = patch_vt1708S}, 12483218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 1249d949cac1SHarald Welte .patch = patch_vt1708S}, 12503218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 1251d949cac1SHarald Welte .patch = patch_vt1708S}, 12523218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 1253d949cac1SHarald Welte .patch = patch_vt1708S}, 12543218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 1255d949cac1SHarald Welte .patch = patch_vt1702}, 12563218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 1257d949cac1SHarald Welte .patch = patch_vt1702}, 12583218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 1259d949cac1SHarald Welte .patch = patch_vt1702}, 12603218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 1261d949cac1SHarald Welte .patch = patch_vt1702}, 12623218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 1263d949cac1SHarald Welte .patch = patch_vt1702}, 12643218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 1265d949cac1SHarald Welte .patch = patch_vt1702}, 12663218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 1267d949cac1SHarald Welte .patch = patch_vt1702}, 12683218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 1269d949cac1SHarald Welte .patch = patch_vt1702}, 1270eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 1271eb7188caSLydia Wang .patch = patch_vt1718S}, 1272eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 1273eb7188caSLydia Wang .patch = patch_vt1718S}, 1274bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 1275bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1276bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 1277bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1278f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 1279f3db423dSLydia Wang .patch = patch_vt1716S}, 1280f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 1281f3db423dSLydia Wang .patch = patch_vt1716S}, 128225eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 128325eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 1284ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 128536dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 128636dd5c4aSLydia Wang .patch = patch_vt1708S}, 128711890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 128811890956SLydia Wang .patch = patch_vt2002P}, 128911890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 129011890956SLydia Wang .patch = patch_vt2002P}, 129143737e0aSLydia Wang { .id = 0x11064760, .name = "VT1705CF", 129243737e0aSLydia Wang .patch = patch_vt3476}, 12936121b84aSLydia Wang { .id = 0x11064761, .name = "VT1708SCE", 12946121b84aSLydia Wang .patch = patch_vt3476}, 12956121b84aSLydia Wang { .id = 0x11064762, .name = "VT1808", 12966121b84aSLydia Wang .patch = patch_vt3476}, 1297c577b8a1SJoseph Chan {} /* terminator */ 1298c577b8a1SJoseph Chan }; 12991289e9e8STakashi Iwai 13001289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 13011289e9e8STakashi Iwai 1302d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = { 13031289e9e8STakashi Iwai .preset = snd_hda_preset_via, 13041289e9e8STakashi Iwai }; 13051289e9e8STakashi Iwai 13061289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 13071289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 13081289e9e8STakashi Iwai 1309d8a766a1STakashi Iwai module_hda_codec_driver(via_driver); 1310