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; 10224088a58STakashi Iwai unsigned int no_pin_power_ctl; 1031f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1041f2e99feSLydia Wang 105e9d010c2STakashi Iwai /* analog low-power control */ 106e9d010c2STakashi Iwai bool alc_mode; 107e9d010c2STakashi Iwai 1081f2e99feSLydia Wang /* work to check hp jack state */ 109187d333eSTakashi Iwai int hp_work_active; 110e06e5a29STakashi Iwai int vt1708_jack_detect; 1113e95b9abSLydia Wang 1123e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 11343737e0aSLydia Wang unsigned int dac_stream_tag[4]; 1141f2e99feSLydia Wang }; 1151f2e99feSLydia Wang 1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 118b3f6008fSTakashi Iwai struct hda_codec *codec, 119b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 120b3f6008fSTakashi Iwai int action); 121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); 122b3f6008fSTakashi Iwai 1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1245b0cb1d8SJaroslav Kysela { 1255b0cb1d8SJaroslav Kysela struct via_spec *spec; 1265b0cb1d8SJaroslav Kysela 1275b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1285b0cb1d8SJaroslav Kysela if (spec == NULL) 1295b0cb1d8SJaroslav Kysela return NULL; 1305b0cb1d8SJaroslav Kysela 1315b0cb1d8SJaroslav Kysela codec->spec = spec; 132b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1330341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1340341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1350341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1360341ccd7SLydia Wang spec->codec_type = VT1708S; 137b3f6008fSTakashi Iwai spec->no_pin_power_ctl = 1; 13813961170STakashi Iwai spec->gen.indep_hp = 1; 139b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 1405b0cb1d8SJaroslav Kysela return spec; 1415b0cb1d8SJaroslav Kysela } 1425b0cb1d8SJaroslav Kysela 143744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 144d7426329SHarald Welte { 145744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 146d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 147d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 148d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 149d7426329SHarald Welte 150d7426329SHarald Welte /* get codec type */ 151d7426329SHarald Welte if (ven_id != 0x1106) 152d7426329SHarald Welte codec_type = UNKNOWN; 153d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 154d7426329SHarald Welte codec_type = VT1708; 155d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 156d7426329SHarald Welte codec_type = VT1709_10CH; 157d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 158d7426329SHarald Welte codec_type = VT1709_6CH; 159518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 160d7426329SHarald Welte codec_type = VT1708B_8CH; 161518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 162518bf3baSLydia Wang codec_type = VT1708BCE; 163518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 164d7426329SHarald Welte codec_type = VT1708B_4CH; 165d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 166d7426329SHarald Welte && (dev_id >> 12) < 8) 167d7426329SHarald Welte codec_type = VT1708S; 168d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 169d7426329SHarald Welte && (dev_id >> 12) < 8) 170d7426329SHarald Welte codec_type = VT1702; 171eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 172eb7188caSLydia Wang && (dev_id >> 12) < 8) 173eb7188caSLydia Wang codec_type = VT1718S; 174f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 175f3db423dSLydia Wang codec_type = VT1716S; 176bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 177bb3c6bfcSLydia Wang codec_type = VT1718S; 17825eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 17925eaba2fSLydia Wang codec_type = VT2002P; 180ab6734e7SLydia Wang else if (dev_id == 0x0448) 181ab6734e7SLydia Wang codec_type = VT1812; 18236dd5c4aSLydia Wang else if (dev_id == 0x0440) 18336dd5c4aSLydia Wang codec_type = VT1708S; 18411890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 18511890956SLydia Wang codec_type = VT1802; 18643737e0aSLydia Wang else if (dev_id == 0x4760) 18743737e0aSLydia Wang codec_type = VT1705CF; 1886121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762) 1896121b84aSLydia Wang codec_type = VT1808; 190d7426329SHarald Welte else 191d7426329SHarald Welte codec_type = UNKNOWN; 192d7426329SHarald Welte return codec_type; 193d7426329SHarald Welte }; 194d7426329SHarald Welte 195ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 196ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 1971f2e99feSLydia Wang 198187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \ 199187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ 200187d333eSTakashi Iwai !is_aa_path_mute(codec)) 2011f2e99feSLydia Wang 202b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec) 2031f2e99feSLydia Wang { 204b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 205b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 2061f2e99feSLydia Wang return; 207187d333eSTakashi Iwai if (spec->hp_work_active) { 208b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); 209b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work); 210b3f6008fSTakashi Iwai spec->hp_work_active = false; 211b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 212187d333eSTakashi Iwai } 213187d333eSTakashi Iwai } 214187d333eSTakashi Iwai 215b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec) 216187d333eSTakashi Iwai { 217b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 218b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 219187d333eSTakashi Iwai return; 220187d333eSTakashi Iwai if (spec->vt1708_jack_detect && 221b3f6008fSTakashi Iwai (spec->gen.active_streams || hp_detect_with_aa(codec))) { 222187d333eSTakashi Iwai if (!spec->hp_work_active) { 223b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 224b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0); 225b3f6008fSTakashi Iwai queue_delayed_work(codec->bus->workq, 226b3f6008fSTakashi Iwai &codec->jackpoll_work, 0); 227b3f6008fSTakashi Iwai spec->hp_work_active = true; 228187d333eSTakashi Iwai } 229b3f6008fSTakashi Iwai } else if (!hp_detect_with_aa(codec)) 230b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 2311f2e99feSLydia Wang } 232f5271101SLydia Wang 2333e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2343e95b9abSLydia Wang { 2353e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2363e95b9abSLydia Wang if (spec->set_widgets_power_state) 2373e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2383e95b9abSLydia Wang } 23925eaba2fSLydia Wang 240054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid, 241054d867eSTakashi Iwai unsigned int parm) 242054d867eSTakashi Iwai { 2439040d102STakashi Iwai if (snd_hda_check_power_state(codec, nid, parm)) 244054d867eSTakashi Iwai return; 245054d867eSTakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 246054d867eSTakashi Iwai } 247054d867eSTakashi Iwai 24843737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, 24943737e0aSLydia Wang unsigned int parm, unsigned int index) 25043737e0aSLydia Wang { 25143737e0aSLydia Wang struct via_spec *spec = codec->spec; 25243737e0aSLydia Wang unsigned int format; 2539040d102STakashi Iwai 2549040d102STakashi Iwai if (snd_hda_check_power_state(codec, nid, parm)) 25543737e0aSLydia Wang return; 25643737e0aSLydia Wang format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 25743737e0aSLydia Wang if (format && (spec->dac_stream_tag[index] != format)) 25843737e0aSLydia Wang spec->dac_stream_tag[index] = format; 25943737e0aSLydia Wang 26043737e0aSLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 26143737e0aSLydia Wang if (parm == AC_PWRST_D0) { 26243737e0aSLydia Wang format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 26343737e0aSLydia Wang if (!format && (spec->dac_stream_tag[index] != format)) 26443737e0aSLydia Wang snd_hda_codec_write(codec, nid, 0, 26543737e0aSLydia Wang AC_VERB_SET_CHANNEL_STREAMID, 26643737e0aSLydia Wang spec->dac_stream_tag[index]); 26743737e0aSLydia Wang } 26843737e0aSLydia Wang } 26943737e0aSLydia Wang 270b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec) 271b3f6008fSTakashi Iwai { 272b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 273b3f6008fSTakashi Iwai return spec->gen.ext_channel_count > 2; 274b3f6008fSTakashi Iwai } 275b3f6008fSTakashi Iwai 276b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 277b3f6008fSTakashi Iwai { 278b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 279b3f6008fSTakashi Iwai int i; 280b3f6008fSTakashi Iwai 281b3f6008fSTakashi Iwai for (i = 0; i < spec->gen.multi_ios; i++) 282b3f6008fSTakashi Iwai if (spec->gen.multi_io[i].pin == pin) 283b3f6008fSTakashi Iwai return true; 284b3f6008fSTakashi Iwai return false; 285b3f6008fSTakashi Iwai } 286b3f6008fSTakashi Iwai 287f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 288f5271101SLydia Wang unsigned int *affected_parm) 289f5271101SLydia Wang { 290f5271101SLydia Wang unsigned parm; 291f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 292f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 293f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 294f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 2951564b287SLydia Wang struct via_spec *spec = codec->spec; 29624088a58STakashi Iwai unsigned present = 0; 29724088a58STakashi Iwai 29824088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 29924088a58STakashi Iwai if (!no_presence) 30024088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 301b3f6008fSTakashi Iwai if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) 3021564b287SLydia Wang || ((no_presence || present) 3031564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 304f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 305f5271101SLydia Wang parm = AC_PWRST_D0; 306f5271101SLydia Wang } else 307f5271101SLydia Wang parm = AC_PWRST_D3; 308f5271101SLydia Wang 309054d867eSTakashi Iwai update_power_state(codec, nid, parm); 310f5271101SLydia Wang } 311f5271101SLydia Wang 31224088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 31324088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 31424088a58STakashi Iwai { 315dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 31624088a58STakashi Iwai } 31724088a58STakashi Iwai 31824088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 31924088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 32024088a58STakashi Iwai { 32124088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 32224088a58STakashi Iwai struct via_spec *spec = codec->spec; 32324088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 32424088a58STakashi Iwai return 0; 32524088a58STakashi Iwai } 32624088a58STakashi Iwai 32724088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 32824088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 32924088a58STakashi Iwai { 33024088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33124088a58STakashi Iwai struct via_spec *spec = codec->spec; 33224088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 33324088a58STakashi Iwai 33424088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 33524088a58STakashi Iwai return 0; 33624088a58STakashi Iwai spec->no_pin_power_ctl = val; 33724088a58STakashi Iwai set_widgets_power_state(codec); 338e9d010c2STakashi Iwai analog_low_current_mode(codec); 33924088a58STakashi Iwai return 1; 34024088a58STakashi Iwai } 34124088a58STakashi Iwai 342b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { 343b3f6008fSTakashi Iwai { 34424088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 34524088a58STakashi Iwai .name = "Dynamic Power-Control", 34624088a58STakashi Iwai .info = via_pin_power_ctl_info, 34724088a58STakashi Iwai .get = via_pin_power_ctl_get, 34824088a58STakashi Iwai .put = via_pin_power_ctl_put, 349b3f6008fSTakashi Iwai }, 350b3f6008fSTakashi Iwai {} /* terminator */ 35124088a58STakashi Iwai }; 35224088a58STakashi Iwai 35324088a58STakashi Iwai 354f5271101SLydia Wang /* check AA path's mute status */ 355ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 356ada509ecSTakashi Iwai { 357ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 358ada509ecSTakashi Iwai const struct hda_amp_list *p; 359ada509ecSTakashi Iwai int i, ch, v; 360ada509ecSTakashi Iwai 361b3f6008fSTakashi Iwai for (i = 0; i < spec->gen.num_loopbacks; i++) { 362b3f6008fSTakashi Iwai p = &spec->gen.loopback_list[i]; 363ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 364ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 365ada509ecSTakashi Iwai p->idx); 366ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 367ada509ecSTakashi Iwai return false; 368f5271101SLydia Wang } 369f5271101SLydia Wang } 370ada509ecSTakashi Iwai return true; 371f5271101SLydia Wang } 372f5271101SLydia Wang 373f5271101SLydia Wang /* enter/exit analog low-current mode */ 374e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 375f5271101SLydia Wang { 376f5271101SLydia Wang struct via_spec *spec = codec->spec; 377ada509ecSTakashi Iwai bool enable; 378ada509ecSTakashi Iwai unsigned int verb, parm; 379f5271101SLydia Wang 380e9d010c2STakashi Iwai if (spec->no_pin_power_ctl) 381e9d010c2STakashi Iwai enable = false; 382e9d010c2STakashi Iwai else 383b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 384e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 385e9d010c2STakashi Iwai return; 386e9d010c2STakashi Iwai spec->alc_mode = enable; 387f5271101SLydia Wang 388f5271101SLydia Wang /* decide low current mode's verb & parameter */ 389f5271101SLydia Wang switch (spec->codec_type) { 390f5271101SLydia Wang case VT1708B_8CH: 391f5271101SLydia Wang case VT1708B_4CH: 392f5271101SLydia Wang verb = 0xf70; 393f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 394f5271101SLydia Wang break; 395f5271101SLydia Wang case VT1708S: 396eb7188caSLydia Wang case VT1718S: 397f3db423dSLydia Wang case VT1716S: 398f5271101SLydia Wang verb = 0xf73; 399f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 400f5271101SLydia Wang break; 401f5271101SLydia Wang case VT1702: 402f5271101SLydia Wang verb = 0xf73; 403f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 404f5271101SLydia Wang break; 40525eaba2fSLydia Wang case VT2002P: 406ab6734e7SLydia Wang case VT1812: 40711890956SLydia Wang case VT1802: 40825eaba2fSLydia Wang verb = 0xf93; 40925eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 41025eaba2fSLydia Wang break; 41143737e0aSLydia Wang case VT1705CF: 4126121b84aSLydia Wang case VT1808: 41343737e0aSLydia Wang verb = 0xf82; 41443737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 41543737e0aSLydia Wang break; 416f5271101SLydia Wang default: 417f5271101SLydia Wang return; /* other codecs are not supported */ 418f5271101SLydia Wang } 419f5271101SLydia Wang /* send verb */ 420f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 421f5271101SLydia Wang } 422f5271101SLydia Wang 423e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 424e9d010c2STakashi Iwai { 425e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 426e9d010c2STakashi Iwai } 427e9d010c2STakashi Iwai 428c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 429c577b8a1SJoseph Chan { 430c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 4315b0cb1d8SJaroslav Kysela int err, i; 432c577b8a1SJoseph Chan 433b3f6008fSTakashi Iwai err = snd_hda_gen_build_controls(codec); 434b3f6008fSTakashi Iwai if (err < 0) 435b3f6008fSTakashi Iwai return err; 436b3f6008fSTakashi Iwai 43724088a58STakashi Iwai if (spec->set_widgets_power_state) 438b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; 43924088a58STakashi Iwai 440c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 441c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 442c577b8a1SJoseph Chan if (err < 0) 443c577b8a1SJoseph Chan return err; 444c577b8a1SJoseph Chan } 445c577b8a1SJoseph Chan 446c577b8a1SJoseph Chan return 0; 447c577b8a1SJoseph Chan } 448c577b8a1SJoseph Chan 449b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 450b3f6008fSTakashi Iwai struct hda_codec *codec, 451b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 452b3f6008fSTakashi Iwai int action) 453c577b8a1SJoseph Chan { 454b3f6008fSTakashi Iwai analog_low_current_mode(codec); 455b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 456c577b8a1SJoseph Chan } 457c577b8a1SJoseph Chan 458c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 459c577b8a1SJoseph Chan { 460c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 461c577b8a1SJoseph Chan 462c577b8a1SJoseph Chan if (!spec) 463c577b8a1SJoseph Chan return; 464c577b8a1SJoseph Chan 465b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 466b3f6008fSTakashi Iwai snd_hda_gen_spec_free(&spec->gen); 467a86a88eaSTakashi Iwai kfree(spec); 468c577b8a1SJoseph Chan } 469c577b8a1SJoseph Chan 4702a43952aSTakashi Iwai #ifdef CONFIG_PM 47168cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 4721f2e99feSLydia Wang { 4731f2e99feSLydia Wang struct via_spec *spec = codec->spec; 474b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 47594c142a1SDavid Henningsson 47694c142a1SDavid Henningsson if (spec->codec_type == VT1802) { 47794c142a1SDavid Henningsson /* Fix pop noise on headphones */ 47894c142a1SDavid Henningsson int i; 479b3f6008fSTakashi Iwai for (i = 0; i < spec->gen.autocfg.hp_outs; i++) 480b3f6008fSTakashi Iwai snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0); 48194c142a1SDavid Henningsson } 48294c142a1SDavid Henningsson 4831f2e99feSLydia Wang return 0; 4841f2e99feSLydia Wang } 4851f2e99feSLydia Wang #endif 4861f2e99feSLydia Wang 48783012a7cSTakashi Iwai #ifdef CONFIG_PM 488cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 489cb53c626STakashi Iwai { 490cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 491b3f6008fSTakashi Iwai set_widgets_power_state(codec); 492b3f6008fSTakashi Iwai analog_low_current_mode(codec); 493b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 494b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 495cb53c626STakashi Iwai } 496cb53c626STakashi Iwai #endif 497cb53c626STakashi Iwai 498c577b8a1SJoseph Chan /* 499c577b8a1SJoseph Chan */ 5005d41762aSTakashi Iwai 5015d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 5025d41762aSTakashi Iwai 50390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 504c577b8a1SJoseph Chan .build_controls = via_build_controls, 505b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 506c577b8a1SJoseph Chan .init = via_init, 507c577b8a1SJoseph Chan .free = via_free, 5084e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 5092a43952aSTakashi Iwai #ifdef CONFIG_PM 5101f2e99feSLydia Wang .suspend = via_suspend, 511cb53c626STakashi Iwai .check_power_status = via_check_power_status, 512cb53c626STakashi Iwai #endif 513c577b8a1SJoseph Chan }; 514c577b8a1SJoseph Chan 5154a79616dSTakashi Iwai 516b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 517b3f6008fSTakashi Iwai /* power down jack detect function */ 518b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 519b3f6008fSTakashi Iwai { } 5204a79616dSTakashi Iwai }; 52176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 52276d9b0ddSHarald Welte { 52376d9b0ddSHarald Welte unsigned int def_conf; 52476d9b0ddSHarald Welte unsigned char seqassoc; 52576d9b0ddSHarald Welte 5262f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 52776d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 52876d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 52982ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 53082ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 53176d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 5322f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 53376d9b0ddSHarald Welte } 53476d9b0ddSHarald Welte 53576d9b0ddSHarald Welte return; 53676d9b0ddSHarald Welte } 53776d9b0ddSHarald Welte 538e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 5391f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5401f2e99feSLydia Wang { 5411f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5421f2e99feSLydia Wang struct via_spec *spec = codec->spec; 5431f2e99feSLydia Wang 5441f2e99feSLydia Wang if (spec->codec_type != VT1708) 5451f2e99feSLydia Wang return 0; 546e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 5471f2e99feSLydia Wang return 0; 5481f2e99feSLydia Wang } 5491f2e99feSLydia Wang 550e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 5511f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5521f2e99feSLydia Wang { 5531f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5541f2e99feSLydia Wang struct via_spec *spec = codec->spec; 555187d333eSTakashi Iwai int val; 5561f2e99feSLydia Wang 5571f2e99feSLydia Wang if (spec->codec_type != VT1708) 5581f2e99feSLydia Wang return 0; 559187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 560187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 561187d333eSTakashi Iwai return 0; 562187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 563b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 564187d333eSTakashi Iwai return 1; 5651f2e99feSLydia Wang } 5661f2e99feSLydia Wang 567b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 568b3f6008fSTakashi Iwai { 5691f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5701f2e99feSLydia Wang .name = "Jack Detect", 5711f2e99feSLydia Wang .count = 1, 5721f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 573e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 574e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 575b3f6008fSTakashi Iwai }, 576b3f6008fSTakashi Iwai {} /* terminator */ 5771f2e99feSLydia Wang }; 5781f2e99feSLydia Wang 579b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) 5804e2d16d3SDavid Henningsson { 5814e2d16d3SDavid Henningsson set_widgets_power_state(codec); 582b3f6008fSTakashi Iwai snd_hda_gen_hp_automute(codec, tbl); 583b3f6008fSTakashi Iwai } 584b3f6008fSTakashi Iwai 585b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) 586b3f6008fSTakashi Iwai { 587b3f6008fSTakashi Iwai set_widgets_power_state(codec); 588b3f6008fSTakashi Iwai snd_hda_gen_line_automute(codec, tbl); 5894e2d16d3SDavid Henningsson } 5904e2d16d3SDavid Henningsson 5914e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) 5924e2d16d3SDavid Henningsson { 5934e2d16d3SDavid Henningsson set_widgets_power_state(codec); 5944e2d16d3SDavid Henningsson } 5954e2d16d3SDavid Henningsson 596b3f6008fSTakashi Iwai #define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) 597b3f6008fSTakashi Iwai 598b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec) 5994a918ffeSTakashi Iwai { 6004a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 601b3f6008fSTakashi Iwai struct auto_pin_cfg *cfg = &spec->gen.autocfg; 602b3f6008fSTakashi Iwai hda_nid_t pin; 6034a918ffeSTakashi Iwai int i; 6044a918ffeSTakashi Iwai 605b3f6008fSTakashi Iwai spec->gen.hp_automute_hook = via_hp_automute; 6064a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 607b3f6008fSTakashi Iwai spec->gen.line_automute_hook = via_line_automute; 6084e2d16d3SDavid Henningsson 6094a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 610b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 611b3f6008fSTakashi Iwai if (pin && !snd_hda_jack_tbl_get(codec, pin) && 612b3f6008fSTakashi Iwai is_jack_detectable(codec, pin)) 613b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 6144e2d16d3SDavid Henningsson VIA_JACK_EVENT, 6154e2d16d3SDavid Henningsson via_jack_powerstate_event); 6164a918ffeSTakashi Iwai } 617b3f6008fSTakashi Iwai 618b3f6008fSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 619b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 620b3f6008fSTakashi Iwai if (pin && !snd_hda_jack_tbl_get(codec, pin) && 621b3f6008fSTakashi Iwai is_jack_detectable(codec, pin)) 622b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 623b3f6008fSTakashi Iwai VIA_JACK_EVENT, 624b3f6008fSTakashi Iwai via_jack_powerstate_event); 625b3f6008fSTakashi Iwai } 626b3f6008fSTakashi Iwai } 627b3f6008fSTakashi Iwai 628b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 629b3f6008fSTakashi Iwai { 630b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 631b3f6008fSTakashi Iwai int err; 632b3f6008fSTakashi Iwai 633b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 634b3f6008fSTakashi Iwai if (err < 0) 635b3f6008fSTakashi Iwai return err; 636b3f6008fSTakashi Iwai 637b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 638b3f6008fSTakashi Iwai if (err < 0) 639b3f6008fSTakashi Iwai return err; 640b3f6008fSTakashi Iwai 641b3f6008fSTakashi Iwai via_set_jack_unsol_events(codec); 642b3f6008fSTakashi Iwai return 0; 6434a918ffeSTakashi Iwai } 6444a918ffeSTakashi Iwai 6455d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 6465d41762aSTakashi Iwai { 6475d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 6485d41762aSTakashi Iwai int i; 6495d41762aSTakashi Iwai 6505d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 6515d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 6525d41762aSTakashi Iwai 653e9d010c2STakashi Iwai /* init power states */ 654e9d010c2STakashi Iwai set_widgets_power_state(codec); 655e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 656e9d010c2STakashi Iwai 657b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 65811890956SLydia Wang 659b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 66025eaba2fSLydia Wang 661c577b8a1SJoseph Chan return 0; 662c577b8a1SJoseph Chan } 663c577b8a1SJoseph Chan 664*f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 665*f672f65aSDavid Henningsson { 666*f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 667*f672f65aSDavid Henningsson temporary enable jackpoll */ 668*f672f65aSDavid Henningsson int err; 669*f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 670*f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 671*f672f65aSDavid Henningsson err = via_build_controls(codec); 672*f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 673*f672f65aSDavid Henningsson return err; 674*f672f65aSDavid Henningsson } 675*f672f65aSDavid Henningsson 676b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 677337b9d02STakashi Iwai { 678337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 679b3f6008fSTakashi Iwai int i, err; 680337b9d02STakashi Iwai 681b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 682b3f6008fSTakashi Iwai if (err < 0 || codec->vendor_id != 0x11061708) 683b3f6008fSTakashi Iwai return err; 684b3f6008fSTakashi Iwai 685b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 686b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 687b3f6008fSTakashi Iwai * disable the 24bit format, so far. 688b3f6008fSTakashi Iwai */ 689b3f6008fSTakashi Iwai for (i = 0; i < codec->num_pcms; i++) { 690b3f6008fSTakashi Iwai struct hda_pcm *info = &spec->gen.pcm_rec[i]; 691b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 692b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 693b3f6008fSTakashi Iwai continue; 694b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 695b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 696337b9d02STakashi Iwai } 697b3f6008fSTakashi Iwai 6981c55d521STakashi Iwai return 0; 699337b9d02STakashi Iwai } 700337b9d02STakashi Iwai 701c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 702c577b8a1SJoseph Chan { 703c577b8a1SJoseph Chan struct via_spec *spec; 704c577b8a1SJoseph Chan int err; 705c577b8a1SJoseph Chan 706c577b8a1SJoseph Chan /* create a codec specific record */ 7075b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 708c577b8a1SJoseph Chan if (spec == NULL) 709c577b8a1SJoseph Chan return -ENOMEM; 710c577b8a1SJoseph Chan 711b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 712b3f6008fSTakashi Iwai 713b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 714b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 715b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 716b3f6008fSTakashi Iwai 717b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 718b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 719b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 720620e2b28STakashi Iwai 72112daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 72212daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 72312daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 72412daef65STakashi Iwai 725c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 72612daef65STakashi Iwai err = via_parse_auto_config(codec); 727c577b8a1SJoseph Chan if (err < 0) { 728c577b8a1SJoseph Chan via_free(codec); 729c577b8a1SJoseph Chan return err; 730c577b8a1SJoseph Chan } 731c577b8a1SJoseph Chan 73212daef65STakashi Iwai /* add jack detect on/off control */ 733b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 734c577b8a1SJoseph Chan 735e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 736e322a36dSLydia Wang 737c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 738*f672f65aSDavid Henningsson codec->patch_ops.build_controls = vt1708_build_controls; 739b3f6008fSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 740c577b8a1SJoseph Chan 741b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 742b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 743b3f6008fSTakashi Iwai 744c577b8a1SJoseph Chan return 0; 745c577b8a1SJoseph Chan } 746c577b8a1SJoseph Chan 747ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 748c577b8a1SJoseph Chan { 749c577b8a1SJoseph Chan struct via_spec *spec; 750c577b8a1SJoseph Chan int err; 751c577b8a1SJoseph Chan 752c577b8a1SJoseph Chan /* create a codec specific record */ 7535b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 754c577b8a1SJoseph Chan if (spec == NULL) 755c577b8a1SJoseph Chan return -ENOMEM; 756c577b8a1SJoseph Chan 757b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 758620e2b28STakashi Iwai 75912daef65STakashi Iwai err = via_parse_auto_config(codec); 760c577b8a1SJoseph Chan if (err < 0) { 761c577b8a1SJoseph Chan via_free(codec); 762c577b8a1SJoseph Chan return err; 763c577b8a1SJoseph Chan } 764c577b8a1SJoseph Chan 765c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 766c577b8a1SJoseph Chan 767f7278fd0SJosepch Chan return 0; 768f7278fd0SJosepch Chan } 769f7278fd0SJosepch Chan 7703e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 7713e95b9abSLydia Wang { 7723e95b9abSLydia Wang struct via_spec *spec = codec->spec; 7733e95b9abSLydia Wang int imux_is_smixer; 7743e95b9abSLydia Wang unsigned int parm; 7753e95b9abSLydia Wang int is_8ch = 0; 776bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 777bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 7783e95b9abSLydia Wang is_8ch = 1; 7793e95b9abSLydia Wang 7803e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 7813e95b9abSLydia Wang imux_is_smixer = 7823e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 7833e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 7843e95b9abSLydia Wang /* inputs */ 7853e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 7863e95b9abSLydia Wang parm = AC_PWRST_D3; 7873e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 7883e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 7893e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 7903e95b9abSLydia Wang if (imux_is_smixer) 7913e95b9abSLydia Wang parm = AC_PWRST_D0; 7923e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 793054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 794054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 795054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 7963e95b9abSLydia Wang 7973e95b9abSLydia Wang /* outputs */ 7983e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 7993e95b9abSLydia Wang parm = AC_PWRST_D3; 8003e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 801b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8023e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 803054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 804054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 8053e95b9abSLydia Wang 8063e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 8073e95b9abSLydia Wang if (is_8ch) { 8083e95b9abSLydia Wang parm = AC_PWRST_D3; 8093e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 810b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8113e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 812054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 813054d867eSTakashi Iwai update_power_state(codec, 0x24, parm); 814bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 815bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 816bc92df7fSLydia Wang parm = AC_PWRST_D3; 817bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 818b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 819bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 820054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 821054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8223e95b9abSLydia Wang } 8233e95b9abSLydia Wang 8243e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 8253e95b9abSLydia Wang parm = AC_PWRST_D3; 8263e95b9abSLydia Wang /* force to D0 for internal Speaker */ 8273e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 8283e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 8293e95b9abSLydia Wang if (is_8ch) 8303e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 8313e95b9abSLydia Wang 8323e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 833054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 834054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 8353e95b9abSLydia Wang if (is_8ch) { 836054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 837054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 838b3f6008fSTakashi Iwai } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) 839054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8403e95b9abSLydia Wang } 8413e95b9abSLydia Wang 842518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 843ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 844f7278fd0SJosepch Chan { 845f7278fd0SJosepch Chan struct via_spec *spec; 846f7278fd0SJosepch Chan int err; 847f7278fd0SJosepch Chan 848518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 849518bf3baSLydia Wang return patch_vt1708S(codec); 850ddd304d8STakashi Iwai 851f7278fd0SJosepch Chan /* create a codec specific record */ 8525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 853f7278fd0SJosepch Chan if (spec == NULL) 854f7278fd0SJosepch Chan return -ENOMEM; 855f7278fd0SJosepch Chan 856b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 857620e2b28STakashi Iwai 858f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 85912daef65STakashi Iwai err = via_parse_auto_config(codec); 860f7278fd0SJosepch Chan if (err < 0) { 861f7278fd0SJosepch Chan via_free(codec); 862f7278fd0SJosepch Chan return err; 863f7278fd0SJosepch Chan } 864f7278fd0SJosepch Chan 865f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 866f7278fd0SJosepch Chan 8673e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 8683e95b9abSLydia Wang 869f7278fd0SJosepch Chan return 0; 870f7278fd0SJosepch Chan } 871f7278fd0SJosepch Chan 872d949cac1SHarald Welte /* Patch for VT1708S */ 873096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 874d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 875d7426329SHarald Welte {0x1, 0xf98, 0x1}, 876bc7e7e5cSLydia Wang /* don't bybass mixer */ 877bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 878d949cac1SHarald Welte { } 879d949cac1SHarald Welte }; 880d949cac1SHarald Welte 8816369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 8826369bcfcSLydia Wang int offset, int num_steps, int step_size) 8836369bcfcSLydia Wang { 8846369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 8856369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 8866369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 8876369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 8886369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 8896369bcfcSLydia Wang } 8906369bcfcSLydia Wang 891d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 892d949cac1SHarald Welte { 893d949cac1SHarald Welte struct via_spec *spec; 894d949cac1SHarald Welte int err; 895d949cac1SHarald Welte 896d949cac1SHarald Welte /* create a codec specific record */ 8975b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 898d949cac1SHarald Welte if (spec == NULL) 899d949cac1SHarald Welte return -ENOMEM; 900d949cac1SHarald Welte 901b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 902d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 903d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 904620e2b28STakashi Iwai 905518bf3baSLydia Wang /* correct names for VT1708BCE */ 906518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 907518bf3baSLydia Wang kfree(codec->chip_name); 908518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 909518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 910518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 911518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 912970f630fSLydia Wang } 913bc92df7fSLydia Wang /* correct names for VT1705 */ 914bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 915bc92df7fSLydia Wang kfree(codec->chip_name); 916bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 917bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 918bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 919bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 920bc92df7fSLydia Wang } 921b3f6008fSTakashi Iwai 922b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 923b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 924b3f6008fSTakashi Iwai if (err < 0) { 925b3f6008fSTakashi Iwai via_free(codec); 926b3f6008fSTakashi Iwai return err; 927b3f6008fSTakashi Iwai } 928b3f6008fSTakashi Iwai 929b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 930b3f6008fSTakashi Iwai 931b3f6008fSTakashi Iwai codec->patch_ops = via_patch_ops; 932b3f6008fSTakashi Iwai 9333e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 934d949cac1SHarald Welte return 0; 935d949cac1SHarald Welte } 936d949cac1SHarald Welte 937d949cac1SHarald Welte /* Patch for VT1702 */ 938d949cac1SHarald Welte 939096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 940bc7e7e5cSLydia Wang /* mixer enable */ 941bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 942bc7e7e5cSLydia Wang /* GPIO 0~2 */ 943bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 944d949cac1SHarald Welte { } 945d949cac1SHarald Welte }; 946d949cac1SHarald Welte 9473e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 9483e95b9abSLydia Wang { 9493e95b9abSLydia Wang int imux_is_smixer = 9503e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 9513e95b9abSLydia Wang unsigned int parm; 9523e95b9abSLydia Wang /* inputs */ 9533e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 9543e95b9abSLydia Wang parm = AC_PWRST_D3; 9553e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 9563e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 9573e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 9583e95b9abSLydia Wang if (imux_is_smixer) 9593e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 9603e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 961054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 962054d867eSTakashi Iwai update_power_state(codec, 0x12, parm); 963054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 964054d867eSTakashi Iwai update_power_state(codec, 0x20, parm); 9653e95b9abSLydia Wang 9663e95b9abSLydia Wang /* outputs */ 9673e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 9683e95b9abSLydia Wang parm = AC_PWRST_D3; 9693e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 9703e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 9713e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 972054d867eSTakashi Iwai update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); 973054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 974054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 9753e95b9abSLydia Wang } 9763e95b9abSLydia Wang 977d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 978d949cac1SHarald Welte { 979d949cac1SHarald Welte struct via_spec *spec; 980d949cac1SHarald Welte int err; 981d949cac1SHarald Welte 982d949cac1SHarald Welte /* create a codec specific record */ 9835b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 984d949cac1SHarald Welte if (spec == NULL) 985d949cac1SHarald Welte return -ENOMEM; 986d949cac1SHarald Welte 987b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 988620e2b28STakashi Iwai 98912daef65STakashi Iwai /* limit AA path volume to 0 dB */ 99012daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 99112daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 99212daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 99312daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 99412daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 99512daef65STakashi Iwai 996d949cac1SHarald Welte /* automatic parse from the BIOS config */ 99712daef65STakashi Iwai err = via_parse_auto_config(codec); 998d949cac1SHarald Welte if (err < 0) { 999d949cac1SHarald Welte via_free(codec); 1000d949cac1SHarald Welte return err; 1001d949cac1SHarald Welte } 1002d949cac1SHarald Welte 1003096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 1004d949cac1SHarald Welte 1005d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 1006d949cac1SHarald Welte 10073e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 1008d949cac1SHarald Welte return 0; 1009d949cac1SHarald Welte } 1010d949cac1SHarald Welte 1011eb7188caSLydia Wang /* Patch for VT1718S */ 1012eb7188caSLydia Wang 1013096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 10144ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 10154ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 1016eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 1017eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 10185d41762aSTakashi Iwai 1019eb7188caSLydia Wang { } 1020eb7188caSLydia Wang }; 1021eb7188caSLydia Wang 10223e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 10233e95b9abSLydia Wang { 10243e95b9abSLydia Wang struct via_spec *spec = codec->spec; 10253e95b9abSLydia Wang int imux_is_smixer; 10266162552bSTakashi Iwai unsigned int parm, parm2; 10273e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 10283e95b9abSLydia Wang imux_is_smixer = 10293e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 10303e95b9abSLydia Wang /* inputs */ 10313e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 10323e95b9abSLydia Wang parm = AC_PWRST_D3; 10333e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 10343e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 10353e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 10363e95b9abSLydia Wang if (imux_is_smixer) 10373e95b9abSLydia Wang parm = AC_PWRST_D0; 10383e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 1039054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1040054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1041054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1042054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 10433e95b9abSLydia Wang 10443e95b9abSLydia Wang /* outputs */ 10453e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 10463e95b9abSLydia Wang parm = AC_PWRST_D3; 10473e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 1048054d867eSTakashi Iwai update_power_state(codec, 0x1a, parm); 10496162552bSTakashi Iwai parm2 = parm; /* for pin 0x0b */ 10503e95b9abSLydia Wang 10513e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 10523e95b9abSLydia Wang parm = AC_PWRST_D3; 10533e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1054b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 10553e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 1056054d867eSTakashi Iwai update_power_state(codec, 0xa, parm); 10573e95b9abSLydia Wang 10583e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 10593e95b9abSLydia Wang parm = AC_PWRST_D3; 10603e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 1061b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ 10623e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1063054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 1064b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 10656162552bSTakashi Iwai parm = parm2; 10666162552bSTakashi Iwai update_power_state(codec, 0xb, parm); 10673e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 1068054d867eSTakashi Iwai update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); 10693e95b9abSLydia Wang 10703e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 10713e95b9abSLydia Wang parm = AC_PWRST_D3; 10723e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1073b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 10743e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 1075054d867eSTakashi Iwai update_power_state(codec, 0x9, parm); 10763e95b9abSLydia Wang 1077b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) { 10783e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 10793e95b9abSLydia Wang parm = AC_PWRST_D3; 10803e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1081054d867eSTakashi Iwai update_power_state(codec, 0x1b, parm); 1082054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 1083054d867eSTakashi Iwai update_power_state(codec, 0xc, parm); 10843e95b9abSLydia Wang } 10853e95b9abSLydia Wang } 10863e95b9abSLydia Wang 108730b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 108830b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 108930b45033STakashi Iwai */ 109030b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 109130b45033STakashi Iwai { 109230b45033STakashi Iwai struct via_spec *spec = codec->spec; 109330b45033STakashi Iwai int i, nums; 109430b45033STakashi Iwai hda_nid_t conn[8]; 109530b45033STakashi Iwai hda_nid_t nid; 109630b45033STakashi Iwai 1097b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 109830b45033STakashi Iwai return 0; 1099b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 110030b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 110130b45033STakashi Iwai for (i = 0; i < nums; i++) { 110230b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 110330b45033STakashi Iwai return 0; 110430b45033STakashi Iwai } 110530b45033STakashi Iwai 110630b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 110730b45033STakashi Iwai nid = codec->start_nid; 110830b45033STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 110930b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 111030b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 111130b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 111230b45033STakashi Iwai conn[nums++] = nid; 111330b45033STakashi Iwai return snd_hda_override_conn_list(codec, 1114b3f6008fSTakashi Iwai spec->gen.mixer_nid, 111530b45033STakashi Iwai nums, conn); 111630b45033STakashi Iwai } 111730b45033STakashi Iwai } 111830b45033STakashi Iwai return 0; 111930b45033STakashi Iwai } 112030b45033STakashi Iwai 112130b45033STakashi Iwai 1122eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 1123eb7188caSLydia Wang { 1124eb7188caSLydia Wang struct via_spec *spec; 1125eb7188caSLydia Wang int err; 1126eb7188caSLydia Wang 1127eb7188caSLydia Wang /* create a codec specific record */ 11285b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1129eb7188caSLydia Wang if (spec == NULL) 1130eb7188caSLydia Wang return -ENOMEM; 1131eb7188caSLydia Wang 1132b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1133d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1134d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 113530b45033STakashi Iwai add_secret_dac_path(codec); 1136620e2b28STakashi Iwai 1137eb7188caSLydia Wang /* automatic parse from the BIOS config */ 113812daef65STakashi Iwai err = via_parse_auto_config(codec); 1139eb7188caSLydia Wang if (err < 0) { 1140eb7188caSLydia Wang via_free(codec); 1141eb7188caSLydia Wang return err; 1142eb7188caSLydia Wang } 1143eb7188caSLydia Wang 1144096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 1145eb7188caSLydia Wang 1146eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 1147eb7188caSLydia Wang 11483e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 11493e95b9abSLydia Wang 1150eb7188caSLydia Wang return 0; 1151eb7188caSLydia Wang } 1152f3db423dSLydia Wang 1153f3db423dSLydia Wang /* Patch for VT1716S */ 1154f3db423dSLydia Wang 1155f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 1156f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 1157f3db423dSLydia Wang { 1158f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1159f3db423dSLydia Wang uinfo->count = 1; 1160f3db423dSLydia Wang uinfo->value.integer.min = 0; 1161f3db423dSLydia Wang uinfo->value.integer.max = 1; 1162f3db423dSLydia Wang return 0; 1163f3db423dSLydia Wang } 1164f3db423dSLydia Wang 1165f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 1166f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1167f3db423dSLydia Wang { 1168f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1169f3db423dSLydia Wang int index = 0; 1170f3db423dSLydia Wang 1171f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 1172f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 1173f3db423dSLydia Wang if (index != -1) 1174f3db423dSLydia Wang *ucontrol->value.integer.value = index; 1175f3db423dSLydia Wang 1176f3db423dSLydia Wang return 0; 1177f3db423dSLydia Wang } 1178f3db423dSLydia Wang 1179f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 1180f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1181f3db423dSLydia Wang { 1182f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1183f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1184f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 1185f3db423dSLydia Wang 1186f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 1187f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 1188f3db423dSLydia Wang spec->dmic_enabled = index; 11893e95b9abSLydia Wang set_widgets_power_state(codec); 1190f3db423dSLydia Wang return 1; 1191f3db423dSLydia Wang } 1192f3db423dSLydia Wang 119390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 1194f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 1195f3db423dSLydia Wang { 1196f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1197f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 11985b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 1199f3db423dSLydia Wang .count = 1, 1200f3db423dSLydia Wang .info = vt1716s_dmic_info, 1201f3db423dSLydia Wang .get = vt1716s_dmic_get, 1202f3db423dSLydia Wang .put = vt1716s_dmic_put, 1203f3db423dSLydia Wang }, 1204f3db423dSLydia Wang {} /* end */ 1205f3db423dSLydia Wang }; 1206f3db423dSLydia Wang 1207f3db423dSLydia Wang 1208f3db423dSLydia Wang /* mono-out mixer elements */ 120990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 1210f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 1211f3db423dSLydia Wang { } /* end */ 1212f3db423dSLydia Wang }; 1213f3db423dSLydia Wang 1214096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 1215f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 1216f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 1217f3db423dSLydia Wang /* don't bybass mixer */ 1218f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 1219f3db423dSLydia Wang /* Enable mono output */ 1220f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 1221f3db423dSLydia Wang { } 1222f3db423dSLydia Wang }; 1223f3db423dSLydia Wang 12243e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 12253e95b9abSLydia Wang { 12263e95b9abSLydia Wang struct via_spec *spec = codec->spec; 12273e95b9abSLydia Wang int imux_is_smixer; 12283e95b9abSLydia Wang unsigned int parm; 12293e95b9abSLydia Wang unsigned int mono_out, present; 12303e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 12313e95b9abSLydia Wang imux_is_smixer = 12323e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 12333e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 12343e95b9abSLydia Wang /* inputs */ 12353e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 12363e95b9abSLydia Wang parm = AC_PWRST_D3; 12373e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 12383e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 12393e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12403e95b9abSLydia Wang if (imux_is_smixer) 12413e95b9abSLydia Wang parm = AC_PWRST_D0; 12423e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 1243054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1244054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 12453e95b9abSLydia Wang 12463e95b9abSLydia Wang parm = AC_PWRST_D3; 12473e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12483e95b9abSLydia Wang /* PW11 (22h) */ 12493e95b9abSLydia Wang if (spec->dmic_enabled) 12503e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 12513e95b9abSLydia Wang else 1252054d867eSTakashi Iwai update_power_state(codec, 0x22, AC_PWRST_D3); 12533e95b9abSLydia Wang 12543e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 1255054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 1256054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 12573e95b9abSLydia Wang 12583e95b9abSLydia Wang /* outputs */ 12593e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 12603e95b9abSLydia Wang parm = AC_PWRST_D3; 12613e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 12623e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 1263b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12643e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 1265054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1266054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 12673e95b9abSLydia Wang 12683e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 12693e95b9abSLydia Wang parm = AC_PWRST_D3; 12703e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 12713e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 1272b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12733e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 1274054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 12753e95b9abSLydia Wang 12763e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 1277b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12783e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 1279054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 12803e95b9abSLydia Wang 12813e95b9abSLydia Wang /* Mono out */ 12823e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 12833e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 12843e95b9abSLydia Wang 12853e95b9abSLydia Wang if (present) 12863e95b9abSLydia Wang mono_out = 0; 12873e95b9abSLydia Wang else { 12883e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 1289b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && present) 12903e95b9abSLydia Wang mono_out = 0; 12913e95b9abSLydia Wang else 12923e95b9abSLydia Wang mono_out = 1; 12933e95b9abSLydia Wang } 12943e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 1295054d867eSTakashi Iwai update_power_state(codec, 0x28, parm); 1296054d867eSTakashi Iwai update_power_state(codec, 0x29, parm); 1297054d867eSTakashi Iwai update_power_state(codec, 0x2a, parm); 12983e95b9abSLydia Wang 12993e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 13003e95b9abSLydia Wang parm = AC_PWRST_D3; 13013e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 13023e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 13033e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 1304b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1305054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 13063e95b9abSLydia Wang 13073e95b9abSLydia Wang /* force to D0 for internal Speaker */ 13083e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 1309054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 1310054d867eSTakashi Iwai update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); 13113e95b9abSLydia Wang } 13123e95b9abSLydia Wang 1313f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 1314f3db423dSLydia Wang { 1315f3db423dSLydia Wang struct via_spec *spec; 1316f3db423dSLydia Wang int err; 1317f3db423dSLydia Wang 1318f3db423dSLydia Wang /* create a codec specific record */ 13195b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1320f3db423dSLydia Wang if (spec == NULL) 1321f3db423dSLydia Wang return -ENOMEM; 1322f3db423dSLydia Wang 1323b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 1324d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 1325d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 1326620e2b28STakashi Iwai 1327f3db423dSLydia Wang /* automatic parse from the BIOS config */ 132812daef65STakashi Iwai err = via_parse_auto_config(codec); 1329f3db423dSLydia Wang if (err < 0) { 1330f3db423dSLydia Wang via_free(codec); 1331f3db423dSLydia Wang return err; 1332f3db423dSLydia Wang } 1333f3db423dSLydia Wang 1334096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 1335f3db423dSLydia Wang 1336b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 1337f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 1338f3db423dSLydia Wang 1339f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 1340f3db423dSLydia Wang 13413e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 1342f3db423dSLydia Wang return 0; 1343f3db423dSLydia Wang } 134425eaba2fSLydia Wang 134525eaba2fSLydia Wang /* for vt2002P */ 134625eaba2fSLydia Wang 1347096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 1348eadb9a80SLydia Wang /* Class-D speaker related verbs */ 1349eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 1350eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 1351eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 135225eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 135325eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 135425eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 135525eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 135625eaba2fSLydia Wang { } 135725eaba2fSLydia Wang }; 13584a918ffeSTakashi Iwai 1359096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 136011890956SLydia Wang /* Enable Boost Volume backdoor */ 136111890956SLydia Wang {0x1, 0xfb9, 0x24}, 136211890956SLydia Wang /* Enable AOW0 to MW9 */ 136311890956SLydia Wang {0x1, 0xfb8, 0x88}, 136411890956SLydia Wang { } 136511890956SLydia Wang }; 136625eaba2fSLydia Wang 13673e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 13683e95b9abSLydia Wang { 13693e95b9abSLydia Wang struct via_spec *spec = codec->spec; 13703e95b9abSLydia Wang int imux_is_smixer; 13713e95b9abSLydia Wang unsigned int parm; 13723e95b9abSLydia Wang unsigned int present; 13733e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 13743e95b9abSLydia Wang imux_is_smixer = 13753e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 13763e95b9abSLydia Wang /* inputs */ 13773e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 13783e95b9abSLydia Wang parm = AC_PWRST_D3; 13793e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 13803e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 13813e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 13823e95b9abSLydia Wang parm = AC_PWRST_D0; 13833e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 1384054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1385054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1386054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1387054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 13883e95b9abSLydia Wang 13893e95b9abSLydia Wang /* outputs */ 13903e95b9abSLydia Wang /* AOW0 (8h)*/ 1391054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 13923e95b9abSLydia Wang 139311890956SLydia Wang if (spec->codec_type == VT1802) { 139411890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 139511890956SLydia Wang parm = AC_PWRST_D3; 139611890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 1397054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1398054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 139911890956SLydia Wang } else { 14003e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 14013e95b9abSLydia Wang parm = AC_PWRST_D3; 14023e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1403054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1404054d867eSTakashi Iwai update_power_state(codec, 0x37, parm); 140511890956SLydia Wang } 14063e95b9abSLydia Wang 140711890956SLydia Wang if (spec->codec_type == VT1802) { 140811890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 140911890956SLydia Wang parm = AC_PWRST_D3; 141011890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 1411054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1412054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 141311890956SLydia Wang } else { 14143e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 14153e95b9abSLydia Wang parm = AC_PWRST_D3; 14163e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1417054d867eSTakashi Iwai update_power_state(codec, 0x19, parm); 1418054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 141911890956SLydia Wang } 14203e95b9abSLydia Wang 1421b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1422054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 14233e95b9abSLydia Wang 14243e95b9abSLydia Wang /* Class-D */ 14253e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 14263e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 14273e95b9abSLydia Wang 14283e95b9abSLydia Wang parm = AC_PWRST_D3; 14293e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 14303e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 143111890956SLydia Wang if (spec->codec_type == VT1802) 1432054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 143311890956SLydia Wang else 1434054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1435054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 14363e95b9abSLydia Wang 14373e95b9abSLydia Wang /* Mono Out */ 14383e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 14393e95b9abSLydia Wang 14403e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 144111890956SLydia Wang if (spec->codec_type == VT1802) { 144211890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 1443054d867eSTakashi Iwai update_power_state(codec, 0x33, parm); 1444054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1445054d867eSTakashi Iwai update_power_state(codec, 0x3c, parm); 144611890956SLydia Wang } else { 14473e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 1448054d867eSTakashi Iwai update_power_state(codec, 0x31, parm); 1449054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1450054d867eSTakashi Iwai update_power_state(codec, 0x3b, parm); 145111890956SLydia Wang } 14523e95b9abSLydia Wang /* MW9 (21h) */ 14533e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1454054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D0); 14553e95b9abSLydia Wang else 1456054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D3); 14573e95b9abSLydia Wang } 145825eaba2fSLydia Wang 14594b527b65SDavid Henningsson /* 14604b527b65SDavid Henningsson * pin fix-up 14614b527b65SDavid Henningsson */ 14624b527b65SDavid Henningsson enum { 14634b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1464d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 14654b527b65SDavid Henningsson }; 14664b527b65SDavid Henningsson 14674b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 14684b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 14694b527b65SDavid Henningsson { 14704b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 14714b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 14724b527b65SDavid Henningsson } 14734b527b65SDavid Henningsson 14744b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 14754b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 14764b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 14774b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 14784b527b65SDavid Henningsson }, 1479d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1480d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1481d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1482d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1483d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1484d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1485d5266125STakashi Iwai { } 1486d5266125STakashi Iwai } 1487d5266125STakashi Iwai }, 14884b527b65SDavid Henningsson }; 14894b527b65SDavid Henningsson 14904b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1491d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 14924b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 14934b527b65SDavid Henningsson {} 14944b527b65SDavid Henningsson }; 14954b527b65SDavid Henningsson 1496ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1497ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1498ef4da458STakashi Iwai */ 1499ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1500ef4da458STakashi Iwai { 1501ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1502ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1503ef4da458STakashi Iwai 1504ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1505ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1506ef4da458STakashi Iwai } 1507ef4da458STakashi Iwai 150825eaba2fSLydia Wang /* patch for vt2002P */ 150925eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 151025eaba2fSLydia Wang { 151125eaba2fSLydia Wang struct via_spec *spec; 151225eaba2fSLydia Wang int err; 151325eaba2fSLydia Wang 151425eaba2fSLydia Wang /* create a codec specific record */ 15155b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 151625eaba2fSLydia Wang if (spec == NULL) 151725eaba2fSLydia Wang return -ENOMEM; 151825eaba2fSLydia Wang 1519b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1520d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1521d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1522ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1523ef4da458STakashi Iwai fix_vt1802_connections(codec); 152430b45033STakashi Iwai add_secret_dac_path(codec); 1525620e2b28STakashi Iwai 15264b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 15274b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 15284b527b65SDavid Henningsson 152925eaba2fSLydia Wang /* automatic parse from the BIOS config */ 153012daef65STakashi Iwai err = via_parse_auto_config(codec); 153125eaba2fSLydia Wang if (err < 0) { 153225eaba2fSLydia Wang via_free(codec); 153325eaba2fSLydia Wang return err; 153425eaba2fSLydia Wang } 153525eaba2fSLydia Wang 153611890956SLydia Wang if (spec->codec_type == VT1802) 15374a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 153811890956SLydia Wang else 15394a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 154011890956SLydia Wang 154125eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 154225eaba2fSLydia Wang 15433e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 154425eaba2fSLydia Wang return 0; 154525eaba2fSLydia Wang } 1546ab6734e7SLydia Wang 1547ab6734e7SLydia Wang /* for vt1812 */ 1548ab6734e7SLydia Wang 1549096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1550ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1551ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1552ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1553ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1554ab6734e7SLydia Wang { } 1555ab6734e7SLydia Wang }; 1556ab6734e7SLydia Wang 15573e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 15583e95b9abSLydia Wang { 15593e95b9abSLydia Wang struct via_spec *spec = codec->spec; 15603e95b9abSLydia Wang unsigned int parm; 15613e95b9abSLydia Wang unsigned int present; 15623e95b9abSLydia Wang /* inputs */ 15633e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 15643e95b9abSLydia Wang parm = AC_PWRST_D3; 15653e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 15663e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 15673e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 15683e95b9abSLydia Wang parm = AC_PWRST_D0; 15693e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 1570054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1571054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1572054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1573054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 15743e95b9abSLydia Wang 15753e95b9abSLydia Wang /* outputs */ 15763e95b9abSLydia Wang /* AOW0 (8h)*/ 1577054d867eSTakashi Iwai update_power_state(codec, 0x8, AC_PWRST_D0); 15783e95b9abSLydia Wang 15793e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 15803e95b9abSLydia Wang parm = AC_PWRST_D3; 15813e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1582054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1583054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 15843e95b9abSLydia Wang 15853e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 15863e95b9abSLydia Wang parm = AC_PWRST_D3; 15873e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1588054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1589054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 1590b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1591054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 15923e95b9abSLydia Wang 15933e95b9abSLydia Wang /* Internal Speaker */ 15943e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 15953e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 15963e95b9abSLydia Wang 15973e95b9abSLydia Wang parm = AC_PWRST_D3; 15983e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 15993e95b9abSLydia Wang if (present) { 1600054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D3); 1601054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D3); 16023e95b9abSLydia Wang } else { 1603054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D0); 1604054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D0); 16053e95b9abSLydia Wang } 16063e95b9abSLydia Wang 16073e95b9abSLydia Wang 16083e95b9abSLydia Wang /* Mono Out */ 16093e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 16103e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 16113e95b9abSLydia Wang 16123e95b9abSLydia Wang parm = AC_PWRST_D3; 16133e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 16143e95b9abSLydia Wang if (present) { 1615054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D3); 1616054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D3); 1617054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D3); 16183e95b9abSLydia Wang } else { 1619054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D0); 1620054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D0); 1621054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D0); 16223e95b9abSLydia Wang } 16233e95b9abSLydia Wang 16243e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 16253e95b9abSLydia Wang parm = AC_PWRST_D3; 16263e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 1627054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 1628054d867eSTakashi Iwai update_power_state(codec, 0x3d, parm); 16293e95b9abSLydia Wang 16303e95b9abSLydia Wang } 1631ab6734e7SLydia Wang 1632ab6734e7SLydia Wang /* patch for vt1812 */ 1633ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1634ab6734e7SLydia Wang { 1635ab6734e7SLydia Wang struct via_spec *spec; 1636ab6734e7SLydia Wang int err; 1637ab6734e7SLydia Wang 1638ab6734e7SLydia Wang /* create a codec specific record */ 16395b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1640ab6734e7SLydia Wang if (spec == NULL) 1641ab6734e7SLydia Wang return -ENOMEM; 1642ab6734e7SLydia Wang 1643b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1644d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1645d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 164630b45033STakashi Iwai add_secret_dac_path(codec); 1647620e2b28STakashi Iwai 1648ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 164912daef65STakashi Iwai err = via_parse_auto_config(codec); 1650ab6734e7SLydia Wang if (err < 0) { 1651ab6734e7SLydia Wang via_free(codec); 1652ab6734e7SLydia Wang return err; 1653ab6734e7SLydia Wang } 1654ab6734e7SLydia Wang 1655096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1656ab6734e7SLydia Wang 1657ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 1658ab6734e7SLydia Wang 16593e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 1660ab6734e7SLydia Wang return 0; 1661ab6734e7SLydia Wang } 1662ab6734e7SLydia Wang 166343737e0aSLydia Wang /* patch for vt3476 */ 166443737e0aSLydia Wang 166543737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 166643737e0aSLydia Wang /* Enable DMic 8/16/32K */ 166743737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 166843737e0aSLydia Wang /* Enable Boost Volume backdoor */ 166943737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 167043737e0aSLydia Wang /* Enable AOW-MW9 path */ 167143737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 167243737e0aSLydia Wang { } 167343737e0aSLydia Wang }; 167443737e0aSLydia Wang 167543737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec) 167643737e0aSLydia Wang { 167743737e0aSLydia Wang struct via_spec *spec = codec->spec; 167843737e0aSLydia Wang int imux_is_smixer; 167943737e0aSLydia Wang unsigned int parm, parm2; 168043737e0aSLydia Wang /* MUX10 (1eh) = stereo mixer */ 168143737e0aSLydia Wang imux_is_smixer = 168243737e0aSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; 168343737e0aSLydia Wang /* inputs */ 168443737e0aSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 168543737e0aSLydia Wang parm = AC_PWRST_D3; 168643737e0aSLydia Wang set_pin_power_state(codec, 0x29, &parm); 168743737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 168843737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 168943737e0aSLydia Wang if (imux_is_smixer) 169043737e0aSLydia Wang parm = AC_PWRST_D0; 169143737e0aSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 169243737e0aSLydia Wang update_power_state(codec, 0x1e, parm); 169343737e0aSLydia Wang update_power_state(codec, 0x1f, parm); 169443737e0aSLydia Wang update_power_state(codec, 0x10, parm); 169543737e0aSLydia Wang update_power_state(codec, 0x11, parm); 169643737e0aSLydia Wang 169743737e0aSLydia Wang /* outputs */ 169843737e0aSLydia Wang /* PW3 (27h), MW3(37h), AOW3 (bh) */ 169943737e0aSLydia Wang if (spec->codec_type == VT1705CF) { 170043737e0aSLydia Wang parm = AC_PWRST_D3; 170143737e0aSLydia Wang update_power_state(codec, 0x27, parm); 170243737e0aSLydia Wang update_power_state(codec, 0x37, parm); 170343737e0aSLydia Wang } else { 170443737e0aSLydia Wang parm = AC_PWRST_D3; 170543737e0aSLydia Wang set_pin_power_state(codec, 0x27, &parm); 170643737e0aSLydia Wang update_power_state(codec, 0x37, parm); 170743737e0aSLydia Wang } 170843737e0aSLydia Wang 170943737e0aSLydia Wang /* PW2 (26h), MW2(36h), AOW2 (ah) */ 171043737e0aSLydia Wang parm = AC_PWRST_D3; 171143737e0aSLydia Wang set_pin_power_state(codec, 0x26, &parm); 171243737e0aSLydia Wang update_power_state(codec, 0x36, parm); 1713b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 171443737e0aSLydia Wang /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ 171543737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 171643737e0aSLydia Wang update_power_state(codec, 0x3b, parm); 171743737e0aSLydia Wang update_power_state(codec, 0x1b, parm); 171843737e0aSLydia Wang } 171943737e0aSLydia Wang update_conv_power_state(codec, 0xa, parm, 2); 172043737e0aSLydia Wang 172143737e0aSLydia Wang /* PW1 (25h), MW1(35h), AOW1 (9h) */ 172243737e0aSLydia Wang parm = AC_PWRST_D3; 172343737e0aSLydia Wang set_pin_power_state(codec, 0x25, &parm); 172443737e0aSLydia Wang update_power_state(codec, 0x35, parm); 1725b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 172643737e0aSLydia Wang /* PW6(2ah), MW6(3ah), MUX6(1ah) */ 172743737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 172843737e0aSLydia Wang update_power_state(codec, 0x3a, parm); 172943737e0aSLydia Wang update_power_state(codec, 0x1a, parm); 173043737e0aSLydia Wang } 173143737e0aSLydia Wang update_conv_power_state(codec, 0x9, parm, 1); 173243737e0aSLydia Wang 173343737e0aSLydia Wang /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ 173443737e0aSLydia Wang parm = AC_PWRST_D3; 173543737e0aSLydia Wang set_pin_power_state(codec, 0x28, &parm); 173643737e0aSLydia Wang update_power_state(codec, 0x38, parm); 173743737e0aSLydia Wang update_power_state(codec, 0x18, parm); 1738b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 173943737e0aSLydia Wang update_conv_power_state(codec, 0xb, parm, 3); 174043737e0aSLydia Wang parm2 = parm; /* for pin 0x0b */ 174143737e0aSLydia Wang 174243737e0aSLydia Wang /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ 174343737e0aSLydia Wang parm = AC_PWRST_D3; 174443737e0aSLydia Wang set_pin_power_state(codec, 0x24, &parm); 174543737e0aSLydia Wang update_power_state(codec, 0x34, parm); 1746b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 174743737e0aSLydia Wang parm = parm2; 174843737e0aSLydia Wang update_conv_power_state(codec, 0x8, parm, 0); 174943737e0aSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 175043737e0aSLydia Wang update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); 175143737e0aSLydia Wang } 175243737e0aSLydia Wang 175343737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 175443737e0aSLydia Wang { 175543737e0aSLydia Wang struct via_spec *spec; 175643737e0aSLydia Wang int err; 175743737e0aSLydia Wang 175843737e0aSLydia Wang /* create a codec specific record */ 175943737e0aSLydia Wang spec = via_new_spec(codec); 176043737e0aSLydia Wang if (spec == NULL) 176143737e0aSLydia Wang return -ENOMEM; 176243737e0aSLydia Wang 1763b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 176443737e0aSLydia Wang add_secret_dac_path(codec); 176543737e0aSLydia Wang 176643737e0aSLydia Wang /* automatic parse from the BIOS config */ 176743737e0aSLydia Wang err = via_parse_auto_config(codec); 176843737e0aSLydia Wang if (err < 0) { 176943737e0aSLydia Wang via_free(codec); 177043737e0aSLydia Wang return err; 177143737e0aSLydia Wang } 177243737e0aSLydia Wang 177343737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 177443737e0aSLydia Wang 177543737e0aSLydia Wang codec->patch_ops = via_patch_ops; 177643737e0aSLydia Wang 177743737e0aSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt3476; 177843737e0aSLydia Wang 177943737e0aSLydia Wang return 0; 178043737e0aSLydia Wang } 178143737e0aSLydia Wang 1782c577b8a1SJoseph Chan /* 1783c577b8a1SJoseph Chan * patch entries 1784c577b8a1SJoseph Chan */ 178590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 17863218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 17873218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 17883218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 17893218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 17903218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 1791ddd304d8STakashi Iwai .patch = patch_vt1709}, 17923218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 1793ddd304d8STakashi Iwai .patch = patch_vt1709}, 17943218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 1795ddd304d8STakashi Iwai .patch = patch_vt1709}, 17963218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 1797ddd304d8STakashi Iwai .patch = patch_vt1709}, 17983218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 1799ddd304d8STakashi Iwai .patch = patch_vt1709}, 18003218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 1801ddd304d8STakashi Iwai .patch = patch_vt1709}, 18023218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 1803ddd304d8STakashi Iwai .patch = patch_vt1709}, 18043218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 1805ddd304d8STakashi Iwai .patch = patch_vt1709}, 18063218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 1807ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18083218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 1809ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18103218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 1811ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18123218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 1813ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18143218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 1815ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18163218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 1817ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18183218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 1819ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18203218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 1821ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18223218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 1823d949cac1SHarald Welte .patch = patch_vt1708S}, 18243218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 1825d949cac1SHarald Welte .patch = patch_vt1708S}, 18263218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 1827d949cac1SHarald Welte .patch = patch_vt1708S}, 18283218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 1829d949cac1SHarald Welte .patch = patch_vt1708S}, 1830bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 1831d949cac1SHarald Welte .patch = patch_vt1708S}, 18323218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 1833d949cac1SHarald Welte .patch = patch_vt1708S}, 18343218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 1835d949cac1SHarald Welte .patch = patch_vt1708S}, 18363218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 1837d949cac1SHarald Welte .patch = patch_vt1708S}, 18383218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 1839d949cac1SHarald Welte .patch = patch_vt1702}, 18403218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 1841d949cac1SHarald Welte .patch = patch_vt1702}, 18423218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 1843d949cac1SHarald Welte .patch = patch_vt1702}, 18443218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 1845d949cac1SHarald Welte .patch = patch_vt1702}, 18463218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 1847d949cac1SHarald Welte .patch = patch_vt1702}, 18483218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 1849d949cac1SHarald Welte .patch = patch_vt1702}, 18503218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 1851d949cac1SHarald Welte .patch = patch_vt1702}, 18523218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 1853d949cac1SHarald Welte .patch = patch_vt1702}, 1854eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 1855eb7188caSLydia Wang .patch = patch_vt1718S}, 1856eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 1857eb7188caSLydia Wang .patch = patch_vt1718S}, 1858bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 1859bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1860bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 1861bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1862f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 1863f3db423dSLydia Wang .patch = patch_vt1716S}, 1864f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 1865f3db423dSLydia Wang .patch = patch_vt1716S}, 186625eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 186725eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 1868ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 186936dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 187036dd5c4aSLydia Wang .patch = patch_vt1708S}, 187111890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 187211890956SLydia Wang .patch = patch_vt2002P}, 187311890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 187411890956SLydia Wang .patch = patch_vt2002P}, 187543737e0aSLydia Wang { .id = 0x11064760, .name = "VT1705CF", 187643737e0aSLydia Wang .patch = patch_vt3476}, 18776121b84aSLydia Wang { .id = 0x11064761, .name = "VT1708SCE", 18786121b84aSLydia Wang .patch = patch_vt3476}, 18796121b84aSLydia Wang { .id = 0x11064762, .name = "VT1808", 18806121b84aSLydia Wang .patch = patch_vt3476}, 1881c577b8a1SJoseph Chan {} /* terminator */ 1882c577b8a1SJoseph Chan }; 18831289e9e8STakashi Iwai 18841289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 18851289e9e8STakashi Iwai 18861289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 18871289e9e8STakashi Iwai .preset = snd_hda_preset_via, 18881289e9e8STakashi Iwai .owner = THIS_MODULE, 18891289e9e8STakashi Iwai }; 18901289e9e8STakashi Iwai 18911289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 18921289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 18931289e9e8STakashi Iwai 18941289e9e8STakashi Iwai static int __init patch_via_init(void) 18951289e9e8STakashi Iwai { 18961289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 18971289e9e8STakashi Iwai } 18981289e9e8STakashi Iwai 18991289e9e8STakashi Iwai static void __exit patch_via_exit(void) 19001289e9e8STakashi Iwai { 19011289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 19021289e9e8STakashi Iwai } 19031289e9e8STakashi Iwai 19041289e9e8STakashi Iwai module_init(patch_via_init) 19051289e9e8STakashi Iwai module_exit(patch_via_exit) 1906