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; 1101f2e99feSLydia Wang }; 1111f2e99feSLydia Wang 1120341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 113b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 114b3f6008fSTakashi Iwai struct hda_codec *codec, 115b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 116b3f6008fSTakashi Iwai int action); 117b3f6008fSTakashi Iwai 1185b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1195b0cb1d8SJaroslav Kysela { 1205b0cb1d8SJaroslav Kysela struct via_spec *spec; 1215b0cb1d8SJaroslav Kysela 1225b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1235b0cb1d8SJaroslav Kysela if (spec == NULL) 1245b0cb1d8SJaroslav Kysela return NULL; 1255b0cb1d8SJaroslav Kysela 1265b0cb1d8SJaroslav Kysela codec->spec = spec; 127b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1280341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1290341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1300341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1310341ccd7SLydia Wang spec->codec_type = VT1708S; 13213961170STakashi Iwai spec->gen.indep_hp = 1; 13305909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1; 134b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 13574f14b36STakashi Iwai spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; 136967b1307STakashi Iwai codec->power_save_node = 1; 137688b12ccSTakashi Iwai spec->gen.power_down_unused = 1; 1385b0cb1d8SJaroslav Kysela return spec; 1395b0cb1d8SJaroslav Kysela } 1405b0cb1d8SJaroslav Kysela 141744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 142d7426329SHarald Welte { 143*7639a06cSTakashi Iwai u32 vendor_id = codec->core.vendor_id; 144d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 145d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 146d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 147d7426329SHarald Welte 148d7426329SHarald Welte /* get codec type */ 149d7426329SHarald Welte if (ven_id != 0x1106) 150d7426329SHarald Welte codec_type = UNKNOWN; 151d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 152d7426329SHarald Welte codec_type = VT1708; 153d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 154d7426329SHarald Welte codec_type = VT1709_10CH; 155d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 156d7426329SHarald Welte codec_type = VT1709_6CH; 157518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 158d7426329SHarald Welte codec_type = VT1708B_8CH; 159518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 160518bf3baSLydia Wang codec_type = VT1708BCE; 161518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 162d7426329SHarald Welte codec_type = VT1708B_4CH; 163d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 164d7426329SHarald Welte && (dev_id >> 12) < 8) 165d7426329SHarald Welte codec_type = VT1708S; 166d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 167d7426329SHarald Welte && (dev_id >> 12) < 8) 168d7426329SHarald Welte codec_type = VT1702; 169eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 170eb7188caSLydia Wang && (dev_id >> 12) < 8) 171eb7188caSLydia Wang codec_type = VT1718S; 172f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 173f3db423dSLydia Wang codec_type = VT1716S; 174bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 175bb3c6bfcSLydia Wang codec_type = VT1718S; 17625eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 17725eaba2fSLydia Wang codec_type = VT2002P; 178ab6734e7SLydia Wang else if (dev_id == 0x0448) 179ab6734e7SLydia Wang codec_type = VT1812; 18036dd5c4aSLydia Wang else if (dev_id == 0x0440) 18136dd5c4aSLydia Wang codec_type = VT1708S; 18211890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 18311890956SLydia Wang codec_type = VT1802; 18443737e0aSLydia Wang else if (dev_id == 0x4760) 18543737e0aSLydia Wang codec_type = VT1705CF; 1866121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762) 1876121b84aSLydia Wang codec_type = VT1808; 188d7426329SHarald Welte else 189d7426329SHarald Welte codec_type = UNKNOWN; 190d7426329SHarald Welte return codec_type; 191d7426329SHarald Welte }; 192d7426329SHarald Welte 193ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 194ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 1951f2e99feSLydia Wang 196187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \ 197187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ 198187d333eSTakashi Iwai !is_aa_path_mute(codec)) 1991f2e99feSLydia Wang 200b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec) 2011f2e99feSLydia Wang { 202b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 203b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 2041f2e99feSLydia Wang return; 205187d333eSTakashi Iwai if (spec->hp_work_active) { 206b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); 2077eaa9161SWang Xingchao codec->jackpoll_interval = 0; 208b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work); 209b3f6008fSTakashi Iwai spec->hp_work_active = false; 210187d333eSTakashi Iwai } 211187d333eSTakashi Iwai } 212187d333eSTakashi Iwai 213b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec) 214187d333eSTakashi Iwai { 215b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 216b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 217187d333eSTakashi Iwai return; 21805dc0fc9SDavid Henningsson if (spec->vt1708_jack_detect) { 219187d333eSTakashi Iwai if (!spec->hp_work_active) { 220b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 221b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); 2222f35c630STakashi Iwai schedule_delayed_work(&codec->jackpoll_work, 0); 223b3f6008fSTakashi Iwai spec->hp_work_active = true; 224187d333eSTakashi Iwai } 225b3f6008fSTakashi Iwai } else if (!hp_detect_with_aa(codec)) 226b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 2271f2e99feSLydia Wang } 228f5271101SLydia Wang 22924088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 23024088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 23124088a58STakashi Iwai { 232dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 23324088a58STakashi Iwai } 23424088a58STakashi Iwai 23524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 23624088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 23724088a58STakashi Iwai { 23824088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 239967b1307STakashi Iwai ucontrol->value.enumerated.item[0] = codec->power_save_node; 24024088a58STakashi Iwai return 0; 24124088a58STakashi Iwai } 24224088a58STakashi Iwai 24324088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 24424088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 24524088a58STakashi Iwai { 24624088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24724088a58STakashi Iwai struct via_spec *spec = codec->spec; 248688b12ccSTakashi Iwai bool val = !!ucontrol->value.enumerated.item[0]; 24924088a58STakashi Iwai 250967b1307STakashi Iwai if (val == codec->power_save_node) 25124088a58STakashi Iwai return 0; 252967b1307STakashi Iwai codec->power_save_node = val; 253688b12ccSTakashi Iwai spec->gen.power_down_unused = val; 254e9d010c2STakashi Iwai analog_low_current_mode(codec); 25524088a58STakashi Iwai return 1; 25624088a58STakashi Iwai } 25724088a58STakashi Iwai 258b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { 259b3f6008fSTakashi Iwai { 26024088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 26124088a58STakashi Iwai .name = "Dynamic Power-Control", 26224088a58STakashi Iwai .info = via_pin_power_ctl_info, 26324088a58STakashi Iwai .get = via_pin_power_ctl_get, 26424088a58STakashi Iwai .put = via_pin_power_ctl_put, 265b3f6008fSTakashi Iwai }, 266b3f6008fSTakashi Iwai {} /* terminator */ 26724088a58STakashi Iwai }; 26824088a58STakashi Iwai 26924088a58STakashi Iwai 270f5271101SLydia Wang /* check AA path's mute status */ 271ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 272ada509ecSTakashi Iwai { 273ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 274ada509ecSTakashi Iwai const struct hda_amp_list *p; 2750186f4f4STakashi Iwai int ch, v; 276ada509ecSTakashi Iwai 2770186f4f4STakashi Iwai p = spec->gen.loopback.amplist; 2780186f4f4STakashi Iwai if (!p) 2790186f4f4STakashi Iwai return true; 2800186f4f4STakashi Iwai for (; p->nid; p++) { 281ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 282ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 283ada509ecSTakashi Iwai p->idx); 284ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 285ada509ecSTakashi Iwai return false; 286f5271101SLydia Wang } 287f5271101SLydia Wang } 288ada509ecSTakashi Iwai return true; 289f5271101SLydia Wang } 290f5271101SLydia Wang 291f5271101SLydia Wang /* enter/exit analog low-current mode */ 292e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 293f5271101SLydia Wang { 294f5271101SLydia Wang struct via_spec *spec = codec->spec; 295ada509ecSTakashi Iwai bool enable; 296ada509ecSTakashi Iwai unsigned int verb, parm; 297f5271101SLydia Wang 298967b1307STakashi Iwai if (!codec->power_save_node) 299e9d010c2STakashi Iwai enable = false; 300e9d010c2STakashi Iwai else 301b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 302e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 303e9d010c2STakashi Iwai return; 304e9d010c2STakashi Iwai spec->alc_mode = enable; 305f5271101SLydia Wang 306f5271101SLydia Wang /* decide low current mode's verb & parameter */ 307f5271101SLydia Wang switch (spec->codec_type) { 308f5271101SLydia Wang case VT1708B_8CH: 309f5271101SLydia Wang case VT1708B_4CH: 310f5271101SLydia Wang verb = 0xf70; 311f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 312f5271101SLydia Wang break; 313f5271101SLydia Wang case VT1708S: 314eb7188caSLydia Wang case VT1718S: 315f3db423dSLydia Wang case VT1716S: 316f5271101SLydia Wang verb = 0xf73; 317f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 318f5271101SLydia Wang break; 319f5271101SLydia Wang case VT1702: 320f5271101SLydia Wang verb = 0xf73; 321f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 322f5271101SLydia Wang break; 32325eaba2fSLydia Wang case VT2002P: 324ab6734e7SLydia Wang case VT1812: 32511890956SLydia Wang case VT1802: 32625eaba2fSLydia Wang verb = 0xf93; 32725eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 32825eaba2fSLydia Wang break; 32943737e0aSLydia Wang case VT1705CF: 3306121b84aSLydia Wang case VT1808: 33143737e0aSLydia Wang verb = 0xf82; 33243737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 33343737e0aSLydia Wang break; 334f5271101SLydia Wang default: 335f5271101SLydia Wang return; /* other codecs are not supported */ 336f5271101SLydia Wang } 337f5271101SLydia Wang /* send verb */ 338*7639a06cSTakashi Iwai snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm); 339f5271101SLydia Wang } 340f5271101SLydia Wang 341e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 342e9d010c2STakashi Iwai { 343e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 344e9d010c2STakashi Iwai } 345e9d010c2STakashi Iwai 346c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 347c577b8a1SJoseph Chan { 348c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 3495b0cb1d8SJaroslav Kysela int err, i; 350c577b8a1SJoseph Chan 351b3f6008fSTakashi Iwai err = snd_hda_gen_build_controls(codec); 352b3f6008fSTakashi Iwai if (err < 0) 353b3f6008fSTakashi Iwai return err; 354b3f6008fSTakashi Iwai 355b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; 35624088a58STakashi Iwai 357c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 358c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 359c577b8a1SJoseph Chan if (err < 0) 360c577b8a1SJoseph Chan return err; 361c577b8a1SJoseph Chan } 362c577b8a1SJoseph Chan 363c577b8a1SJoseph Chan return 0; 364c577b8a1SJoseph Chan } 365c577b8a1SJoseph Chan 366b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 367b3f6008fSTakashi Iwai struct hda_codec *codec, 368b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 369b3f6008fSTakashi Iwai int action) 370c577b8a1SJoseph Chan { 371b3f6008fSTakashi Iwai analog_low_current_mode(codec); 372b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 373c577b8a1SJoseph Chan } 374c577b8a1SJoseph Chan 375c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 376c577b8a1SJoseph Chan { 377b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 378a8dca460STakashi Iwai snd_hda_gen_free(codec); 379c577b8a1SJoseph Chan } 380c577b8a1SJoseph Chan 3812a43952aSTakashi Iwai #ifdef CONFIG_PM 38268cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 3831f2e99feSLydia Wang { 3841f2e99feSLydia Wang struct via_spec *spec = codec->spec; 385b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 38694c142a1SDavid Henningsson 38794c142a1SDavid Henningsson /* Fix pop noise on headphones */ 3882c38d990STakashi Iwai if (spec->codec_type == VT1802) 3892c38d990STakashi Iwai snd_hda_shutup_pins(codec); 39094c142a1SDavid Henningsson 3911f2e99feSLydia Wang return 0; 3921f2e99feSLydia Wang } 3931f2e99feSLydia Wang #endif 3941f2e99feSLydia Wang 39583012a7cSTakashi Iwai #ifdef CONFIG_PM 396cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 397cb53c626STakashi Iwai { 398cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 399b3f6008fSTakashi Iwai analog_low_current_mode(codec); 400b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 401b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 402cb53c626STakashi Iwai } 403cb53c626STakashi Iwai #endif 404cb53c626STakashi Iwai 405c577b8a1SJoseph Chan /* 406c577b8a1SJoseph Chan */ 4075d41762aSTakashi Iwai 4085d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 4095d41762aSTakashi Iwai 41090dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 411c577b8a1SJoseph Chan .build_controls = via_build_controls, 412b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 413c577b8a1SJoseph Chan .init = via_init, 414c577b8a1SJoseph Chan .free = via_free, 4154e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 4162a43952aSTakashi Iwai #ifdef CONFIG_PM 4171f2e99feSLydia Wang .suspend = via_suspend, 418cb53c626STakashi Iwai .check_power_status = via_check_power_status, 419cb53c626STakashi Iwai #endif 420c577b8a1SJoseph Chan }; 421c577b8a1SJoseph Chan 4224a79616dSTakashi Iwai 423b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 424b3f6008fSTakashi Iwai /* power down jack detect function */ 425b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 426b3f6008fSTakashi Iwai { } 4274a79616dSTakashi Iwai }; 42876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 42976d9b0ddSHarald Welte { 43076d9b0ddSHarald Welte unsigned int def_conf; 43176d9b0ddSHarald Welte unsigned char seqassoc; 43276d9b0ddSHarald Welte 4332f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 43476d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 43576d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 43682ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 43782ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 43876d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 4392f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 44076d9b0ddSHarald Welte } 44176d9b0ddSHarald Welte 44276d9b0ddSHarald Welte return; 44376d9b0ddSHarald Welte } 44476d9b0ddSHarald Welte 445e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 4461f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 4471f2e99feSLydia Wang { 4481f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4491f2e99feSLydia Wang struct via_spec *spec = codec->spec; 4501f2e99feSLydia Wang 4511f2e99feSLydia Wang if (spec->codec_type != VT1708) 4521f2e99feSLydia Wang return 0; 453e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 4541f2e99feSLydia Wang return 0; 4551f2e99feSLydia Wang } 4561f2e99feSLydia Wang 457e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 4581f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 4591f2e99feSLydia Wang { 4601f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4611f2e99feSLydia Wang struct via_spec *spec = codec->spec; 462187d333eSTakashi Iwai int val; 4631f2e99feSLydia Wang 4641f2e99feSLydia Wang if (spec->codec_type != VT1708) 4651f2e99feSLydia Wang return 0; 466187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 467187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 468187d333eSTakashi Iwai return 0; 469187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 470b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 471187d333eSTakashi Iwai return 1; 4721f2e99feSLydia Wang } 4731f2e99feSLydia Wang 474b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 475b3f6008fSTakashi Iwai { 4761f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4771f2e99feSLydia Wang .name = "Jack Detect", 4781f2e99feSLydia Wang .count = 1, 4791f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 480e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 481e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 482b3f6008fSTakashi Iwai }, 483b3f6008fSTakashi Iwai {} /* terminator */ 4841f2e99feSLydia Wang }; 4851f2e99feSLydia Wang 4864abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 4874abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 4884abdbd1cSTakashi Iwai .no_dac = 0x4000, 4894abdbd1cSTakashi Iwai .shared_primary = 0x10000, 4904abdbd1cSTakashi Iwai .shared_surr = 0x20, 4914abdbd1cSTakashi Iwai .shared_clfe = 0x20, 4924abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 4934abdbd1cSTakashi Iwai }; 4944abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 4954abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 4964abdbd1cSTakashi Iwai .no_dac = 0x4000, 4974abdbd1cSTakashi Iwai .shared_primary = 0x12, 4984abdbd1cSTakashi Iwai .shared_surr = 0x20, 4994abdbd1cSTakashi Iwai .shared_clfe = 0x20, 5004abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 5014abdbd1cSTakashi Iwai }; 5024abdbd1cSTakashi Iwai 503b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 504b3f6008fSTakashi Iwai { 505b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 506b3f6008fSTakashi Iwai int err; 507b3f6008fSTakashi Iwai 5084abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 5094abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 5104abdbd1cSTakashi Iwai 511b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 512b3f6008fSTakashi Iwai if (err < 0) 513b3f6008fSTakashi Iwai return err; 514b3f6008fSTakashi Iwai 515b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 516b3f6008fSTakashi Iwai if (err < 0) 517b3f6008fSTakashi Iwai return err; 518b3f6008fSTakashi Iwai 519688b12ccSTakashi Iwai /* disable widget PM at start for compatibility */ 520967b1307STakashi Iwai codec->power_save_node = 0; 521688b12ccSTakashi Iwai spec->gen.power_down_unused = 0; 522b3f6008fSTakashi Iwai return 0; 5234a918ffeSTakashi Iwai } 5244a918ffeSTakashi Iwai 5255d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 5265d41762aSTakashi Iwai { 5275d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 5285d41762aSTakashi Iwai int i; 5295d41762aSTakashi Iwai 5305d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 5315d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 5325d41762aSTakashi Iwai 533e9d010c2STakashi Iwai /* init power states */ 534e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 535e9d010c2STakashi Iwai 536b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 53711890956SLydia Wang 538b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 53925eaba2fSLydia Wang 540c577b8a1SJoseph Chan return 0; 541c577b8a1SJoseph Chan } 542c577b8a1SJoseph Chan 543f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 544f672f65aSDavid Henningsson { 545f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 546f672f65aSDavid Henningsson temporary enable jackpoll */ 547f672f65aSDavid Henningsson int err; 548f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 549f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 550f672f65aSDavid Henningsson err = via_build_controls(codec); 551f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 552f672f65aSDavid Henningsson return err; 553f672f65aSDavid Henningsson } 554f672f65aSDavid Henningsson 555b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 556337b9d02STakashi Iwai { 557337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 558b3f6008fSTakashi Iwai int i, err; 559337b9d02STakashi Iwai 560b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 561*7639a06cSTakashi Iwai if (err < 0 || codec->core.vendor_id != 0x11061708) 562b3f6008fSTakashi Iwai return err; 563b3f6008fSTakashi Iwai 564b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 565b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 566b3f6008fSTakashi Iwai * disable the 24bit format, so far. 567b3f6008fSTakashi Iwai */ 568bbbc7e85STakashi Iwai for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) { 569bbbc7e85STakashi Iwai struct hda_pcm *info = spec->gen.pcm_rec[i]; 570bbbc7e85STakashi Iwai if (!info) 571bbbc7e85STakashi Iwai continue; 572b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 573b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 574b3f6008fSTakashi Iwai continue; 575b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 576b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 577337b9d02STakashi Iwai } 578b3f6008fSTakashi Iwai 5791c55d521STakashi Iwai return 0; 580337b9d02STakashi Iwai } 581337b9d02STakashi Iwai 582c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 583c577b8a1SJoseph Chan { 584c577b8a1SJoseph Chan struct via_spec *spec; 585c577b8a1SJoseph Chan int err; 586c577b8a1SJoseph Chan 587c577b8a1SJoseph Chan /* create a codec specific record */ 5885b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 589c577b8a1SJoseph Chan if (spec == NULL) 590c577b8a1SJoseph Chan return -ENOMEM; 591c577b8a1SJoseph Chan 592b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 593b3f6008fSTakashi Iwai 594b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 595b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 596b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 597b3f6008fSTakashi Iwai 598b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 599b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 600b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 601eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 602eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 603620e2b28STakashi Iwai 60412daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 60512daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 60612daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 60712daef65STakashi Iwai 608c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 60912daef65STakashi Iwai err = via_parse_auto_config(codec); 610c577b8a1SJoseph Chan if (err < 0) { 611c577b8a1SJoseph Chan via_free(codec); 612c577b8a1SJoseph Chan return err; 613c577b8a1SJoseph Chan } 614c577b8a1SJoseph Chan 61512daef65STakashi Iwai /* add jack detect on/off control */ 616b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 617c577b8a1SJoseph Chan 618e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 619e322a36dSLydia Wang 620c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 621f672f65aSDavid Henningsson codec->patch_ops.build_controls = vt1708_build_controls; 622b3f6008fSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 623c577b8a1SJoseph Chan 624b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 625b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 626b3f6008fSTakashi Iwai 627c577b8a1SJoseph Chan return 0; 628c577b8a1SJoseph Chan } 629c577b8a1SJoseph Chan 630ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 631c577b8a1SJoseph Chan { 632c577b8a1SJoseph Chan struct via_spec *spec; 633c577b8a1SJoseph Chan int err; 634c577b8a1SJoseph Chan 635c577b8a1SJoseph Chan /* create a codec specific record */ 6365b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 637c577b8a1SJoseph Chan if (spec == NULL) 638c577b8a1SJoseph Chan return -ENOMEM; 639c577b8a1SJoseph Chan 640b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 641620e2b28STakashi Iwai 64212daef65STakashi Iwai err = via_parse_auto_config(codec); 643c577b8a1SJoseph Chan if (err < 0) { 644c577b8a1SJoseph Chan via_free(codec); 645c577b8a1SJoseph Chan return err; 646c577b8a1SJoseph Chan } 647c577b8a1SJoseph Chan 648c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 649c577b8a1SJoseph Chan 650f7278fd0SJosepch Chan return 0; 651f7278fd0SJosepch Chan } 652f7278fd0SJosepch Chan 653518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 654ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 655f7278fd0SJosepch Chan { 656f7278fd0SJosepch Chan struct via_spec *spec; 657f7278fd0SJosepch Chan int err; 658f7278fd0SJosepch Chan 659518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 660518bf3baSLydia Wang return patch_vt1708S(codec); 661ddd304d8STakashi Iwai 662f7278fd0SJosepch Chan /* create a codec specific record */ 6635b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 664f7278fd0SJosepch Chan if (spec == NULL) 665f7278fd0SJosepch Chan return -ENOMEM; 666f7278fd0SJosepch Chan 667b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 668620e2b28STakashi Iwai 669f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 67012daef65STakashi Iwai err = via_parse_auto_config(codec); 671f7278fd0SJosepch Chan if (err < 0) { 672f7278fd0SJosepch Chan via_free(codec); 673f7278fd0SJosepch Chan return err; 674f7278fd0SJosepch Chan } 675f7278fd0SJosepch Chan 676f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 677f7278fd0SJosepch Chan return 0; 678f7278fd0SJosepch Chan } 679f7278fd0SJosepch Chan 680d949cac1SHarald Welte /* Patch for VT1708S */ 681096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 682d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 683d7426329SHarald Welte {0x1, 0xf98, 0x1}, 684bc7e7e5cSLydia Wang /* don't bybass mixer */ 685bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 686d949cac1SHarald Welte { } 687d949cac1SHarald Welte }; 688d949cac1SHarald Welte 6896369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 6906369bcfcSLydia Wang int offset, int num_steps, int step_size) 6916369bcfcSLydia Wang { 692d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 693d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 6946369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 6956369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 6966369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 6976369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 6986369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 6996369bcfcSLydia Wang } 7006369bcfcSLydia Wang 701d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 702d949cac1SHarald Welte { 703d949cac1SHarald Welte struct via_spec *spec; 704d949cac1SHarald Welte int err; 705d949cac1SHarald Welte 706d949cac1SHarald Welte /* create a codec specific record */ 7075b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 708d949cac1SHarald Welte if (spec == NULL) 709d949cac1SHarald Welte return -ENOMEM; 710d949cac1SHarald Welte 711b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 712d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 713d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 714620e2b28STakashi Iwai 715518bf3baSLydia Wang /* correct names for VT1708BCE */ 716518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 717*7639a06cSTakashi Iwai kfree(codec->core.chip_name); 718*7639a06cSTakashi Iwai codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 7196efdd851STakashi Iwai snprintf(codec->card->mixername, 7206efdd851STakashi Iwai sizeof(codec->card->mixername), 721*7639a06cSTakashi Iwai "%s %s", codec->core.vendor_name, codec->core.chip_name); 722970f630fSLydia Wang } 723bc92df7fSLydia Wang /* correct names for VT1705 */ 724*7639a06cSTakashi Iwai if (codec->core.vendor_id == 0x11064397) { 725*7639a06cSTakashi Iwai kfree(codec->core.chip_name); 726*7639a06cSTakashi Iwai codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL); 7276efdd851STakashi Iwai snprintf(codec->card->mixername, 7286efdd851STakashi Iwai sizeof(codec->card->mixername), 729*7639a06cSTakashi Iwai "%s %s", codec->core.vendor_name, codec->core.chip_name); 730bc92df7fSLydia Wang } 731b3f6008fSTakashi Iwai 732b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 733b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 734b3f6008fSTakashi Iwai if (err < 0) { 735b3f6008fSTakashi Iwai via_free(codec); 736b3f6008fSTakashi Iwai return err; 737b3f6008fSTakashi Iwai } 738b3f6008fSTakashi Iwai 739b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 740b3f6008fSTakashi Iwai 741b3f6008fSTakashi Iwai codec->patch_ops = via_patch_ops; 742d949cac1SHarald Welte return 0; 743d949cac1SHarald Welte } 744d949cac1SHarald Welte 745d949cac1SHarald Welte /* Patch for VT1702 */ 746d949cac1SHarald Welte 747096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 748bc7e7e5cSLydia Wang /* mixer enable */ 749bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 750bc7e7e5cSLydia Wang /* GPIO 0~2 */ 751bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 752d949cac1SHarald Welte { } 753d949cac1SHarald Welte }; 754d949cac1SHarald Welte 755d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 756d949cac1SHarald Welte { 757d949cac1SHarald Welte struct via_spec *spec; 758d949cac1SHarald Welte int err; 759d949cac1SHarald Welte 760d949cac1SHarald Welte /* create a codec specific record */ 7615b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 762d949cac1SHarald Welte if (spec == NULL) 763d949cac1SHarald Welte return -ENOMEM; 764d949cac1SHarald Welte 765b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 766620e2b28STakashi Iwai 76712daef65STakashi Iwai /* limit AA path volume to 0 dB */ 76812daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 76912daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 77012daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 77112daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 77212daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 77312daef65STakashi Iwai 774d949cac1SHarald Welte /* automatic parse from the BIOS config */ 77512daef65STakashi Iwai err = via_parse_auto_config(codec); 776d949cac1SHarald Welte if (err < 0) { 777d949cac1SHarald Welte via_free(codec); 778d949cac1SHarald Welte return err; 779d949cac1SHarald Welte } 780d949cac1SHarald Welte 781096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 782d949cac1SHarald Welte 783d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 784d949cac1SHarald Welte return 0; 785d949cac1SHarald Welte } 786d949cac1SHarald Welte 787eb7188caSLydia Wang /* Patch for VT1718S */ 788eb7188caSLydia Wang 789096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 7904ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 7914ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 792eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 793eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 7945d41762aSTakashi Iwai 795eb7188caSLydia Wang { } 796eb7188caSLydia Wang }; 797eb7188caSLydia Wang 79830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 79930b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 80030b45033STakashi Iwai */ 80130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 80230b45033STakashi Iwai { 80330b45033STakashi Iwai struct via_spec *spec = codec->spec; 80430b45033STakashi Iwai int i, nums; 80530b45033STakashi Iwai hda_nid_t conn[8]; 80630b45033STakashi Iwai hda_nid_t nid; 80730b45033STakashi Iwai 808b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 80930b45033STakashi Iwai return 0; 810b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 81130b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 81230b45033STakashi Iwai for (i = 0; i < nums; i++) { 81330b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 81430b45033STakashi Iwai return 0; 81530b45033STakashi Iwai } 81630b45033STakashi Iwai 81730b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 818*7639a06cSTakashi Iwai for_each_hda_codec_node(nid, codec) { 81930b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 82030b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 82130b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 82230b45033STakashi Iwai conn[nums++] = nid; 82330b45033STakashi Iwai return snd_hda_override_conn_list(codec, 824b3f6008fSTakashi Iwai spec->gen.mixer_nid, 82530b45033STakashi Iwai nums, conn); 82630b45033STakashi Iwai } 82730b45033STakashi Iwai } 82830b45033STakashi Iwai return 0; 82930b45033STakashi Iwai } 83030b45033STakashi Iwai 83130b45033STakashi Iwai 832eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 833eb7188caSLydia Wang { 834eb7188caSLydia Wang struct via_spec *spec; 835eb7188caSLydia Wang int err; 836eb7188caSLydia Wang 837eb7188caSLydia Wang /* create a codec specific record */ 8385b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 839eb7188caSLydia Wang if (spec == NULL) 840eb7188caSLydia Wang return -ENOMEM; 841eb7188caSLydia Wang 842b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 843d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 844d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 84530b45033STakashi Iwai add_secret_dac_path(codec); 846620e2b28STakashi Iwai 847eb7188caSLydia Wang /* automatic parse from the BIOS config */ 84812daef65STakashi Iwai err = via_parse_auto_config(codec); 849eb7188caSLydia Wang if (err < 0) { 850eb7188caSLydia Wang via_free(codec); 851eb7188caSLydia Wang return err; 852eb7188caSLydia Wang } 853eb7188caSLydia Wang 854096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 855eb7188caSLydia Wang 856eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 857eb7188caSLydia Wang return 0; 858eb7188caSLydia Wang } 859f3db423dSLydia Wang 860f3db423dSLydia Wang /* Patch for VT1716S */ 861f3db423dSLydia Wang 862f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 863f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 864f3db423dSLydia Wang { 865f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 866f3db423dSLydia Wang uinfo->count = 1; 867f3db423dSLydia Wang uinfo->value.integer.min = 0; 868f3db423dSLydia Wang uinfo->value.integer.max = 1; 869f3db423dSLydia Wang return 0; 870f3db423dSLydia Wang } 871f3db423dSLydia Wang 872f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 873f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 874f3db423dSLydia Wang { 875f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 876f3db423dSLydia Wang int index = 0; 877f3db423dSLydia Wang 878f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 879f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 880f3db423dSLydia Wang if (index != -1) 881f3db423dSLydia Wang *ucontrol->value.integer.value = index; 882f3db423dSLydia Wang 883f3db423dSLydia Wang return 0; 884f3db423dSLydia Wang } 885f3db423dSLydia Wang 886f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 887f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 888f3db423dSLydia Wang { 889f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 890f3db423dSLydia Wang struct via_spec *spec = codec->spec; 891f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 892f3db423dSLydia Wang 893f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 894f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 895f3db423dSLydia Wang spec->dmic_enabled = index; 896f3db423dSLydia Wang return 1; 897f3db423dSLydia Wang } 898f3db423dSLydia Wang 89990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 900f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 901f3db423dSLydia Wang { 902f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 903f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 9045b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 905f3db423dSLydia Wang .count = 1, 906f3db423dSLydia Wang .info = vt1716s_dmic_info, 907f3db423dSLydia Wang .get = vt1716s_dmic_get, 908f3db423dSLydia Wang .put = vt1716s_dmic_put, 909f3db423dSLydia Wang }, 910f3db423dSLydia Wang {} /* end */ 911f3db423dSLydia Wang }; 912f3db423dSLydia Wang 913f3db423dSLydia Wang 914f3db423dSLydia Wang /* mono-out mixer elements */ 91590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 916f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 917f3db423dSLydia Wang { } /* end */ 918f3db423dSLydia Wang }; 919f3db423dSLydia Wang 920096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 921f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 922f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 923f3db423dSLydia Wang /* don't bybass mixer */ 924f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 925f3db423dSLydia Wang /* Enable mono output */ 926f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 927f3db423dSLydia Wang { } 928f3db423dSLydia Wang }; 929f3db423dSLydia Wang 930f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 931f3db423dSLydia Wang { 932f3db423dSLydia Wang struct via_spec *spec; 933f3db423dSLydia Wang int err; 934f3db423dSLydia Wang 935f3db423dSLydia Wang /* create a codec specific record */ 9365b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 937f3db423dSLydia Wang if (spec == NULL) 938f3db423dSLydia Wang return -ENOMEM; 939f3db423dSLydia Wang 940b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 941d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 942d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 943620e2b28STakashi Iwai 944f3db423dSLydia Wang /* automatic parse from the BIOS config */ 94512daef65STakashi Iwai err = via_parse_auto_config(codec); 946f3db423dSLydia Wang if (err < 0) { 947f3db423dSLydia Wang via_free(codec); 948f3db423dSLydia Wang return err; 949f3db423dSLydia Wang } 950f3db423dSLydia Wang 951096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 952f3db423dSLydia Wang 953b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 954f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 955f3db423dSLydia Wang 956f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 957f3db423dSLydia Wang return 0; 958f3db423dSLydia Wang } 95925eaba2fSLydia Wang 96025eaba2fSLydia Wang /* for vt2002P */ 96125eaba2fSLydia Wang 962096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 963eadb9a80SLydia Wang /* Class-D speaker related verbs */ 964eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 965eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 966eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 96725eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 96825eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 96925eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 97025eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 97125eaba2fSLydia Wang { } 97225eaba2fSLydia Wang }; 9734a918ffeSTakashi Iwai 974096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 97511890956SLydia Wang /* Enable Boost Volume backdoor */ 97611890956SLydia Wang {0x1, 0xfb9, 0x24}, 97711890956SLydia Wang /* Enable AOW0 to MW9 */ 97811890956SLydia Wang {0x1, 0xfb8, 0x88}, 97911890956SLydia Wang { } 98011890956SLydia Wang }; 98125eaba2fSLydia Wang 9824b527b65SDavid Henningsson /* 9834b527b65SDavid Henningsson * pin fix-up 9844b527b65SDavid Henningsson */ 9854b527b65SDavid Henningsson enum { 9864b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 987d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 9884b527b65SDavid Henningsson }; 9894b527b65SDavid Henningsson 9904b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 9914b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 9924b527b65SDavid Henningsson { 9934b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 9944b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 9954b527b65SDavid Henningsson } 9964b527b65SDavid Henningsson 9974b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 9984b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 9994b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 10004b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 10014b527b65SDavid Henningsson }, 1002d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1003d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1004d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1005d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1006d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1007d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1008d5266125STakashi Iwai { } 1009d5266125STakashi Iwai } 1010d5266125STakashi Iwai }, 10114b527b65SDavid Henningsson }; 10124b527b65SDavid Henningsson 10134b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1014d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 10154b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 10164b527b65SDavid Henningsson {} 10174b527b65SDavid Henningsson }; 10184b527b65SDavid Henningsson 1019ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1020ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1021ef4da458STakashi Iwai */ 1022ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1023ef4da458STakashi Iwai { 1024ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1025ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1026ef4da458STakashi Iwai 1027ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1028ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1029ef4da458STakashi Iwai } 1030ef4da458STakashi Iwai 103125eaba2fSLydia Wang /* patch for vt2002P */ 103225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 103325eaba2fSLydia Wang { 103425eaba2fSLydia Wang struct via_spec *spec; 103525eaba2fSLydia Wang int err; 103625eaba2fSLydia Wang 103725eaba2fSLydia Wang /* create a codec specific record */ 10385b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 103925eaba2fSLydia Wang if (spec == NULL) 104025eaba2fSLydia Wang return -ENOMEM; 104125eaba2fSLydia Wang 1042b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1043d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1044d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1045ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1046ef4da458STakashi Iwai fix_vt1802_connections(codec); 104730b45033STakashi Iwai add_secret_dac_path(codec); 1048620e2b28STakashi Iwai 10494b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 10504b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 10514b527b65SDavid Henningsson 105225eaba2fSLydia Wang /* automatic parse from the BIOS config */ 105312daef65STakashi Iwai err = via_parse_auto_config(codec); 105425eaba2fSLydia Wang if (err < 0) { 105525eaba2fSLydia Wang via_free(codec); 105625eaba2fSLydia Wang return err; 105725eaba2fSLydia Wang } 105825eaba2fSLydia Wang 105911890956SLydia Wang if (spec->codec_type == VT1802) 10604a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 106111890956SLydia Wang else 10624a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 106311890956SLydia Wang 106425eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 106525eaba2fSLydia Wang return 0; 106625eaba2fSLydia Wang } 1067ab6734e7SLydia Wang 1068ab6734e7SLydia Wang /* for vt1812 */ 1069ab6734e7SLydia Wang 1070096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1071ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1072ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1073ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1074ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1075ab6734e7SLydia Wang { } 1076ab6734e7SLydia Wang }; 1077ab6734e7SLydia Wang 1078ab6734e7SLydia Wang /* patch for vt1812 */ 1079ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1080ab6734e7SLydia Wang { 1081ab6734e7SLydia Wang struct via_spec *spec; 1082ab6734e7SLydia Wang int err; 1083ab6734e7SLydia Wang 1084ab6734e7SLydia Wang /* create a codec specific record */ 10855b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1086ab6734e7SLydia Wang if (spec == NULL) 1087ab6734e7SLydia Wang return -ENOMEM; 1088ab6734e7SLydia Wang 1089b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1090d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1091d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 109230b45033STakashi Iwai add_secret_dac_path(codec); 1093620e2b28STakashi Iwai 1094ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 109512daef65STakashi Iwai err = via_parse_auto_config(codec); 1096ab6734e7SLydia Wang if (err < 0) { 1097ab6734e7SLydia Wang via_free(codec); 1098ab6734e7SLydia Wang return err; 1099ab6734e7SLydia Wang } 1100ab6734e7SLydia Wang 1101096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1102ab6734e7SLydia Wang 1103ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 1104ab6734e7SLydia Wang return 0; 1105ab6734e7SLydia Wang } 1106ab6734e7SLydia Wang 110743737e0aSLydia Wang /* patch for vt3476 */ 110843737e0aSLydia Wang 110943737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 111043737e0aSLydia Wang /* Enable DMic 8/16/32K */ 111143737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 111243737e0aSLydia Wang /* Enable Boost Volume backdoor */ 111343737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 111443737e0aSLydia Wang /* Enable AOW-MW9 path */ 111543737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 111643737e0aSLydia Wang { } 111743737e0aSLydia Wang }; 111843737e0aSLydia Wang 111943737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 112043737e0aSLydia Wang { 112143737e0aSLydia Wang struct via_spec *spec; 112243737e0aSLydia Wang int err; 112343737e0aSLydia Wang 112443737e0aSLydia Wang /* create a codec specific record */ 112543737e0aSLydia Wang spec = via_new_spec(codec); 112643737e0aSLydia Wang if (spec == NULL) 112743737e0aSLydia Wang return -ENOMEM; 112843737e0aSLydia Wang 1129b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 113043737e0aSLydia Wang add_secret_dac_path(codec); 113143737e0aSLydia Wang 113243737e0aSLydia Wang /* automatic parse from the BIOS config */ 113343737e0aSLydia Wang err = via_parse_auto_config(codec); 113443737e0aSLydia Wang if (err < 0) { 113543737e0aSLydia Wang via_free(codec); 113643737e0aSLydia Wang return err; 113743737e0aSLydia Wang } 113843737e0aSLydia Wang 113943737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 114043737e0aSLydia Wang 114143737e0aSLydia Wang codec->patch_ops = via_patch_ops; 114243737e0aSLydia Wang return 0; 114343737e0aSLydia Wang } 114443737e0aSLydia Wang 1145c577b8a1SJoseph Chan /* 1146c577b8a1SJoseph Chan * patch entries 1147c577b8a1SJoseph Chan */ 114890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 11493218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 11503218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 11513218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 11523218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 11533218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 1154ddd304d8STakashi Iwai .patch = patch_vt1709}, 11553218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 1156ddd304d8STakashi Iwai .patch = patch_vt1709}, 11573218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 1158ddd304d8STakashi Iwai .patch = patch_vt1709}, 11593218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 1160ddd304d8STakashi Iwai .patch = patch_vt1709}, 11613218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 1162ddd304d8STakashi Iwai .patch = patch_vt1709}, 11633218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 1164ddd304d8STakashi Iwai .patch = patch_vt1709}, 11653218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 1166ddd304d8STakashi Iwai .patch = patch_vt1709}, 11673218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 1168ddd304d8STakashi Iwai .patch = patch_vt1709}, 11693218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 1170ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11713218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 1172ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11733218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 1174ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11753218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 1176ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11773218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 1178ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11793218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 1180ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11813218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 1182ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11833218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 1184ddd304d8STakashi Iwai .patch = patch_vt1708B}, 11853218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 1186d949cac1SHarald Welte .patch = patch_vt1708S}, 11873218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 1188d949cac1SHarald Welte .patch = patch_vt1708S}, 11893218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 1190d949cac1SHarald Welte .patch = patch_vt1708S}, 11913218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 1192d949cac1SHarald Welte .patch = patch_vt1708S}, 1193bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 1194d949cac1SHarald Welte .patch = patch_vt1708S}, 11953218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 1196d949cac1SHarald Welte .patch = patch_vt1708S}, 11973218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 1198d949cac1SHarald Welte .patch = patch_vt1708S}, 11993218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 1200d949cac1SHarald Welte .patch = patch_vt1708S}, 12013218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 1202d949cac1SHarald Welte .patch = patch_vt1702}, 12033218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 1204d949cac1SHarald Welte .patch = patch_vt1702}, 12053218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 1206d949cac1SHarald Welte .patch = patch_vt1702}, 12073218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 1208d949cac1SHarald Welte .patch = patch_vt1702}, 12093218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 1210d949cac1SHarald Welte .patch = patch_vt1702}, 12113218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 1212d949cac1SHarald Welte .patch = patch_vt1702}, 12133218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 1214d949cac1SHarald Welte .patch = patch_vt1702}, 12153218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 1216d949cac1SHarald Welte .patch = patch_vt1702}, 1217eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 1218eb7188caSLydia Wang .patch = patch_vt1718S}, 1219eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 1220eb7188caSLydia Wang .patch = patch_vt1718S}, 1221bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 1222bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1223bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 1224bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1225f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 1226f3db423dSLydia Wang .patch = patch_vt1716S}, 1227f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 1228f3db423dSLydia Wang .patch = patch_vt1716S}, 122925eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 123025eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 1231ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 123236dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 123336dd5c4aSLydia Wang .patch = patch_vt1708S}, 123411890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 123511890956SLydia Wang .patch = patch_vt2002P}, 123611890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 123711890956SLydia Wang .patch = patch_vt2002P}, 123843737e0aSLydia Wang { .id = 0x11064760, .name = "VT1705CF", 123943737e0aSLydia Wang .patch = patch_vt3476}, 12406121b84aSLydia Wang { .id = 0x11064761, .name = "VT1708SCE", 12416121b84aSLydia Wang .patch = patch_vt3476}, 12426121b84aSLydia Wang { .id = 0x11064762, .name = "VT1808", 12436121b84aSLydia Wang .patch = patch_vt3476}, 1244c577b8a1SJoseph Chan {} /* terminator */ 1245c577b8a1SJoseph Chan }; 12461289e9e8STakashi Iwai 12471289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 12481289e9e8STakashi Iwai 1249d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = { 12501289e9e8STakashi Iwai .preset = snd_hda_preset_via, 12511289e9e8STakashi Iwai }; 12521289e9e8STakashi Iwai 12531289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 12541289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 12551289e9e8STakashi Iwai 1256d8a766a1STakashi Iwai module_hda_codec_driver(via_driver); 1257