1c577b8a1SJoseph Chan /* 2c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec 3c577b8a1SJoseph Chan * 48e86597fSLydia Wang * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec 5c577b8a1SJoseph Chan * 68e86597fSLydia Wang * (C) 2006-2009 VIA Technology, Inc. 78e86597fSLydia Wang * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> 8c577b8a1SJoseph Chan * 9c577b8a1SJoseph Chan * This driver is free software; you can redistribute it and/or modify 10c577b8a1SJoseph Chan * it under the terms of the GNU General Public License as published by 11c577b8a1SJoseph Chan * the Free Software Foundation; either version 2 of the License, or 12c577b8a1SJoseph Chan * (at your option) any later version. 13c577b8a1SJoseph Chan * 14c577b8a1SJoseph Chan * This driver is distributed in the hope that it will be useful, 15c577b8a1SJoseph Chan * but WITHOUT ANY WARRANTY; without even the implied warranty of 16c577b8a1SJoseph Chan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17c577b8a1SJoseph Chan * GNU General Public License for more details. 18c577b8a1SJoseph Chan * 19c577b8a1SJoseph Chan * You should have received a copy of the GNU General Public License 20c577b8a1SJoseph Chan * along with this program; if not, write to the Free Software 21c577b8a1SJoseph Chan * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22c577b8a1SJoseph Chan */ 23c577b8a1SJoseph Chan 24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ 25c577b8a1SJoseph Chan /* */ 26c577b8a1SJoseph Chan /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ 27c577b8a1SJoseph Chan /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ 28c577b8a1SJoseph Chan /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ 29c577b8a1SJoseph Chan /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ 30f7278fd0SJosepch Chan /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ 31f7278fd0SJosepch Chan /* 2007-09-17 Lydia Wang Add VT1708B codec support */ 3276d9b0ddSHarald Welte /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ 33fb4cb772SHarald Welte /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ 34d949cac1SHarald Welte /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ 3569e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ 360aa62aefSHarald Welte /* 2008-04-09 Lydia Wang Add Independent HP feature */ 3798aa34c0SHarald Welte /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ 38d7426329SHarald Welte /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ 398e86597fSLydia Wang /* 2009-02-16 Logan Li Add support for VT1718S */ 408e86597fSLydia Wang /* 2009-03-13 Logan Li Add support for VT1716S */ 418e86597fSLydia Wang /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ 428e86597fSLydia Wang /* 2009-07-08 Lydia Wang Add support for VT2002P */ 438e86597fSLydia Wang /* 2009-07-21 Lydia Wang Add support for VT1812 */ 4436dd5c4aSLydia Wang /* 2009-09-19 Lydia Wang Add support for VT1818S */ 45c577b8a1SJoseph Chan /* */ 46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan 49c577b8a1SJoseph Chan #include <linux/init.h> 50c577b8a1SJoseph Chan #include <linux/delay.h> 51c577b8a1SJoseph Chan #include <linux/slab.h> 52da155d5bSPaul Gortmaker #include <linux/module.h> 53c577b8a1SJoseph Chan #include <sound/core.h> 540aa62aefSHarald Welte #include <sound/asoundef.h> 55c577b8a1SJoseph Chan #include "hda_codec.h" 56c577b8a1SJoseph Chan #include "hda_local.h" 57128bc4baSTakashi Iwai #include "hda_auto_parser.h" 581835a0f9STakashi Iwai #include "hda_jack.h" 59b3f6008fSTakashi Iwai #include "hda_generic.h" 60c577b8a1SJoseph Chan 61c577b8a1SJoseph Chan /* Pin Widget NID */ 6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 64c577b8a1SJoseph Chan 65d7426329SHarald Welte enum VIA_HDA_CODEC { 66d7426329SHarald Welte UNKNOWN = -1, 67d7426329SHarald Welte VT1708, 68d7426329SHarald Welte VT1709_10CH, 69d7426329SHarald Welte VT1709_6CH, 70d7426329SHarald Welte VT1708B_8CH, 71d7426329SHarald Welte VT1708B_4CH, 72d7426329SHarald Welte VT1708S, 73518bf3baSLydia Wang VT1708BCE, 74d7426329SHarald Welte VT1702, 75eb7188caSLydia Wang VT1718S, 76f3db423dSLydia Wang VT1716S, 7725eaba2fSLydia Wang VT2002P, 78ab6734e7SLydia Wang VT1812, 7911890956SLydia Wang VT1802, 8043737e0aSLydia Wang VT1705CF, 816121b84aSLydia Wang VT1808, 82d7426329SHarald Welte CODEC_TYPES, 83d7426329SHarald Welte }; 84d7426329SHarald Welte 8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 8611890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 8711890956SLydia Wang (spec)->codec_type == VT1812 ||\ 8811890956SLydia Wang (spec)->codec_type == VT1802) 8911890956SLydia Wang 901f2e99feSLydia Wang struct via_spec { 91b3f6008fSTakashi Iwai struct hda_gen_spec gen; 92b3f6008fSTakashi Iwai 931f2e99feSLydia Wang /* codec parameterization */ 9490dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 951f2e99feSLydia Wang unsigned int num_mixers; 961f2e99feSLydia Wang 9790dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 981f2e99feSLydia Wang unsigned int num_iverbs; 991f2e99feSLydia Wang 1001f2e99feSLydia Wang /* HP mode source */ 101f3db423dSLydia Wang unsigned int dmic_enabled; 10224088a58STakashi Iwai unsigned int no_pin_power_ctl; 1031f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1041f2e99feSLydia Wang 105e9d010c2STakashi Iwai /* analog low-power control */ 106e9d010c2STakashi Iwai bool alc_mode; 107e9d010c2STakashi Iwai 1081f2e99feSLydia Wang /* work to check hp jack state */ 109187d333eSTakashi Iwai int hp_work_active; 110e06e5a29STakashi Iwai int vt1708_jack_detect; 1113e95b9abSLydia Wang 1123e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 11343737e0aSLydia Wang unsigned int dac_stream_tag[4]; 1141f2e99feSLydia Wang }; 1151f2e99feSLydia Wang 1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo, 118b3f6008fSTakashi Iwai struct hda_codec *codec, 119b3f6008fSTakashi Iwai struct snd_pcm_substream *substream, 120b3f6008fSTakashi Iwai int action); 121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl); 122b3f6008fSTakashi Iwai 1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec) 1245b0cb1d8SJaroslav Kysela { 1255b0cb1d8SJaroslav Kysela struct via_spec *spec; 1265b0cb1d8SJaroslav Kysela 1275b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1285b0cb1d8SJaroslav Kysela if (spec == NULL) 1295b0cb1d8SJaroslav Kysela return NULL; 1305b0cb1d8SJaroslav Kysela 1315b0cb1d8SJaroslav Kysela codec->spec = spec; 132b3f6008fSTakashi Iwai snd_hda_gen_spec_init(&spec->gen); 1330341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1340341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1350341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1360341ccd7SLydia Wang spec->codec_type = VT1708S; 137b3f6008fSTakashi Iwai spec->no_pin_power_ctl = 1; 13813961170STakashi Iwai spec->gen.indep_hp = 1; 13905909d5cSTakashi Iwai spec->gen.keep_eapd_on = 1; 140b3f6008fSTakashi Iwai spec->gen.pcm_playback_hook = via_playback_pcm_hook; 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); 210*7eaa9161SWang 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 { 467c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 468c577b8a1SJoseph Chan 469c577b8a1SJoseph Chan if (!spec) 470c577b8a1SJoseph Chan return; 471c577b8a1SJoseph Chan 472b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 473b3f6008fSTakashi Iwai snd_hda_gen_spec_free(&spec->gen); 474a86a88eaSTakashi Iwai kfree(spec); 475c577b8a1SJoseph Chan } 476c577b8a1SJoseph Chan 4772a43952aSTakashi Iwai #ifdef CONFIG_PM 47868cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec) 4791f2e99feSLydia Wang { 4801f2e99feSLydia Wang struct via_spec *spec = codec->spec; 481b3f6008fSTakashi Iwai vt1708_stop_hp_work(codec); 48294c142a1SDavid Henningsson 48394c142a1SDavid Henningsson /* Fix pop noise on headphones */ 4842c38d990STakashi Iwai if (spec->codec_type == VT1802) 4852c38d990STakashi Iwai snd_hda_shutup_pins(codec); 48694c142a1SDavid Henningsson 4871f2e99feSLydia Wang return 0; 4881f2e99feSLydia Wang } 4891f2e99feSLydia Wang #endif 4901f2e99feSLydia Wang 49183012a7cSTakashi Iwai #ifdef CONFIG_PM 492cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 493cb53c626STakashi Iwai { 494cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 495b3f6008fSTakashi Iwai set_widgets_power_state(codec); 496b3f6008fSTakashi Iwai analog_low_current_mode(codec); 497b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 498b3f6008fSTakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid); 499cb53c626STakashi Iwai } 500cb53c626STakashi Iwai #endif 501cb53c626STakashi Iwai 502c577b8a1SJoseph Chan /* 503c577b8a1SJoseph Chan */ 5045d41762aSTakashi Iwai 5055d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 5065d41762aSTakashi Iwai 50790dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 508c577b8a1SJoseph Chan .build_controls = via_build_controls, 509b3f6008fSTakashi Iwai .build_pcms = snd_hda_gen_build_pcms, 510c577b8a1SJoseph Chan .init = via_init, 511c577b8a1SJoseph Chan .free = via_free, 5124e2d16d3SDavid Henningsson .unsol_event = snd_hda_jack_unsol_event, 5132a43952aSTakashi Iwai #ifdef CONFIG_PM 5141f2e99feSLydia Wang .suspend = via_suspend, 515cb53c626STakashi Iwai .check_power_status = via_check_power_status, 516cb53c626STakashi Iwai #endif 517c577b8a1SJoseph Chan }; 518c577b8a1SJoseph Chan 5194a79616dSTakashi Iwai 520b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 521b3f6008fSTakashi Iwai /* power down jack detect function */ 522b3f6008fSTakashi Iwai {0x1, 0xf81, 0x1}, 523b3f6008fSTakashi Iwai { } 5244a79616dSTakashi Iwai }; 52576d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 52676d9b0ddSHarald Welte { 52776d9b0ddSHarald Welte unsigned int def_conf; 52876d9b0ddSHarald Welte unsigned char seqassoc; 52976d9b0ddSHarald Welte 5302f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 53176d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 53276d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 53382ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 53482ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 53576d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 5362f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 53776d9b0ddSHarald Welte } 53876d9b0ddSHarald Welte 53976d9b0ddSHarald Welte return; 54076d9b0ddSHarald Welte } 54176d9b0ddSHarald Welte 542e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 5431f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5441f2e99feSLydia Wang { 5451f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5461f2e99feSLydia Wang struct via_spec *spec = codec->spec; 5471f2e99feSLydia Wang 5481f2e99feSLydia Wang if (spec->codec_type != VT1708) 5491f2e99feSLydia Wang return 0; 550e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 5511f2e99feSLydia Wang return 0; 5521f2e99feSLydia Wang } 5531f2e99feSLydia Wang 554e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 5551f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 5561f2e99feSLydia Wang { 5571f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5581f2e99feSLydia Wang struct via_spec *spec = codec->spec; 559187d333eSTakashi Iwai int val; 5601f2e99feSLydia Wang 5611f2e99feSLydia Wang if (spec->codec_type != VT1708) 5621f2e99feSLydia Wang return 0; 563187d333eSTakashi Iwai val = !!ucontrol->value.integer.value[0]; 564187d333eSTakashi Iwai if (spec->vt1708_jack_detect == val) 565187d333eSTakashi Iwai return 0; 566187d333eSTakashi Iwai spec->vt1708_jack_detect = val; 567b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 568187d333eSTakashi Iwai return 1; 5691f2e99feSLydia Wang } 5701f2e99feSLydia Wang 571b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = { 572b3f6008fSTakashi Iwai { 5731f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5741f2e99feSLydia Wang .name = "Jack Detect", 5751f2e99feSLydia Wang .count = 1, 5761f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 577e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 578e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 579b3f6008fSTakashi Iwai }, 580b3f6008fSTakashi Iwai {} /* terminator */ 5811f2e99feSLydia Wang }; 5821f2e99feSLydia Wang 583b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) 5844e2d16d3SDavid Henningsson { 5854e2d16d3SDavid Henningsson set_widgets_power_state(codec); 586b3f6008fSTakashi Iwai snd_hda_gen_hp_automute(codec, tbl); 587b3f6008fSTakashi Iwai } 588b3f6008fSTakashi Iwai 589b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl) 590b3f6008fSTakashi Iwai { 591b3f6008fSTakashi Iwai set_widgets_power_state(codec); 592b3f6008fSTakashi Iwai snd_hda_gen_line_automute(codec, tbl); 5934e2d16d3SDavid Henningsson } 5944e2d16d3SDavid Henningsson 5954e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl) 5964e2d16d3SDavid Henningsson { 5974e2d16d3SDavid Henningsson set_widgets_power_state(codec); 5984e2d16d3SDavid Henningsson } 5994e2d16d3SDavid Henningsson 600b3f6008fSTakashi Iwai #define VIA_JACK_EVENT (HDA_GEN_LAST_EVENT + 1) 601b3f6008fSTakashi Iwai 602b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec) 6034a918ffeSTakashi Iwai { 6044a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 605b3f6008fSTakashi Iwai struct auto_pin_cfg *cfg = &spec->gen.autocfg; 606b3f6008fSTakashi Iwai hda_nid_t pin; 6074a918ffeSTakashi Iwai int i; 6084a918ffeSTakashi Iwai 609b3f6008fSTakashi Iwai spec->gen.hp_automute_hook = via_hp_automute; 6104a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 611b3f6008fSTakashi Iwai spec->gen.line_automute_hook = via_line_automute; 6124e2d16d3SDavid Henningsson 6134a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 614b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 615b3f6008fSTakashi Iwai if (pin && !snd_hda_jack_tbl_get(codec, pin) && 616b3f6008fSTakashi Iwai is_jack_detectable(codec, pin)) 617b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 6184e2d16d3SDavid Henningsson VIA_JACK_EVENT, 6194e2d16d3SDavid Henningsson via_jack_powerstate_event); 6204a918ffeSTakashi Iwai } 621b3f6008fSTakashi Iwai 622b3f6008fSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 623b3f6008fSTakashi Iwai pin = cfg->line_out_pins[i]; 624b3f6008fSTakashi Iwai if (pin && !snd_hda_jack_tbl_get(codec, pin) && 625b3f6008fSTakashi Iwai is_jack_detectable(codec, pin)) 626b3f6008fSTakashi Iwai snd_hda_jack_detect_enable_callback(codec, pin, 627b3f6008fSTakashi Iwai VIA_JACK_EVENT, 628b3f6008fSTakashi Iwai via_jack_powerstate_event); 629b3f6008fSTakashi Iwai } 630b3f6008fSTakashi Iwai } 631b3f6008fSTakashi Iwai 6324abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = { 6334abdbd1cSTakashi Iwai .no_primary_dac = 0x10000, 6344abdbd1cSTakashi Iwai .no_dac = 0x4000, 6354abdbd1cSTakashi Iwai .shared_primary = 0x10000, 6364abdbd1cSTakashi Iwai .shared_surr = 0x20, 6374abdbd1cSTakashi Iwai .shared_clfe = 0x20, 6384abdbd1cSTakashi Iwai .shared_surr_main = 0x20, 6394abdbd1cSTakashi Iwai }; 6404abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = { 6414abdbd1cSTakashi Iwai .no_primary_dac = 0x4000, 6424abdbd1cSTakashi Iwai .no_dac = 0x4000, 6434abdbd1cSTakashi Iwai .shared_primary = 0x12, 6444abdbd1cSTakashi Iwai .shared_surr = 0x20, 6454abdbd1cSTakashi Iwai .shared_clfe = 0x20, 6464abdbd1cSTakashi Iwai .shared_surr_main = 0x10, 6474abdbd1cSTakashi Iwai }; 6484abdbd1cSTakashi Iwai 649b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 650b3f6008fSTakashi Iwai { 651b3f6008fSTakashi Iwai struct via_spec *spec = codec->spec; 652b3f6008fSTakashi Iwai int err; 653b3f6008fSTakashi Iwai 6544abdbd1cSTakashi Iwai spec->gen.main_out_badness = &via_main_out_badness; 6554abdbd1cSTakashi Iwai spec->gen.extra_out_badness = &via_extra_out_badness; 6564abdbd1cSTakashi Iwai 657b3f6008fSTakashi Iwai err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); 658b3f6008fSTakashi Iwai if (err < 0) 659b3f6008fSTakashi Iwai return err; 660b3f6008fSTakashi Iwai 661b3f6008fSTakashi Iwai err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg); 662b3f6008fSTakashi Iwai if (err < 0) 663b3f6008fSTakashi Iwai return err; 664b3f6008fSTakashi Iwai 665b3f6008fSTakashi Iwai via_set_jack_unsol_events(codec); 666b3f6008fSTakashi Iwai return 0; 6674a918ffeSTakashi Iwai } 6684a918ffeSTakashi Iwai 6695d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 6705d41762aSTakashi Iwai { 6715d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 6725d41762aSTakashi Iwai int i; 6735d41762aSTakashi Iwai 6745d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 6755d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 6765d41762aSTakashi Iwai 677e9d010c2STakashi Iwai /* init power states */ 678e9d010c2STakashi Iwai set_widgets_power_state(codec); 679e9d010c2STakashi Iwai __analog_low_current_mode(codec, true); 680e9d010c2STakashi Iwai 681b3f6008fSTakashi Iwai snd_hda_gen_init(codec); 68211890956SLydia Wang 683b3f6008fSTakashi Iwai vt1708_update_hp_work(codec); 68425eaba2fSLydia Wang 685c577b8a1SJoseph Chan return 0; 686c577b8a1SJoseph Chan } 687c577b8a1SJoseph Chan 688f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec) 689f672f65aSDavid Henningsson { 690f672f65aSDavid Henningsson /* In order not to create "Phantom Jack" controls, 691f672f65aSDavid Henningsson temporary enable jackpoll */ 692f672f65aSDavid Henningsson int err; 693f672f65aSDavid Henningsson int old_interval = codec->jackpoll_interval; 694f672f65aSDavid Henningsson codec->jackpoll_interval = msecs_to_jiffies(100); 695f672f65aSDavid Henningsson err = via_build_controls(codec); 696f672f65aSDavid Henningsson codec->jackpoll_interval = old_interval; 697f672f65aSDavid Henningsson return err; 698f672f65aSDavid Henningsson } 699f672f65aSDavid Henningsson 700b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec) 701337b9d02STakashi Iwai { 702337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 703b3f6008fSTakashi Iwai int i, err; 704337b9d02STakashi Iwai 705b3f6008fSTakashi Iwai err = snd_hda_gen_build_pcms(codec); 706b3f6008fSTakashi Iwai if (err < 0 || codec->vendor_id != 0x11061708) 707b3f6008fSTakashi Iwai return err; 708b3f6008fSTakashi Iwai 709b3f6008fSTakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 710b3f6008fSTakashi Iwai * 24bit samples are used. Until any workaround is found, 711b3f6008fSTakashi Iwai * disable the 24bit format, so far. 712b3f6008fSTakashi Iwai */ 713b3f6008fSTakashi Iwai for (i = 0; i < codec->num_pcms; i++) { 714b3f6008fSTakashi Iwai struct hda_pcm *info = &spec->gen.pcm_rec[i]; 715b3f6008fSTakashi Iwai if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams || 716b3f6008fSTakashi Iwai info->pcm_type != HDA_PCM_TYPE_AUDIO) 717b3f6008fSTakashi Iwai continue; 718b3f6008fSTakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats = 719b3f6008fSTakashi Iwai SNDRV_PCM_FMTBIT_S16_LE; 720337b9d02STakashi Iwai } 721b3f6008fSTakashi Iwai 7221c55d521STakashi Iwai return 0; 723337b9d02STakashi Iwai } 724337b9d02STakashi Iwai 725c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 726c577b8a1SJoseph Chan { 727c577b8a1SJoseph Chan struct via_spec *spec; 728c577b8a1SJoseph Chan int err; 729c577b8a1SJoseph Chan 730c577b8a1SJoseph Chan /* create a codec specific record */ 7315b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 732c577b8a1SJoseph Chan if (spec == NULL) 733c577b8a1SJoseph Chan return -ENOMEM; 734c577b8a1SJoseph Chan 735b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x17; 736b3f6008fSTakashi Iwai 737b3f6008fSTakashi Iwai /* set jackpoll_interval while parsing the codec */ 738b3f6008fSTakashi Iwai codec->jackpoll_interval = msecs_to_jiffies(100); 739b3f6008fSTakashi Iwai spec->vt1708_jack_detect = 1; 740b3f6008fSTakashi Iwai 741b3f6008fSTakashi Iwai /* don't support the input jack switching due to lack of unsol event */ 742b3f6008fSTakashi Iwai /* (it may work with polling, though, but it needs testing) */ 743b3f6008fSTakashi Iwai spec->gen.suppress_auto_mic = 1; 744eb33ccf7STakashi Iwai /* Some machines show the broken speaker mute */ 745eb33ccf7STakashi Iwai spec->gen.auto_mute_via_amp = 1; 746620e2b28STakashi Iwai 74712daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 74812daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 74912daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 75012daef65STakashi Iwai 751c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 75212daef65STakashi Iwai err = via_parse_auto_config(codec); 753c577b8a1SJoseph Chan if (err < 0) { 754c577b8a1SJoseph Chan via_free(codec); 755c577b8a1SJoseph Chan return err; 756c577b8a1SJoseph Chan } 757c577b8a1SJoseph Chan 75812daef65STakashi Iwai /* add jack detect on/off control */ 759b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl; 760c577b8a1SJoseph Chan 761e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 762e322a36dSLydia Wang 763c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 764f672f65aSDavid Henningsson codec->patch_ops.build_controls = vt1708_build_controls; 765b3f6008fSTakashi Iwai codec->patch_ops.build_pcms = vt1708_build_pcms; 766c577b8a1SJoseph Chan 767b3f6008fSTakashi Iwai /* clear jackpoll_interval again; it's set dynamically */ 768b3f6008fSTakashi Iwai codec->jackpoll_interval = 0; 769b3f6008fSTakashi Iwai 770c577b8a1SJoseph Chan return 0; 771c577b8a1SJoseph Chan } 772c577b8a1SJoseph Chan 773ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 774c577b8a1SJoseph Chan { 775c577b8a1SJoseph Chan struct via_spec *spec; 776c577b8a1SJoseph Chan int err; 777c577b8a1SJoseph Chan 778c577b8a1SJoseph Chan /* create a codec specific record */ 7795b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 780c577b8a1SJoseph Chan if (spec == NULL) 781c577b8a1SJoseph Chan return -ENOMEM; 782c577b8a1SJoseph Chan 783b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x18; 784620e2b28STakashi Iwai 78512daef65STakashi Iwai err = via_parse_auto_config(codec); 786c577b8a1SJoseph Chan if (err < 0) { 787c577b8a1SJoseph Chan via_free(codec); 788c577b8a1SJoseph Chan return err; 789c577b8a1SJoseph Chan } 790c577b8a1SJoseph Chan 791c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 792c577b8a1SJoseph Chan 793f7278fd0SJosepch Chan return 0; 794f7278fd0SJosepch Chan } 795f7278fd0SJosepch Chan 7963e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 7973e95b9abSLydia Wang { 7983e95b9abSLydia Wang struct via_spec *spec = codec->spec; 7993e95b9abSLydia Wang int imux_is_smixer; 8003e95b9abSLydia Wang unsigned int parm; 8013e95b9abSLydia Wang int is_8ch = 0; 802bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 803bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 8043e95b9abSLydia Wang is_8ch = 1; 8053e95b9abSLydia Wang 8063e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 8073e95b9abSLydia Wang imux_is_smixer = 8083e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 8093e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 8103e95b9abSLydia Wang /* inputs */ 8113e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 8123e95b9abSLydia Wang parm = AC_PWRST_D3; 8133e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 8143e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 8153e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 8163e95b9abSLydia Wang if (imux_is_smixer) 8173e95b9abSLydia Wang parm = AC_PWRST_D0; 8183e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 819054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 820054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 821054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 8223e95b9abSLydia Wang 8233e95b9abSLydia Wang /* outputs */ 8243e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 8253e95b9abSLydia Wang parm = AC_PWRST_D3; 8263e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 827b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8283e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 829054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 830054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 8313e95b9abSLydia Wang 8323e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 8333e95b9abSLydia Wang if (is_8ch) { 8343e95b9abSLydia Wang parm = AC_PWRST_D3; 8353e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 836b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 8373e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 838054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 839054d867eSTakashi Iwai update_power_state(codec, 0x24, parm); 840bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 841bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 842bc92df7fSLydia Wang parm = AC_PWRST_D3; 843bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 844b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 845bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 846054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 847054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8483e95b9abSLydia Wang } 8493e95b9abSLydia Wang 8503e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 8513e95b9abSLydia Wang parm = AC_PWRST_D3; 8523e95b9abSLydia Wang /* force to D0 for internal Speaker */ 8533e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 8543e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 8553e95b9abSLydia Wang if (is_8ch) 8563e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 8573e95b9abSLydia Wang 8583e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 859054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 860054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 8613e95b9abSLydia Wang if (is_8ch) { 862054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 863054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 864b3f6008fSTakashi Iwai } else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled) 865054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 8663e95b9abSLydia Wang } 8673e95b9abSLydia Wang 868518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 869ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 870f7278fd0SJosepch Chan { 871f7278fd0SJosepch Chan struct via_spec *spec; 872f7278fd0SJosepch Chan int err; 873f7278fd0SJosepch Chan 874518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 875518bf3baSLydia Wang return patch_vt1708S(codec); 876ddd304d8STakashi Iwai 877f7278fd0SJosepch Chan /* create a codec specific record */ 8785b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 879f7278fd0SJosepch Chan if (spec == NULL) 880f7278fd0SJosepch Chan return -ENOMEM; 881f7278fd0SJosepch Chan 882b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 883620e2b28STakashi Iwai 884f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 88512daef65STakashi Iwai err = via_parse_auto_config(codec); 886f7278fd0SJosepch Chan if (err < 0) { 887f7278fd0SJosepch Chan via_free(codec); 888f7278fd0SJosepch Chan return err; 889f7278fd0SJosepch Chan } 890f7278fd0SJosepch Chan 891f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 892f7278fd0SJosepch Chan 8933e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 8943e95b9abSLydia Wang 895f7278fd0SJosepch Chan return 0; 896f7278fd0SJosepch Chan } 897f7278fd0SJosepch Chan 898d949cac1SHarald Welte /* Patch for VT1708S */ 899096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 900d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 901d7426329SHarald Welte {0x1, 0xf98, 0x1}, 902bc7e7e5cSLydia Wang /* don't bybass mixer */ 903bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 904d949cac1SHarald Welte { } 905d949cac1SHarald Welte }; 906d949cac1SHarald Welte 9076369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 9086369bcfcSLydia Wang int offset, int num_steps, int step_size) 9096369bcfcSLydia Wang { 910d045c5dcSTakashi Iwai snd_hda_override_wcaps(codec, pin, 911d045c5dcSTakashi Iwai get_wcaps(codec, pin) | AC_WCAP_IN_AMP); 9126369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 9136369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 9146369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 9156369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 9166369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 9176369bcfcSLydia Wang } 9186369bcfcSLydia Wang 919d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 920d949cac1SHarald Welte { 921d949cac1SHarald Welte struct via_spec *spec; 922d949cac1SHarald Welte int err; 923d949cac1SHarald Welte 924d949cac1SHarald Welte /* create a codec specific record */ 9255b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 926d949cac1SHarald Welte if (spec == NULL) 927d949cac1SHarald Welte return -ENOMEM; 928d949cac1SHarald Welte 929b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 930d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 931d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 932620e2b28STakashi Iwai 933518bf3baSLydia Wang /* correct names for VT1708BCE */ 934518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 935518bf3baSLydia Wang kfree(codec->chip_name); 936518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 937518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 938518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 939518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 940970f630fSLydia Wang } 941bc92df7fSLydia Wang /* correct names for VT1705 */ 942bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 943bc92df7fSLydia Wang kfree(codec->chip_name); 944bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 945bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 946bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 947bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 948bc92df7fSLydia Wang } 949b3f6008fSTakashi Iwai 950b3f6008fSTakashi Iwai /* automatic parse from the BIOS config */ 951b3f6008fSTakashi Iwai err = via_parse_auto_config(codec); 952b3f6008fSTakashi Iwai if (err < 0) { 953b3f6008fSTakashi Iwai via_free(codec); 954b3f6008fSTakashi Iwai return err; 955b3f6008fSTakashi Iwai } 956b3f6008fSTakashi Iwai 957b3f6008fSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 958b3f6008fSTakashi Iwai 959b3f6008fSTakashi Iwai codec->patch_ops = via_patch_ops; 960b3f6008fSTakashi Iwai 9613e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 962d949cac1SHarald Welte return 0; 963d949cac1SHarald Welte } 964d949cac1SHarald Welte 965d949cac1SHarald Welte /* Patch for VT1702 */ 966d949cac1SHarald Welte 967096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 968bc7e7e5cSLydia Wang /* mixer enable */ 969bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 970bc7e7e5cSLydia Wang /* GPIO 0~2 */ 971bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 972d949cac1SHarald Welte { } 973d949cac1SHarald Welte }; 974d949cac1SHarald Welte 9753e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 9763e95b9abSLydia Wang { 9773e95b9abSLydia Wang int imux_is_smixer = 9783e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 9793e95b9abSLydia Wang unsigned int parm; 9803e95b9abSLydia Wang /* inputs */ 9813e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 9823e95b9abSLydia Wang parm = AC_PWRST_D3; 9833e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 9843e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 9853e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 9863e95b9abSLydia Wang if (imux_is_smixer) 9873e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 9883e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 989054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 990054d867eSTakashi Iwai update_power_state(codec, 0x12, parm); 991054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 992054d867eSTakashi Iwai update_power_state(codec, 0x20, parm); 9933e95b9abSLydia Wang 9943e95b9abSLydia Wang /* outputs */ 9953e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 9963e95b9abSLydia Wang parm = AC_PWRST_D3; 9973e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 9983e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 9993e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 1000054d867eSTakashi Iwai update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm); 1001054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1002054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 10033e95b9abSLydia Wang } 10043e95b9abSLydia Wang 1005d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 1006d949cac1SHarald Welte { 1007d949cac1SHarald Welte struct via_spec *spec; 1008d949cac1SHarald Welte int err; 1009d949cac1SHarald Welte 1010d949cac1SHarald Welte /* create a codec specific record */ 10115b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1012d949cac1SHarald Welte if (spec == NULL) 1013d949cac1SHarald Welte return -ENOMEM; 1014d949cac1SHarald Welte 1015b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x1a; 1016620e2b28STakashi Iwai 101712daef65STakashi Iwai /* limit AA path volume to 0 dB */ 101812daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 101912daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 102012daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 102112daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 102212daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 102312daef65STakashi Iwai 1024d949cac1SHarald Welte /* automatic parse from the BIOS config */ 102512daef65STakashi Iwai err = via_parse_auto_config(codec); 1026d949cac1SHarald Welte if (err < 0) { 1027d949cac1SHarald Welte via_free(codec); 1028d949cac1SHarald Welte return err; 1029d949cac1SHarald Welte } 1030d949cac1SHarald Welte 1031096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 1032d949cac1SHarald Welte 1033d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 1034d949cac1SHarald Welte 10353e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 1036d949cac1SHarald Welte return 0; 1037d949cac1SHarald Welte } 1038d949cac1SHarald Welte 1039eb7188caSLydia Wang /* Patch for VT1718S */ 1040eb7188caSLydia Wang 1041096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 10424ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 10434ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 1044eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 1045eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 10465d41762aSTakashi Iwai 1047eb7188caSLydia Wang { } 1048eb7188caSLydia Wang }; 1049eb7188caSLydia Wang 10503e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 10513e95b9abSLydia Wang { 10523e95b9abSLydia Wang struct via_spec *spec = codec->spec; 10533e95b9abSLydia Wang int imux_is_smixer; 10546162552bSTakashi Iwai unsigned int parm, parm2; 10553e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 10563e95b9abSLydia Wang imux_is_smixer = 10573e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 10583e95b9abSLydia Wang /* inputs */ 10593e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 10603e95b9abSLydia Wang parm = AC_PWRST_D3; 10613e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 10623e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 10633e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 10643e95b9abSLydia Wang if (imux_is_smixer) 10653e95b9abSLydia Wang parm = AC_PWRST_D0; 10663e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 1067054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1068054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1069054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1070054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 10713e95b9abSLydia Wang 10723e95b9abSLydia Wang /* outputs */ 10733e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 10743e95b9abSLydia Wang parm = AC_PWRST_D3; 10753e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 1076054d867eSTakashi Iwai update_power_state(codec, 0x1a, parm); 10776162552bSTakashi Iwai parm2 = parm; /* for pin 0x0b */ 10783e95b9abSLydia Wang 10793e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 10803e95b9abSLydia Wang parm = AC_PWRST_D3; 10813e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1082b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 10833e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 1084054d867eSTakashi Iwai update_power_state(codec, 0xa, parm); 10853e95b9abSLydia Wang 10863e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 10873e95b9abSLydia Wang parm = AC_PWRST_D3; 10883e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 1089b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled) /* check for redirected HP */ 10903e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1091054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 1092b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 10936162552bSTakashi Iwai parm = parm2; 10946162552bSTakashi Iwai update_power_state(codec, 0xb, parm); 10953e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 1096054d867eSTakashi Iwai update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm); 10973e95b9abSLydia Wang 10983e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 10993e95b9abSLydia Wang parm = AC_PWRST_D3; 11003e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1101b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 11023e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 1103054d867eSTakashi Iwai update_power_state(codec, 0x9, parm); 11043e95b9abSLydia Wang 1105b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) { 11063e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 11073e95b9abSLydia Wang parm = AC_PWRST_D3; 11083e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1109054d867eSTakashi Iwai update_power_state(codec, 0x1b, parm); 1110054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 1111054d867eSTakashi Iwai update_power_state(codec, 0xc, parm); 11123e95b9abSLydia Wang } 11133e95b9abSLydia Wang } 11143e95b9abSLydia Wang 111530b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 111630b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 111730b45033STakashi Iwai */ 111830b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 111930b45033STakashi Iwai { 112030b45033STakashi Iwai struct via_spec *spec = codec->spec; 112130b45033STakashi Iwai int i, nums; 112230b45033STakashi Iwai hda_nid_t conn[8]; 112330b45033STakashi Iwai hda_nid_t nid; 112430b45033STakashi Iwai 1125b3f6008fSTakashi Iwai if (!spec->gen.mixer_nid) 112630b45033STakashi Iwai return 0; 1127b3f6008fSTakashi Iwai nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn, 112830b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 112930b45033STakashi Iwai for (i = 0; i < nums; i++) { 113030b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 113130b45033STakashi Iwai return 0; 113230b45033STakashi Iwai } 113330b45033STakashi Iwai 113430b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 113530b45033STakashi Iwai nid = codec->start_nid; 113630b45033STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 113730b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 113830b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 113930b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 114030b45033STakashi Iwai conn[nums++] = nid; 114130b45033STakashi Iwai return snd_hda_override_conn_list(codec, 1142b3f6008fSTakashi Iwai spec->gen.mixer_nid, 114330b45033STakashi Iwai nums, conn); 114430b45033STakashi Iwai } 114530b45033STakashi Iwai } 114630b45033STakashi Iwai return 0; 114730b45033STakashi Iwai } 114830b45033STakashi Iwai 114930b45033STakashi Iwai 1150eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 1151eb7188caSLydia Wang { 1152eb7188caSLydia Wang struct via_spec *spec; 1153eb7188caSLydia Wang int err; 1154eb7188caSLydia Wang 1155eb7188caSLydia Wang /* create a codec specific record */ 11565b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1157eb7188caSLydia Wang if (spec == NULL) 1158eb7188caSLydia Wang return -ENOMEM; 1159eb7188caSLydia Wang 1160b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1161d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1162d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 116330b45033STakashi Iwai add_secret_dac_path(codec); 1164620e2b28STakashi Iwai 1165eb7188caSLydia Wang /* automatic parse from the BIOS config */ 116612daef65STakashi Iwai err = via_parse_auto_config(codec); 1167eb7188caSLydia Wang if (err < 0) { 1168eb7188caSLydia Wang via_free(codec); 1169eb7188caSLydia Wang return err; 1170eb7188caSLydia Wang } 1171eb7188caSLydia Wang 1172096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 1173eb7188caSLydia Wang 1174eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 1175eb7188caSLydia Wang 11763e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 11773e95b9abSLydia Wang 1178eb7188caSLydia Wang return 0; 1179eb7188caSLydia Wang } 1180f3db423dSLydia Wang 1181f3db423dSLydia Wang /* Patch for VT1716S */ 1182f3db423dSLydia Wang 1183f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 1184f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 1185f3db423dSLydia Wang { 1186f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 1187f3db423dSLydia Wang uinfo->count = 1; 1188f3db423dSLydia Wang uinfo->value.integer.min = 0; 1189f3db423dSLydia Wang uinfo->value.integer.max = 1; 1190f3db423dSLydia Wang return 0; 1191f3db423dSLydia Wang } 1192f3db423dSLydia Wang 1193f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 1194f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1195f3db423dSLydia Wang { 1196f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1197f3db423dSLydia Wang int index = 0; 1198f3db423dSLydia Wang 1199f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 1200f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 1201f3db423dSLydia Wang if (index != -1) 1202f3db423dSLydia Wang *ucontrol->value.integer.value = index; 1203f3db423dSLydia Wang 1204f3db423dSLydia Wang return 0; 1205f3db423dSLydia Wang } 1206f3db423dSLydia Wang 1207f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 1208f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 1209f3db423dSLydia Wang { 1210f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1211f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1212f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 1213f3db423dSLydia Wang 1214f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 1215f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 1216f3db423dSLydia Wang spec->dmic_enabled = index; 12173e95b9abSLydia Wang set_widgets_power_state(codec); 1218f3db423dSLydia Wang return 1; 1219f3db423dSLydia Wang } 1220f3db423dSLydia Wang 122190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 1222f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 1223f3db423dSLydia Wang { 1224f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1225f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 12265b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 1227f3db423dSLydia Wang .count = 1, 1228f3db423dSLydia Wang .info = vt1716s_dmic_info, 1229f3db423dSLydia Wang .get = vt1716s_dmic_get, 1230f3db423dSLydia Wang .put = vt1716s_dmic_put, 1231f3db423dSLydia Wang }, 1232f3db423dSLydia Wang {} /* end */ 1233f3db423dSLydia Wang }; 1234f3db423dSLydia Wang 1235f3db423dSLydia Wang 1236f3db423dSLydia Wang /* mono-out mixer elements */ 123790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 1238f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 1239f3db423dSLydia Wang { } /* end */ 1240f3db423dSLydia Wang }; 1241f3db423dSLydia Wang 1242096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 1243f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 1244f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 1245f3db423dSLydia Wang /* don't bybass mixer */ 1246f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 1247f3db423dSLydia Wang /* Enable mono output */ 1248f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 1249f3db423dSLydia Wang { } 1250f3db423dSLydia Wang }; 1251f3db423dSLydia Wang 12523e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 12533e95b9abSLydia Wang { 12543e95b9abSLydia Wang struct via_spec *spec = codec->spec; 12553e95b9abSLydia Wang int imux_is_smixer; 12563e95b9abSLydia Wang unsigned int parm; 12573e95b9abSLydia Wang unsigned int mono_out, present; 12583e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 12593e95b9abSLydia Wang imux_is_smixer = 12603e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 12613e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 12623e95b9abSLydia Wang /* inputs */ 12633e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 12643e95b9abSLydia Wang parm = AC_PWRST_D3; 12653e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 12663e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 12673e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12683e95b9abSLydia Wang if (imux_is_smixer) 12693e95b9abSLydia Wang parm = AC_PWRST_D0; 12703e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 1271054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1272054d867eSTakashi Iwai update_power_state(codec, 0x13, parm); 12733e95b9abSLydia Wang 12743e95b9abSLydia Wang parm = AC_PWRST_D3; 12753e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 12763e95b9abSLydia Wang /* PW11 (22h) */ 12773e95b9abSLydia Wang if (spec->dmic_enabled) 12783e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 12793e95b9abSLydia Wang else 1280054d867eSTakashi Iwai update_power_state(codec, 0x22, AC_PWRST_D3); 12813e95b9abSLydia Wang 12823e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 1283054d867eSTakashi Iwai update_power_state(codec, 0x26, parm); 1284054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 12853e95b9abSLydia Wang 12863e95b9abSLydia Wang /* outputs */ 12873e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 12883e95b9abSLydia Wang parm = AC_PWRST_D3; 12893e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 12903e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 1291b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 12923e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 1293054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1294054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 12953e95b9abSLydia Wang 12963e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 12973e95b9abSLydia Wang parm = AC_PWRST_D3; 12983e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 12993e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 1300b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 13013e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 1302054d867eSTakashi Iwai update_power_state(codec, 0x27, parm); 13033e95b9abSLydia Wang 13043e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 1305b3f6008fSTakashi Iwai if (smart51_enabled(codec)) 13063e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 1307054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 13083e95b9abSLydia Wang 13093e95b9abSLydia Wang /* Mono out */ 13103e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 13113e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 13123e95b9abSLydia Wang 13133e95b9abSLydia Wang if (present) 13143e95b9abSLydia Wang mono_out = 0; 13153e95b9abSLydia Wang else { 13163e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 1317b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && present) 13183e95b9abSLydia Wang mono_out = 0; 13193e95b9abSLydia Wang else 13203e95b9abSLydia Wang mono_out = 1; 13213e95b9abSLydia Wang } 13223e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 1323054d867eSTakashi Iwai update_power_state(codec, 0x28, parm); 1324054d867eSTakashi Iwai update_power_state(codec, 0x29, parm); 1325054d867eSTakashi Iwai update_power_state(codec, 0x2a, parm); 13263e95b9abSLydia Wang 13273e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 13283e95b9abSLydia Wang parm = AC_PWRST_D3; 13293e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 13303e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 13313e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 1332b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1333054d867eSTakashi Iwai update_power_state(codec, 0x25, parm); 13343e95b9abSLydia Wang 13353e95b9abSLydia Wang /* force to D0 for internal Speaker */ 13363e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 1337054d867eSTakashi Iwai update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm); 1338054d867eSTakashi Iwai update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm); 13393e95b9abSLydia Wang } 13403e95b9abSLydia Wang 1341f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 1342f3db423dSLydia Wang { 1343f3db423dSLydia Wang struct via_spec *spec; 1344f3db423dSLydia Wang int err; 1345f3db423dSLydia Wang 1346f3db423dSLydia Wang /* create a codec specific record */ 13475b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1348f3db423dSLydia Wang if (spec == NULL) 1349f3db423dSLydia Wang return -ENOMEM; 1350f3db423dSLydia Wang 1351b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x16; 1352d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 1353d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 1354620e2b28STakashi Iwai 1355f3db423dSLydia Wang /* automatic parse from the BIOS config */ 135612daef65STakashi Iwai err = via_parse_auto_config(codec); 1357f3db423dSLydia Wang if (err < 0) { 1358f3db423dSLydia Wang via_free(codec); 1359f3db423dSLydia Wang return err; 1360f3db423dSLydia Wang } 1361f3db423dSLydia Wang 1362096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 1363f3db423dSLydia Wang 1364b3f6008fSTakashi Iwai spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer; 1365f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 1366f3db423dSLydia Wang 1367f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 1368f3db423dSLydia Wang 13693e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 1370f3db423dSLydia Wang return 0; 1371f3db423dSLydia Wang } 137225eaba2fSLydia Wang 137325eaba2fSLydia Wang /* for vt2002P */ 137425eaba2fSLydia Wang 1375096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 1376eadb9a80SLydia Wang /* Class-D speaker related verbs */ 1377eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 1378eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 1379eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 138025eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 138125eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 138225eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 138325eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 138425eaba2fSLydia Wang { } 138525eaba2fSLydia Wang }; 13864a918ffeSTakashi Iwai 1387096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 138811890956SLydia Wang /* Enable Boost Volume backdoor */ 138911890956SLydia Wang {0x1, 0xfb9, 0x24}, 139011890956SLydia Wang /* Enable AOW0 to MW9 */ 139111890956SLydia Wang {0x1, 0xfb8, 0x88}, 139211890956SLydia Wang { } 139311890956SLydia Wang }; 139425eaba2fSLydia Wang 13953e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 13963e95b9abSLydia Wang { 13973e95b9abSLydia Wang struct via_spec *spec = codec->spec; 13983e95b9abSLydia Wang int imux_is_smixer; 13993e95b9abSLydia Wang unsigned int parm; 14003e95b9abSLydia Wang unsigned int present; 14013e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 14023e95b9abSLydia Wang imux_is_smixer = 14033e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 14043e95b9abSLydia Wang /* inputs */ 14053e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 14063e95b9abSLydia Wang parm = AC_PWRST_D3; 14073e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 14083e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 14093e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 14103e95b9abSLydia Wang parm = AC_PWRST_D0; 14113e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 1412054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1413054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1414054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1415054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 14163e95b9abSLydia Wang 14173e95b9abSLydia Wang /* outputs */ 14183e95b9abSLydia Wang /* AOW0 (8h)*/ 1419054d867eSTakashi Iwai update_power_state(codec, 0x8, parm); 14203e95b9abSLydia Wang 142111890956SLydia Wang if (spec->codec_type == VT1802) { 142211890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 142311890956SLydia Wang parm = AC_PWRST_D3; 142411890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 1425054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1426054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 142711890956SLydia Wang } else { 14283e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 14293e95b9abSLydia Wang parm = AC_PWRST_D3; 14303e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 1431054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1432054d867eSTakashi Iwai update_power_state(codec, 0x37, parm); 143311890956SLydia Wang } 14343e95b9abSLydia Wang 143511890956SLydia Wang if (spec->codec_type == VT1802) { 143611890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 143711890956SLydia Wang parm = AC_PWRST_D3; 143811890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 1439054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1440054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 144111890956SLydia Wang } else { 14423e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 14433e95b9abSLydia Wang parm = AC_PWRST_D3; 14443e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1445054d867eSTakashi Iwai update_power_state(codec, 0x19, parm); 1446054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 144711890956SLydia Wang } 14483e95b9abSLydia Wang 1449b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1450054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 14513e95b9abSLydia Wang 14523e95b9abSLydia Wang /* Class-D */ 14533e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 14543e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 14553e95b9abSLydia Wang 14563e95b9abSLydia Wang parm = AC_PWRST_D3; 14573e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 14583e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 145911890956SLydia Wang if (spec->codec_type == VT1802) 1460054d867eSTakashi Iwai update_power_state(codec, 0x14, parm); 146111890956SLydia Wang else 1462054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1463054d867eSTakashi Iwai update_power_state(codec, 0x34, parm); 14643e95b9abSLydia Wang 14653e95b9abSLydia Wang /* Mono Out */ 14663e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 14673e95b9abSLydia Wang 14683e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 146911890956SLydia Wang if (spec->codec_type == VT1802) { 147011890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 1471054d867eSTakashi Iwai update_power_state(codec, 0x33, parm); 1472054d867eSTakashi Iwai update_power_state(codec, 0x1c, parm); 1473054d867eSTakashi Iwai update_power_state(codec, 0x3c, parm); 147411890956SLydia Wang } else { 14753e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 1476054d867eSTakashi Iwai update_power_state(codec, 0x31, parm); 1477054d867eSTakashi Iwai update_power_state(codec, 0x17, parm); 1478054d867eSTakashi Iwai update_power_state(codec, 0x3b, parm); 147911890956SLydia Wang } 14803e95b9abSLydia Wang /* MW9 (21h) */ 14813e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1482054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D0); 14833e95b9abSLydia Wang else 1484054d867eSTakashi Iwai update_power_state(codec, 0x21, AC_PWRST_D3); 14853e95b9abSLydia Wang } 148625eaba2fSLydia Wang 14874b527b65SDavid Henningsson /* 14884b527b65SDavid Henningsson * pin fix-up 14894b527b65SDavid Henningsson */ 14904b527b65SDavid Henningsson enum { 14914b527b65SDavid Henningsson VIA_FIXUP_INTMIC_BOOST, 1492d5266125STakashi Iwai VIA_FIXUP_ASUS_G75, 14934b527b65SDavid Henningsson }; 14944b527b65SDavid Henningsson 14954b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec, 14964b527b65SDavid Henningsson const struct hda_fixup *fix, int action) 14974b527b65SDavid Henningsson { 14984b527b65SDavid Henningsson if (action == HDA_FIXUP_ACT_PRE_PROBE) 14994b527b65SDavid Henningsson override_mic_boost(codec, 0x30, 0, 2, 40); 15004b527b65SDavid Henningsson } 15014b527b65SDavid Henningsson 15024b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = { 15034b527b65SDavid Henningsson [VIA_FIXUP_INTMIC_BOOST] = { 15044b527b65SDavid Henningsson .type = HDA_FIXUP_FUNC, 15054b527b65SDavid Henningsson .v.func = via_fixup_intmic_boost, 15064b527b65SDavid Henningsson }, 1507d5266125STakashi Iwai [VIA_FIXUP_ASUS_G75] = { 1508d5266125STakashi Iwai .type = HDA_FIXUP_PINS, 1509d5266125STakashi Iwai .v.pins = (const struct hda_pintbl[]) { 1510d5266125STakashi Iwai /* set 0x24 and 0x33 as speakers */ 1511d5266125STakashi Iwai { 0x24, 0x991301f0 }, 1512d5266125STakashi Iwai { 0x33, 0x991301f1 }, /* subwoofer */ 1513d5266125STakashi Iwai { } 1514d5266125STakashi Iwai } 1515d5266125STakashi Iwai }, 15164b527b65SDavid Henningsson }; 15174b527b65SDavid Henningsson 15184b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = { 1519d5266125STakashi Iwai SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75), 15204b527b65SDavid Henningsson SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST), 15214b527b65SDavid Henningsson {} 15224b527b65SDavid Henningsson }; 15234b527b65SDavid Henningsson 1524ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e 1525ef4da458STakashi Iwai * Replace this with mixer NID 0x1c 1526ef4da458STakashi Iwai */ 1527ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec) 1528ef4da458STakashi Iwai { 1529ef4da458STakashi Iwai static hda_nid_t conn_24[] = { 0x14, 0x1c }; 1530ef4da458STakashi Iwai static hda_nid_t conn_33[] = { 0x1c }; 1531ef4da458STakashi Iwai 1532ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24); 1533ef4da458STakashi Iwai snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33); 1534ef4da458STakashi Iwai } 1535ef4da458STakashi Iwai 153625eaba2fSLydia Wang /* patch for vt2002P */ 153725eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 153825eaba2fSLydia Wang { 153925eaba2fSLydia Wang struct via_spec *spec; 154025eaba2fSLydia Wang int err; 154125eaba2fSLydia Wang 154225eaba2fSLydia Wang /* create a codec specific record */ 15435b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 154425eaba2fSLydia Wang if (spec == NULL) 154525eaba2fSLydia Wang return -ENOMEM; 154625eaba2fSLydia Wang 1547b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1548d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1549d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 1550ef4da458STakashi Iwai if (spec->codec_type == VT1802) 1551ef4da458STakashi Iwai fix_vt1802_connections(codec); 155230b45033STakashi Iwai add_secret_dac_path(codec); 1553620e2b28STakashi Iwai 15544b527b65SDavid Henningsson snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups); 15554b527b65SDavid Henningsson snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); 15564b527b65SDavid Henningsson 155725eaba2fSLydia Wang /* automatic parse from the BIOS config */ 155812daef65STakashi Iwai err = via_parse_auto_config(codec); 155925eaba2fSLydia Wang if (err < 0) { 156025eaba2fSLydia Wang via_free(codec); 156125eaba2fSLydia Wang return err; 156225eaba2fSLydia Wang } 156325eaba2fSLydia Wang 156411890956SLydia Wang if (spec->codec_type == VT1802) 15654a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 156611890956SLydia Wang else 15674a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 156811890956SLydia Wang 156925eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 157025eaba2fSLydia Wang 15713e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 157225eaba2fSLydia Wang return 0; 157325eaba2fSLydia Wang } 1574ab6734e7SLydia Wang 1575ab6734e7SLydia Wang /* for vt1812 */ 1576ab6734e7SLydia Wang 1577096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 1578ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 1579ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 1580ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 1581ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 1582ab6734e7SLydia Wang { } 1583ab6734e7SLydia Wang }; 1584ab6734e7SLydia Wang 15853e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 15863e95b9abSLydia Wang { 15873e95b9abSLydia Wang struct via_spec *spec = codec->spec; 15883e95b9abSLydia Wang unsigned int parm; 15893e95b9abSLydia Wang unsigned int present; 15903e95b9abSLydia Wang /* inputs */ 15913e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 15923e95b9abSLydia Wang parm = AC_PWRST_D3; 15933e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 15943e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 15953e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 15963e95b9abSLydia Wang parm = AC_PWRST_D0; 15973e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 1598054d867eSTakashi Iwai update_power_state(codec, 0x1e, parm); 1599054d867eSTakashi Iwai update_power_state(codec, 0x1f, parm); 1600054d867eSTakashi Iwai update_power_state(codec, 0x10, parm); 1601054d867eSTakashi Iwai update_power_state(codec, 0x11, parm); 16023e95b9abSLydia Wang 16033e95b9abSLydia Wang /* outputs */ 16043e95b9abSLydia Wang /* AOW0 (8h)*/ 1605054d867eSTakashi Iwai update_power_state(codec, 0x8, AC_PWRST_D0); 16063e95b9abSLydia Wang 16073e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 16083e95b9abSLydia Wang parm = AC_PWRST_D3; 16093e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 1610054d867eSTakashi Iwai update_power_state(codec, 0x18, parm); 1611054d867eSTakashi Iwai update_power_state(codec, 0x38, parm); 16123e95b9abSLydia Wang 16133e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 16143e95b9abSLydia Wang parm = AC_PWRST_D3; 16153e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 1616054d867eSTakashi Iwai update_power_state(codec, 0x15, parm); 1617054d867eSTakashi Iwai update_power_state(codec, 0x35, parm); 1618b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 1619054d867eSTakashi Iwai update_power_state(codec, 0x9, AC_PWRST_D0); 16203e95b9abSLydia Wang 16213e95b9abSLydia Wang /* Internal Speaker */ 16223e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 16233e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 16243e95b9abSLydia Wang 16253e95b9abSLydia Wang parm = AC_PWRST_D3; 16263e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 16273e95b9abSLydia Wang if (present) { 1628054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D3); 1629054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D3); 16303e95b9abSLydia Wang } else { 1631054d867eSTakashi Iwai update_power_state(codec, 0x14, AC_PWRST_D0); 1632054d867eSTakashi Iwai update_power_state(codec, 0x34, AC_PWRST_D0); 16333e95b9abSLydia Wang } 16343e95b9abSLydia Wang 16353e95b9abSLydia Wang 16363e95b9abSLydia Wang /* Mono Out */ 16373e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 16383e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 16393e95b9abSLydia Wang 16403e95b9abSLydia Wang parm = AC_PWRST_D3; 16413e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 16423e95b9abSLydia Wang if (present) { 1643054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D3); 1644054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D3); 1645054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D3); 16463e95b9abSLydia Wang } else { 1647054d867eSTakashi Iwai update_power_state(codec, 0x1c, AC_PWRST_D0); 1648054d867eSTakashi Iwai update_power_state(codec, 0x3c, AC_PWRST_D0); 1649054d867eSTakashi Iwai update_power_state(codec, 0x3e, AC_PWRST_D0); 16503e95b9abSLydia Wang } 16513e95b9abSLydia Wang 16523e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 16533e95b9abSLydia Wang parm = AC_PWRST_D3; 16543e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 1655054d867eSTakashi Iwai update_power_state(codec, 0x1d, parm); 1656054d867eSTakashi Iwai update_power_state(codec, 0x3d, parm); 16573e95b9abSLydia Wang 16583e95b9abSLydia Wang } 1659ab6734e7SLydia Wang 1660ab6734e7SLydia Wang /* patch for vt1812 */ 1661ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 1662ab6734e7SLydia Wang { 1663ab6734e7SLydia Wang struct via_spec *spec; 1664ab6734e7SLydia Wang int err; 1665ab6734e7SLydia Wang 1666ab6734e7SLydia Wang /* create a codec specific record */ 16675b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 1668ab6734e7SLydia Wang if (spec == NULL) 1669ab6734e7SLydia Wang return -ENOMEM; 1670ab6734e7SLydia Wang 1671b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x21; 1672d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 1673d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 167430b45033STakashi Iwai add_secret_dac_path(codec); 1675620e2b28STakashi Iwai 1676ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 167712daef65STakashi Iwai err = via_parse_auto_config(codec); 1678ab6734e7SLydia Wang if (err < 0) { 1679ab6734e7SLydia Wang via_free(codec); 1680ab6734e7SLydia Wang return err; 1681ab6734e7SLydia Wang } 1682ab6734e7SLydia Wang 1683096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 1684ab6734e7SLydia Wang 1685ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 1686ab6734e7SLydia Wang 16873e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 1688ab6734e7SLydia Wang return 0; 1689ab6734e7SLydia Wang } 1690ab6734e7SLydia Wang 169143737e0aSLydia Wang /* patch for vt3476 */ 169243737e0aSLydia Wang 169343737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = { 169443737e0aSLydia Wang /* Enable DMic 8/16/32K */ 169543737e0aSLydia Wang {0x1, 0xF7B, 0x30}, 169643737e0aSLydia Wang /* Enable Boost Volume backdoor */ 169743737e0aSLydia Wang {0x1, 0xFB9, 0x20}, 169843737e0aSLydia Wang /* Enable AOW-MW9 path */ 169943737e0aSLydia Wang {0x1, 0xFB8, 0x10}, 170043737e0aSLydia Wang { } 170143737e0aSLydia Wang }; 170243737e0aSLydia Wang 170343737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec) 170443737e0aSLydia Wang { 170543737e0aSLydia Wang struct via_spec *spec = codec->spec; 170643737e0aSLydia Wang int imux_is_smixer; 170743737e0aSLydia Wang unsigned int parm, parm2; 170843737e0aSLydia Wang /* MUX10 (1eh) = stereo mixer */ 170943737e0aSLydia Wang imux_is_smixer = 171043737e0aSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4; 171143737e0aSLydia Wang /* inputs */ 171243737e0aSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 171343737e0aSLydia Wang parm = AC_PWRST_D3; 171443737e0aSLydia Wang set_pin_power_state(codec, 0x29, &parm); 171543737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 171643737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 171743737e0aSLydia Wang if (imux_is_smixer) 171843737e0aSLydia Wang parm = AC_PWRST_D0; 171943737e0aSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 172043737e0aSLydia Wang update_power_state(codec, 0x1e, parm); 172143737e0aSLydia Wang update_power_state(codec, 0x1f, parm); 172243737e0aSLydia Wang update_power_state(codec, 0x10, parm); 172343737e0aSLydia Wang update_power_state(codec, 0x11, parm); 172443737e0aSLydia Wang 172543737e0aSLydia Wang /* outputs */ 172643737e0aSLydia Wang /* PW3 (27h), MW3(37h), AOW3 (bh) */ 172743737e0aSLydia Wang if (spec->codec_type == VT1705CF) { 172843737e0aSLydia Wang parm = AC_PWRST_D3; 172943737e0aSLydia Wang update_power_state(codec, 0x27, parm); 173043737e0aSLydia Wang update_power_state(codec, 0x37, parm); 173143737e0aSLydia Wang } else { 173243737e0aSLydia Wang parm = AC_PWRST_D3; 173343737e0aSLydia Wang set_pin_power_state(codec, 0x27, &parm); 173443737e0aSLydia Wang update_power_state(codec, 0x37, parm); 173543737e0aSLydia Wang } 173643737e0aSLydia Wang 173743737e0aSLydia Wang /* PW2 (26h), MW2(36h), AOW2 (ah) */ 173843737e0aSLydia Wang parm = AC_PWRST_D3; 173943737e0aSLydia Wang set_pin_power_state(codec, 0x26, &parm); 174043737e0aSLydia Wang update_power_state(codec, 0x36, parm); 1741b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 174243737e0aSLydia Wang /* PW7(2bh), MW7(3bh), MUX7(1Bh) */ 174343737e0aSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 174443737e0aSLydia Wang update_power_state(codec, 0x3b, parm); 174543737e0aSLydia Wang update_power_state(codec, 0x1b, parm); 174643737e0aSLydia Wang } 174743737e0aSLydia Wang update_conv_power_state(codec, 0xa, parm, 2); 174843737e0aSLydia Wang 174943737e0aSLydia Wang /* PW1 (25h), MW1(35h), AOW1 (9h) */ 175043737e0aSLydia Wang parm = AC_PWRST_D3; 175143737e0aSLydia Wang set_pin_power_state(codec, 0x25, &parm); 175243737e0aSLydia Wang update_power_state(codec, 0x35, parm); 1753b3f6008fSTakashi Iwai if (smart51_enabled(codec)) { 175443737e0aSLydia Wang /* PW6(2ah), MW6(3ah), MUX6(1ah) */ 175543737e0aSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 175643737e0aSLydia Wang update_power_state(codec, 0x3a, parm); 175743737e0aSLydia Wang update_power_state(codec, 0x1a, parm); 175843737e0aSLydia Wang } 175943737e0aSLydia Wang update_conv_power_state(codec, 0x9, parm, 1); 176043737e0aSLydia Wang 176143737e0aSLydia Wang /* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */ 176243737e0aSLydia Wang parm = AC_PWRST_D3; 176343737e0aSLydia Wang set_pin_power_state(codec, 0x28, &parm); 176443737e0aSLydia Wang update_power_state(codec, 0x38, parm); 176543737e0aSLydia Wang update_power_state(codec, 0x18, parm); 1766b3f6008fSTakashi Iwai if (spec->gen.indep_hp_enabled) 176743737e0aSLydia Wang update_conv_power_state(codec, 0xb, parm, 3); 176843737e0aSLydia Wang parm2 = parm; /* for pin 0x0b */ 176943737e0aSLydia Wang 177043737e0aSLydia Wang /* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */ 177143737e0aSLydia Wang parm = AC_PWRST_D3; 177243737e0aSLydia Wang set_pin_power_state(codec, 0x24, &parm); 177343737e0aSLydia Wang update_power_state(codec, 0x34, parm); 1774b3f6008fSTakashi Iwai if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3) 177543737e0aSLydia Wang parm = parm2; 177643737e0aSLydia Wang update_conv_power_state(codec, 0x8, parm, 0); 177743737e0aSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 177843737e0aSLydia Wang update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm); 177943737e0aSLydia Wang } 178043737e0aSLydia Wang 178143737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec) 178243737e0aSLydia Wang { 178343737e0aSLydia Wang struct via_spec *spec; 178443737e0aSLydia Wang int err; 178543737e0aSLydia Wang 178643737e0aSLydia Wang /* create a codec specific record */ 178743737e0aSLydia Wang spec = via_new_spec(codec); 178843737e0aSLydia Wang if (spec == NULL) 178943737e0aSLydia Wang return -ENOMEM; 179043737e0aSLydia Wang 1791b3f6008fSTakashi Iwai spec->gen.mixer_nid = 0x3f; 179243737e0aSLydia Wang add_secret_dac_path(codec); 179343737e0aSLydia Wang 179443737e0aSLydia Wang /* automatic parse from the BIOS config */ 179543737e0aSLydia Wang err = via_parse_auto_config(codec); 179643737e0aSLydia Wang if (err < 0) { 179743737e0aSLydia Wang via_free(codec); 179843737e0aSLydia Wang return err; 179943737e0aSLydia Wang } 180043737e0aSLydia Wang 180143737e0aSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs; 180243737e0aSLydia Wang 180343737e0aSLydia Wang codec->patch_ops = via_patch_ops; 180443737e0aSLydia Wang 180543737e0aSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt3476; 180643737e0aSLydia Wang 180743737e0aSLydia Wang return 0; 180843737e0aSLydia Wang } 180943737e0aSLydia Wang 1810c577b8a1SJoseph Chan /* 1811c577b8a1SJoseph Chan * patch entries 1812c577b8a1SJoseph Chan */ 181390dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 18143218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 18153218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 18163218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 18173218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 18183218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 1819ddd304d8STakashi Iwai .patch = patch_vt1709}, 18203218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 1821ddd304d8STakashi Iwai .patch = patch_vt1709}, 18223218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 1823ddd304d8STakashi Iwai .patch = patch_vt1709}, 18243218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 1825ddd304d8STakashi Iwai .patch = patch_vt1709}, 18263218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 1827ddd304d8STakashi Iwai .patch = patch_vt1709}, 18283218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 1829ddd304d8STakashi Iwai .patch = patch_vt1709}, 18303218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 1831ddd304d8STakashi Iwai .patch = patch_vt1709}, 18323218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 1833ddd304d8STakashi Iwai .patch = patch_vt1709}, 18343218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 1835ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18363218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 1837ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18383218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 1839ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18403218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 1841ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18423218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 1843ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18443218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 1845ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18463218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 1847ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18483218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 1849ddd304d8STakashi Iwai .patch = patch_vt1708B}, 18503218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 1851d949cac1SHarald Welte .patch = patch_vt1708S}, 18523218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 1853d949cac1SHarald Welte .patch = patch_vt1708S}, 18543218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 1855d949cac1SHarald Welte .patch = patch_vt1708S}, 18563218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 1857d949cac1SHarald Welte .patch = patch_vt1708S}, 1858bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 1859d949cac1SHarald Welte .patch = patch_vt1708S}, 18603218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 1861d949cac1SHarald Welte .patch = patch_vt1708S}, 18623218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 1863d949cac1SHarald Welte .patch = patch_vt1708S}, 18643218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 1865d949cac1SHarald Welte .patch = patch_vt1708S}, 18663218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 1867d949cac1SHarald Welte .patch = patch_vt1702}, 18683218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 1869d949cac1SHarald Welte .patch = patch_vt1702}, 18703218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 1871d949cac1SHarald Welte .patch = patch_vt1702}, 18723218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 1873d949cac1SHarald Welte .patch = patch_vt1702}, 18743218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 1875d949cac1SHarald Welte .patch = patch_vt1702}, 18763218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 1877d949cac1SHarald Welte .patch = patch_vt1702}, 18783218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 1879d949cac1SHarald Welte .patch = patch_vt1702}, 18803218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 1881d949cac1SHarald Welte .patch = patch_vt1702}, 1882eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 1883eb7188caSLydia Wang .patch = patch_vt1718S}, 1884eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 1885eb7188caSLydia Wang .patch = patch_vt1718S}, 1886bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 1887bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1888bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 1889bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 1890f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 1891f3db423dSLydia Wang .patch = patch_vt1716S}, 1892f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 1893f3db423dSLydia Wang .patch = patch_vt1716S}, 189425eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 189525eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 1896ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 189736dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 189836dd5c4aSLydia Wang .patch = patch_vt1708S}, 189911890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 190011890956SLydia Wang .patch = patch_vt2002P}, 190111890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 190211890956SLydia Wang .patch = patch_vt2002P}, 190343737e0aSLydia Wang { .id = 0x11064760, .name = "VT1705CF", 190443737e0aSLydia Wang .patch = patch_vt3476}, 19056121b84aSLydia Wang { .id = 0x11064761, .name = "VT1708SCE", 19066121b84aSLydia Wang .patch = patch_vt3476}, 19076121b84aSLydia Wang { .id = 0x11064762, .name = "VT1808", 19086121b84aSLydia Wang .patch = patch_vt3476}, 1909c577b8a1SJoseph Chan {} /* terminator */ 1910c577b8a1SJoseph Chan }; 19111289e9e8STakashi Iwai 19121289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 19131289e9e8STakashi Iwai 19141289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 19151289e9e8STakashi Iwai .preset = snd_hda_preset_via, 19161289e9e8STakashi Iwai .owner = THIS_MODULE, 19171289e9e8STakashi Iwai }; 19181289e9e8STakashi Iwai 19191289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 19201289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 19211289e9e8STakashi Iwai 19221289e9e8STakashi Iwai static int __init patch_via_init(void) 19231289e9e8STakashi Iwai { 19241289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 19251289e9e8STakashi Iwai } 19261289e9e8STakashi Iwai 19271289e9e8STakashi Iwai static void __exit patch_via_exit(void) 19281289e9e8STakashi Iwai { 19291289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 19301289e9e8STakashi Iwai } 19311289e9e8STakashi Iwai 19321289e9e8STakashi Iwai module_init(patch_via_init) 19331289e9e8STakashi Iwai module_exit(patch_via_exit) 1934