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 1225b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1235b0cb1d8SJaroslav Kysela { 1245b0cb1d8SJaroslav Kysela struct via_spec *spec; 1255b0cb1d8SJaroslav Kysela 1265b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1275b0cb1d8SJaroslav Kysela if (spec == NULL) 1285b0cb1d8SJaroslav Kysela return NULL; 1295b0cb1d8SJaroslav Kysela 1305b0cb1d8SJaroslav Kysela codec->spec = spec; 131b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1320341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1330341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1340341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1350341ccd7SLydia Wang spec->codec_type = VT1708S; 136b3f6008fSTakashi Iwai spec->no_pin_power_ctl = 1; 13713961170STakashi Iwai spec->gen.indep_hp = 1; 13805909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1; 139b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 140*74f14b36STakashi Iwai spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO; 1415b0cb1d8SJaroslav Kysela return spec; 1425b0cb1d8SJaroslav Kysela } 1435b0cb1d8SJaroslav Kysela 144744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 145d7426329SHarald Welte { 146744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 147d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 148d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 149d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 150d7426329SHarald Welte 151d7426329SHarald Welte /* get codec type */ 152d7426329SHarald Welte if (ven_id != 0x1106) 153d7426329SHarald Welte codec_type = UNKNOWN; 154d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 155d7426329SHarald Welte codec_type = VT1708; 156d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 157d7426329SHarald Welte codec_type = VT1709_10CH; 158d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 159d7426329SHarald Welte codec_type = VT1709_6CH; 160518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 161d7426329SHarald Welte codec_type = VT1708B_8CH; 162518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 163518bf3baSLydia Wang codec_type = VT1708BCE; 164518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 165d7426329SHarald Welte codec_type = VT1708B_4CH; 166d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 167d7426329SHarald Welte && (dev_id >> 12) < 8) 168d7426329SHarald Welte codec_type = VT1708S; 169d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 170d7426329SHarald Welte && (dev_id >> 12) < 8) 171d7426329SHarald Welte codec_type = VT1702; 172eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 173eb7188caSLydia Wang && (dev_id >> 12) < 8) 174eb7188caSLydia Wang codec_type = VT1718S; 175f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 176f3db423dSLydia Wang codec_type = VT1716S; 177bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 178bb3c6bfcSLydia Wang codec_type = VT1718S; 17925eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 18025eaba2fSLydia Wang codec_type = VT2002P; 181ab6734e7SLydia Wang else if (dev_id == 0x0448) 182ab6734e7SLydia Wang codec_type = VT1812; 18336dd5c4aSLydia Wang else if (dev_id == 0x0440) 18436dd5c4aSLydia Wang codec_type = VT1708S; 18511890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 18611890956SLydia Wang codec_type = VT1802; 18743737e0aSLydia Wang else if (dev_id == 0x4760) 18843737e0aSLydia Wang codec_type = VT1705CF; 1896121b84aSLydia Wang else if (dev_id == 0x4761 || dev_id == 0x4762) 1906121b84aSLydia Wang codec_type = VT1808; 191d7426329SHarald Welte else 192d7426329SHarald Welte codec_type = UNKNOWN; 193d7426329SHarald Welte return codec_type; 194d7426329SHarald Welte }; 195d7426329SHarald Welte 196ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 197ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 1981f2e99feSLydia Wang 199187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \ 200187d333eSTakashi Iwai (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \ 201187d333eSTakashi Iwai !is_aa_path_mute(codec)) 2021f2e99feSLydia Wang 203b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec) 2041f2e99feSLydia Wang { 205b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 206b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 2071f2e99feSLydia Wang return; 208187d333eSTakashi Iwai if (spec->hp_work_active) { 209b3f6008fSTakashi Iwai snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); 2107eaa9161SWang Xingchao codec->jackpoll_interval = 0; 211b3f6008fSTakashi Iwai cancel_delayed_work_sync(&codec->jackpoll_work); 212b3f6008fSTakashi Iwai spec->hp_work_active = false; 213187d333eSTakashi Iwai } 214187d333eSTakashi Iwai } 215187d333eSTakashi Iwai 216b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec) 217187d333eSTakashi Iwai { 218b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 219b3f6008fSTakashi Iwai if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs) 220187d333eSTakashi Iwai return; 22105dc0fc9SDavid Henningsson if (spec->vt1708_jack_detect) { 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 { 235087c2e3bSTakashi Iwai #if 0 /* FIXME: the assumed connections don't match always with the 236087c2e3bSTakashi Iwai * actual routes by the generic parser, so better to disable 237087c2e3bSTakashi Iwai * the control for safety. 238087c2e3bSTakashi Iwai */ 2393e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2403e95b9abSLydia Wang if (spec->set_widgets_power_state) 2413e95b9abSLydia Wang spec->set_widgets_power_state(codec); 242087c2e3bSTakashi Iwai #endif 2433e95b9abSLydia Wang } 24425eaba2fSLydia Wang 245054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid, 246054d867eSTakashi Iwai unsigned int parm) 247054d867eSTakashi Iwai { 2489040d102STakashi Iwai if (snd_hda_check_power_state(codec, nid, parm)) 249054d867eSTakashi Iwai return; 250054d867eSTakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 251054d867eSTakashi Iwai } 252054d867eSTakashi Iwai 25343737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid, 25443737e0aSLydia Wang unsigned int parm, unsigned int index) 25543737e0aSLydia Wang { 25643737e0aSLydia Wang struct via_spec *spec = codec->spec; 25743737e0aSLydia Wang unsigned int format; 2589040d102STakashi Iwai 2599040d102STakashi Iwai if (snd_hda_check_power_state(codec, nid, parm)) 26043737e0aSLydia Wang return; 26143737e0aSLydia Wang format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 26243737e0aSLydia Wang if (format && (spec->dac_stream_tag[index] != format)) 26343737e0aSLydia Wang spec->dac_stream_tag[index] = format; 26443737e0aSLydia Wang 26543737e0aSLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 26643737e0aSLydia Wang if (parm == AC_PWRST_D0) { 26743737e0aSLydia Wang format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0); 26843737e0aSLydia Wang if (!format && (spec->dac_stream_tag[index] != format)) 26943737e0aSLydia Wang snd_hda_codec_write(codec, nid, 0, 27043737e0aSLydia Wang AC_VERB_SET_CHANNEL_STREAMID, 27143737e0aSLydia Wang spec->dac_stream_tag[index]); 27243737e0aSLydia Wang } 27343737e0aSLydia Wang } 27443737e0aSLydia Wang 275b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec) 276b3f6008fSTakashi Iwai { 277b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 278b3f6008fSTakashi Iwai return spec->gen.ext_channel_count > 2; 279b3f6008fSTakashi Iwai } 280b3f6008fSTakashi Iwai 281b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 282b3f6008fSTakashi Iwai { 283b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 284b3f6008fSTakashi Iwai int i; 285b3f6008fSTakashi Iwai 286b3f6008fSTakashi Iwai for (i = 0; i < spec->gen.multi_ios; i++) 287b3f6008fSTakashi Iwai if (spec->gen.multi_io[i].pin == pin) 288b3f6008fSTakashi Iwai return true; 289b3f6008fSTakashi Iwai return false; 290b3f6008fSTakashi Iwai } 291b3f6008fSTakashi Iwai 292f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 293f5271101SLydia Wang unsigned int *affected_parm) 294f5271101SLydia Wang { 295f5271101SLydia Wang unsigned parm; 296f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 297f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 298f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 299f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 3001564b287SLydia Wang struct via_spec *spec = codec->spec; 30124088a58STakashi Iwai unsigned present = 0; 30224088a58STakashi Iwai 30324088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 30424088a58STakashi Iwai if (!no_presence) 30524088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 306b3f6008fSTakashi Iwai if ((smart51_enabled(codec) && is_smart51_pins(codec, nid)) 3071564b287SLydia Wang || ((no_presence || present) 3081564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 309f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 310f5271101SLydia Wang parm = AC_PWRST_D0; 311f5271101SLydia Wang } else 312f5271101SLydia Wang parm = AC_PWRST_D3; 313f5271101SLydia Wang 314054d867eSTakashi Iwai update_power_state(codec, nid, parm); 315f5271101SLydia Wang } 316f5271101SLydia Wang 31724088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 31824088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 31924088a58STakashi Iwai { 320dda415d4STakashi Iwai return snd_hda_enum_bool_helper_info(kcontrol, uinfo); 32124088a58STakashi Iwai } 32224088a58STakashi Iwai 32324088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 32424088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 32524088a58STakashi Iwai { 32624088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 32724088a58STakashi Iwai struct via_spec *spec = codec->spec; 32824088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 32924088a58STakashi Iwai return 0; 33024088a58STakashi Iwai } 33124088a58STakashi Iwai 33224088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 33324088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 33424088a58STakashi Iwai { 33524088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33624088a58STakashi Iwai struct via_spec *spec = codec->spec; 33724088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 33824088a58STakashi Iwai 33924088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 34024088a58STakashi Iwai return 0; 34124088a58STakashi Iwai spec->no_pin_power_ctl = val; 34224088a58STakashi Iwai set_widgets_power_state(codec); 343e9d010c2STakashi Iwai analog_low_current_mode(codec); 34424088a58STakashi Iwai return 1; 34524088a58STakashi Iwai } 34624088a58STakashi Iwai 347b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = { 348b3f6008fSTakashi Iwai { 34924088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 35024088a58STakashi Iwai .name = "Dynamic Power-Control", 35124088a58STakashi Iwai .info = via_pin_power_ctl_info, 35224088a58STakashi Iwai .get = via_pin_power_ctl_get, 35324088a58STakashi Iwai .put = via_pin_power_ctl_put, 354b3f6008fSTakashi Iwai }, 355b3f6008fSTakashi Iwai {} /* terminator */ 35624088a58STakashi Iwai }; 35724088a58STakashi Iwai 35824088a58STakashi Iwai 359f5271101SLydia Wang /* check AA path's mute status */ 360ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 361ada509ecSTakashi Iwai { 362ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 363ada509ecSTakashi Iwai const struct hda_amp_list *p; 3640186f4f4STakashi Iwai int ch, v; 365ada509ecSTakashi Iwai 3660186f4f4STakashi Iwai p = spec->gen.loopback.amplist; 3670186f4f4STakashi Iwai if (!p) 3680186f4f4STakashi Iwai return true; 3690186f4f4STakashi Iwai for (; p->nid; p++) { 370ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 371ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 372ada509ecSTakashi Iwai p->idx); 373ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 374ada509ecSTakashi Iwai return false; 375f5271101SLydia Wang } 376f5271101SLydia Wang } 377ada509ecSTakashi Iwai return true; 378f5271101SLydia Wang } 379f5271101SLydia Wang 380f5271101SLydia Wang /* enter/exit analog low-current mode */ 381e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force) 382f5271101SLydia Wang { 383f5271101SLydia Wang struct via_spec *spec = codec->spec; 384ada509ecSTakashi Iwai bool enable; 385ada509ecSTakashi Iwai unsigned int verb, parm; 386f5271101SLydia Wang 387e9d010c2STakashi Iwai if (spec->no_pin_power_ctl) 388e9d010c2STakashi Iwai enable = false; 389e9d010c2STakashi Iwai else 390b3f6008fSTakashi Iwai enable = is_aa_path_mute(codec) && !spec->gen.active_streams; 391e9d010c2STakashi Iwai if (enable == spec->alc_mode && !force) 392e9d010c2STakashi Iwai return; 393e9d010c2STakashi Iwai spec->alc_mode = enable; 394f5271101SLydia Wang 395f5271101SLydia Wang /* decide low current mode's verb & parameter */ 396f5271101SLydia Wang switch (spec->codec_type) { 397f5271101SLydia Wang case VT1708B_8CH: 398f5271101SLydia Wang case VT1708B_4CH: 399f5271101SLydia Wang verb = 0xf70; 400f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 401f5271101SLydia Wang break; 402f5271101SLydia Wang case VT1708S: 403eb7188caSLydia Wang case VT1718S: 404f3db423dSLydia Wang case VT1716S: 405f5271101SLydia Wang verb = 0xf73; 406f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 407f5271101SLydia Wang break; 408f5271101SLydia Wang case VT1702: 409f5271101SLydia Wang verb = 0xf73; 410f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 411f5271101SLydia Wang break; 41225eaba2fSLydia Wang case VT2002P: 413ab6734e7SLydia Wang case VT1812: 41411890956SLydia Wang case VT1802: 41525eaba2fSLydia Wang verb = 0xf93; 41625eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 41725eaba2fSLydia Wang break; 41843737e0aSLydia Wang case VT1705CF: 4196121b84aSLydia Wang case VT1808: 42043737e0aSLydia Wang verb = 0xf82; 42143737e0aSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 42243737e0aSLydia Wang break; 423f5271101SLydia Wang default: 424f5271101SLydia Wang return; /* other codecs are not supported */ 425f5271101SLydia Wang } 426f5271101SLydia Wang /* send verb */ 427f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 428f5271101SLydia Wang } 429f5271101SLydia Wang 430e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 431e9d010c2STakashi Iwai { 432e9d010c2STakashi Iwai return __analog_low_current_mode(codec, false); 433e9d010c2STakashi Iwai } 434e9d010c2STakashi Iwai 435c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 436c577b8a1SJoseph Chan { 437c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 4385b0cb1d8SJaroslav Kysela int err, i; 439c577b8a1SJoseph Chan 440b3f6008fSTakashi Iwai err = snd_hda_gen_build_controls(codec); 441b3f6008fSTakashi Iwai if (err < 0) 442b3f6008fSTakashi Iwai return err; 443b3f6008fSTakashi Iwai 44424088a58STakashi Iwai if (spec->set_widgets_power_state) 445b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum; 44624088a58STakashi Iwai 447c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 448c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 449c577b8a1SJoseph Chan if (err < 0) 450c577b8a1SJoseph Chan return err; 451c577b8a1SJoseph Chan } 452c577b8a1SJoseph Chan 453c577b8a1SJoseph Chan return 0; 454c577b8a1SJoseph Chan } 455c577b8a1SJoseph Chan 456b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 457b3f6008fSTakashi Iwai struct hda_codec *codec, 458b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 459b3f6008fSTakashi Iwai int action) 460c577b8a1SJoseph Chan { 461b3f6008fSTakashi Iwai analog_low_current_mode(codec); 462b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 463c577b8a1SJoseph Chan } 464c577b8a1SJoseph Chan 465c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 466c577b8a1SJoseph Chan { 467b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 468a8dca460STakashi Iwai snd_hda_gen_free(codec); 469c577b8a1SJoseph Chan } 470c577b8a1SJoseph Chan 4712a43952aSTakashi Iwai #ifdef CONFIG_PM 47268cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 4731f2e99feSLydia Wang { 4741f2e99feSLydia Wang struct via_spec *spec = codec->spec; 475b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 47694c142a1SDavid Henningsson 47794c142a1SDavid Henningsson /* Fix pop noise on headphones */ 4782c38d990STakashi Iwai if (spec->codec_type == VT1802) 4792c38d990STakashi Iwai snd_hda_shutup_pins(codec); 48094c142a1SDavid Henningsson 4811f2e99feSLydia Wang return 0; 4821f2e99feSLydia Wang } 4831f2e99feSLydia Wang #endif 4841f2e99feSLydia Wang 48583012a7cSTakashi Iwai #ifdef CONFIG_PM 486cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 487cb53c626STakashi Iwai { 488cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 489b3f6008fSTakashi Iwai set_widgets_power_state(codec); 490b3f6008fSTakashi Iwai analog_low_current_mode(codec); 491b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 492b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 493cb53c626STakashi Iwai } 494cb53c626STakashi Iwai #endif 495cb53c626STakashi Iwai 496c577b8a1SJoseph Chan /* 497c577b8a1SJoseph Chan */ 4985d41762aSTakashi Iwai 4995d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 5005d41762aSTakashi Iwai 50190dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 502c577b8a1SJoseph Chan .build_controls = via_build_controls, 503b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 504c577b8a1SJoseph Chan .init = via_init, 505c577b8a1SJoseph Chan .free = via_free, 5064e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 5072a43952aSTakashi Iwai #ifdef CONFIG_PM 5081f2e99feSLydia Wang .suspend = via_suspend, 509cb53c626STakashi Iwai .check_power_status = via_check_power_status, 510cb53c626STakashi Iwai #endif 511c577b8a1SJoseph Chan }; 512c577b8a1SJoseph Chan 5134a79616dSTakashi Iwai 514b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 515b3f6008fSTakashi Iwai /* power down jack detect function */ 516b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 517b3f6008fSTakashi Iwai { } 5184a79616dSTakashi Iwai }; 51976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 52076d9b0ddSHarald Welte { 52176d9b0ddSHarald Welte unsigned int def_conf; 52276d9b0ddSHarald Welte unsigned char seqassoc; 52376d9b0ddSHarald Welte 5242f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 52576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 52676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 52782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 52882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 52976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 5302f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 53176d9b0ddSHarald Welte } 53276d9b0ddSHarald Welte 53376d9b0ddSHarald Welte return; 53476d9b0ddSHarald Welte } 53576d9b0ddSHarald Welte 536e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 5371f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5381f2e99feSLydia Wang { 5391f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5401f2e99feSLydia Wang struct via_spec *spec = codec->spec; 5411f2e99feSLydia Wang 5421f2e99feSLydia Wang if (spec->codec_type != VT1708) 5431f2e99feSLydia Wang return 0; 544e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 5451f2e99feSLydia Wang return 0; 5461f2e99feSLydia Wang } 5471f2e99feSLydia Wang 548e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 5491f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5501f2e99feSLydia Wang { 5511f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5521f2e99feSLydia Wang struct via_spec *spec = codec->spec; 553187d333eSTakashi Iwai int val; 5541f2e99feSLydia Wang 5551f2e99feSLydia Wang if (spec->codec_type != VT1708) 5561f2e99feSLydia Wang return 0; 557187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 558187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 559187d333eSTakashi Iwai return 0; 560187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 561b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 562187d333eSTakashi Iwai return 1; 5631f2e99feSLydia Wang } 5641f2e99feSLydia Wang 565b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 566b3f6008fSTakashi Iwai { 5671f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5681f2e99feSLydia Wang .name = "Jack Detect", 5691f2e99feSLydia Wang .count = 1, 5701f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 571e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 572e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 573b3f6008fSTakashi Iwai }, 574b3f6008fSTakashi Iwai {} /* terminator */ 5751f2e99feSLydia Wang }; 5761f2e99feSLydia Wang 5771a4f69d5STakashi Iwai static void via_jack_powerstate_event(struct hda_codec *codec, 5781a4f69d5STakashi Iwai struct hda_jack_callback *tbl) 5794e2d16d3SDavid Henningsson { 5804e2d16d3SDavid Henningsson set_widgets_power_state(codec); 5814e2d16d3SDavid Henningsson } 5824e2d16d3SDavid Henningsson 583b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec) 5844a918ffeSTakashi Iwai { 5854a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 586b3f6008fSTakashi Iwai struct auto_pin_cfg *cfg = &spec->gen.autocfg; 587b3f6008fSTakashi Iwai hda_nid_t pin; 5884a918ffeSTakashi Iwai int i; 5894a918ffeSTakashi Iwai 5904a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 591b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 5927c3008c4STakashi Iwai if (pin && is_jack_detectable(codec, pin)) 593b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 5944e2d16d3SDavid Henningsson via_jack_powerstate_event); 5954a918ffeSTakashi Iwai } 596b3f6008fSTakashi Iwai 597b3f6008fSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 598b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 5997c3008c4STakashi Iwai if (pin && is_jack_detectable(codec, pin)) 600b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 601b3f6008fSTakashi Iwai via_jack_powerstate_event); 602b3f6008fSTakashi Iwai } 603b3f6008fSTakashi Iwai } 604b3f6008fSTakashi Iwai 6054abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 6064abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 6074abdbd1cSTakashi Iwai .no_dac = 0x4000, 6084abdbd1cSTakashi Iwai .shared_primary = 0x10000, 6094abdbd1cSTakashi Iwai .shared_surr = 0x20, 6104abdbd1cSTakashi Iwai .shared_clfe = 0x20, 6114abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 6124abdbd1cSTakashi Iwai }; 6134abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 6144abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 6154abdbd1cSTakashi Iwai .no_dac = 0x4000, 6164abdbd1cSTakashi Iwai .shared_primary = 0x12, 6174abdbd1cSTakashi Iwai .shared_surr = 0x20, 6184abdbd1cSTakashi Iwai .shared_clfe = 0x20, 6194abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 6204abdbd1cSTakashi Iwai }; 6214abdbd1cSTakashi Iwai 622b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 623b3f6008fSTakashi Iwai { 624b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 625b3f6008fSTakashi Iwai int err; 626b3f6008fSTakashi Iwai 6274abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 6284abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 6294abdbd1cSTakashi Iwai 630b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 631b3f6008fSTakashi Iwai if (err < 0) 632b3f6008fSTakashi Iwai return err; 633b3f6008fSTakashi Iwai 634b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 635b3f6008fSTakashi Iwai if (err < 0) 636b3f6008fSTakashi Iwai return err; 637b3f6008fSTakashi Iwai 638b3f6008fSTakashi Iwai via_set_jack_unsol_events(codec); 639b3f6008fSTakashi Iwai return 0; 6404a918ffeSTakashi Iwai } 6414a918ffeSTakashi Iwai 6425d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 6435d41762aSTakashi Iwai { 6445d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 6455d41762aSTakashi Iwai int i; 6465d41762aSTakashi Iwai 6475d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 6485d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 6495d41762aSTakashi Iwai 650e9d010c2STakashi Iwai /* init power states */ 651e9d010c2STakashi Iwai set_widgets_power_state(codec); 652e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 653e9d010c2STakashi Iwai 654b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 65511890956SLydia Wang 656b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 65725eaba2fSLydia Wang 658c577b8a1SJoseph Chan return 0; 659c577b8a1SJoseph Chan } 660c577b8a1SJoseph Chan 661f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 662f672f65aSDavid Henningsson { 663f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 664f672f65aSDavid Henningsson temporary enable jackpoll */ 665f672f65aSDavid Henningsson int err; 666f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 667f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 668f672f65aSDavid Henningsson err = via_build_controls(codec); 669f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 670f672f65aSDavid Henningsson return err; 671f672f65aSDavid Henningsson } 672f672f65aSDavid Henningsson 673b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 674337b9d02STakashi Iwai { 675337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 676b3f6008fSTakashi Iwai int i, err; 677337b9d02STakashi Iwai 678b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 679b3f6008fSTakashi Iwai if (err < 0 || codec->vendor_id != 0x11061708) 680b3f6008fSTakashi Iwai return err; 681b3f6008fSTakashi Iwai 682b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 683b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 684b3f6008fSTakashi Iwai * disable the 24bit format, so far. 685b3f6008fSTakashi Iwai */ 686b3f6008fSTakashi Iwai for (i = 0; i < codec->num_pcms; i++) { 687b3f6008fSTakashi Iwai struct hda_pcm *info = &spec->gen.pcm_rec[i]; 688b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 689b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 690b3f6008fSTakashi Iwai continue; 691b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 692b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 693337b9d02STakashi Iwai } 694b3f6008fSTakashi Iwai 6951c55d521STakashi Iwai return 0; 696337b9d02STakashi Iwai } 697337b9d02STakashi Iwai 698c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 699c577b8a1SJoseph Chan { 700c577b8a1SJoseph Chan struct via_spec *spec; 701c577b8a1SJoseph Chan int err; 702c577b8a1SJoseph Chan 703c577b8a1SJoseph Chan /* create a codec specific record */ 7045b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 705c577b8a1SJoseph Chan if (spec == NULL) 706c577b8a1SJoseph Chan return -ENOMEM; 707c577b8a1SJoseph Chan 708b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 709b3f6008fSTakashi Iwai 710b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 711b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 712b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 713b3f6008fSTakashi Iwai 714b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 715b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 716b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 717eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 718eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 719620e2b28STakashi Iwai 72012daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 72112daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 72212daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 72312daef65STakashi Iwai 724c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 72512daef65STakashi Iwai err = via_parse_auto_config(codec); 726c577b8a1SJoseph Chan if (err < 0) { 727c577b8a1SJoseph Chan via_free(codec); 728c577b8a1SJoseph Chan return err; 729c577b8a1SJoseph Chan } 730c577b8a1SJoseph Chan 73112daef65STakashi Iwai /* add jack detect on/off control */ 732b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 733c577b8a1SJoseph Chan 734e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 735e322a36dSLydia Wang 736c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 737f672f65aSDavid Henningsson codec->patch_ops.build_controls = vt1708_build_controls; 738b3f6008fSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 739c577b8a1SJoseph Chan 740b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 741b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 742b3f6008fSTakashi Iwai 743c577b8a1SJoseph Chan return 0; 744c577b8a1SJoseph Chan } 745c577b8a1SJoseph Chan 746ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 747c577b8a1SJoseph Chan { 748c577b8a1SJoseph Chan struct via_spec *spec; 749c577b8a1SJoseph Chan int err; 750c577b8a1SJoseph Chan 751c577b8a1SJoseph Chan /* create a codec specific record */ 7525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 753c577b8a1SJoseph Chan if (spec == NULL) 754c577b8a1SJoseph Chan return -ENOMEM; 755c577b8a1SJoseph Chan 756b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 757620e2b28STakashi Iwai 75812daef65STakashi Iwai err = via_parse_auto_config(codec); 759c577b8a1SJoseph Chan if (err < 0) { 760c577b8a1SJoseph Chan via_free(codec); 761c577b8a1SJoseph Chan return err; 762c577b8a1SJoseph Chan } 763c577b8a1SJoseph Chan 764c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 765c577b8a1SJoseph Chan 766f7278fd0SJosepch Chan return 0; 767f7278fd0SJosepch Chan } 768f7278fd0SJosepch Chan 7693e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 7703e95b9abSLydia Wang { 7713e95b9abSLydia Wang struct via_spec *spec = codec->spec; 7723e95b9abSLydia Wang int imux_is_smixer; 7733e95b9abSLydia Wang unsigned int parm; 7743e95b9abSLydia Wang int is_8ch = 0; 775bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 776bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 7773e95b9abSLydia Wang is_8ch = 1; 7783e95b9abSLydia Wang 7793e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 7803e95b9abSLydia Wang imux_is_smixer = 7813e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 7823e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 7833e95b9abSLydia Wang /* inputs */ 7843e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 7853e95b9abSLydia Wang parm = AC_PWRST_D3; 7863e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 7873e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 7883e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 7893e95b9abSLydia Wang if (imux_is_smixer) 7903e95b9abSLydia Wang parm = AC_PWRST_D0; 7913e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 792054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 793054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 794054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 7953e95b9abSLydia Wang 7963e95b9abSLydia Wang /* outputs */ 7973e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 7983e95b9abSLydia Wang parm = AC_PWRST_D3; 7993e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 800b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8013e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 802054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 803054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 8043e95b9abSLydia Wang 8053e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 8063e95b9abSLydia Wang if (is_8ch) { 8073e95b9abSLydia Wang parm = AC_PWRST_D3; 8083e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 809b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8103e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 811054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 812054d867eSTakashi Iwai update_power_state(codec, 0x24, parm); 813bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 814bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 815bc92df7fSLydia Wang parm = AC_PWRST_D3; 816bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 817b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 818bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 819054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 820054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8213e95b9abSLydia Wang } 8223e95b9abSLydia Wang 8233e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 8243e95b9abSLydia Wang parm = AC_PWRST_D3; 8253e95b9abSLydia Wang /* force to D0 for internal Speaker */ 8263e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 8273e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 8283e95b9abSLydia Wang if (is_8ch) 8293e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 8303e95b9abSLydia Wang 8313e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 832054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 833054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 8343e95b9abSLydia Wang if (is_8ch) { 835054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 836054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 837b3f6008fSTakashi Iwai } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) 838054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8393e95b9abSLydia Wang } 8403e95b9abSLydia Wang 841518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 842ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 843f7278fd0SJosepch Chan { 844f7278fd0SJosepch Chan struct via_spec *spec; 845f7278fd0SJosepch Chan int err; 846f7278fd0SJosepch Chan 847518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 848518bf3baSLydia Wang return patch_vt1708S(codec); 849ddd304d8STakashi Iwai 850f7278fd0SJosepch Chan /* create a codec specific record */ 8515b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 852f7278fd0SJosepch Chan if (spec == NULL) 853f7278fd0SJosepch Chan return -ENOMEM; 854f7278fd0SJosepch Chan 855b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 856620e2b28STakashi Iwai 857f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 85812daef65STakashi Iwai err = via_parse_auto_config(codec); 859f7278fd0SJosepch Chan if (err < 0) { 860f7278fd0SJosepch Chan via_free(codec); 861f7278fd0SJosepch Chan return err; 862f7278fd0SJosepch Chan } 863f7278fd0SJosepch Chan 864f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 865f7278fd0SJosepch Chan 8663e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 8673e95b9abSLydia Wang 868f7278fd0SJosepch Chan return 0; 869f7278fd0SJosepch Chan } 870f7278fd0SJosepch Chan 871d949cac1SHarald Welte /* Patch for VT1708S */ 872096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 873d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 874d7426329SHarald Welte {0x1, 0xf98, 0x1}, 875bc7e7e5cSLydia Wang /* don't bybass mixer */ 876bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 877d949cac1SHarald Welte { } 878d949cac1SHarald Welte }; 879d949cac1SHarald Welte 8806369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 8816369bcfcSLydia Wang int offset, int num_steps, int step_size) 8826369bcfcSLydia Wang { 883d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 884d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 8856369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 8866369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 8876369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 8886369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 8896369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 8906369bcfcSLydia Wang } 8916369bcfcSLydia Wang 892d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 893d949cac1SHarald Welte { 894d949cac1SHarald Welte struct via_spec *spec; 895d949cac1SHarald Welte int err; 896d949cac1SHarald Welte 897d949cac1SHarald Welte /* create a codec specific record */ 8985b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 899d949cac1SHarald Welte if (spec == NULL) 900d949cac1SHarald Welte return -ENOMEM; 901d949cac1SHarald Welte 902b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 903d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 904d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 905620e2b28STakashi Iwai 906518bf3baSLydia Wang /* correct names for VT1708BCE */ 907518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 908518bf3baSLydia Wang kfree(codec->chip_name); 909518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 910518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 911518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 912518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 913970f630fSLydia Wang } 914bc92df7fSLydia Wang /* correct names for VT1705 */ 915bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 916bc92df7fSLydia Wang kfree(codec->chip_name); 917bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 918bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 919bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 920bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 921bc92df7fSLydia Wang } 922b3f6008fSTakashi Iwai 923b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 924b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 925b3f6008fSTakashi Iwai if (err < 0) { 926b3f6008fSTakashi Iwai via_free(codec); 927b3f6008fSTakashi Iwai return err; 928b3f6008fSTakashi Iwai } 929b3f6008fSTakashi Iwai 930b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 931b3f6008fSTakashi Iwai 932b3f6008fSTakashi Iwai codec->patch_ops = via_patch_ops; 933b3f6008fSTakashi Iwai 9343e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 935d949cac1SHarald Welte return 0; 936d949cac1SHarald Welte } 937d949cac1SHarald Welte 938d949cac1SHarald Welte /* Patch for VT1702 */ 939d949cac1SHarald Welte 940096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 941bc7e7e5cSLydia Wang /* mixer enable */ 942bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 943bc7e7e5cSLydia Wang /* GPIO 0~2 */ 944bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 945d949cac1SHarald Welte { } 946d949cac1SHarald Welte }; 947d949cac1SHarald Welte 9483e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 9493e95b9abSLydia Wang { 9503e95b9abSLydia Wang int imux_is_smixer = 9513e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 9523e95b9abSLydia Wang unsigned int parm; 9533e95b9abSLydia Wang /* inputs */ 9543e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 9553e95b9abSLydia Wang parm = AC_PWRST_D3; 9563e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 9573e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 9583e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 9593e95b9abSLydia Wang if (imux_is_smixer) 9603e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 9613e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 962054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 963054d867eSTakashi Iwai update_power_state(codec, 0x12, parm); 964054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 965054d867eSTakashi Iwai update_power_state(codec, 0x20, parm); 9663e95b9abSLydia Wang 9673e95b9abSLydia Wang /* outputs */ 9683e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 9693e95b9abSLydia Wang parm = AC_PWRST_D3; 9703e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 9713e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 9723e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 973054d867eSTakashi Iwai update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); 974054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 975054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 9763e95b9abSLydia Wang } 9773e95b9abSLydia Wang 978d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 979d949cac1SHarald Welte { 980d949cac1SHarald Welte struct via_spec *spec; 981d949cac1SHarald Welte int err; 982d949cac1SHarald Welte 983d949cac1SHarald Welte /* create a codec specific record */ 9845b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 985d949cac1SHarald Welte if (spec == NULL) 986d949cac1SHarald Welte return -ENOMEM; 987d949cac1SHarald Welte 988b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 989620e2b28STakashi Iwai 99012daef65STakashi Iwai /* limit AA path volume to 0 dB */ 99112daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 99212daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 99312daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 99412daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 99512daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 99612daef65STakashi Iwai 997d949cac1SHarald Welte /* automatic parse from the BIOS config */ 99812daef65STakashi Iwai err = via_parse_auto_config(codec); 999d949cac1SHarald Welte if (err < 0) { 1000d949cac1SHarald Welte via_free(codec); 1001d949cac1SHarald Welte return err; 1002d949cac1SHarald Welte } 1003d949cac1SHarald Welte 1004096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 1005d949cac1SHarald Welte 1006d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 1007d949cac1SHarald Welte 10083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 1009d949cac1SHarald Welte return 0; 1010d949cac1SHarald Welte } 1011d949cac1SHarald Welte 1012eb7188caSLydia Wang /* Patch for VT1718S */ 1013eb7188caSLydia Wang 1014096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 10154ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 10164ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 1017eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 1018eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 10195d41762aSTakashi Iwai 1020eb7188caSLydia Wang { } 1021eb7188caSLydia Wang }; 1022eb7188caSLydia Wang 10233e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 10243e95b9abSLydia Wang { 10253e95b9abSLydia Wang struct via_spec *spec = codec->spec; 10263e95b9abSLydia Wang int imux_is_smixer; 10276162552bSTakashi Iwai unsigned int parm, parm2; 10283e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 10293e95b9abSLydia Wang imux_is_smixer = 10303e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 10313e95b9abSLydia Wang /* inputs */ 10323e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 10333e95b9abSLydia Wang parm = AC_PWRST_D3; 10343e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 10353e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 10363e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 10373e95b9abSLydia Wang if (imux_is_smixer) 10383e95b9abSLydia Wang parm = AC_PWRST_D0; 10393e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 1040054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1041054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1042054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1043054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 10443e95b9abSLydia Wang 10453e95b9abSLydia Wang /* outputs */ 10463e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 10473e95b9abSLydia Wang parm = AC_PWRST_D3; 10483e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 1049054d867eSTakashi Iwai update_power_state(codec, 0x1a, parm); 10506162552bSTakashi Iwai parm2 = parm; /* for pin 0x0b */ 10513e95b9abSLydia Wang 10523e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 10533e95b9abSLydia Wang parm = AC_PWRST_D3; 10543e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1055b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 10563e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 1057054d867eSTakashi Iwai update_power_state(codec, 0xa, parm); 10583e95b9abSLydia Wang 10593e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 10603e95b9abSLydia Wang parm = AC_PWRST_D3; 10613e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 1062b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ 10633e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1064054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 1065b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 10666162552bSTakashi Iwai parm = parm2; 10676162552bSTakashi Iwai update_power_state(codec, 0xb, parm); 10683e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 1069054d867eSTakashi Iwai update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); 10703e95b9abSLydia Wang 10713e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 10723e95b9abSLydia Wang parm = AC_PWRST_D3; 10733e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1074b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 10753e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 1076054d867eSTakashi Iwai update_power_state(codec, 0x9, parm); 10773e95b9abSLydia Wang 1078b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) { 10793e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 10803e95b9abSLydia Wang parm = AC_PWRST_D3; 10813e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1082054d867eSTakashi Iwai update_power_state(codec, 0x1b, parm); 1083054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 1084054d867eSTakashi Iwai update_power_state(codec, 0xc, parm); 10853e95b9abSLydia Wang } 10863e95b9abSLydia Wang } 10873e95b9abSLydia Wang 108830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 108930b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 109030b45033STakashi Iwai */ 109130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 109230b45033STakashi Iwai { 109330b45033STakashi Iwai struct via_spec *spec = codec->spec; 109430b45033STakashi Iwai int i, nums; 109530b45033STakashi Iwai hda_nid_t conn[8]; 109630b45033STakashi Iwai hda_nid_t nid; 109730b45033STakashi Iwai 1098b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 109930b45033STakashi Iwai return 0; 1100b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 110130b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 110230b45033STakashi Iwai for (i = 0; i < nums; i++) { 110330b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 110430b45033STakashi Iwai return 0; 110530b45033STakashi Iwai } 110630b45033STakashi Iwai 110730b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 110830b45033STakashi Iwai nid = codec->start_nid; 110930b45033STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 111030b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 111130b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 111230b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 111330b45033STakashi Iwai conn[nums++] = nid; 111430b45033STakashi Iwai return snd_hda_override_conn_list(codec, 1115b3f6008fSTakashi Iwai spec->gen.mixer_nid, 111630b45033STakashi Iwai nums, conn); 111730b45033STakashi Iwai } 111830b45033STakashi Iwai } 111930b45033STakashi Iwai return 0; 112030b45033STakashi Iwai } 112130b45033STakashi Iwai 112230b45033STakashi Iwai 1123eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 1124eb7188caSLydia Wang { 1125eb7188caSLydia Wang struct via_spec *spec; 1126eb7188caSLydia Wang int err; 1127eb7188caSLydia Wang 1128eb7188caSLydia Wang /* create a codec specific record */ 11295b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1130eb7188caSLydia Wang if (spec == NULL) 1131eb7188caSLydia Wang return -ENOMEM; 1132eb7188caSLydia Wang 1133b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1134d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1135d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 113630b45033STakashi Iwai add_secret_dac_path(codec); 1137620e2b28STakashi Iwai 1138eb7188caSLydia Wang /* automatic parse from the BIOS config */ 113912daef65STakashi Iwai err = via_parse_auto_config(codec); 1140eb7188caSLydia Wang if (err < 0) { 1141eb7188caSLydia Wang via_free(codec); 1142eb7188caSLydia Wang return err; 1143eb7188caSLydia Wang } 1144eb7188caSLydia Wang 1145096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 1146eb7188caSLydia Wang 1147eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 1148eb7188caSLydia Wang 11493e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 11503e95b9abSLydia Wang 1151eb7188caSLydia Wang return 0; 1152eb7188caSLydia Wang } 1153f3db423dSLydia Wang 1154f3db423dSLydia Wang /* Patch for VT1716S */ 1155f3db423dSLydia Wang 1156f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 1157f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 1158f3db423dSLydia Wang { 1159f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1160f3db423dSLydia Wang uinfo->count = 1; 1161f3db423dSLydia Wang uinfo->value.integer.min = 0; 1162f3db423dSLydia Wang uinfo->value.integer.max = 1; 1163f3db423dSLydia Wang return 0; 1164f3db423dSLydia Wang } 1165f3db423dSLydia Wang 1166f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 1167f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1168f3db423dSLydia Wang { 1169f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1170f3db423dSLydia Wang int index = 0; 1171f3db423dSLydia Wang 1172f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 1173f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 1174f3db423dSLydia Wang if (index != -1) 1175f3db423dSLydia Wang *ucontrol->value.integer.value = index; 1176f3db423dSLydia Wang 1177f3db423dSLydia Wang return 0; 1178f3db423dSLydia Wang } 1179f3db423dSLydia Wang 1180f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 1181f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1182f3db423dSLydia Wang { 1183f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1184f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1185f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 1186f3db423dSLydia Wang 1187f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 1188f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 1189f3db423dSLydia Wang spec->dmic_enabled = index; 11903e95b9abSLydia Wang set_widgets_power_state(codec); 1191f3db423dSLydia Wang return 1; 1192f3db423dSLydia Wang } 1193f3db423dSLydia Wang 119490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 1195f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 1196f3db423dSLydia Wang { 1197f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1198f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 11995b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 1200f3db423dSLydia Wang .count = 1, 1201f3db423dSLydia Wang .info = vt1716s_dmic_info, 1202f3db423dSLydia Wang .get = vt1716s_dmic_get, 1203f3db423dSLydia Wang .put = vt1716s_dmic_put, 1204f3db423dSLydia Wang }, 1205f3db423dSLydia Wang {} /* end */ 1206f3db423dSLydia Wang }; 1207f3db423dSLydia Wang 1208f3db423dSLydia Wang 1209f3db423dSLydia Wang /* mono-out mixer elements */ 121090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 1211f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 1212f3db423dSLydia Wang { } /* end */ 1213f3db423dSLydia Wang }; 1214f3db423dSLydia Wang 1215096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 1216f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 1217f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 1218f3db423dSLydia Wang /* don't bybass mixer */ 1219f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 1220f3db423dSLydia Wang /* Enable mono output */ 1221f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 1222f3db423dSLydia Wang { } 1223f3db423dSLydia Wang }; 1224f3db423dSLydia Wang 12253e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 12263e95b9abSLydia Wang { 12273e95b9abSLydia Wang struct via_spec *spec = codec->spec; 12283e95b9abSLydia Wang int imux_is_smixer; 12293e95b9abSLydia Wang unsigned int parm; 12303e95b9abSLydia Wang unsigned int mono_out, present; 12313e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 12323e95b9abSLydia Wang imux_is_smixer = 12333e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 12343e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 12353e95b9abSLydia Wang /* inputs */ 12363e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 12373e95b9abSLydia Wang parm = AC_PWRST_D3; 12383e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 12393e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 12403e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12413e95b9abSLydia Wang if (imux_is_smixer) 12423e95b9abSLydia Wang parm = AC_PWRST_D0; 12433e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 1244054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1245054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 12463e95b9abSLydia Wang 12473e95b9abSLydia Wang parm = AC_PWRST_D3; 12483e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12493e95b9abSLydia Wang /* PW11 (22h) */ 12503e95b9abSLydia Wang if (spec->dmic_enabled) 12513e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 12523e95b9abSLydia Wang else 1253054d867eSTakashi Iwai update_power_state(codec, 0x22, AC_PWRST_D3); 12543e95b9abSLydia Wang 12553e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 1256054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 1257054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 12583e95b9abSLydia Wang 12593e95b9abSLydia Wang /* outputs */ 12603e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 12613e95b9abSLydia Wang parm = AC_PWRST_D3; 12623e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 12633e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 1264b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12653e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 1266054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1267054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 12683e95b9abSLydia Wang 12693e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 12703e95b9abSLydia Wang parm = AC_PWRST_D3; 12713e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 12723e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 1273b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12743e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 1275054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 12763e95b9abSLydia Wang 12773e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 1278b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12793e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 1280054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 12813e95b9abSLydia Wang 12823e95b9abSLydia Wang /* Mono out */ 12833e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 12843e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 12853e95b9abSLydia Wang 12863e95b9abSLydia Wang if (present) 12873e95b9abSLydia Wang mono_out = 0; 12883e95b9abSLydia Wang else { 12893e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 1290b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && present) 12913e95b9abSLydia Wang mono_out = 0; 12923e95b9abSLydia Wang else 12933e95b9abSLydia Wang mono_out = 1; 12943e95b9abSLydia Wang } 12953e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 1296054d867eSTakashi Iwai update_power_state(codec, 0x28, parm); 1297054d867eSTakashi Iwai update_power_state(codec, 0x29, parm); 1298054d867eSTakashi Iwai update_power_state(codec, 0x2a, parm); 12993e95b9abSLydia Wang 13003e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 13013e95b9abSLydia Wang parm = AC_PWRST_D3; 13023e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 13033e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 13043e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 1305b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1306054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 13073e95b9abSLydia Wang 13083e95b9abSLydia Wang /* force to D0 for internal Speaker */ 13093e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 1310054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 1311054d867eSTakashi Iwai update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); 13123e95b9abSLydia Wang } 13133e95b9abSLydia Wang 1314f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 1315f3db423dSLydia Wang { 1316f3db423dSLydia Wang struct via_spec *spec; 1317f3db423dSLydia Wang int err; 1318f3db423dSLydia Wang 1319f3db423dSLydia Wang /* create a codec specific record */ 13205b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1321f3db423dSLydia Wang if (spec == NULL) 1322f3db423dSLydia Wang return -ENOMEM; 1323f3db423dSLydia Wang 1324b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 1325d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 1326d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 1327620e2b28STakashi Iwai 1328f3db423dSLydia Wang /* automatic parse from the BIOS config */ 132912daef65STakashi Iwai err = via_parse_auto_config(codec); 1330f3db423dSLydia Wang if (err < 0) { 1331f3db423dSLydia Wang via_free(codec); 1332f3db423dSLydia Wang return err; 1333f3db423dSLydia Wang } 1334f3db423dSLydia Wang 1335096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 1336f3db423dSLydia Wang 1337b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 1338f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 1339f3db423dSLydia Wang 1340f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 1341f3db423dSLydia Wang 13423e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 1343f3db423dSLydia Wang return 0; 1344f3db423dSLydia Wang } 134525eaba2fSLydia Wang 134625eaba2fSLydia Wang /* for vt2002P */ 134725eaba2fSLydia Wang 1348096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 1349eadb9a80SLydia Wang /* Class-D speaker related verbs */ 1350eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 1351eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 1352eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 135325eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 135425eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 135525eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 135625eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 135725eaba2fSLydia Wang { } 135825eaba2fSLydia Wang }; 13594a918ffeSTakashi Iwai 1360096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 136111890956SLydia Wang /* Enable Boost Volume backdoor */ 136211890956SLydia Wang {0x1, 0xfb9, 0x24}, 136311890956SLydia Wang /* Enable AOW0 to MW9 */ 136411890956SLydia Wang {0x1, 0xfb8, 0x88}, 136511890956SLydia Wang { } 136611890956SLydia Wang }; 136725eaba2fSLydia Wang 13683e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 13693e95b9abSLydia Wang { 13703e95b9abSLydia Wang struct via_spec *spec = codec->spec; 13713e95b9abSLydia Wang int imux_is_smixer; 13723e95b9abSLydia Wang unsigned int parm; 13733e95b9abSLydia Wang unsigned int present; 13743e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 13753e95b9abSLydia Wang imux_is_smixer = 13763e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 13773e95b9abSLydia Wang /* inputs */ 13783e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 13793e95b9abSLydia Wang parm = AC_PWRST_D3; 13803e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 13813e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 13823e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 13833e95b9abSLydia Wang parm = AC_PWRST_D0; 13843e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 1385054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1386054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1387054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1388054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 13893e95b9abSLydia Wang 13903e95b9abSLydia Wang /* outputs */ 13913e95b9abSLydia Wang /* AOW0 (8h)*/ 1392054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 13933e95b9abSLydia Wang 139411890956SLydia Wang if (spec->codec_type == VT1802) { 139511890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 139611890956SLydia Wang parm = AC_PWRST_D3; 139711890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 1398054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1399054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 140011890956SLydia Wang } else { 14013e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 14023e95b9abSLydia Wang parm = AC_PWRST_D3; 14033e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1404054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1405054d867eSTakashi Iwai update_power_state(codec, 0x37, parm); 140611890956SLydia Wang } 14073e95b9abSLydia Wang 140811890956SLydia Wang if (spec->codec_type == VT1802) { 140911890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 141011890956SLydia Wang parm = AC_PWRST_D3; 141111890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 1412054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1413054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 141411890956SLydia Wang } else { 14153e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 14163e95b9abSLydia Wang parm = AC_PWRST_D3; 14173e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1418054d867eSTakashi Iwai update_power_state(codec, 0x19, parm); 1419054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 142011890956SLydia Wang } 14213e95b9abSLydia Wang 1422b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1423054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 14243e95b9abSLydia Wang 14253e95b9abSLydia Wang /* Class-D */ 14263e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 14273e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 14283e95b9abSLydia Wang 14293e95b9abSLydia Wang parm = AC_PWRST_D3; 14303e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 14313e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 143211890956SLydia Wang if (spec->codec_type == VT1802) 1433054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 143411890956SLydia Wang else 1435054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1436054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 14373e95b9abSLydia Wang 14383e95b9abSLydia Wang /* Mono Out */ 14393e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 14403e95b9abSLydia Wang 14413e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 144211890956SLydia Wang if (spec->codec_type == VT1802) { 144311890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 1444054d867eSTakashi Iwai update_power_state(codec, 0x33, parm); 1445054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1446054d867eSTakashi Iwai update_power_state(codec, 0x3c, parm); 144711890956SLydia Wang } else { 14483e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 1449054d867eSTakashi Iwai update_power_state(codec, 0x31, parm); 1450054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1451054d867eSTakashi Iwai update_power_state(codec, 0x3b, parm); 145211890956SLydia Wang } 14533e95b9abSLydia Wang /* MW9 (21h) */ 14543e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1455054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D0); 14563e95b9abSLydia Wang else 1457054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D3); 14583e95b9abSLydia Wang } 145925eaba2fSLydia Wang 14604b527b65SDavid Henningsson /* 14614b527b65SDavid Henningsson * pin fix-up 14624b527b65SDavid Henningsson */ 14634b527b65SDavid Henningsson enum { 14644b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1465d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 14664b527b65SDavid Henningsson }; 14674b527b65SDavid Henningsson 14684b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 14694b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 14704b527b65SDavid Henningsson { 14714b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 14724b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 14734b527b65SDavid Henningsson } 14744b527b65SDavid Henningsson 14754b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 14764b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 14774b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 14784b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 14794b527b65SDavid Henningsson }, 1480d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1481d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1482d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1483d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1484d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1485d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1486d5266125STakashi Iwai { } 1487d5266125STakashi Iwai } 1488d5266125STakashi Iwai }, 14894b527b65SDavid Henningsson }; 14904b527b65SDavid Henningsson 14914b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1492d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 14934b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 14944b527b65SDavid Henningsson {} 14954b527b65SDavid Henningsson }; 14964b527b65SDavid Henningsson 1497ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1498ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1499ef4da458STakashi Iwai */ 1500ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1501ef4da458STakashi Iwai { 1502ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1503ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1504ef4da458STakashi Iwai 1505ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1506ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1507ef4da458STakashi Iwai } 1508ef4da458STakashi Iwai 150925eaba2fSLydia Wang /* patch for vt2002P */ 151025eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 151125eaba2fSLydia Wang { 151225eaba2fSLydia Wang struct via_spec *spec; 151325eaba2fSLydia Wang int err; 151425eaba2fSLydia Wang 151525eaba2fSLydia Wang /* create a codec specific record */ 15165b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 151725eaba2fSLydia Wang if (spec == NULL) 151825eaba2fSLydia Wang return -ENOMEM; 151925eaba2fSLydia Wang 1520b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1521d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1522d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1523ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1524ef4da458STakashi Iwai fix_vt1802_connections(codec); 152530b45033STakashi Iwai add_secret_dac_path(codec); 1526620e2b28STakashi Iwai 15274b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 15284b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 15294b527b65SDavid Henningsson 153025eaba2fSLydia Wang /* automatic parse from the BIOS config */ 153112daef65STakashi Iwai err = via_parse_auto_config(codec); 153225eaba2fSLydia Wang if (err < 0) { 153325eaba2fSLydia Wang via_free(codec); 153425eaba2fSLydia Wang return err; 153525eaba2fSLydia Wang } 153625eaba2fSLydia Wang 153711890956SLydia Wang if (spec->codec_type == VT1802) 15384a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 153911890956SLydia Wang else 15404a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 154111890956SLydia Wang 154225eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 154325eaba2fSLydia Wang 15443e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 154525eaba2fSLydia Wang return 0; 154625eaba2fSLydia Wang } 1547ab6734e7SLydia Wang 1548ab6734e7SLydia Wang /* for vt1812 */ 1549ab6734e7SLydia Wang 1550096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1551ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1552ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1553ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1554ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1555ab6734e7SLydia Wang { } 1556ab6734e7SLydia Wang }; 1557ab6734e7SLydia Wang 15583e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 15593e95b9abSLydia Wang { 15603e95b9abSLydia Wang struct via_spec *spec = codec->spec; 15613e95b9abSLydia Wang unsigned int parm; 15623e95b9abSLydia Wang unsigned int present; 15633e95b9abSLydia Wang /* inputs */ 15643e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 15653e95b9abSLydia Wang parm = AC_PWRST_D3; 15663e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 15673e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 15683e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 15693e95b9abSLydia Wang parm = AC_PWRST_D0; 15703e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 1571054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1572054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1573054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1574054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 15753e95b9abSLydia Wang 15763e95b9abSLydia Wang /* outputs */ 15773e95b9abSLydia Wang /* AOW0 (8h)*/ 1578054d867eSTakashi Iwai update_power_state(codec, 0x8, AC_PWRST_D0); 15793e95b9abSLydia Wang 15803e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 15813e95b9abSLydia Wang parm = AC_PWRST_D3; 15823e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1583054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1584054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 15853e95b9abSLydia Wang 15863e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 15873e95b9abSLydia Wang parm = AC_PWRST_D3; 15883e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1589054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1590054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 1591b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1592054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 15933e95b9abSLydia Wang 15943e95b9abSLydia Wang /* Internal Speaker */ 15953e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 15963e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 15973e95b9abSLydia Wang 15983e95b9abSLydia Wang parm = AC_PWRST_D3; 15993e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 16003e95b9abSLydia Wang if (present) { 1601054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D3); 1602054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D3); 16033e95b9abSLydia Wang } else { 1604054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D0); 1605054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D0); 16063e95b9abSLydia Wang } 16073e95b9abSLydia Wang 16083e95b9abSLydia Wang 16093e95b9abSLydia Wang /* Mono Out */ 16103e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 16113e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 16123e95b9abSLydia Wang 16133e95b9abSLydia Wang parm = AC_PWRST_D3; 16143e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 16153e95b9abSLydia Wang if (present) { 1616054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D3); 1617054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D3); 1618054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D3); 16193e95b9abSLydia Wang } else { 1620054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D0); 1621054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D0); 1622054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D0); 16233e95b9abSLydia Wang } 16243e95b9abSLydia Wang 16253e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 16263e95b9abSLydia Wang parm = AC_PWRST_D3; 16273e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 1628054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 1629054d867eSTakashi Iwai update_power_state(codec, 0x3d, parm); 16303e95b9abSLydia Wang 16313e95b9abSLydia Wang } 1632ab6734e7SLydia Wang 1633ab6734e7SLydia Wang /* patch for vt1812 */ 1634ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1635ab6734e7SLydia Wang { 1636ab6734e7SLydia Wang struct via_spec *spec; 1637ab6734e7SLydia Wang int err; 1638ab6734e7SLydia Wang 1639ab6734e7SLydia Wang /* create a codec specific record */ 16405b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1641ab6734e7SLydia Wang if (spec == NULL) 1642ab6734e7SLydia Wang return -ENOMEM; 1643ab6734e7SLydia Wang 1644b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1645d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1646d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 164730b45033STakashi Iwai add_secret_dac_path(codec); 1648620e2b28STakashi Iwai 1649ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 165012daef65STakashi Iwai err = via_parse_auto_config(codec); 1651ab6734e7SLydia Wang if (err < 0) { 1652ab6734e7SLydia Wang via_free(codec); 1653ab6734e7SLydia Wang return err; 1654ab6734e7SLydia Wang } 1655ab6734e7SLydia Wang 1656096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1657ab6734e7SLydia Wang 1658ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 1659ab6734e7SLydia Wang 16603e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 1661ab6734e7SLydia Wang return 0; 1662ab6734e7SLydia Wang } 1663ab6734e7SLydia Wang 166443737e0aSLydia Wang /* patch for vt3476 */ 166543737e0aSLydia Wang 166643737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 166743737e0aSLydia Wang /* Enable DMic 8/16/32K */ 166843737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 166943737e0aSLydia Wang /* Enable Boost Volume backdoor */ 167043737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 167143737e0aSLydia Wang /* Enable AOW-MW9 path */ 167243737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 167343737e0aSLydia Wang { } 167443737e0aSLydia Wang }; 167543737e0aSLydia Wang 167643737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec) 167743737e0aSLydia Wang { 167843737e0aSLydia Wang struct via_spec *spec = codec->spec; 167943737e0aSLydia Wang int imux_is_smixer; 168043737e0aSLydia Wang unsigned int parm, parm2; 168143737e0aSLydia Wang /* MUX10 (1eh) = stereo mixer */ 168243737e0aSLydia Wang imux_is_smixer = 168343737e0aSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; 168443737e0aSLydia Wang /* inputs */ 168543737e0aSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 168643737e0aSLydia Wang parm = AC_PWRST_D3; 168743737e0aSLydia Wang set_pin_power_state(codec, 0x29, &parm); 168843737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 168943737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 169043737e0aSLydia Wang if (imux_is_smixer) 169143737e0aSLydia Wang parm = AC_PWRST_D0; 169243737e0aSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 169343737e0aSLydia Wang update_power_state(codec, 0x1e, parm); 169443737e0aSLydia Wang update_power_state(codec, 0x1f, parm); 169543737e0aSLydia Wang update_power_state(codec, 0x10, parm); 169643737e0aSLydia Wang update_power_state(codec, 0x11, parm); 169743737e0aSLydia Wang 169843737e0aSLydia Wang /* outputs */ 169943737e0aSLydia Wang /* PW3 (27h), MW3(37h), AOW3 (bh) */ 170043737e0aSLydia Wang if (spec->codec_type == VT1705CF) { 170143737e0aSLydia Wang parm = AC_PWRST_D3; 170243737e0aSLydia Wang update_power_state(codec, 0x27, parm); 170343737e0aSLydia Wang update_power_state(codec, 0x37, parm); 170443737e0aSLydia Wang } else { 170543737e0aSLydia Wang parm = AC_PWRST_D3; 170643737e0aSLydia Wang set_pin_power_state(codec, 0x27, &parm); 170743737e0aSLydia Wang update_power_state(codec, 0x37, parm); 170843737e0aSLydia Wang } 170943737e0aSLydia Wang 171043737e0aSLydia Wang /* PW2 (26h), MW2(36h), AOW2 (ah) */ 171143737e0aSLydia Wang parm = AC_PWRST_D3; 171243737e0aSLydia Wang set_pin_power_state(codec, 0x26, &parm); 171343737e0aSLydia Wang update_power_state(codec, 0x36, parm); 1714b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 171543737e0aSLydia Wang /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ 171643737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 171743737e0aSLydia Wang update_power_state(codec, 0x3b, parm); 171843737e0aSLydia Wang update_power_state(codec, 0x1b, parm); 171943737e0aSLydia Wang } 172043737e0aSLydia Wang update_conv_power_state(codec, 0xa, parm, 2); 172143737e0aSLydia Wang 172243737e0aSLydia Wang /* PW1 (25h), MW1(35h), AOW1 (9h) */ 172343737e0aSLydia Wang parm = AC_PWRST_D3; 172443737e0aSLydia Wang set_pin_power_state(codec, 0x25, &parm); 172543737e0aSLydia Wang update_power_state(codec, 0x35, parm); 1726b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 172743737e0aSLydia Wang /* PW6(2ah), MW6(3ah), MUX6(1ah) */ 172843737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 172943737e0aSLydia Wang update_power_state(codec, 0x3a, parm); 173043737e0aSLydia Wang update_power_state(codec, 0x1a, parm); 173143737e0aSLydia Wang } 173243737e0aSLydia Wang update_conv_power_state(codec, 0x9, parm, 1); 173343737e0aSLydia Wang 173443737e0aSLydia Wang /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ 173543737e0aSLydia Wang parm = AC_PWRST_D3; 173643737e0aSLydia Wang set_pin_power_state(codec, 0x28, &parm); 173743737e0aSLydia Wang update_power_state(codec, 0x38, parm); 173843737e0aSLydia Wang update_power_state(codec, 0x18, parm); 1739b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 174043737e0aSLydia Wang update_conv_power_state(codec, 0xb, parm, 3); 174143737e0aSLydia Wang parm2 = parm; /* for pin 0x0b */ 174243737e0aSLydia Wang 174343737e0aSLydia Wang /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ 174443737e0aSLydia Wang parm = AC_PWRST_D3; 174543737e0aSLydia Wang set_pin_power_state(codec, 0x24, &parm); 174643737e0aSLydia Wang update_power_state(codec, 0x34, parm); 1747b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 174843737e0aSLydia Wang parm = parm2; 174943737e0aSLydia Wang update_conv_power_state(codec, 0x8, parm, 0); 175043737e0aSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 175143737e0aSLydia Wang update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); 175243737e0aSLydia Wang } 175343737e0aSLydia Wang 175443737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 175543737e0aSLydia Wang { 175643737e0aSLydia Wang struct via_spec *spec; 175743737e0aSLydia Wang int err; 175843737e0aSLydia Wang 175943737e0aSLydia Wang /* create a codec specific record */ 176043737e0aSLydia Wang spec = via_new_spec(codec); 176143737e0aSLydia Wang if (spec == NULL) 176243737e0aSLydia Wang return -ENOMEM; 176343737e0aSLydia Wang 1764b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 176543737e0aSLydia Wang add_secret_dac_path(codec); 176643737e0aSLydia Wang 176743737e0aSLydia Wang /* automatic parse from the BIOS config */ 176843737e0aSLydia Wang err = via_parse_auto_config(codec); 176943737e0aSLydia Wang if (err < 0) { 177043737e0aSLydia Wang via_free(codec); 177143737e0aSLydia Wang return err; 177243737e0aSLydia Wang } 177343737e0aSLydia Wang 177443737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 177543737e0aSLydia Wang 177643737e0aSLydia Wang codec->patch_ops = via_patch_ops; 177743737e0aSLydia Wang 177843737e0aSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt3476; 177943737e0aSLydia Wang 178043737e0aSLydia Wang return 0; 178143737e0aSLydia Wang } 178243737e0aSLydia Wang 1783c577b8a1SJoseph Chan /* 1784c577b8a1SJoseph Chan * patch entries 1785c577b8a1SJoseph Chan */ 178690dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 17873218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 17883218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 17893218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 17903218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 17913218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 1792ddd304d8STakashi Iwai .patch = patch_vt1709}, 17933218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 1794ddd304d8STakashi Iwai .patch = patch_vt1709}, 17953218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 1796ddd304d8STakashi Iwai .patch = patch_vt1709}, 17973218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 1798ddd304d8STakashi Iwai .patch = patch_vt1709}, 17993218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 1800ddd304d8STakashi Iwai .patch = patch_vt1709}, 18013218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 1802ddd304d8STakashi Iwai .patch = patch_vt1709}, 18033218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 1804ddd304d8STakashi Iwai .patch = patch_vt1709}, 18053218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 1806ddd304d8STakashi Iwai .patch = patch_vt1709}, 18073218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 1808ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18093218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 1810ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18113218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 1812ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18133218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 1814ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18153218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 1816ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18173218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 1818ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18193218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 1820ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18213218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 1822ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18233218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 1824d949cac1SHarald Welte .patch = patch_vt1708S}, 18253218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 1826d949cac1SHarald Welte .patch = patch_vt1708S}, 18273218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 1828d949cac1SHarald Welte .patch = patch_vt1708S}, 18293218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 1830d949cac1SHarald Welte .patch = patch_vt1708S}, 1831bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 1832d949cac1SHarald Welte .patch = patch_vt1708S}, 18333218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 1834d949cac1SHarald Welte .patch = patch_vt1708S}, 18353218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 1836d949cac1SHarald Welte .patch = patch_vt1708S}, 18373218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 1838d949cac1SHarald Welte .patch = patch_vt1708S}, 18393218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 1840d949cac1SHarald Welte .patch = patch_vt1702}, 18413218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 1842d949cac1SHarald Welte .patch = patch_vt1702}, 18433218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 1844d949cac1SHarald Welte .patch = patch_vt1702}, 18453218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 1846d949cac1SHarald Welte .patch = patch_vt1702}, 18473218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 1848d949cac1SHarald Welte .patch = patch_vt1702}, 18493218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 1850d949cac1SHarald Welte .patch = patch_vt1702}, 18513218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 1852d949cac1SHarald Welte .patch = patch_vt1702}, 18533218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 1854d949cac1SHarald Welte .patch = patch_vt1702}, 1855eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 1856eb7188caSLydia Wang .patch = patch_vt1718S}, 1857eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 1858eb7188caSLydia Wang .patch = patch_vt1718S}, 1859bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 1860bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1861bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 1862bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1863f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 1864f3db423dSLydia Wang .patch = patch_vt1716S}, 1865f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 1866f3db423dSLydia Wang .patch = patch_vt1716S}, 186725eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 186825eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 1869ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 187036dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 187136dd5c4aSLydia Wang .patch = patch_vt1708S}, 187211890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 187311890956SLydia Wang .patch = patch_vt2002P}, 187411890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 187511890956SLydia Wang .patch = patch_vt2002P}, 187643737e0aSLydia Wang { .id = 0x11064760, .name = "VT1705CF", 187743737e0aSLydia Wang .patch = patch_vt3476}, 18786121b84aSLydia Wang { .id = 0x11064761, .name = "VT1708SCE", 18796121b84aSLydia Wang .patch = patch_vt3476}, 18806121b84aSLydia Wang { .id = 0x11064762, .name = "VT1808", 18816121b84aSLydia Wang .patch = patch_vt3476}, 1882c577b8a1SJoseph Chan {} /* terminator */ 1883c577b8a1SJoseph Chan }; 18841289e9e8STakashi Iwai 18851289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 18861289e9e8STakashi Iwai 18871289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 18881289e9e8STakashi Iwai .preset = snd_hda_preset_via, 18891289e9e8STakashi Iwai .owner = THIS_MODULE, 18901289e9e8STakashi Iwai }; 18911289e9e8STakashi Iwai 18921289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 18931289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 18941289e9e8STakashi Iwai 18951289e9e8STakashi Iwai static int __init patch_via_init(void) 18961289e9e8STakashi Iwai { 18971289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 18981289e9e8STakashi Iwai } 18991289e9e8STakashi Iwai 19001289e9e8STakashi Iwai static void __exit patch_via_exit(void) 19011289e9e8STakashi Iwai { 19021289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 19031289e9e8STakashi Iwai } 19041289e9e8STakashi Iwai 19051289e9e8STakashi Iwai module_init(patch_via_init) 19061289e9e8STakashi Iwai module_exit(patch_via_exit) 1907