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> 52c577b8a1SJoseph Chan #include <sound/core.h> 530aa62aefSHarald Welte #include <sound/asoundef.h> 54c577b8a1SJoseph Chan #include "hda_codec.h" 55c577b8a1SJoseph Chan #include "hda_local.h" 56c577b8a1SJoseph Chan 575b0cb1d8SJaroslav Kysela #define NID_MAPPING (-1) 585b0cb1d8SJaroslav Kysela 59c577b8a1SJoseph Chan /* amp values */ 60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 62c577b8a1SJoseph Chan 63c577b8a1SJoseph Chan /* Pin Widget NID */ 64c577b8a1SJoseph Chan #define VT1708_HP_NID 0x13 65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID 0x14 66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID 0x16 67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN 0x26 6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 70c577b8a1SJoseph Chan 71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID 0x28 72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID 0x13 73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID 0x17 74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN 0x25 75f7278fd0SJosepch Chan 76f7278fd0SJosepch Chan #define VT1708B_HP_NID 0x25 77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID 0x12 78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID 0x15 79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN 0x21 80c577b8a1SJoseph Chan 81d949cac1SHarald Welte #define VT1708S_HP_NID 0x25 82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID 0x12 83d949cac1SHarald Welte 84d949cac1SHarald Welte #define VT1702_HP_NID 0x17 85d949cac1SHarald Welte #define VT1702_DIGOUT_NID 0x11 86d949cac1SHarald Welte 87d7426329SHarald Welte enum VIA_HDA_CODEC { 88d7426329SHarald Welte UNKNOWN = -1, 89d7426329SHarald Welte VT1708, 90d7426329SHarald Welte VT1709_10CH, 91d7426329SHarald Welte VT1709_6CH, 92d7426329SHarald Welte VT1708B_8CH, 93d7426329SHarald Welte VT1708B_4CH, 94d7426329SHarald Welte VT1708S, 95518bf3baSLydia Wang VT1708BCE, 96d7426329SHarald Welte VT1702, 97eb7188caSLydia Wang VT1718S, 98f3db423dSLydia Wang VT1716S, 9925eaba2fSLydia Wang VT2002P, 100ab6734e7SLydia Wang VT1812, 10111890956SLydia Wang VT1802, 102d7426329SHarald Welte CODEC_TYPES, 103d7426329SHarald Welte }; 104d7426329SHarald Welte 10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 10611890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 10711890956SLydia Wang (spec)->codec_type == VT1812 ||\ 10811890956SLydia Wang (spec)->codec_type == VT1802) 10911890956SLydia Wang 1104a79616dSTakashi Iwai struct nid_path { 1114a79616dSTakashi Iwai int depth; 1124a79616dSTakashi Iwai hda_nid_t path[5]; 1134a79616dSTakashi Iwai short idx[5]; 1144a79616dSTakashi Iwai }; 1154a79616dSTakashi Iwai 1161f2e99feSLydia Wang struct via_spec { 1171f2e99feSLydia Wang /* codec parameterization */ 11890dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1191f2e99feSLydia Wang unsigned int num_mixers; 1201f2e99feSLydia Wang 12190dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1221f2e99feSLydia Wang unsigned int num_iverbs; 1231f2e99feSLydia Wang 12482673bc8STakashi Iwai char stream_name_analog[32]; 1257eb56e84STakashi Iwai char stream_name_hp[32]; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12790dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1281f2e99feSLydia Wang 12982673bc8STakashi Iwai char stream_name_digital[32]; 13090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 13190dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1321f2e99feSLydia Wang 1331f2e99feSLydia Wang /* playback */ 1341f2e99feSLydia Wang struct hda_multi_out multiout; 1351f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1361f2e99feSLydia Wang 1374a79616dSTakashi Iwai struct nid_path out_path[4]; 1384a79616dSTakashi Iwai struct nid_path hp_path; 1394a79616dSTakashi Iwai struct nid_path hp_dep_path; 1404a79616dSTakashi Iwai 1411f2e99feSLydia Wang /* capture */ 1421f2e99feSLydia Wang unsigned int num_adc_nids; 143a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1441f2e99feSLydia Wang hda_nid_t mux_nids[3]; 145620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1461f2e99feSLydia Wang hda_nid_t dig_in_nid; 1471f2e99feSLydia Wang hda_nid_t dig_in_pin; 1481f2e99feSLydia Wang 1491f2e99feSLydia Wang /* capture source */ 1501f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1511f2e99feSLydia Wang unsigned int cur_mux[3]; 1521f2e99feSLydia Wang 1531f2e99feSLydia Wang /* PCM information */ 1541f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1551f2e99feSLydia Wang 1561f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1571f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1581f2e99feSLydia Wang struct snd_array kctls; 1591f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1601f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1611f2e99feSLydia Wang 1621f2e99feSLydia Wang /* HP mode source */ 1631f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1641f2e99feSLydia Wang unsigned int hp_independent_mode; 1651f2e99feSLydia Wang unsigned int hp_independent_mode_index; 166f4a7828bSTakashi Iwai unsigned int can_smart51; 1671f2e99feSLydia Wang unsigned int smart51_enabled; 168f3db423dSLydia Wang unsigned int dmic_enabled; 16924088a58STakashi Iwai unsigned int no_pin_power_ctl; 1701f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1711f2e99feSLydia Wang 1721f2e99feSLydia Wang /* work to check hp jack state */ 1731f2e99feSLydia Wang struct hda_codec *codec; 1741f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 175e06e5a29STakashi Iwai int vt1708_jack_detect; 1761f2e99feSLydia Wang int vt1708_hp_present; 1773e95b9abSLydia Wang 1783e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1793e95b9abSLydia Wang 1801f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1811f2e99feSLydia Wang struct hda_loopback_check loopback; 1821f2e99feSLydia Wang #endif 1831f2e99feSLydia Wang }; 1841f2e99feSLydia Wang 1850341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1865b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1875b0cb1d8SJaroslav Kysela { 1885b0cb1d8SJaroslav Kysela struct via_spec *spec; 1895b0cb1d8SJaroslav Kysela 1905b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1915b0cb1d8SJaroslav Kysela if (spec == NULL) 1925b0cb1d8SJaroslav Kysela return NULL; 1935b0cb1d8SJaroslav Kysela 1945b0cb1d8SJaroslav Kysela codec->spec = spec; 1955b0cb1d8SJaroslav Kysela spec->codec = codec; 1960341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1970341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1980341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1990341ccd7SLydia Wang spec->codec_type = VT1708S; 2005b0cb1d8SJaroslav Kysela return spec; 2015b0cb1d8SJaroslav Kysela } 2025b0cb1d8SJaroslav Kysela 203744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 204d7426329SHarald Welte { 205744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 206d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 207d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 208d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 209d7426329SHarald Welte 210d7426329SHarald Welte /* get codec type */ 211d7426329SHarald Welte if (ven_id != 0x1106) 212d7426329SHarald Welte codec_type = UNKNOWN; 213d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 214d7426329SHarald Welte codec_type = VT1708; 215d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 216d7426329SHarald Welte codec_type = VT1709_10CH; 217d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 218d7426329SHarald Welte codec_type = VT1709_6CH; 219518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 220d7426329SHarald Welte codec_type = VT1708B_8CH; 221518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 222518bf3baSLydia Wang codec_type = VT1708BCE; 223518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 224d7426329SHarald Welte codec_type = VT1708B_4CH; 225d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 226d7426329SHarald Welte && (dev_id >> 12) < 8) 227d7426329SHarald Welte codec_type = VT1708S; 228d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 229d7426329SHarald Welte && (dev_id >> 12) < 8) 230d7426329SHarald Welte codec_type = VT1702; 231eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 232eb7188caSLydia Wang && (dev_id >> 12) < 8) 233eb7188caSLydia Wang codec_type = VT1718S; 234f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 235f3db423dSLydia Wang codec_type = VT1716S; 236bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 237bb3c6bfcSLydia Wang codec_type = VT1718S; 23825eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 23925eaba2fSLydia Wang codec_type = VT2002P; 240ab6734e7SLydia Wang else if (dev_id == 0x0448) 241ab6734e7SLydia Wang codec_type = VT1812; 24236dd5c4aSLydia Wang else if (dev_id == 0x0440) 24336dd5c4aSLydia Wang codec_type = VT1708S; 24411890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 24511890956SLydia Wang codec_type = VT1802; 246d7426329SHarald Welte else 247d7426329SHarald Welte codec_type = UNKNOWN; 248d7426329SHarald Welte return codec_type; 249d7426329SHarald Welte }; 250d7426329SHarald Welte 251ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25269e52a80SHarald Welte #define VIA_HP_EVENT 0x01 25369e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 254ec7e7e42SLydia Wang #define VIA_MONO_EVENT 0x03 255ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT 0x04 256ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT 0x05 25769e52a80SHarald Welte 258c577b8a1SJoseph Chan enum { 259c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 260c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 261f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 26225eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 263c577b8a1SJoseph Chan }; 264c577b8a1SJoseph Chan 265c577b8a1SJoseph Chan enum { 266eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 267c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 268c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 269c577b8a1SJoseph Chan AUTO_SEQ_SIDE 270c577b8a1SJoseph Chan }; 271c577b8a1SJoseph Chan 272f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2731f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2741f2e99feSLydia Wang 2751f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2761f2e99feSLydia Wang { 2771f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2781f2e99feSLydia Wang return; 2791f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 280e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2811f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2821f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2831f2e99feSLydia Wang msecs_to_jiffies(100)); 2841f2e99feSLydia Wang } 2851f2e99feSLydia Wang 2861f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2871f2e99feSLydia Wang { 2881f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2891f2e99feSLydia Wang return; 2901f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2911f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2921f2e99feSLydia Wang return; 2931f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 294e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2955b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2961f2e99feSLydia Wang } 297f5271101SLydia Wang 2983e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2993e95b9abSLydia Wang { 3003e95b9abSLydia Wang struct via_spec *spec = codec->spec; 3013e95b9abSLydia Wang if (spec->set_widgets_power_state) 3023e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3033e95b9abSLydia Wang } 30425eaba2fSLydia Wang 305f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 306f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 307f5271101SLydia Wang { 308f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 309f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 310f5271101SLydia Wang 3113e95b9abSLydia Wang set_widgets_power_state(codec); 312f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3131f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3141f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3151f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3161f2e99feSLydia Wang else 3171f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3181f2e99feSLydia Wang } 319f5271101SLydia Wang return change; 320f5271101SLydia Wang } 321f5271101SLydia Wang 322f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 323f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 324f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 325f5271101SLydia Wang .name = NULL, \ 326f5271101SLydia Wang .index = 0, \ 327f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 328f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 329f5271101SLydia Wang .put = analog_input_switch_put, \ 330f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 331f5271101SLydia Wang 33225eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 33325eaba2fSLydia Wang 33425eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 33525eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 33625eaba2fSLydia Wang { 33725eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 33925eaba2fSLydia Wang int i; 34025eaba2fSLydia Wang int change = 0; 34125eaba2fSLydia Wang 34225eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 34325eaba2fSLydia Wang int lmute, rmute; 34425eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 34525eaba2fSLydia Wang snd_printd("Invalid control!\n"); 34625eaba2fSLydia Wang return change; 34725eaba2fSLydia Wang } 34825eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 34925eaba2fSLydia Wang ucontrol); 35025eaba2fSLydia Wang /* Get mute value */ 35125eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 35225eaba2fSLydia Wang valp++; 35325eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 35425eaba2fSLydia Wang 35525eaba2fSLydia Wang /* Set hp pins */ 35625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 35725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 35825eaba2fSLydia Wang snd_hda_codec_amp_update( 35925eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36025eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36125eaba2fSLydia Wang lmute); 36225eaba2fSLydia Wang snd_hda_codec_amp_update( 36325eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36425eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36525eaba2fSLydia Wang rmute); 36625eaba2fSLydia Wang } 36725eaba2fSLydia Wang } 36825eaba2fSLydia Wang 36925eaba2fSLydia Wang if (!lmute && !rmute) { 37025eaba2fSLydia Wang /* Line Outs */ 37125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 37225eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37325eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 37425eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37525eaba2fSLydia Wang /* Speakers */ 37625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37725eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37825eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 37925eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 38025eaba2fSLydia Wang /* unmute */ 38125eaba2fSLydia Wang via_hp_bind_automute(codec); 38225eaba2fSLydia Wang 38325eaba2fSLydia Wang } else { 38425eaba2fSLydia Wang if (lmute) { 38525eaba2fSLydia Wang /* Mute all left channels */ 38625eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38725eaba2fSLydia Wang snd_hda_codec_amp_update( 38825eaba2fSLydia Wang codec, 38925eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 39025eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39125eaba2fSLydia Wang lmute); 39225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39325eaba2fSLydia Wang snd_hda_codec_amp_update( 39425eaba2fSLydia Wang codec, 39525eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39725eaba2fSLydia Wang lmute); 39825eaba2fSLydia Wang } 39925eaba2fSLydia Wang if (rmute) { 40025eaba2fSLydia Wang /* mute all right channels */ 40125eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 40225eaba2fSLydia Wang snd_hda_codec_amp_update( 40325eaba2fSLydia Wang codec, 40425eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 40525eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 40625eaba2fSLydia Wang rmute); 40725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 40825eaba2fSLydia Wang snd_hda_codec_amp_update( 40925eaba2fSLydia Wang codec, 41025eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 41125eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 41225eaba2fSLydia Wang rmute); 41325eaba2fSLydia Wang } 41425eaba2fSLydia Wang } 41525eaba2fSLydia Wang return change; 41625eaba2fSLydia Wang } 41725eaba2fSLydia Wang 41825eaba2fSLydia Wang #define BIND_PIN_MUTE \ 41925eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 42025eaba2fSLydia Wang .name = NULL, \ 42125eaba2fSLydia Wang .index = 0, \ 42225eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 42325eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 42425eaba2fSLydia Wang .put = bind_pin_switch_put, \ 42525eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 42625eaba2fSLydia Wang 42790dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 428c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 429c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 430f5271101SLydia Wang ANALOG_INPUT_MUTE, 43125eaba2fSLydia Wang BIND_PIN_MUTE, 432c577b8a1SJoseph Chan }; 433c577b8a1SJoseph Chan 434ab6734e7SLydia Wang 435c577b8a1SJoseph Chan /* add dynamic controls */ 436291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 437291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 438291c9e33STakashi Iwai const char *name) 439c577b8a1SJoseph Chan { 440c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 441c577b8a1SJoseph Chan 442603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 443603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 444c577b8a1SJoseph Chan if (!knew) 445291c9e33STakashi Iwai return NULL; 446291c9e33STakashi Iwai *knew = *tmpl; 447291c9e33STakashi Iwai if (!name) 448291c9e33STakashi Iwai name = tmpl->name; 449291c9e33STakashi Iwai if (name) { 450c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 451c577b8a1SJoseph Chan if (!knew->name) 452291c9e33STakashi Iwai return NULL; 453291c9e33STakashi Iwai } 454291c9e33STakashi Iwai return knew; 455291c9e33STakashi Iwai } 456291c9e33STakashi Iwai 457291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 458291c9e33STakashi Iwai int idx, unsigned long val) 459291c9e33STakashi Iwai { 460291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 461291c9e33STakashi Iwai 462291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 463291c9e33STakashi Iwai if (!knew) 464c577b8a1SJoseph Chan return -ENOMEM; 465*d7a99cceSTakashi Iwai knew->index = idx; 4664d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4675e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 468c577b8a1SJoseph Chan knew->private_value = val; 469c577b8a1SJoseph Chan return 0; 470c577b8a1SJoseph Chan } 471c577b8a1SJoseph Chan 4727b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4737b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4747b315bb4STakashi Iwai 475291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 4765b0cb1d8SJaroslav Kysela 477603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 478603c4019STakashi Iwai { 479603c4019STakashi Iwai struct via_spec *spec = codec->spec; 480603c4019STakashi Iwai 481603c4019STakashi Iwai if (spec->kctls.list) { 482603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 483603c4019STakashi Iwai int i; 484603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 485603c4019STakashi Iwai kfree(kctl[i].name); 486603c4019STakashi Iwai } 487603c4019STakashi Iwai snd_array_free(&spec->kctls); 488603c4019STakashi Iwai } 489603c4019STakashi Iwai 490c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4919510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4927b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 493c577b8a1SJoseph Chan { 494c577b8a1SJoseph Chan char name[32]; 495c577b8a1SJoseph Chan int err; 496c577b8a1SJoseph Chan 497c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4987b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 499c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 500c577b8a1SJoseph Chan if (err < 0) 501c577b8a1SJoseph Chan return err; 502c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5037b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 504c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 505c577b8a1SJoseph Chan if (err < 0) 506c577b8a1SJoseph Chan return err; 507c577b8a1SJoseph Chan return 0; 508c577b8a1SJoseph Chan } 509c577b8a1SJoseph Chan 510c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 511c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 512c577b8a1SJoseph Chan int dac_idx) 513c577b8a1SJoseph Chan { 514c577b8a1SJoseph Chan /* set as output */ 515c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 516c577b8a1SJoseph Chan pin_type); 517c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 518c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 519d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 520d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 521d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 522c577b8a1SJoseph Chan } 523c577b8a1SJoseph Chan 524c577b8a1SJoseph Chan 525c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 526c577b8a1SJoseph Chan { 527c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 528c577b8a1SJoseph Chan int i; 529c577b8a1SJoseph Chan 530c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 531c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 532c577b8a1SJoseph Chan if (nid) 533c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 534c577b8a1SJoseph Chan } 535c577b8a1SJoseph Chan } 536c577b8a1SJoseph Chan 537c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 538c577b8a1SJoseph Chan { 539c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 540c577b8a1SJoseph Chan hda_nid_t pin; 54125eaba2fSLydia Wang int i; 542c577b8a1SJoseph Chan 54325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 54425eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 545c577b8a1SJoseph Chan if (pin) /* connect to front */ 546c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 547c577b8a1SJoseph Chan } 54825eaba2fSLydia Wang } 549c577b8a1SJoseph Chan 550f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 55132e0191dSClemens Ladisch 552c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 553c577b8a1SJoseph Chan { 554c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5557b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 55632e0191dSClemens Ladisch unsigned int ctl; 557c577b8a1SJoseph Chan int i; 558c577b8a1SJoseph Chan 5597b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5607b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 561f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 56232e0191dSClemens Ladisch ctl = PIN_OUT; 56330649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56432e0191dSClemens Ladisch ctl = PIN_VREF50; 56532e0191dSClemens Ladisch else 56632e0191dSClemens Ladisch ctl = PIN_IN; 567c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56832e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 569c577b8a1SJoseph Chan } 570c577b8a1SJoseph Chan } 571f5271101SLydia Wang 572f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 573f5271101SLydia Wang unsigned int *affected_parm) 574f5271101SLydia Wang { 575f5271101SLydia Wang unsigned parm; 576f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 577f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 578f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 579f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5801564b287SLydia Wang struct via_spec *spec = codec->spec; 58124088a58STakashi Iwai unsigned present = 0; 58224088a58STakashi Iwai 58324088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 58424088a58STakashi Iwai if (!no_presence) 58524088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 586f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 5871564b287SLydia Wang || ((no_presence || present) 5881564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 589f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 590f5271101SLydia Wang parm = AC_PWRST_D0; 591f5271101SLydia Wang } else 592f5271101SLydia Wang parm = AC_PWRST_D3; 593f5271101SLydia Wang 594f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 595f5271101SLydia Wang } 596f5271101SLydia Wang 59724088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 59824088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 59924088a58STakashi Iwai { 60024088a58STakashi Iwai static const char * const texts[] = { 60124088a58STakashi Iwai "Disabled", "Enabled" 60224088a58STakashi Iwai }; 60324088a58STakashi Iwai 60424088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 60524088a58STakashi Iwai uinfo->count = 1; 60624088a58STakashi Iwai uinfo->value.enumerated.items = 2; 60724088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 60824088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 60924088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 61024088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 61124088a58STakashi Iwai return 0; 61224088a58STakashi Iwai } 61324088a58STakashi Iwai 61424088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 61524088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61624088a58STakashi Iwai { 61724088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61824088a58STakashi Iwai struct via_spec *spec = codec->spec; 61924088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 62024088a58STakashi Iwai return 0; 62124088a58STakashi Iwai } 62224088a58STakashi Iwai 62324088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 62424088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 62524088a58STakashi Iwai { 62624088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 62724088a58STakashi Iwai struct via_spec *spec = codec->spec; 62824088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 62924088a58STakashi Iwai 63024088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 63124088a58STakashi Iwai return 0; 63224088a58STakashi Iwai spec->no_pin_power_ctl = val; 63324088a58STakashi Iwai set_widgets_power_state(codec); 63424088a58STakashi Iwai return 1; 63524088a58STakashi Iwai } 63624088a58STakashi Iwai 63724088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 63824088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 63924088a58STakashi Iwai .name = "Dynamic Power-Control", 64024088a58STakashi Iwai .info = via_pin_power_ctl_info, 64124088a58STakashi Iwai .get = via_pin_power_ctl_get, 64224088a58STakashi Iwai .put = via_pin_power_ctl_put, 64324088a58STakashi Iwai }; 64424088a58STakashi Iwai 64524088a58STakashi Iwai 646c577b8a1SJoseph Chan /* 647c577b8a1SJoseph Chan * input MUX handling 648c577b8a1SJoseph Chan */ 649c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 650c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 651c577b8a1SJoseph Chan { 652c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 653c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 654c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 655c577b8a1SJoseph Chan } 656c577b8a1SJoseph Chan 657c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 658c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 659c577b8a1SJoseph Chan { 660c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 661c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 662c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 663c577b8a1SJoseph Chan 664c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 665c577b8a1SJoseph Chan return 0; 666c577b8a1SJoseph Chan } 667c577b8a1SJoseph Chan 668c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 669c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 670c577b8a1SJoseph Chan { 671c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 672c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 673c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 674bff5fbf5SLydia Wang int ret; 675c577b8a1SJoseph Chan 676337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 677337b9d02STakashi Iwai return -EINVAL; 678a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 679a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 680a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 681a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 682a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 683bff5fbf5SLydia Wang 684bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 685bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 686bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 687a80e6e3cSLydia Wang /* update jack power state */ 6883e95b9abSLydia Wang set_widgets_power_state(codec); 689a80e6e3cSLydia Wang 690bff5fbf5SLydia Wang return ret; 691c577b8a1SJoseph Chan } 692c577b8a1SJoseph Chan 6930aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6940aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6950aa62aefSHarald Welte { 6960aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6970aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6980aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6990aa62aefSHarald Welte } 7000aa62aefSHarald Welte 7010aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7020aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7030aa62aefSHarald Welte { 7040aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7055b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 706eb7188caSLydia Wang unsigned int pinsel; 707eb7188caSLydia Wang 708eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 709eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 7100aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 7110aa62aefSHarald Welte 0x00); 7120aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 7130aa62aefSHarald Welte 7140aa62aefSHarald Welte return 0; 7150aa62aefSHarald Welte } 7160aa62aefSHarald Welte 7170713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7180713efebSLydia Wang { 7190713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7200713efebSLydia Wang if (ctl) { 7210713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7220713efebSLydia Wang ctl->vd[0].access |= active 7230713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7240713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7250713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7260713efebSLydia Wang } 7270713efebSLydia Wang } 7280713efebSLydia Wang 7295b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7305b0cb1d8SJaroslav Kysela { 7315b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7325b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7335b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7345b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7355b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 736e87885feSLydia Wang case VT2002P: return 0x19; 737e87885feSLydia Wang case VT1802: return 0x15; 738e87885feSLydia Wang case VT1812: return 0x15; 7395b0cb1d8SJaroslav Kysela default: return 0; 7405b0cb1d8SJaroslav Kysela } 7415b0cb1d8SJaroslav Kysela } 7425b0cb1d8SJaroslav Kysela 743cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 744cdc1784dSLydia Wang { 745cdc1784dSLydia Wang /* mute side channel */ 746cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 747e87885feSLydia Wang unsigned int parm; 7485b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 749cdc1784dSLydia Wang 750e87885feSLydia Wang if (sw3) { 751e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 752e87885feSLydia Wang parm = spec->hp_independent_mode ? 753e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 754e87885feSLydia Wang else 755e87885feSLydia Wang parm = spec->hp_independent_mode ? 756e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 757e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 758e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 759e87885feSLydia Wang if (spec->codec_type == VT1812) 760e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 761e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 762e87885feSLydia Wang } 763cdc1784dSLydia Wang return 0; 764cdc1784dSLydia Wang } 765cdc1784dSLydia Wang 7660aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7670aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7680aa62aefSHarald Welte { 7690aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7700aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7715b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7720aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 773cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 774cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 775cdc1784dSLydia Wang ? 1 : 0; 776ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 777ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 778ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 779ce0e5a9eSLydia Wang else 780ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 781ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7820aa62aefSHarald Welte 783ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 784ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 785ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 786cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 787cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 788cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7890aa62aefSHarald Welte 0, 0, 0); 7900aa62aefSHarald Welte 791cdc1784dSLydia Wang update_side_mute_status(codec); 7920713efebSLydia Wang /* update HP volume/swtich active state */ 7930713efebSLydia Wang if (spec->codec_type == VT1708S 794eb7188caSLydia Wang || spec->codec_type == VT1702 795f3db423dSLydia Wang || spec->codec_type == VT1718S 79625eaba2fSLydia Wang || spec->codec_type == VT1716S 79711890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7980713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7990713efebSLydia Wang spec->hp_independent_mode); 8000713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 8010713efebSLydia Wang spec->hp_independent_mode); 8020713efebSLydia Wang } 803ce0e5a9eSLydia Wang /* update jack power state */ 8043e95b9abSLydia Wang set_widgets_power_state(codec); 8050aa62aefSHarald Welte return 0; 8060aa62aefSHarald Welte } 8070aa62aefSHarald Welte 80890dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 8090aa62aefSHarald Welte { 8100aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8110aa62aefSHarald Welte .name = "Independent HP", 8120aa62aefSHarald Welte .info = via_independent_hp_info, 8130aa62aefSHarald Welte .get = via_independent_hp_get, 8140aa62aefSHarald Welte .put = via_independent_hp_put, 8150aa62aefSHarald Welte }, 8165b0cb1d8SJaroslav Kysela { 8175b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8185b0cb1d8SJaroslav Kysela .name = "Independent HP", 8195b0cb1d8SJaroslav Kysela }, 8200aa62aefSHarald Welte }; 8210aa62aefSHarald Welte 8223d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8235b0cb1d8SJaroslav Kysela { 8243d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8255b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8265b0cb1d8SJaroslav Kysela hda_nid_t nid; 8273d83e577STakashi Iwai int nums; 8283d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8295b0cb1d8SJaroslav Kysela 8305b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8315b0cb1d8SJaroslav Kysela case VT1718S: 8325b0cb1d8SJaroslav Kysela nid = 0x34; 8335b0cb1d8SJaroslav Kysela break; 8345b0cb1d8SJaroslav Kysela case VT2002P: 83511890956SLydia Wang case VT1802: 8365b0cb1d8SJaroslav Kysela nid = 0x35; 8375b0cb1d8SJaroslav Kysela break; 8385b0cb1d8SJaroslav Kysela case VT1812: 8395b0cb1d8SJaroslav Kysela nid = 0x3d; 8405b0cb1d8SJaroslav Kysela break; 8415b0cb1d8SJaroslav Kysela default: 8425b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8435b0cb1d8SJaroslav Kysela break; 8445b0cb1d8SJaroslav Kysela } 8455b0cb1d8SJaroslav Kysela 846ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 847ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 848ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8493d83e577STakashi Iwai if (nums <= 1) 8503d83e577STakashi Iwai return 0; 851ee3c35c0SLydia Wang } 8523d83e577STakashi Iwai 8533d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8543d83e577STakashi Iwai if (knew == NULL) 8553d83e577STakashi Iwai return -ENOMEM; 8563d83e577STakashi Iwai 8575b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8585b0cb1d8SJaroslav Kysela knew->private_value = nid; 8595b0cb1d8SJaroslav Kysela 8605b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8615b0cb1d8SJaroslav Kysela if (knew == NULL) 8625b0cb1d8SJaroslav Kysela return -ENOMEM; 8635b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8645b0cb1d8SJaroslav Kysela 8655b0cb1d8SJaroslav Kysela return 0; 8665b0cb1d8SJaroslav Kysela } 8675b0cb1d8SJaroslav Kysela 8681564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8691564b287SLydia Wang { 8701564b287SLydia Wang int i; 8711564b287SLydia Wang struct snd_ctl_elem_id id; 872525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 873525566cbSLydia Wang struct snd_kcontrol *ctl; 8741564b287SLydia Wang 8751564b287SLydia Wang memset(&id, 0, sizeof(id)); 8761564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8771564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8781564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 879525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 880525566cbSLydia Wang if (ctl) 881525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 882525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 883525566cbSLydia Wang &ctl->id); 8841564b287SLydia Wang } 8851564b287SLydia Wang } 8861564b287SLydia Wang 8871564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8881564b287SLydia Wang { 8891564b287SLydia Wang struct via_spec *spec = codec->spec; 8901564b287SLydia Wang int start_idx; 8911564b287SLydia Wang int end_idx; 8921564b287SLydia Wang int i; 8931564b287SLydia Wang /* get nid of MW0 and start & end index */ 8941564b287SLydia Wang switch (spec->codec_type) { 8951564b287SLydia Wang case VT1708: 8961564b287SLydia Wang start_idx = 2; 8971564b287SLydia Wang end_idx = 4; 8981564b287SLydia Wang break; 8991564b287SLydia Wang case VT1709_10CH: 9001564b287SLydia Wang case VT1709_6CH: 9011564b287SLydia Wang start_idx = 2; 9021564b287SLydia Wang end_idx = 4; 9031564b287SLydia Wang break; 9041564b287SLydia Wang case VT1708B_8CH: 9051564b287SLydia Wang case VT1708B_4CH: 9061564b287SLydia Wang case VT1708S: 907f3db423dSLydia Wang case VT1716S: 9081564b287SLydia Wang start_idx = 2; 9091564b287SLydia Wang end_idx = 4; 9101564b287SLydia Wang break; 911ab657e0cSLydia Wang case VT1718S: 912ab657e0cSLydia Wang start_idx = 1; 913ab657e0cSLydia Wang end_idx = 3; 914ab657e0cSLydia Wang break; 9151564b287SLydia Wang default: 9161564b287SLydia Wang return; 9171564b287SLydia Wang } 9181564b287SLydia Wang /* check AA path's mute status */ 9191564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9201564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 921620e2b28STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, 9221564b287SLydia Wang HDA_AMP_MUTE, val); 9231564b287SLydia Wang } 9241564b287SLydia Wang } 925f4a7828bSTakashi Iwai 926f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 9271564b287SLydia Wang { 928f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9297b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9307b315bb4STakashi Iwai int i; 9317b315bb4STakashi Iwai 9327b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 933f4a7828bSTakashi Iwai unsigned int defcfg; 934f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 935f4a7828bSTakashi Iwai continue; 936f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 937f4a7828bSTakashi Iwai return false; 938f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 939f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 940f4a7828bSTakashi Iwai return false; 941f4a7828bSTakashi Iwai return true; 9421564b287SLydia Wang } 943f4a7828bSTakashi Iwai return false; 9441564b287SLydia Wang } 9451564b287SLydia Wang 9461564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9471564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9481564b287SLydia Wang { 9491564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9501564b287SLydia Wang uinfo->count = 1; 9511564b287SLydia Wang uinfo->value.integer.min = 0; 9521564b287SLydia Wang uinfo->value.integer.max = 1; 9531564b287SLydia Wang return 0; 9541564b287SLydia Wang } 9551564b287SLydia Wang 9561564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9571564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9581564b287SLydia Wang { 9591564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9601564b287SLydia Wang struct via_spec *spec = codec->spec; 9617b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9621564b287SLydia Wang int on = 1; 9631564b287SLydia Wang int i; 9641564b287SLydia Wang 9657b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9667b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 967f4a7828bSTakashi Iwai unsigned int ctl; 96886e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9697b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9701564b287SLydia Wang continue; /* ignore FMic for independent HP */ 971f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 972f4a7828bSTakashi Iwai continue; 973f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 974f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9757b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9761564b287SLydia Wang on = 0; 9771564b287SLydia Wang } 9781564b287SLydia Wang *ucontrol->value.integer.value = on; 9791564b287SLydia Wang return 0; 9801564b287SLydia Wang } 9811564b287SLydia Wang 9821564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9831564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9841564b287SLydia Wang { 9851564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9861564b287SLydia Wang struct via_spec *spec = codec->spec; 9877b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9881564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9891564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9901564b287SLydia Wang int i; 9911564b287SLydia Wang 9927b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9937b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9947b315bb4STakashi Iwai unsigned int parm; 9957b315bb4STakashi Iwai 99686e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9977b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9981564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 999f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 1000f4a7828bSTakashi Iwai continue; 10017b315bb4STakashi Iwai 10027b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 10031564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 10041564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 10051564b287SLydia Wang parm |= out_in; 10061564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 10071564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 10081564b287SLydia Wang parm); 10091564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 10101564b287SLydia Wang mute_aa_path(codec, 1); 10111564b287SLydia Wang notify_aa_path_ctls(codec); 10121564b287SLydia Wang } 10131564b287SLydia Wang } 10141564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10153e95b9abSLydia Wang set_widgets_power_state(codec); 10161564b287SLydia Wang return 1; 10171564b287SLydia Wang } 10181564b287SLydia Wang 10195f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10201564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10211564b287SLydia Wang .name = "Smart 5.1", 10221564b287SLydia Wang .count = 1, 10231564b287SLydia Wang .info = via_smart51_info, 10241564b287SLydia Wang .get = via_smart51_get, 10251564b287SLydia Wang .put = via_smart51_put, 10261564b287SLydia Wang }; 10271564b287SLydia Wang 1028f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 10295b0cb1d8SJaroslav Kysela { 1030f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 10315b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10327b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10335b0cb1d8SJaroslav Kysela hda_nid_t nid; 10345b0cb1d8SJaroslav Kysela int i; 10355b0cb1d8SJaroslav Kysela 1036f4a7828bSTakashi Iwai if (!spec->can_smart51) 1037cb34c207SLydia Wang return 0; 1038cb34c207SLydia Wang 10395f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10405b0cb1d8SJaroslav Kysela if (knew == NULL) 10415b0cb1d8SJaroslav Kysela return -ENOMEM; 10425b0cb1d8SJaroslav Kysela 10437b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10447b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 1045f4a7828bSTakashi Iwai if (is_smart51_pins(codec, nid)) { 10465f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10477b315bb4STakashi Iwai break; 10485b0cb1d8SJaroslav Kysela } 10495b0cb1d8SJaroslav Kysela } 10505b0cb1d8SJaroslav Kysela 10515b0cb1d8SJaroslav Kysela return 0; 10525b0cb1d8SJaroslav Kysela } 10535b0cb1d8SJaroslav Kysela 1054f5271101SLydia Wang /* check AA path's mute statue */ 1055f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1056f5271101SLydia Wang { 1057f5271101SLydia Wang int mute = 1; 1058f5271101SLydia Wang int start_idx; 1059f5271101SLydia Wang int end_idx; 1060f5271101SLydia Wang int i; 1061f5271101SLydia Wang struct via_spec *spec = codec->spec; 1062f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1063f5271101SLydia Wang switch (spec->codec_type) { 1064f5271101SLydia Wang case VT1708B_8CH: 1065f5271101SLydia Wang case VT1708B_4CH: 1066f5271101SLydia Wang case VT1708S: 1067f3db423dSLydia Wang case VT1716S: 1068f5271101SLydia Wang start_idx = 2; 1069f5271101SLydia Wang end_idx = 4; 1070f5271101SLydia Wang break; 1071f5271101SLydia Wang case VT1702: 1072f5271101SLydia Wang start_idx = 1; 1073f5271101SLydia Wang end_idx = 3; 1074f5271101SLydia Wang break; 1075eb7188caSLydia Wang case VT1718S: 1076eb7188caSLydia Wang start_idx = 1; 1077eb7188caSLydia Wang end_idx = 3; 1078eb7188caSLydia Wang break; 107925eaba2fSLydia Wang case VT2002P: 1080ab6734e7SLydia Wang case VT1812: 108111890956SLydia Wang case VT1802: 108225eaba2fSLydia Wang start_idx = 0; 108325eaba2fSLydia Wang end_idx = 2; 108425eaba2fSLydia Wang break; 1085f5271101SLydia Wang default: 1086f5271101SLydia Wang return 0; 1087f5271101SLydia Wang } 1088f5271101SLydia Wang /* check AA path's mute status */ 1089f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1090f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1091620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1092f5271101SLydia Wang int shift = 8 * (i % 4); 1093f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1094f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1095f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1096f5271101SLydia Wang /* check mute status while the pin is connected */ 1097620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 1098f5271101SLydia Wang HDA_INPUT, i) >> 7; 1099620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 1100f5271101SLydia Wang HDA_INPUT, i) >> 7; 1101f5271101SLydia Wang if (!mute_l || !mute_r) { 1102f5271101SLydia Wang mute = 0; 1103f5271101SLydia Wang break; 1104f5271101SLydia Wang } 1105f5271101SLydia Wang } 1106f5271101SLydia Wang } 1107f5271101SLydia Wang return mute; 1108f5271101SLydia Wang } 1109f5271101SLydia Wang 1110f5271101SLydia Wang /* enter/exit analog low-current mode */ 1111f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1112f5271101SLydia Wang { 1113f5271101SLydia Wang struct via_spec *spec = codec->spec; 1114f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1115f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1116f5271101SLydia Wang unsigned int verb = 0; 1117f5271101SLydia Wang unsigned int parm = 0; 1118f5271101SLydia Wang 1119f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1120f5271101SLydia Wang enable = enable && saved_stream_idle; 1121f5271101SLydia Wang else { 1122f5271101SLydia Wang enable = enable && stream_idle; 1123f5271101SLydia Wang saved_stream_idle = stream_idle; 1124f5271101SLydia Wang } 1125f5271101SLydia Wang 1126f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1127f5271101SLydia Wang switch (spec->codec_type) { 1128f5271101SLydia Wang case VT1708B_8CH: 1129f5271101SLydia Wang case VT1708B_4CH: 1130f5271101SLydia Wang verb = 0xf70; 1131f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1132f5271101SLydia Wang break; 1133f5271101SLydia Wang case VT1708S: 1134eb7188caSLydia Wang case VT1718S: 1135f3db423dSLydia Wang case VT1716S: 1136f5271101SLydia Wang verb = 0xf73; 1137f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1138f5271101SLydia Wang break; 1139f5271101SLydia Wang case VT1702: 1140f5271101SLydia Wang verb = 0xf73; 1141f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1142f5271101SLydia Wang break; 114325eaba2fSLydia Wang case VT2002P: 1144ab6734e7SLydia Wang case VT1812: 114511890956SLydia Wang case VT1802: 114625eaba2fSLydia Wang verb = 0xf93; 114725eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 114825eaba2fSLydia Wang break; 1149f5271101SLydia Wang default: 1150f5271101SLydia Wang return; /* other codecs are not supported */ 1151f5271101SLydia Wang } 1152f5271101SLydia Wang /* send verb */ 1153f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1154f5271101SLydia Wang } 1155f5271101SLydia Wang 1156c577b8a1SJoseph Chan /* 1157c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1158c577b8a1SJoseph Chan */ 115990dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1160c577b8a1SJoseph Chan /* 1161c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1162c577b8a1SJoseph Chan */ 1163c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1164c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1165c577b8a1SJoseph Chan 1166c577b8a1SJoseph Chan 1167f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1168c577b8a1SJoseph Chan * mixer widget 1169c577b8a1SJoseph Chan */ 1170c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1171f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1172f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1173f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1174f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1175f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1176c577b8a1SJoseph Chan 1177c577b8a1SJoseph Chan /* 1178c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1179c577b8a1SJoseph Chan */ 1180c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1181c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1182c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1183c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1184c577b8a1SJoseph Chan 1185bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1186bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1187c577b8a1SJoseph Chan /* PW9 Output enable */ 1188c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1189aa266fccSLydia Wang /* power down jack detect function */ 1190aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1191f7278fd0SJosepch Chan { } 1192c577b8a1SJoseph Chan }; 1193c577b8a1SJoseph Chan 11947eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec, 11957eb56e84STakashi Iwai struct snd_pcm_substream *substream) 11967eb56e84STakashi Iwai { 11977eb56e84STakashi Iwai int idle = substream->pstr->substream_opened == 1 11987eb56e84STakashi Iwai && substream->ref_count == 0; 11997eb56e84STakashi Iwai analog_low_current_mode(codec, idle); 12007eb56e84STakashi Iwai } 12017eb56e84STakashi Iwai 1202c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1203c577b8a1SJoseph Chan struct hda_codec *codec, 1204c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1205c577b8a1SJoseph Chan { 1206c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 12077eb56e84STakashi Iwai substream_set_idle(codec, substream); 12089a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12099a08160bSTakashi Iwai hinfo); 1210c577b8a1SJoseph Chan } 1211c577b8a1SJoseph Chan 12129af74210STakashi Iwai static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, 12139af74210STakashi Iwai struct hda_codec *codec, 12149af74210STakashi Iwai struct snd_pcm_substream *substream) 12159af74210STakashi Iwai { 12167eb56e84STakashi Iwai substream_set_idle(codec, substream); 12179af74210STakashi Iwai return 0; 12189af74210STakashi Iwai } 12199af74210STakashi Iwai 12207eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 12217eb56e84STakashi Iwai struct hda_codec *codec, 12227eb56e84STakashi Iwai struct snd_pcm_substream *substream) 12237eb56e84STakashi Iwai { 12247eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 12257eb56e84STakashi Iwai struct hda_multi_out *mout = &spec->multiout; 12267eb56e84STakashi Iwai 12277eb56e84STakashi Iwai if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] || 12287eb56e84STakashi Iwai !spec->hp_independent_mode) 12297eb56e84STakashi Iwai return -EINVAL; 12307eb56e84STakashi Iwai substream_set_idle(codec, substream); 12317eb56e84STakashi Iwai return 0; 12327eb56e84STakashi Iwai } 12337eb56e84STakashi Iwai 12347eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 12357eb56e84STakashi Iwai struct hda_codec *codec, 12360aa62aefSHarald Welte unsigned int stream_tag, 12370aa62aefSHarald Welte unsigned int format, 12380aa62aefSHarald Welte struct snd_pcm_substream *substream) 12390aa62aefSHarald Welte { 12400aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12410aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1242dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12430aa62aefSHarald Welte int chs = substream->runtime->channels; 12440aa62aefSHarald Welte int i; 12457c935976SStephen Warren struct hda_spdif_out *spdif = 12467c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12470aa62aefSHarald Welte 12480aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12490aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12500aa62aefSHarald Welte if (chs == 2 && 12510aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12520aa62aefSHarald Welte format) && 12537c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12540aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12550aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12560aa62aefSHarald Welte * be updated */ 12577c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12580aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12590aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12607c935976SStephen Warren spdif->ctls & 12610aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12620aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12630aa62aefSHarald Welte stream_tag, 0, format); 12640aa62aefSHarald Welte /* turn on again (if needed) */ 12657c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12660aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12670aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12687c935976SStephen Warren spdif->ctls & 0xff); 12690aa62aefSHarald Welte } else { 12700aa62aefSHarald Welte mout->dig_out_used = 0; 12710aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12720aa62aefSHarald Welte 0, 0, 0); 12730aa62aefSHarald Welte } 12740aa62aefSHarald Welte } 12750aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12760aa62aefSHarald Welte 12770aa62aefSHarald Welte /* front */ 12780aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12790aa62aefSHarald Welte 0, format); 12800aa62aefSHarald Welte 1281eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1282eb7188caSLydia Wang && !spec->hp_independent_mode) 12830aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12840aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12850aa62aefSHarald Welte 0, format); 12860aa62aefSHarald Welte 12870aa62aefSHarald Welte /* extra outputs copied from front */ 12880aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12890aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12900aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12910aa62aefSHarald Welte mout->extra_out_nid[i], 12920aa62aefSHarald Welte stream_tag, 0, format); 12930aa62aefSHarald Welte 12940aa62aefSHarald Welte /* surrounds */ 12950aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12960aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12970aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12980aa62aefSHarald Welte i * 2, format); 12990aa62aefSHarald Welte else /* copy front */ 13000aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13010aa62aefSHarald Welte 0, format); 13020aa62aefSHarald Welte } 13037eb56e84STakashi Iwai vt1708_start_hp_work(spec); 13047eb56e84STakashi Iwai return 0; 13050aa62aefSHarald Welte } 13060aa62aefSHarald Welte 13077eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 13080aa62aefSHarald Welte struct hda_codec *codec, 13090aa62aefSHarald Welte unsigned int stream_tag, 13100aa62aefSHarald Welte unsigned int format, 13110aa62aefSHarald Welte struct snd_pcm_substream *substream) 13120aa62aefSHarald Welte { 13130aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13140aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 13150aa62aefSHarald Welte 13167eb56e84STakashi Iwai snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); 13171f2e99feSLydia Wang vt1708_start_hp_work(spec); 13180aa62aefSHarald Welte return 0; 13190aa62aefSHarald Welte } 13200aa62aefSHarald Welte 13210aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13220aa62aefSHarald Welte struct hda_codec *codec, 13230aa62aefSHarald Welte struct snd_pcm_substream *substream) 13240aa62aefSHarald Welte { 13250aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13260aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1327dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13280aa62aefSHarald Welte int i; 13290aa62aefSHarald Welte 13300aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13310aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13320aa62aefSHarald Welte 13330aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13340aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13350aa62aefSHarald Welte 0, 0, 0); 13360aa62aefSHarald Welte 13370aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13380aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13390aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13400aa62aefSHarald Welte mout->extra_out_nid[i], 13410aa62aefSHarald Welte 0, 0, 0); 13420aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13430aa62aefSHarald Welte if (mout->dig_out_nid && 13440aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13450aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13460aa62aefSHarald Welte 0, 0, 0); 13470aa62aefSHarald Welte mout->dig_out_used = 0; 13480aa62aefSHarald Welte } 13490aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13507eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 13517eb56e84STakashi Iwai return 0; 13520aa62aefSHarald Welte } 13537eb56e84STakashi Iwai 13547eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 13557eb56e84STakashi Iwai struct hda_codec *codec, 13567eb56e84STakashi Iwai struct snd_pcm_substream *substream) 13577eb56e84STakashi Iwai { 13587eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 13597eb56e84STakashi Iwai struct hda_multi_out *mout = &spec->multiout; 13607eb56e84STakashi Iwai 13617eb56e84STakashi Iwai snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); 13621f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13630aa62aefSHarald Welte return 0; 13640aa62aefSHarald Welte } 13650aa62aefSHarald Welte 1366c577b8a1SJoseph Chan /* 1367c577b8a1SJoseph Chan * Digital out 1368c577b8a1SJoseph Chan */ 1369c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1370c577b8a1SJoseph Chan struct hda_codec *codec, 1371c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1372c577b8a1SJoseph Chan { 1373c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1374c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1375c577b8a1SJoseph Chan } 1376c577b8a1SJoseph Chan 1377c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1378c577b8a1SJoseph Chan struct hda_codec *codec, 1379c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1380c577b8a1SJoseph Chan { 1381c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1382c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1383c577b8a1SJoseph Chan } 1384c577b8a1SJoseph Chan 13855691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 138698aa34c0SHarald Welte struct hda_codec *codec, 138798aa34c0SHarald Welte unsigned int stream_tag, 138898aa34c0SHarald Welte unsigned int format, 138998aa34c0SHarald Welte struct snd_pcm_substream *substream) 139098aa34c0SHarald Welte { 139198aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13929da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13939da29271STakashi Iwai stream_tag, format, substream); 13949da29271STakashi Iwai } 13955691ec7fSHarald Welte 13969da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 13979da29271STakashi Iwai struct hda_codec *codec, 13989da29271STakashi Iwai struct snd_pcm_substream *substream) 13999da29271STakashi Iwai { 14009da29271STakashi Iwai struct via_spec *spec = codec->spec; 14019da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 140298aa34c0SHarald Welte return 0; 140398aa34c0SHarald Welte } 140498aa34c0SHarald Welte 1405c577b8a1SJoseph Chan /* 1406c577b8a1SJoseph Chan * Analog capture 1407c577b8a1SJoseph Chan */ 1408c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1409c577b8a1SJoseph Chan struct hda_codec *codec, 1410c577b8a1SJoseph Chan unsigned int stream_tag, 1411c577b8a1SJoseph Chan unsigned int format, 1412c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1413c577b8a1SJoseph Chan { 1414c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1415c577b8a1SJoseph Chan 1416c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1417c577b8a1SJoseph Chan stream_tag, 0, format); 1418c577b8a1SJoseph Chan return 0; 1419c577b8a1SJoseph Chan } 1420c577b8a1SJoseph Chan 1421c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1422c577b8a1SJoseph Chan struct hda_codec *codec, 1423c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1424c577b8a1SJoseph Chan { 1425c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1426888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1427c577b8a1SJoseph Chan return 0; 1428c577b8a1SJoseph Chan } 1429c577b8a1SJoseph Chan 14309af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 14317eb56e84STakashi Iwai .substreams = 1, 1432c577b8a1SJoseph Chan .channels_min = 2, 1433c577b8a1SJoseph Chan .channels_max = 8, 14349af74210STakashi Iwai /* NID is set in via_build_pcms */ 1435c577b8a1SJoseph Chan .ops = { 1436c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14379af74210STakashi Iwai .close = via_playback_pcm_close, 14380aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14390aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1440c577b8a1SJoseph Chan }, 1441c577b8a1SJoseph Chan }; 1442c577b8a1SJoseph Chan 14437eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 14447eb56e84STakashi Iwai .substreams = 1, 14457eb56e84STakashi Iwai .channels_min = 2, 14467eb56e84STakashi Iwai .channels_max = 2, 14477eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 14487eb56e84STakashi Iwai .ops = { 14497eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 14507eb56e84STakashi Iwai .close = via_playback_pcm_close, 14517eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 14527eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 14537eb56e84STakashi Iwai }, 14547eb56e84STakashi Iwai }; 14557eb56e84STakashi Iwai 145690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 14577eb56e84STakashi Iwai .substreams = 1, 1458bc9b5623STakashi Iwai .channels_min = 2, 1459bc9b5623STakashi Iwai .channels_max = 8, 14609af74210STakashi Iwai /* NID is set in via_build_pcms */ 1461bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1462bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1463bc9b5623STakashi Iwai * disable the 24bit format, so far. 1464bc9b5623STakashi Iwai */ 1465bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1466bc9b5623STakashi Iwai .ops = { 1467bc9b5623STakashi Iwai .open = via_playback_pcm_open, 14689af74210STakashi Iwai .close = via_playback_pcm_close, 1469c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1470c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1471bc9b5623STakashi Iwai }, 1472bc9b5623STakashi Iwai }; 1473bc9b5623STakashi Iwai 14749af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 14757eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1476c577b8a1SJoseph Chan .channels_min = 2, 1477c577b8a1SJoseph Chan .channels_max = 2, 14789af74210STakashi Iwai /* NID is set in via_build_pcms */ 1479c577b8a1SJoseph Chan .ops = { 14809af74210STakashi Iwai .open = via_playback_pcm_open, 14819af74210STakashi Iwai .close = via_playback_pcm_close, 1482c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1483c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1484c577b8a1SJoseph Chan }, 1485c577b8a1SJoseph Chan }; 1486c577b8a1SJoseph Chan 14879af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1488c577b8a1SJoseph Chan .substreams = 1, 1489c577b8a1SJoseph Chan .channels_min = 2, 1490c577b8a1SJoseph Chan .channels_max = 2, 1491c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1492c577b8a1SJoseph Chan .ops = { 1493c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14946b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14959da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14969da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1497c577b8a1SJoseph Chan }, 1498c577b8a1SJoseph Chan }; 1499c577b8a1SJoseph Chan 15009af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1501c577b8a1SJoseph Chan .substreams = 1, 1502c577b8a1SJoseph Chan .channels_min = 2, 1503c577b8a1SJoseph Chan .channels_max = 2, 1504c577b8a1SJoseph Chan }; 1505c577b8a1SJoseph Chan 1506c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1507c577b8a1SJoseph Chan { 1508c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 15095b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 151090dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 15115b0cb1d8SJaroslav Kysela int err, i; 1512c577b8a1SJoseph Chan 151324088a58STakashi Iwai if (spec->set_widgets_power_state) 151424088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 151524088a58STakashi Iwai return -ENOMEM; 151624088a58STakashi Iwai 1517c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1518c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1519c577b8a1SJoseph Chan if (err < 0) 1520c577b8a1SJoseph Chan return err; 1521c577b8a1SJoseph Chan } 1522c577b8a1SJoseph Chan 1523c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1524c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 152574b654c9SStephen Warren spec->multiout.dig_out_nid, 1526c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1527c577b8a1SJoseph Chan if (err < 0) 1528c577b8a1SJoseph Chan return err; 15299a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15309a08160bSTakashi Iwai &spec->multiout); 15319a08160bSTakashi Iwai if (err < 0) 15329a08160bSTakashi Iwai return err; 15339a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1534c577b8a1SJoseph Chan } 1535c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1536c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1537c577b8a1SJoseph Chan if (err < 0) 1538c577b8a1SJoseph Chan return err; 1539c577b8a1SJoseph Chan } 154017314379SLydia Wang 15415b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15425b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15435b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 154421949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15455b0cb1d8SJaroslav Kysela if (err < 0) 15465b0cb1d8SJaroslav Kysela return err; 15475b0cb1d8SJaroslav Kysela } 15485b0cb1d8SJaroslav Kysela 15495b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15505b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15515b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15525b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15535b0cb1d8SJaroslav Kysela continue; 15545b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15555b0cb1d8SJaroslav Kysela if (kctl == NULL) 15565b0cb1d8SJaroslav Kysela continue; 15575b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15585b0cb1d8SJaroslav Kysela knew->subdevice); 15595b0cb1d8SJaroslav Kysela } 15605b0cb1d8SJaroslav Kysela } 15615b0cb1d8SJaroslav Kysela 156217314379SLydia Wang /* init power states */ 15633e95b9abSLydia Wang set_widgets_power_state(codec); 156417314379SLydia Wang analog_low_current_mode(codec, 1); 156517314379SLydia Wang 1566603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1567c577b8a1SJoseph Chan return 0; 1568c577b8a1SJoseph Chan } 1569c577b8a1SJoseph Chan 1570c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1571c577b8a1SJoseph Chan { 1572c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1573c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1574c577b8a1SJoseph Chan 1575c577b8a1SJoseph Chan codec->num_pcms = 1; 1576c577b8a1SJoseph Chan codec->pcm_info = info; 1577c577b8a1SJoseph Chan 157882673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 157982673bc8STakashi Iwai "%s Analog", codec->chip_name); 1580c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 15819af74210STakashi Iwai 15829af74210STakashi Iwai if (!spec->stream_analog_playback) 15839af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1584377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 15859af74210STakashi Iwai *spec->stream_analog_playback; 1586377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1587377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1588c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1589c577b8a1SJoseph Chan spec->multiout.max_channels; 15909af74210STakashi Iwai 15919af74210STakashi Iwai if (!spec->stream_analog_capture) 15929af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 15939af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 15949af74210STakashi Iwai *spec->stream_analog_capture; 15959af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 15969af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 15979af74210STakashi Iwai spec->num_adc_nids; 1598c577b8a1SJoseph Chan 1599c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1600c577b8a1SJoseph Chan codec->num_pcms++; 1601c577b8a1SJoseph Chan info++; 160282673bc8STakashi Iwai snprintf(spec->stream_name_digital, 160382673bc8STakashi Iwai sizeof(spec->stream_name_digital), 160482673bc8STakashi Iwai "%s Digital", codec->chip_name); 1605c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 16067ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1607c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 16089af74210STakashi Iwai if (!spec->stream_digital_playback) 16099af74210STakashi Iwai spec->stream_digital_playback = 16109af74210STakashi Iwai &via_pcm_digital_playback; 1611c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 16129af74210STakashi Iwai *spec->stream_digital_playback; 1613c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1614c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1615c577b8a1SJoseph Chan } 1616c577b8a1SJoseph Chan if (spec->dig_in_nid) { 16179af74210STakashi Iwai if (!spec->stream_digital_capture) 16189af74210STakashi Iwai spec->stream_digital_capture = 16199af74210STakashi Iwai &via_pcm_digital_capture; 1620c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 16219af74210STakashi Iwai *spec->stream_digital_capture; 1622c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1623c577b8a1SJoseph Chan spec->dig_in_nid; 1624c577b8a1SJoseph Chan } 1625c577b8a1SJoseph Chan } 1626c577b8a1SJoseph Chan 16277eb56e84STakashi Iwai if (spec->multiout.hp_nid) { 16287eb56e84STakashi Iwai codec->num_pcms++; 16297eb56e84STakashi Iwai info++; 16307eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 16317eb56e84STakashi Iwai "%s HP", codec->chip_name); 16327eb56e84STakashi Iwai info->name = spec->stream_name_hp; 16337eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 16347eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 16357eb56e84STakashi Iwai spec->multiout.hp_nid; 16367eb56e84STakashi Iwai } 1637c577b8a1SJoseph Chan return 0; 1638c577b8a1SJoseph Chan } 1639c577b8a1SJoseph Chan 1640c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1641c577b8a1SJoseph Chan { 1642c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1643c577b8a1SJoseph Chan 1644c577b8a1SJoseph Chan if (!spec) 1645c577b8a1SJoseph Chan return; 1646c577b8a1SJoseph Chan 1647603c4019STakashi Iwai via_free_kctls(codec); 16481f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1649c577b8a1SJoseph Chan kfree(codec->spec); 1650c577b8a1SJoseph Chan } 1651c577b8a1SJoseph Chan 165264be285bSTakashi Iwai /* mute/unmute outputs */ 165364be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 165464be285bSTakashi Iwai hda_nid_t *pins, bool mute) 165564be285bSTakashi Iwai { 165664be285bSTakashi Iwai int i; 165764be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 165864be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 165964be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 166064be285bSTakashi Iwai mute ? 0 : PIN_OUT); 166164be285bSTakashi Iwai } 166264be285bSTakashi Iwai 166369e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 166469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 166569e52a80SHarald Welte { 1666dcf34c8cSLydia Wang unsigned int present = 0; 166769e52a80SHarald Welte struct via_spec *spec = codec->spec; 166869e52a80SHarald Welte 1669d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1670dcf34c8cSLydia Wang 167164be285bSTakashi Iwai if (!spec->hp_independent_mode) 167264be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 167364be285bSTakashi Iwai spec->autocfg.line_out_pins, 167464be285bSTakashi Iwai present); 167569e52a80SHarald Welte } 167669e52a80SHarald Welte 1677f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1678f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1679f3db423dSLydia Wang { 1680f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1681f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1682f3db423dSLydia Wang 1683f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1684f3db423dSLydia Wang return; 1685f3db423dSLydia Wang 1686d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1687d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1688f3db423dSLydia Wang 1689f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1690f3db423dSLydia Wang if (lineout_present) { 16913e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 16923e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16933e0693e2STakashi Iwai lineout_present ? 0 : PIN_OUT); 1694f3db423dSLydia Wang return; 1695f3db423dSLydia Wang } 1696f3db423dSLydia Wang 1697d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1698f3db423dSLydia Wang 1699f3db423dSLydia Wang if (!spec->hp_independent_mode) 17003e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 17013e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17023e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 1703f3db423dSLydia Wang } 1704f3db423dSLydia Wang 170569e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 170669e52a80SHarald Welte { 170769e52a80SHarald Welte unsigned int gpio_data; 170869e52a80SHarald Welte unsigned int vol_counter; 170969e52a80SHarald Welte unsigned int vol; 171069e52a80SHarald Welte unsigned int master_vol; 171169e52a80SHarald Welte 171269e52a80SHarald Welte struct via_spec *spec = codec->spec; 171369e52a80SHarald Welte 171469e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 171569e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 171669e52a80SHarald Welte 171769e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 171869e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 171969e52a80SHarald Welte 172069e52a80SHarald Welte vol = vol_counter & 0x1F; 172169e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 172269e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 172369e52a80SHarald Welte AC_AMP_GET_INPUT); 172469e52a80SHarald Welte 172569e52a80SHarald Welte if (gpio_data == 0x02) { 172669e52a80SHarald Welte /* unmute line out */ 17273e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17283e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17293e0693e2STakashi Iwai PIN_OUT); 173069e52a80SHarald Welte if (vol_counter & 0x20) { 173169e52a80SHarald Welte /* decrease volume */ 173269e52a80SHarald Welte if (vol > master_vol) 173369e52a80SHarald Welte vol = master_vol; 173469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 173569e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 173669e52a80SHarald Welte master_vol-vol); 173769e52a80SHarald Welte } else { 173869e52a80SHarald Welte /* increase volume */ 173969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 174069e52a80SHarald Welte HDA_AMP_VOLMASK, 174169e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 174269e52a80SHarald Welte (master_vol+vol)); 174369e52a80SHarald Welte } 174469e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 174569e52a80SHarald Welte /* mute line out */ 17463e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17473e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17483e0693e2STakashi Iwai 0); 174969e52a80SHarald Welte } 175069e52a80SHarald Welte } 175169e52a80SHarald Welte 175225eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 175325eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 175425eaba2fSLydia Wang { 175525eaba2fSLydia Wang unsigned int hp_present; 175625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 175725eaba2fSLydia Wang 175827439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 175925eaba2fSLydia Wang return; 176025eaba2fSLydia Wang 1761d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 176225eaba2fSLydia Wang 176364be285bSTakashi Iwai if (!spec->hp_independent_mode) 176464be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 176564be285bSTakashi Iwai spec->autocfg.speaker_pins, 176664be285bSTakashi Iwai hp_present); 176725eaba2fSLydia Wang } 176825eaba2fSLydia Wang 176925eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 177025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 177125eaba2fSLydia Wang { 177264be285bSTakashi Iwai int present; 177325eaba2fSLydia Wang struct via_spec *spec = codec->spec; 177425eaba2fSLydia Wang 177525eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 177625eaba2fSLydia Wang return; 177725eaba2fSLydia Wang 177864be285bSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 177964be285bSTakashi Iwai if (!spec->hp_independent_mode) 178064be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 178164be285bSTakashi Iwai spec->autocfg.line_out_pins, 178264be285bSTakashi Iwai present); 178325eaba2fSLydia Wang 178464be285bSTakashi Iwai if (!present) 178564be285bSTakashi Iwai present = snd_hda_jack_detect(codec, 178664be285bSTakashi Iwai spec->autocfg.line_out_pins[0]); 178725eaba2fSLydia Wang 178825eaba2fSLydia Wang /* Speakers */ 178964be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 179064be285bSTakashi Iwai spec->autocfg.speaker_pins, 179164be285bSTakashi Iwai present); 179225eaba2fSLydia Wang } 179325eaba2fSLydia Wang 179425eaba2fSLydia Wang 179569e52a80SHarald Welte /* unsolicited event for jack sensing */ 179669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 179769e52a80SHarald Welte unsigned int res) 179869e52a80SHarald Welte { 179969e52a80SHarald Welte res >>= 26; 1800ec7e7e42SLydia Wang 1801a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 18023e95b9abSLydia Wang set_widgets_power_state(codec); 1803ec7e7e42SLydia Wang 1804ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1805ec7e7e42SLydia Wang 1806ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1807ec7e7e42SLydia Wang via_hp_automute(codec); 1808ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1809ec7e7e42SLydia Wang via_gpio_control(codec); 1810ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1811f3db423dSLydia Wang via_mono_automute(codec); 1812ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 181325eaba2fSLydia Wang via_speaker_automute(codec); 1814ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 181525eaba2fSLydia Wang via_hp_bind_automute(codec); 181669e52a80SHarald Welte } 181769e52a80SHarald Welte 1818c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1819c577b8a1SJoseph Chan { 1820c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 182169e52a80SHarald Welte int i; 182269e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 182369e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 182469e52a80SHarald Welte 1825f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1826f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 182755d1d6c1STakashi Iwai if (spec->dig_in_pin) { 182855d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1829f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 183012b74c80STakashi Iwai PIN_OUT); 183155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1832f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1833f7278fd0SJosepch Chan } 183412b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 183512b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 183612b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1837f7278fd0SJosepch Chan 18389da29271STakashi Iwai /* assign slave outs */ 18399da29271STakashi Iwai if (spec->slave_dig_outs[0]) 18409da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 18415691ec7fSHarald Welte 1842c577b8a1SJoseph Chan return 0; 1843c577b8a1SJoseph Chan } 1844c577b8a1SJoseph Chan 18451f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18461f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18471f2e99feSLydia Wang { 18481f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18491f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18501f2e99feSLydia Wang return 0; 18511f2e99feSLydia Wang } 18521f2e99feSLydia Wang #endif 18531f2e99feSLydia Wang 1854cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1855cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1856cb53c626STakashi Iwai { 1857cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1858cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1859cb53c626STakashi Iwai } 1860cb53c626STakashi Iwai #endif 1861cb53c626STakashi Iwai 1862c577b8a1SJoseph Chan /* 1863c577b8a1SJoseph Chan */ 186490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1865c577b8a1SJoseph Chan .build_controls = via_build_controls, 1866c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1867c577b8a1SJoseph Chan .init = via_init, 1868c577b8a1SJoseph Chan .free = via_free, 18691f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18701f2e99feSLydia Wang .suspend = via_suspend, 18711f2e99feSLydia Wang #endif 1872cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1873cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1874cb53c626STakashi Iwai #endif 1875c577b8a1SJoseph Chan }; 1876c577b8a1SJoseph Chan 18774a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1878c577b8a1SJoseph Chan { 18794a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 18804a79616dSTakashi Iwai int i; 18814a79616dSTakashi Iwai 18824a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 18834a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 18844a79616dSTakashi Iwai return false; 18854a79616dSTakashi Iwai } 18864a79616dSTakashi Iwai if (spec->multiout.hp_nid == dac) 18874a79616dSTakashi Iwai return false; 18884a79616dSTakashi Iwai return true; 18894a79616dSTakashi Iwai } 18904a79616dSTakashi Iwai 18914a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 18924a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 18934a79616dSTakashi Iwai int depth, int wid_type) 18944a79616dSTakashi Iwai { 18954a79616dSTakashi Iwai hda_nid_t conn[8]; 18964a79616dSTakashi Iwai int i, nums; 18974a79616dSTakashi Iwai 18984a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 18994a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 19004a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 19014a79616dSTakashi Iwai continue; 19024a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 19034a79616dSTakashi Iwai path->path[depth] = conn[i]; 19044a79616dSTakashi Iwai path->idx[depth] = i; 19054a79616dSTakashi Iwai path->depth = ++depth; 19064a79616dSTakashi Iwai return true; 19074a79616dSTakashi Iwai } 19084a79616dSTakashi Iwai } 19094a79616dSTakashi Iwai if (depth > 4) 19104a79616dSTakashi Iwai return false; 19114a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 19124a79616dSTakashi Iwai unsigned int type; 19134a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 19144a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 19154a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 19164a79616dSTakashi Iwai continue; 19174a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 19184a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 19194a79616dSTakashi Iwai path->path[depth] = conn[i]; 19204a79616dSTakashi Iwai path->idx[depth] = i; 19214a79616dSTakashi Iwai return true; 19224a79616dSTakashi Iwai } 19234a79616dSTakashi Iwai } 19244a79616dSTakashi Iwai return false; 19254a79616dSTakashi Iwai } 19264a79616dSTakashi Iwai 19274a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 19284a79616dSTakashi Iwai { 19294a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 19304a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1931c577b8a1SJoseph Chan int i; 1932c577b8a1SJoseph Chan hda_nid_t nid; 1933c577b8a1SJoseph Chan 1934c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 19354a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 19364a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1937c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 19384a79616dSTakashi Iwai if (!nid) 19394a79616dSTakashi Iwai continue; 19404a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 19414a79616dSTakashi Iwai spec->private_dac_nids[i] = 19424a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1943c577b8a1SJoseph Chan } 1944c577b8a1SJoseph Chan return 0; 1945c577b8a1SJoseph Chan } 1946c577b8a1SJoseph Chan 19474a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 19484a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1949c577b8a1SJoseph Chan { 19504a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1951c577b8a1SJoseph Chan char name[32]; 19524a79616dSTakashi Iwai hda_nid_t nid; 19534a79616dSTakashi Iwai int err; 1954c577b8a1SJoseph Chan 19554a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19564a79616dSTakashi Iwai nid = dac; 19574a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19584a79616dSTakashi Iwai nid = pin; 19594a79616dSTakashi Iwai else 19604a79616dSTakashi Iwai nid = 0; 19614a79616dSTakashi Iwai if (nid) { 19624a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1963c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 19644a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1965c577b8a1SJoseph Chan if (err < 0) 1966c577b8a1SJoseph Chan return err; 1967c577b8a1SJoseph Chan } 19684a79616dSTakashi Iwai 19694a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19704a79616dSTakashi Iwai nid = dac; 19714a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19724a79616dSTakashi Iwai nid = pin; 19734a79616dSTakashi Iwai else 19744a79616dSTakashi Iwai nid = 0; 19754a79616dSTakashi Iwai if (nid) { 19764a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 19774a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 19784a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 19794a79616dSTakashi Iwai if (err < 0) 19804a79616dSTakashi Iwai return err; 19814a79616dSTakashi Iwai } 19824a79616dSTakashi Iwai return 0; 19834a79616dSTakashi Iwai } 19844a79616dSTakashi Iwai 19854a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 19864a79616dSTakashi Iwai hda_nid_t nid); 19874a79616dSTakashi Iwai 1988f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1989f4a7828bSTakashi Iwai { 1990f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1991f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1992f4a7828bSTakashi Iwai int i; 1993f4a7828bSTakashi Iwai 1994f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1995f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, cfg->inputs[i].pin)) 1996f4a7828bSTakashi Iwai continue; 1997f4a7828bSTakashi Iwai spec->can_smart51 = 1; 1998f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 1999f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 2000f4a7828bSTakashi Iwai break; 2001f4a7828bSTakashi Iwai } 2002f4a7828bSTakashi Iwai } 2003f4a7828bSTakashi Iwai 20044a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 20054a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 20064a79616dSTakashi Iwai { 20074a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 2008f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 20094a79616dSTakashi Iwai static const char * const chname[4] = { 20104a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 20114a79616dSTakashi Iwai }; 20124a79616dSTakashi Iwai int i, idx, err; 2013f4a7828bSTakashi Iwai int old_line_outs; 2014f4a7828bSTakashi Iwai 2015f4a7828bSTakashi Iwai /* check smart51 */ 2016f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 2017f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 2018f4a7828bSTakashi Iwai mangle_smart51(codec); 20194a79616dSTakashi Iwai 20204a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 20214a79616dSTakashi Iwai hda_nid_t pin, dac; 20224a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 20234a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 20244a79616dSTakashi Iwai if (!pin || !dac) 20254a79616dSTakashi Iwai continue; 20264a79616dSTakashi Iwai if (i == AUTO_SEQ_CENLFE) { 20274a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 20284a79616dSTakashi Iwai if (err < 0) 20294a79616dSTakashi Iwai return err; 20304a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 20314a79616dSTakashi Iwai if (err < 0) 20324a79616dSTakashi Iwai return err; 20334a79616dSTakashi Iwai } else { 20344a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 20354a79616dSTakashi Iwai if (err < 0) 20364a79616dSTakashi Iwai return err; 20374a79616dSTakashi Iwai } 20384a79616dSTakashi Iwai } 20394a79616dSTakashi Iwai 20404a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 20414a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 20424a79616dSTakashi Iwai if (idx >= 0) { 20434a79616dSTakashi Iwai /* add control to mixer */ 20444a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 20454a79616dSTakashi Iwai "PCM Playback Volume", 20464a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20474a79616dSTakashi Iwai idx, HDA_INPUT)); 20484a79616dSTakashi Iwai if (err < 0) 20494a79616dSTakashi Iwai return err; 20504a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 20514a79616dSTakashi Iwai "PCM Playback Switch", 20524a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20534a79616dSTakashi Iwai idx, HDA_INPUT)); 20544a79616dSTakashi Iwai if (err < 0) 20554a79616dSTakashi Iwai return err; 2056c577b8a1SJoseph Chan } 2057c577b8a1SJoseph Chan 2058f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 2059f4a7828bSTakashi Iwai 2060c577b8a1SJoseph Chan return 0; 2061c577b8a1SJoseph Chan } 2062c577b8a1SJoseph Chan 20630aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 20640aa62aefSHarald Welte { 20650aa62aefSHarald Welte int i; 20660aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2067ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 20680aa62aefSHarald Welte 20690aa62aefSHarald Welte /* for hp mode select */ 207010a20af7STakashi Iwai for (i = 0; texts[i]; i++) 207110a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 20720aa62aefSHarald Welte 20730aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 20740aa62aefSHarald Welte } 20750aa62aefSHarald Welte 20764a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 2077c577b8a1SJoseph Chan { 20784a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 20794a79616dSTakashi Iwai hda_nid_t dac = 0; 2080c577b8a1SJoseph Chan int err; 2081c577b8a1SJoseph Chan 2082c577b8a1SJoseph Chan if (!pin) 2083c577b8a1SJoseph Chan return 0; 2084c577b8a1SJoseph Chan 20854a79616dSTakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 20864a79616dSTakashi Iwai &spec->hp_dep_path, 0, -1)) 20874a79616dSTakashi Iwai return 0; 20884a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 20894a79616dSTakashi Iwai dac = spec->hp_path.path[spec->hp_path.depth - 1]; 20904a79616dSTakashi Iwai spec->multiout.hp_nid = dac; 20914a79616dSTakashi Iwai spec->hp_independent_mode_index = 20924a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 20930aa62aefSHarald Welte create_hp_imux(spec); 20944a79616dSTakashi Iwai } 20954a79616dSTakashi Iwai 20964a79616dSTakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, dac, 3); 20974a79616dSTakashi Iwai if (err < 0) 20984a79616dSTakashi Iwai return err; 20990aa62aefSHarald Welte 2100c577b8a1SJoseph Chan return 0; 2101c577b8a1SJoseph Chan } 2102c577b8a1SJoseph Chan 2103a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 2104a766d0d7STakashi Iwai hda_nid_t nid) 2105a766d0d7STakashi Iwai { 2106a766d0d7STakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 2107a766d0d7STakashi Iwai int i, nums; 2108a766d0d7STakashi Iwai 2109a766d0d7STakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 2110a766d0d7STakashi Iwai for (i = 0; i < nums; i++) 2111a766d0d7STakashi Iwai if (conn[i] == nid) 2112a766d0d7STakashi Iwai return i; 2113a766d0d7STakashi Iwai return -1; 2114a766d0d7STakashi Iwai } 2115a766d0d7STakashi Iwai 2116a766d0d7STakashi Iwai /* look for ADCs */ 2117a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2118a766d0d7STakashi Iwai { 2119a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2120a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2121a766d0d7STakashi Iwai int i; 2122a766d0d7STakashi Iwai 2123a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2124a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2125a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2126a766d0d7STakashi Iwai continue; 2127a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2128a766d0d7STakashi Iwai continue; 2129a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2130a766d0d7STakashi Iwai continue; 2131a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2132a766d0d7STakashi Iwai return -ENOMEM; 2133a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2134a766d0d7STakashi Iwai } 2135a766d0d7STakashi Iwai return 0; 2136a766d0d7STakashi Iwai } 2137a766d0d7STakashi Iwai 2138a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2139a766d0d7STakashi Iwai 2140*d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 2141*d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2142*d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 2143*d7a99cceSTakashi Iwai * So call somewhat different.. 2144*d7a99cceSTakashi Iwai */ 2145*d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 2146*d7a99cceSTakashi Iwai .name = "Input Source", 2147*d7a99cceSTakashi Iwai .info = via_mux_enum_info, 2148*d7a99cceSTakashi Iwai .get = via_mux_enum_get, 2149*d7a99cceSTakashi Iwai .put = via_mux_enum_put, 2150*d7a99cceSTakashi Iwai }; 2151*d7a99cceSTakashi Iwai 2152c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2153620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 2154620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 2155c577b8a1SJoseph Chan { 215610a20af7STakashi Iwai struct via_spec *spec = codec->spec; 21570aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2158a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 2159a766d0d7STakashi Iwai hda_nid_t cap_nid; 2160a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 2161a766d0d7STakashi Iwai int num_idxs; 2162a766d0d7STakashi Iwai 2163a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2164a766d0d7STakashi Iwai if (err < 0) 2165a766d0d7STakashi Iwai return err; 2166a766d0d7STakashi Iwai err = get_mux_nids(codec); 2167a766d0d7STakashi Iwai if (err < 0) 2168a766d0d7STakashi Iwai return err; 2169a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 2170a766d0d7STakashi Iwai 2171a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 2172a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 2173a766d0d7STakashi Iwai if (num_idxs <= 0) 2174a766d0d7STakashi Iwai return 0; 2175c577b8a1SJoseph Chan 2176c577b8a1SJoseph Chan /* for internal loopback recording select */ 2177f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2178620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 217910a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2180f3268512STakashi Iwai break; 2181f3268512STakashi Iwai } 2182f3268512STakashi Iwai } 2183c577b8a1SJoseph Chan 21847b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 218510a20af7STakashi Iwai const char *label; 21867b315bb4STakashi Iwai type = cfg->inputs[i].type; 2187f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 21887b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2189c577b8a1SJoseph Chan break; 2190f3268512STakashi Iwai if (idx >= num_idxs) 2191f3268512STakashi Iwai continue; 21927b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 21937b315bb4STakashi Iwai type_idx++; 21947b315bb4STakashi Iwai else 21957b315bb4STakashi Iwai type_idx = 0; 219610a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2197620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 2198620e2b28STakashi Iwai pin_idxs[idx]); 2199a766d0d7STakashi Iwai if (idx2 >= 0) 220016922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2201620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 2202c577b8a1SJoseph Chan if (err < 0) 2203c577b8a1SJoseph Chan return err; 220410a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2205c577b8a1SJoseph Chan } 2206*d7a99cceSTakashi Iwai 2207*d7a99cceSTakashi Iwai /* create capture mixer elements */ 2208*d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2209*d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 2210*d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 2211*d7a99cceSTakashi Iwai "Capture Volume", i, 2212*d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2213*d7a99cceSTakashi Iwai HDA_INPUT)); 2214*d7a99cceSTakashi Iwai if (err < 0) 2215*d7a99cceSTakashi Iwai return err; 2216*d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2217*d7a99cceSTakashi Iwai "Capture Switch", i, 2218*d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2219*d7a99cceSTakashi Iwai HDA_INPUT)); 2220*d7a99cceSTakashi Iwai if (err < 0) 2221*d7a99cceSTakashi Iwai return err; 2222*d7a99cceSTakashi Iwai } 2223*d7a99cceSTakashi Iwai 2224*d7a99cceSTakashi Iwai /* input-source control */ 2225*d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 2226*d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 2227*d7a99cceSTakashi Iwai break; 2228*d7a99cceSTakashi Iwai if (i) { 2229*d7a99cceSTakashi Iwai struct snd_kcontrol_new *knew; 2230*d7a99cceSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 2231*d7a99cceSTakashi Iwai if (!knew) 2232*d7a99cceSTakashi Iwai return -ENOMEM; 2233*d7a99cceSTakashi Iwai knew->count = i; 2234*d7a99cceSTakashi Iwai } 2235*d7a99cceSTakashi Iwai 2236*d7a99cceSTakashi Iwai /* mic-boosts */ 2237*d7a99cceSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2238*d7a99cceSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2239*d7a99cceSTakashi Iwai unsigned int caps; 2240*d7a99cceSTakashi Iwai const char *label; 2241*d7a99cceSTakashi Iwai char name[32]; 2242*d7a99cceSTakashi Iwai 2243*d7a99cceSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 2244*d7a99cceSTakashi Iwai continue; 2245*d7a99cceSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 2246*d7a99cceSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 2247*d7a99cceSTakashi Iwai continue; 2248*d7a99cceSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2249*d7a99cceSTakashi Iwai snprintf(name, sizeof(name), "%s Boost Capture Volume", label); 2250*d7a99cceSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2251*d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 2252*d7a99cceSTakashi Iwai if (err < 0) 2253*d7a99cceSTakashi Iwai return err; 2254*d7a99cceSTakashi Iwai } 2255*d7a99cceSTakashi Iwai 2256c577b8a1SJoseph Chan return 0; 2257c577b8a1SJoseph Chan } 2258c577b8a1SJoseph Chan 2259cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 226090dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2261cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2262cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2263cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2264cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2265cb53c626STakashi Iwai { } /* end */ 2266cb53c626STakashi Iwai }; 2267cb53c626STakashi Iwai #endif 2268cb53c626STakashi Iwai 226976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 227076d9b0ddSHarald Welte { 227176d9b0ddSHarald Welte unsigned int def_conf; 227276d9b0ddSHarald Welte unsigned char seqassoc; 227376d9b0ddSHarald Welte 22742f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 227576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 227676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 227782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 227882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 227976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 22802f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 228176d9b0ddSHarald Welte } 228276d9b0ddSHarald Welte 228376d9b0ddSHarald Welte return; 228476d9b0ddSHarald Welte } 228576d9b0ddSHarald Welte 2286e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 22871f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22881f2e99feSLydia Wang { 22891f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22901f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22911f2e99feSLydia Wang 22921f2e99feSLydia Wang if (spec->codec_type != VT1708) 22931f2e99feSLydia Wang return 0; 2294e06e5a29STakashi Iwai spec->vt1708_jack_detect = 22951f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2296e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 22971f2e99feSLydia Wang return 0; 22981f2e99feSLydia Wang } 22991f2e99feSLydia Wang 2300e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 23011f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 23021f2e99feSLydia Wang { 23031f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 23041f2e99feSLydia Wang struct via_spec *spec = codec->spec; 23051f2e99feSLydia Wang int change; 23061f2e99feSLydia Wang 23071f2e99feSLydia Wang if (spec->codec_type != VT1708) 23081f2e99feSLydia Wang return 0; 2309e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 23101f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2311e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2312e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 23131f2e99feSLydia Wang mute_aa_path(codec, 1); 23141f2e99feSLydia Wang notify_aa_path_ctls(codec); 23151f2e99feSLydia Wang } 23161f2e99feSLydia Wang return change; 23171f2e99feSLydia Wang } 23181f2e99feSLydia Wang 2319e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 23201f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 23211f2e99feSLydia Wang .name = "Jack Detect", 23221f2e99feSLydia Wang .count = 1, 23231f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2324e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2325e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 23261f2e99feSLydia Wang }; 23271f2e99feSLydia Wang 2328c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2329c577b8a1SJoseph Chan { 2330c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2331c577b8a1SJoseph Chan int err; 2332c577b8a1SJoseph Chan 233376d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 233476d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 233576d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 233676d9b0ddSHarald Welte 2337c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2338c577b8a1SJoseph Chan if (err < 0) 2339c577b8a1SJoseph Chan return err; 23404a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2341c577b8a1SJoseph Chan if (err < 0) 2342c577b8a1SJoseph Chan return err; 2343c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2344c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2345c577b8a1SJoseph Chan 23464a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2347c577b8a1SJoseph Chan if (err < 0) 2348c577b8a1SJoseph Chan return err; 23494a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2350c577b8a1SJoseph Chan if (err < 0) 2351c577b8a1SJoseph Chan return err; 2352620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2353c577b8a1SJoseph Chan if (err < 0) 2354c577b8a1SJoseph Chan return err; 23551f2e99feSLydia Wang /* add jack detect on/off control */ 2356e06e5a29STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 2357e06e5a29STakashi Iwai return -ENOMEM; 2358c577b8a1SJoseph Chan 2359c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2360c577b8a1SJoseph Chan 23610852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2362c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 236355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2364c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2365c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2366c577b8a1SJoseph Chan 2367603c4019STakashi Iwai if (spec->kctls.list) 2368603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2369c577b8a1SJoseph Chan 237069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2371c577b8a1SJoseph Chan 23720aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 23730aa62aefSHarald Welte 2374f8fdd495SHarald Welte if (spec->hp_mux) 23753d83e577STakashi Iwai via_hp_build(codec); 2376c577b8a1SJoseph Chan 2377f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2378f4a7828bSTakashi Iwai if (err < 0) 2379f4a7828bSTakashi Iwai return err; 2380f4a7828bSTakashi Iwai 2381c577b8a1SJoseph Chan return 1; 2382c577b8a1SJoseph Chan } 2383c577b8a1SJoseph Chan 2384c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2385c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2386c577b8a1SJoseph Chan { 238725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 238825eaba2fSLydia Wang 2389c577b8a1SJoseph Chan via_init(codec); 2390c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2391c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2392c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 239311890956SLydia Wang 239411890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 239525eaba2fSLydia Wang via_hp_bind_automute(codec); 239625eaba2fSLydia Wang } else { 239725eaba2fSLydia Wang via_hp_automute(codec); 239825eaba2fSLydia Wang via_speaker_automute(codec); 239925eaba2fSLydia Wang } 240025eaba2fSLydia Wang 2401c577b8a1SJoseph Chan return 0; 2402c577b8a1SJoseph Chan } 2403c577b8a1SJoseph Chan 24041f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 24051f2e99feSLydia Wang { 24061f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 24071f2e99feSLydia Wang vt1708_hp_work.work); 24081f2e99feSLydia Wang if (spec->codec_type != VT1708) 24091f2e99feSLydia Wang return; 24101f2e99feSLydia Wang /* if jack state toggled */ 24111f2e99feSLydia Wang if (spec->vt1708_hp_present 2412d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 24131f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 24141f2e99feSLydia Wang via_hp_automute(spec->codec); 24151f2e99feSLydia Wang } 24161f2e99feSLydia Wang vt1708_start_hp_work(spec); 24171f2e99feSLydia Wang } 24181f2e99feSLydia Wang 2419337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2420337b9d02STakashi Iwai { 2421337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2422337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2423337b9d02STakashi Iwai unsigned int type; 2424337b9d02STakashi Iwai int i, n; 2425337b9d02STakashi Iwai 2426337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2427337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2428337b9d02STakashi Iwai while (nid) { 2429a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 24301c55d521STakashi Iwai if (type == AC_WID_PIN) 24311c55d521STakashi Iwai break; 2432337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2433337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2434337b9d02STakashi Iwai if (n <= 0) 2435337b9d02STakashi Iwai break; 2436337b9d02STakashi Iwai if (n > 1) { 2437337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2438337b9d02STakashi Iwai break; 2439337b9d02STakashi Iwai } 2440337b9d02STakashi Iwai nid = conn[0]; 2441337b9d02STakashi Iwai } 2442337b9d02STakashi Iwai } 24431c55d521STakashi Iwai return 0; 2444337b9d02STakashi Iwai } 2445337b9d02STakashi Iwai 2446c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2447c577b8a1SJoseph Chan { 2448c577b8a1SJoseph Chan struct via_spec *spec; 2449c577b8a1SJoseph Chan int err; 2450c577b8a1SJoseph Chan 2451c577b8a1SJoseph Chan /* create a codec specific record */ 24525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2453c577b8a1SJoseph Chan if (spec == NULL) 2454c577b8a1SJoseph Chan return -ENOMEM; 2455c577b8a1SJoseph Chan 2456620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2457620e2b28STakashi Iwai 2458c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2459c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2460c577b8a1SJoseph Chan if (err < 0) { 2461c577b8a1SJoseph Chan via_free(codec); 2462c577b8a1SJoseph Chan return err; 2463c577b8a1SJoseph Chan } else if (!err) { 2464c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2465c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2466c577b8a1SJoseph Chan } 2467c577b8a1SJoseph Chan 2468c577b8a1SJoseph Chan 2469bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2470bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2471bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2472c577b8a1SJoseph Chan 2473c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2474c577b8a1SJoseph Chan 2475c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2476cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2477cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2478cb53c626STakashi Iwai #endif 24791f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2480c577b8a1SJoseph Chan return 0; 2481c577b8a1SJoseph Chan } 2482c577b8a1SJoseph Chan 248390dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2484a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2485a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 248669e52a80SHarald Welte { } 248769e52a80SHarald Welte }; 248869e52a80SHarald Welte 2489c577b8a1SJoseph Chan /* 2490c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2491c577b8a1SJoseph Chan */ 249290dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2493c577b8a1SJoseph Chan /* 2494c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2495c577b8a1SJoseph Chan */ 2496c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2497c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2498c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2499c577b8a1SJoseph Chan 2500c577b8a1SJoseph Chan 2501f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2502c577b8a1SJoseph Chan * mixer widget 2503c577b8a1SJoseph Chan */ 2504c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2505f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2506f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2507f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2508f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2509f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2510c577b8a1SJoseph Chan 2511c577b8a1SJoseph Chan /* 2512c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2513c577b8a1SJoseph Chan */ 2514c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2515c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2516c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2517c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2518c577b8a1SJoseph Chan 2519c577b8a1SJoseph Chan /* 2520c577b8a1SJoseph Chan * Unmute PW3 and PW4 2521c577b8a1SJoseph Chan */ 2522c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2523c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2524c577b8a1SJoseph Chan 2525bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2526bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2527c577b8a1SJoseph Chan /* PW9 Output enable */ 2528c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2529c577b8a1SJoseph Chan { } 2530c577b8a1SJoseph Chan }; 2531c577b8a1SJoseph Chan 2532c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2533c577b8a1SJoseph Chan { 2534c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2535c577b8a1SJoseph Chan int err; 2536c577b8a1SJoseph Chan 2537c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2538c577b8a1SJoseph Chan if (err < 0) 2539c577b8a1SJoseph Chan return err; 25404a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2541c577b8a1SJoseph Chan if (err < 0) 2542c577b8a1SJoseph Chan return err; 2543c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2544c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2545c577b8a1SJoseph Chan 25464a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2547c577b8a1SJoseph Chan if (err < 0) 2548c577b8a1SJoseph Chan return err; 25494a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2550c577b8a1SJoseph Chan if (err < 0) 2551c577b8a1SJoseph Chan return err; 2552620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2553c577b8a1SJoseph Chan if (err < 0) 2554c577b8a1SJoseph Chan return err; 2555c577b8a1SJoseph Chan 2556c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2557c577b8a1SJoseph Chan 25580852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2559c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 256055d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2561c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2562c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2563c577b8a1SJoseph Chan 2564603c4019STakashi Iwai if (spec->kctls.list) 2565603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2566c577b8a1SJoseph Chan 25670aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2568c577b8a1SJoseph Chan 2569f8fdd495SHarald Welte if (spec->hp_mux) 25703d83e577STakashi Iwai via_hp_build(codec); 2571f8fdd495SHarald Welte 2572f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2573f4a7828bSTakashi Iwai if (err < 0) 2574f4a7828bSTakashi Iwai return err; 2575f4a7828bSTakashi Iwai 2576c577b8a1SJoseph Chan return 1; 2577c577b8a1SJoseph Chan } 2578c577b8a1SJoseph Chan 2579cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 258090dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2581cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2582cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2583cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2584cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2585cb53c626STakashi Iwai { } /* end */ 2586cb53c626STakashi Iwai }; 2587cb53c626STakashi Iwai #endif 2588cb53c626STakashi Iwai 2589c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2590c577b8a1SJoseph Chan { 2591c577b8a1SJoseph Chan struct via_spec *spec; 2592c577b8a1SJoseph Chan int err; 2593c577b8a1SJoseph Chan 2594c577b8a1SJoseph Chan /* create a codec specific record */ 25955b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2596c577b8a1SJoseph Chan if (spec == NULL) 2597c577b8a1SJoseph Chan return -ENOMEM; 2598c577b8a1SJoseph Chan 2599620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2600620e2b28STakashi Iwai 2601c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2602c577b8a1SJoseph Chan if (err < 0) { 2603c577b8a1SJoseph Chan via_free(codec); 2604c577b8a1SJoseph Chan return err; 2605c577b8a1SJoseph Chan } else if (!err) { 2606c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2607c577b8a1SJoseph Chan "Using genenic mode...\n"); 2608c577b8a1SJoseph Chan } 2609c577b8a1SJoseph Chan 261069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 261169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2612c577b8a1SJoseph Chan 2613c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2614c577b8a1SJoseph Chan 2615c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 261669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2617cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2618cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2619cb53c626STakashi Iwai #endif 2620c577b8a1SJoseph Chan 2621c577b8a1SJoseph Chan return 0; 2622c577b8a1SJoseph Chan } 2623c577b8a1SJoseph Chan /* 2624c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2625c577b8a1SJoseph Chan */ 262690dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2627c577b8a1SJoseph Chan /* 2628c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2629c577b8a1SJoseph Chan */ 2630c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2631c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2632c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2633c577b8a1SJoseph Chan 2634c577b8a1SJoseph Chan 2635c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2636c577b8a1SJoseph Chan * mixer widget 2637c577b8a1SJoseph Chan */ 2638c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2639c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2640c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2641c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2642c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2643c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2644c577b8a1SJoseph Chan 2645c577b8a1SJoseph Chan /* 2646c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2647c577b8a1SJoseph Chan */ 2648c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2649c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2650c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2651c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2652c577b8a1SJoseph Chan 2653c577b8a1SJoseph Chan /* 2654c577b8a1SJoseph Chan * Unmute PW3 and PW4 2655c577b8a1SJoseph Chan */ 2656c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2657c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2658c577b8a1SJoseph Chan 2659c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2660c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2661c577b8a1SJoseph Chan /* PW9 Output enable */ 2662c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2663c577b8a1SJoseph Chan { } 2664c577b8a1SJoseph Chan }; 2665c577b8a1SJoseph Chan 2666c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2667c577b8a1SJoseph Chan { 2668c577b8a1SJoseph Chan struct via_spec *spec; 2669c577b8a1SJoseph Chan int err; 2670c577b8a1SJoseph Chan 2671c577b8a1SJoseph Chan /* create a codec specific record */ 26725b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2673c577b8a1SJoseph Chan if (spec == NULL) 2674c577b8a1SJoseph Chan return -ENOMEM; 2675c577b8a1SJoseph Chan 2676620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2677620e2b28STakashi Iwai 2678c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2679c577b8a1SJoseph Chan if (err < 0) { 2680c577b8a1SJoseph Chan via_free(codec); 2681c577b8a1SJoseph Chan return err; 2682c577b8a1SJoseph Chan } else if (!err) { 2683c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2684c577b8a1SJoseph Chan "Using genenic mode...\n"); 2685c577b8a1SJoseph Chan } 2686c577b8a1SJoseph Chan 268769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 268869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2689c577b8a1SJoseph Chan 2690c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2691c577b8a1SJoseph Chan 2692c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 269369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2694cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2695cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2696cb53c626STakashi Iwai #endif 2697f7278fd0SJosepch Chan return 0; 2698f7278fd0SJosepch Chan } 2699f7278fd0SJosepch Chan 2700f7278fd0SJosepch Chan /* 2701f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2702f7278fd0SJosepch Chan */ 270390dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2704f7278fd0SJosepch Chan /* 2705f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2706f7278fd0SJosepch Chan */ 2707f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2708f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2709f7278fd0SJosepch Chan 2710f7278fd0SJosepch Chan 2711f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2712f7278fd0SJosepch Chan * mixer widget 2713f7278fd0SJosepch Chan */ 2714f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2715f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2716f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2717f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2718f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2719f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2720f7278fd0SJosepch Chan 2721f7278fd0SJosepch Chan /* 2722f7278fd0SJosepch Chan * Set up output mixers 2723f7278fd0SJosepch Chan */ 2724f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2725f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2726f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2727f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2728f7278fd0SJosepch Chan 2729f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2730bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2731f7278fd0SJosepch Chan /* PW9 Output enable */ 2732f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2733f7278fd0SJosepch Chan /* PW10 Input enable */ 2734f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2735f7278fd0SJosepch Chan { } 2736f7278fd0SJosepch Chan }; 2737f7278fd0SJosepch Chan 273890dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2739f7278fd0SJosepch Chan /* 2740f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2741f7278fd0SJosepch Chan */ 2742f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2743f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2744f7278fd0SJosepch Chan 2745f7278fd0SJosepch Chan 2746f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2747f7278fd0SJosepch Chan * mixer widget 2748f7278fd0SJosepch Chan */ 2749f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2750f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2751f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2752f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2753f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2754f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2755f7278fd0SJosepch Chan 2756f7278fd0SJosepch Chan /* 2757f7278fd0SJosepch Chan * Set up output mixers 2758f7278fd0SJosepch Chan */ 2759f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2760f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2761f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2762f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2763f7278fd0SJosepch Chan 2764f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2765f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2766f7278fd0SJosepch Chan /* PW9 Output enable */ 2767f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2768f7278fd0SJosepch Chan /* PW10 Input enable */ 2769f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2770f7278fd0SJosepch Chan { } 2771f7278fd0SJosepch Chan }; 2772f7278fd0SJosepch Chan 277390dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2774a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2775a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2776a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2777a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2778a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2779a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2780a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2781a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2782a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 278369e52a80SHarald Welte { } 278469e52a80SHarald Welte }; 278569e52a80SHarald Welte 2786f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 2787f7278fd0SJosepch Chan { 2788f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 2789f7278fd0SJosepch Chan int err; 2790f7278fd0SJosepch Chan 2791f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2792f7278fd0SJosepch Chan if (err < 0) 2793f7278fd0SJosepch Chan return err; 27944a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2795f7278fd0SJosepch Chan if (err < 0) 2796f7278fd0SJosepch Chan return err; 2797f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2798f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 2799f7278fd0SJosepch Chan 28004a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2801f7278fd0SJosepch Chan if (err < 0) 2802f7278fd0SJosepch Chan return err; 28034a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2804f7278fd0SJosepch Chan if (err < 0) 2805f7278fd0SJosepch Chan return err; 2806620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2807f7278fd0SJosepch Chan if (err < 0) 2808f7278fd0SJosepch Chan return err; 2809f7278fd0SJosepch Chan 2810f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2811f7278fd0SJosepch Chan 28120852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2813f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 281455d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 2815f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2816f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2817f7278fd0SJosepch Chan 2818603c4019STakashi Iwai if (spec->kctls.list) 2819603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2820f7278fd0SJosepch Chan 28210aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 28220aa62aefSHarald Welte 2823f8fdd495SHarald Welte if (spec->hp_mux) 28243d83e577STakashi Iwai via_hp_build(codec); 2825f7278fd0SJosepch Chan 2826f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2827f4a7828bSTakashi Iwai if (err < 0) 2828f4a7828bSTakashi Iwai return err; 2829f4a7828bSTakashi Iwai 2830f7278fd0SJosepch Chan return 1; 2831f7278fd0SJosepch Chan } 2832f7278fd0SJosepch Chan 2833f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 283490dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2835f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2836f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2837f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2838f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2839f7278fd0SJosepch Chan { } /* end */ 2840f7278fd0SJosepch Chan }; 2841f7278fd0SJosepch Chan #endif 28423e95b9abSLydia Wang 28433e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 28443e95b9abSLydia Wang { 28453e95b9abSLydia Wang struct via_spec *spec = codec->spec; 28463e95b9abSLydia Wang int imux_is_smixer; 28473e95b9abSLydia Wang unsigned int parm; 28483e95b9abSLydia Wang int is_8ch = 0; 2849bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2850bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 28513e95b9abSLydia Wang is_8ch = 1; 28523e95b9abSLydia Wang 28533e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 28543e95b9abSLydia Wang imux_is_smixer = 28553e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 28563e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 28573e95b9abSLydia Wang /* inputs */ 28583e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 28593e95b9abSLydia Wang parm = AC_PWRST_D3; 28603e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28613e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28623e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28633e95b9abSLydia Wang if (imux_is_smixer) 28643e95b9abSLydia Wang parm = AC_PWRST_D0; 28653e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 28663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 28673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 28683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 28693e95b9abSLydia Wang 28703e95b9abSLydia Wang /* outputs */ 28713e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 28723e95b9abSLydia Wang parm = AC_PWRST_D3; 28733e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 28743e95b9abSLydia Wang if (spec->smart51_enabled) 28753e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 28773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28783e95b9abSLydia Wang 28793e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 28803e95b9abSLydia Wang if (is_8ch) { 28813e95b9abSLydia Wang parm = AC_PWRST_D3; 28823e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 28833e95b9abSLydia Wang if (spec->smart51_enabled) 28843e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 28863e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 28883e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2889bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2890bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2891bc92df7fSLydia Wang parm = AC_PWRST_D3; 2892bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2893bc92df7fSLydia Wang if (spec->smart51_enabled) 2894bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2895bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2896bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2897bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2898bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28993e95b9abSLydia Wang } 29003e95b9abSLydia Wang 29013e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 29023e95b9abSLydia Wang parm = AC_PWRST_D3; 29033e95b9abSLydia Wang /* force to D0 for internal Speaker */ 29043e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 29053e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 29063e95b9abSLydia Wang if (is_8ch) 29073e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 29083e95b9abSLydia Wang 29093e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 29103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 29113e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 29123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 29133e95b9abSLydia Wang if (is_8ch) { 29143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 29153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 29173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2918bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2919bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2920bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29213e95b9abSLydia Wang } 29223e95b9abSLydia Wang 2923518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2924f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2925f7278fd0SJosepch Chan { 2926f7278fd0SJosepch Chan struct via_spec *spec; 2927f7278fd0SJosepch Chan int err; 2928f7278fd0SJosepch Chan 2929518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2930518bf3baSLydia Wang return patch_vt1708S(codec); 2931f7278fd0SJosepch Chan /* create a codec specific record */ 29325b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2933f7278fd0SJosepch Chan if (spec == NULL) 2934f7278fd0SJosepch Chan return -ENOMEM; 2935f7278fd0SJosepch Chan 2936620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2937620e2b28STakashi Iwai 2938f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2939f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2940f7278fd0SJosepch Chan if (err < 0) { 2941f7278fd0SJosepch Chan via_free(codec); 2942f7278fd0SJosepch Chan return err; 2943f7278fd0SJosepch Chan } else if (!err) { 2944f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2945f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2946f7278fd0SJosepch Chan } 2947f7278fd0SJosepch Chan 294869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 294969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2950f7278fd0SJosepch Chan 2951f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2952f7278fd0SJosepch Chan 2953f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 295469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2955f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2956f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2957f7278fd0SJosepch Chan #endif 2958f7278fd0SJosepch Chan 29593e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 29603e95b9abSLydia Wang 2961f7278fd0SJosepch Chan return 0; 2962f7278fd0SJosepch Chan } 2963f7278fd0SJosepch Chan 2964f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2965f7278fd0SJosepch Chan { 2966f7278fd0SJosepch Chan struct via_spec *spec; 2967f7278fd0SJosepch Chan int err; 2968f7278fd0SJosepch Chan 2969f7278fd0SJosepch Chan /* create a codec specific record */ 29705b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2971f7278fd0SJosepch Chan if (spec == NULL) 2972f7278fd0SJosepch Chan return -ENOMEM; 2973f7278fd0SJosepch Chan 2974f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2975f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2976f7278fd0SJosepch Chan if (err < 0) { 2977f7278fd0SJosepch Chan via_free(codec); 2978f7278fd0SJosepch Chan return err; 2979f7278fd0SJosepch Chan } else if (!err) { 2980f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2981f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2982f7278fd0SJosepch Chan } 2983f7278fd0SJosepch Chan 298469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 298569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2986f7278fd0SJosepch Chan 2987f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2988f7278fd0SJosepch Chan 2989f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 299069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2991f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2992f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2993f7278fd0SJosepch Chan #endif 2994c577b8a1SJoseph Chan 29953e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 29963e95b9abSLydia Wang 2997c577b8a1SJoseph Chan return 0; 2998c577b8a1SJoseph Chan } 2999c577b8a1SJoseph Chan 3000d949cac1SHarald Welte /* Patch for VT1708S */ 3001d949cac1SHarald Welte 300290dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3003d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3004d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3005d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3006d949cac1SHarald Welte 3007d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3008d949cac1SHarald Welte * analog-loopback mixer widget */ 3009d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3010d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3011d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3012d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3013d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3014d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3015d949cac1SHarald Welte 3016d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3017d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 30185691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3019d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 30205691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3021d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3022d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3023bc7e7e5cSLydia Wang /* don't bybass mixer */ 3024bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3025d949cac1SHarald Welte { } 3026d949cac1SHarald Welte }; 3027d949cac1SHarald Welte 302890dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3029a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3030a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3031a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3032a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3033a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3034a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3035a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3036a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3037a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 303869e52a80SHarald Welte { } 303969e52a80SHarald Welte }; 304069e52a80SHarald Welte 304190dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3042bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3043bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3044bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3045bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3046bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3047bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3048bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3049bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3050bc92df7fSLydia Wang { } 3051bc92df7fSLydia Wang }; 3052bc92df7fSLydia Wang 30539da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 30549da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 30559da29271STakashi Iwai { 30569da29271STakashi Iwai struct via_spec *spec = codec->spec; 30579da29271STakashi Iwai int i; 30589da29271STakashi Iwai 30599da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 30609da29271STakashi Iwai hda_nid_t nid; 30619da29271STakashi Iwai int conn; 30629da29271STakashi Iwai 30639da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 30649da29271STakashi Iwai if (!nid) 30659da29271STakashi Iwai continue; 30669da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 30679da29271STakashi Iwai if (conn < 1) 30689da29271STakashi Iwai continue; 30699da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 30709da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 30719da29271STakashi Iwai else { 30729da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 30739da29271STakashi Iwai break; /* at most two dig outs */ 30749da29271STakashi Iwai } 30759da29271STakashi Iwai } 30769da29271STakashi Iwai } 30779da29271STakashi Iwai 3078d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3079d949cac1SHarald Welte { 3080d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3081d949cac1SHarald Welte int err; 3082d949cac1SHarald Welte 30839da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3084d949cac1SHarald Welte if (err < 0) 3085d949cac1SHarald Welte return err; 30864a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3087d949cac1SHarald Welte if (err < 0) 3088d949cac1SHarald Welte return err; 3089d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3090d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3091d949cac1SHarald Welte 30924a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3093d949cac1SHarald Welte if (err < 0) 3094d949cac1SHarald Welte return err; 30954a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3096d949cac1SHarald Welte if (err < 0) 3097d949cac1SHarald Welte return err; 3098620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3099d949cac1SHarald Welte if (err < 0) 3100d949cac1SHarald Welte return err; 3101d949cac1SHarald Welte 3102d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3103d949cac1SHarald Welte 31049da29271STakashi Iwai fill_dig_outs(codec); 310598aa34c0SHarald Welte 3106603c4019STakashi Iwai if (spec->kctls.list) 3107603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3108d949cac1SHarald Welte 31090aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31100aa62aefSHarald Welte 3111f8fdd495SHarald Welte if (spec->hp_mux) 31123d83e577STakashi Iwai via_hp_build(codec); 3113d949cac1SHarald Welte 3114f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3115f4a7828bSTakashi Iwai if (err < 0) 3116f4a7828bSTakashi Iwai return err; 3117f4a7828bSTakashi Iwai 3118d949cac1SHarald Welte return 1; 3119d949cac1SHarald Welte } 3120d949cac1SHarald Welte 3121d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 312290dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3123d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3124d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3125d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3126d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3127d949cac1SHarald Welte { } /* end */ 3128d949cac1SHarald Welte }; 3129d949cac1SHarald Welte #endif 3130d949cac1SHarald Welte 31316369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 31326369bcfcSLydia Wang int offset, int num_steps, int step_size) 31336369bcfcSLydia Wang { 31346369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 31356369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 31366369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 31376369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 31386369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 31396369bcfcSLydia Wang } 31406369bcfcSLydia Wang 3141d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3142d949cac1SHarald Welte { 3143d949cac1SHarald Welte struct via_spec *spec; 3144d949cac1SHarald Welte int err; 3145d949cac1SHarald Welte 3146d949cac1SHarald Welte /* create a codec specific record */ 31475b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3148d949cac1SHarald Welte if (spec == NULL) 3149d949cac1SHarald Welte return -ENOMEM; 3150d949cac1SHarald Welte 3151620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3152*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3153*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3154620e2b28STakashi Iwai 3155d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3156d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3157d949cac1SHarald Welte if (err < 0) { 3158d949cac1SHarald Welte via_free(codec); 3159d949cac1SHarald Welte return err; 3160d949cac1SHarald Welte } else if (!err) { 3161d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3162d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3163d949cac1SHarald Welte } 3164d949cac1SHarald Welte 316569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3166bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3167bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3168bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3169bc92df7fSLydia Wang else 3170bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3171bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3172d949cac1SHarald Welte 3173d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3174d949cac1SHarald Welte 3175d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 317669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3177d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3178d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3179d949cac1SHarald Welte #endif 3180d949cac1SHarald Welte 3181518bf3baSLydia Wang /* correct names for VT1708BCE */ 3182518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3183518bf3baSLydia Wang kfree(codec->chip_name); 3184518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3185518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3186518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3187518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3188970f630fSLydia Wang } 3189bc92df7fSLydia Wang /* correct names for VT1705 */ 3190bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3191bc92df7fSLydia Wang kfree(codec->chip_name); 3192bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3193bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3194bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3195bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3196bc92df7fSLydia Wang } 31973e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3198d949cac1SHarald Welte return 0; 3199d949cac1SHarald Welte } 3200d949cac1SHarald Welte 3201d949cac1SHarald Welte /* Patch for VT1702 */ 3202d949cac1SHarald Welte 320390dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3204d949cac1SHarald Welte /* 3205d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3206d949cac1SHarald Welte */ 3207d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3208d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3209d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3210d949cac1SHarald Welte 3211d949cac1SHarald Welte 3212d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3213d949cac1SHarald Welte * mixer widget 3214d949cac1SHarald Welte */ 3215d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3216d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3217d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3218d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3219d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3220d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3221d949cac1SHarald Welte 3222d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3223d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3224d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3225d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3226d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3227bc7e7e5cSLydia Wang /* mixer enable */ 3228bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3229bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3230bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3231d949cac1SHarald Welte { } 3232d949cac1SHarald Welte }; 3233d949cac1SHarald Welte 323490dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 3235a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3236a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3237a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3238a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3239a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3240a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 324169e52a80SHarald Welte { } 324269e52a80SHarald Welte }; 324369e52a80SHarald Welte 3244d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 3245d949cac1SHarald Welte { 3246d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3247d949cac1SHarald Welte int err; 3248d949cac1SHarald Welte 32499da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3250d949cac1SHarald Welte if (err < 0) 3251d949cac1SHarald Welte return err; 32524a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3253d949cac1SHarald Welte if (err < 0) 3254d949cac1SHarald Welte return err; 3255d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3256d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3257d949cac1SHarald Welte 32584a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3259d949cac1SHarald Welte if (err < 0) 3260d949cac1SHarald Welte return err; 32614a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3262d949cac1SHarald Welte if (err < 0) 3263d949cac1SHarald Welte return err; 3264c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 3265c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 3266c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 3267c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 3268c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 3269c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 3270620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3271d949cac1SHarald Welte if (err < 0) 3272d949cac1SHarald Welte return err; 3273d949cac1SHarald Welte 3274d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3275d949cac1SHarald Welte 32769da29271STakashi Iwai fill_dig_outs(codec); 327798aa34c0SHarald Welte 3278603c4019STakashi Iwai if (spec->kctls.list) 3279603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3280d949cac1SHarald Welte 32810aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 32820aa62aefSHarald Welte 3283f8fdd495SHarald Welte if (spec->hp_mux) 32843d83e577STakashi Iwai via_hp_build(codec); 3285d949cac1SHarald Welte 3286d949cac1SHarald Welte return 1; 3287d949cac1SHarald Welte } 3288d949cac1SHarald Welte 3289d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 329090dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 3291d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 3292d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 3293d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 3294d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 3295d949cac1SHarald Welte { } /* end */ 3296d949cac1SHarald Welte }; 3297d949cac1SHarald Welte #endif 3298d949cac1SHarald Welte 32993e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 33003e95b9abSLydia Wang { 33013e95b9abSLydia Wang int imux_is_smixer = 33023e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 33033e95b9abSLydia Wang unsigned int parm; 33043e95b9abSLydia Wang /* inputs */ 33053e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 33063e95b9abSLydia Wang parm = AC_PWRST_D3; 33073e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 33083e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 33093e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 33103e95b9abSLydia Wang if (imux_is_smixer) 33113e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 33123e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 33133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 33143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 33153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 33163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 33173e95b9abSLydia Wang 33183e95b9abSLydia Wang /* outputs */ 33193e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 33203e95b9abSLydia Wang parm = AC_PWRST_D3; 33213e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 33223e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 33233e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 33243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 33253e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 33263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 33283e95b9abSLydia Wang } 33293e95b9abSLydia Wang 3330d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3331d949cac1SHarald Welte { 3332d949cac1SHarald Welte struct via_spec *spec; 3333d949cac1SHarald Welte int err; 3334d949cac1SHarald Welte 3335d949cac1SHarald Welte /* create a codec specific record */ 33365b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3337d949cac1SHarald Welte if (spec == NULL) 3338d949cac1SHarald Welte return -ENOMEM; 3339d949cac1SHarald Welte 3340620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 3341620e2b28STakashi Iwai 3342d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3343d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 3344d949cac1SHarald Welte if (err < 0) { 3345d949cac1SHarald Welte via_free(codec); 3346d949cac1SHarald Welte return err; 3347d949cac1SHarald Welte } else if (!err) { 3348d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3349d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3350d949cac1SHarald Welte } 3351d949cac1SHarald Welte 335269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 335369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 3354d949cac1SHarald Welte 3355d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3356d949cac1SHarald Welte 3357d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 335869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3359d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3360d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 3361d949cac1SHarald Welte #endif 3362d949cac1SHarald Welte 33633e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 3364d949cac1SHarald Welte return 0; 3365d949cac1SHarald Welte } 3366d949cac1SHarald Welte 3367eb7188caSLydia Wang /* Patch for VT1718S */ 3368eb7188caSLydia Wang 336990dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 3370eb7188caSLydia Wang /* 3371eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3372eb7188caSLydia Wang */ 3373eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3374eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3375eb7188caSLydia Wang 33764ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 33774ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 3378eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3379eb7188caSLydia Wang * mixer widget 3380eb7188caSLydia Wang */ 3381eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3382eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3383eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3384eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3385eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 33864ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 3387eb7188caSLydia Wang 3388eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 3389eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3390eb7188caSLydia Wang /* PW9 PW10 Output enable */ 3391eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3392eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3393eb7188caSLydia Wang /* PW11 Input enable */ 3394eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 3395eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 3396eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 3397eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 3398eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3399eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3400eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3401eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3402eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3403eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3404eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3405eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3406eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3407eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3408eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 3409eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 3410eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 3411eb7188caSLydia Wang /* Unmute MW4's index 0 */ 3412eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3413eb7188caSLydia Wang { } 3414eb7188caSLydia Wang }; 3415eb7188caSLydia Wang 3416eb7188caSLydia Wang 341790dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 3418eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 3419eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3420eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3421eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3422eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3423eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3424eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3425eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3426eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3427eb7188caSLydia Wang { } 3428eb7188caSLydia Wang }; 3429eb7188caSLydia Wang 3430eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 3431eb7188caSLydia Wang { 3432eb7188caSLydia Wang struct via_spec *spec = codec->spec; 3433eb7188caSLydia Wang int err; 3434eb7188caSLydia Wang 3435eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3436eb7188caSLydia Wang 3437eb7188caSLydia Wang if (err < 0) 3438eb7188caSLydia Wang return err; 34394a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3440eb7188caSLydia Wang if (err < 0) 3441eb7188caSLydia Wang return err; 3442eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3443eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 3444eb7188caSLydia Wang 34454a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3446eb7188caSLydia Wang if (err < 0) 3447eb7188caSLydia Wang return err; 34484a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3449eb7188caSLydia Wang if (err < 0) 3450eb7188caSLydia Wang return err; 3451620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3452eb7188caSLydia Wang if (err < 0) 3453eb7188caSLydia Wang return err; 3454eb7188caSLydia Wang 3455eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3456eb7188caSLydia Wang 3457eb7188caSLydia Wang fill_dig_outs(codec); 3458eb7188caSLydia Wang 3459eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 3460eb7188caSLydia Wang spec->dig_in_nid = 0x13; 3461eb7188caSLydia Wang 3462eb7188caSLydia Wang if (spec->kctls.list) 3463eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3464eb7188caSLydia Wang 3465eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 3466eb7188caSLydia Wang 3467eb7188caSLydia Wang if (spec->hp_mux) 34683d83e577STakashi Iwai via_hp_build(codec); 3469eb7188caSLydia Wang 3470f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3471f4a7828bSTakashi Iwai if (err < 0) 3472f4a7828bSTakashi Iwai return err; 3473eb7188caSLydia Wang 3474eb7188caSLydia Wang return 1; 3475eb7188caSLydia Wang } 3476eb7188caSLydia Wang 3477eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 347890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 3479eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 3480eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 3481eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 3482eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 3483eb7188caSLydia Wang { } /* end */ 3484eb7188caSLydia Wang }; 3485eb7188caSLydia Wang #endif 3486eb7188caSLydia Wang 34873e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 34883e95b9abSLydia Wang { 34893e95b9abSLydia Wang struct via_spec *spec = codec->spec; 34903e95b9abSLydia Wang int imux_is_smixer; 34913e95b9abSLydia Wang unsigned int parm; 34923e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 34933e95b9abSLydia Wang imux_is_smixer = 34943e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 34953e95b9abSLydia Wang /* inputs */ 34963e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 34973e95b9abSLydia Wang parm = AC_PWRST_D3; 34983e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 34993e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 35003e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 35013e95b9abSLydia Wang if (imux_is_smixer) 35023e95b9abSLydia Wang parm = AC_PWRST_D0; 35033e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 35043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 35053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 35063e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 35073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 35083e95b9abSLydia Wang 35093e95b9abSLydia Wang /* outputs */ 35103e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 35113e95b9abSLydia Wang parm = AC_PWRST_D3; 35123e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 35133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 35143e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 35153e95b9abSLydia Wang 35163e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 35173e95b9abSLydia Wang parm = AC_PWRST_D3; 35183e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 35193e95b9abSLydia Wang if (spec->smart51_enabled) 35203e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 35213e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 35223e95b9abSLydia Wang 35233e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 35243e95b9abSLydia Wang parm = AC_PWRST_D3; 35253e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 35263e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 35273e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 35283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 35293e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 35303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 35313e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 35323e95b9abSLydia Wang 35333e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 35343e95b9abSLydia Wang parm = AC_PWRST_D3; 35353e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 35363e95b9abSLydia Wang if (spec->smart51_enabled) 35373e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 35383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 35393e95b9abSLydia Wang 35403e95b9abSLydia Wang if (spec->hp_independent_mode) { 35413e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 35423e95b9abSLydia Wang parm = AC_PWRST_D3; 35433e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 35443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 35453e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 35473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35483e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 35493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35503e95b9abSLydia Wang } 35513e95b9abSLydia Wang } 35523e95b9abSLydia Wang 3553eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 3554eb7188caSLydia Wang { 3555eb7188caSLydia Wang struct via_spec *spec; 3556eb7188caSLydia Wang int err; 3557eb7188caSLydia Wang 3558eb7188caSLydia Wang /* create a codec specific record */ 35595b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3560eb7188caSLydia Wang if (spec == NULL) 3561eb7188caSLydia Wang return -ENOMEM; 3562eb7188caSLydia Wang 3563620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3564*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3565*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3566620e2b28STakashi Iwai 3567eb7188caSLydia Wang /* automatic parse from the BIOS config */ 3568eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 3569eb7188caSLydia Wang if (err < 0) { 3570eb7188caSLydia Wang via_free(codec); 3571eb7188caSLydia Wang return err; 3572eb7188caSLydia Wang } else if (!err) { 3573eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3574eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 3575eb7188caSLydia Wang } 3576eb7188caSLydia Wang 3577eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 3578eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 3579eb7188caSLydia Wang 3580eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 3581eb7188caSLydia Wang 3582eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 35830f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3584eb7188caSLydia Wang 3585eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3586eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 3587eb7188caSLydia Wang #endif 3588eb7188caSLydia Wang 35893e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 35903e95b9abSLydia Wang 3591eb7188caSLydia Wang return 0; 3592eb7188caSLydia Wang } 3593f3db423dSLydia Wang 3594f3db423dSLydia Wang /* Patch for VT1716S */ 3595f3db423dSLydia Wang 3596f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 3597f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 3598f3db423dSLydia Wang { 3599f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 3600f3db423dSLydia Wang uinfo->count = 1; 3601f3db423dSLydia Wang uinfo->value.integer.min = 0; 3602f3db423dSLydia Wang uinfo->value.integer.max = 1; 3603f3db423dSLydia Wang return 0; 3604f3db423dSLydia Wang } 3605f3db423dSLydia Wang 3606f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 3607f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3608f3db423dSLydia Wang { 3609f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3610f3db423dSLydia Wang int index = 0; 3611f3db423dSLydia Wang 3612f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 3613f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 3614f3db423dSLydia Wang if (index != -1) 3615f3db423dSLydia Wang *ucontrol->value.integer.value = index; 3616f3db423dSLydia Wang 3617f3db423dSLydia Wang return 0; 3618f3db423dSLydia Wang } 3619f3db423dSLydia Wang 3620f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 3621f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3622f3db423dSLydia Wang { 3623f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3624f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3625f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 3626f3db423dSLydia Wang 3627f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 3628f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 3629f3db423dSLydia Wang spec->dmic_enabled = index; 36303e95b9abSLydia Wang set_widgets_power_state(codec); 3631f3db423dSLydia Wang return 1; 3632f3db423dSLydia Wang } 3633f3db423dSLydia Wang 363490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 3635f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 3636f3db423dSLydia Wang { 3637f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3638f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 36395b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 3640f3db423dSLydia Wang .count = 1, 3641f3db423dSLydia Wang .info = vt1716s_dmic_info, 3642f3db423dSLydia Wang .get = vt1716s_dmic_get, 3643f3db423dSLydia Wang .put = vt1716s_dmic_put, 3644f3db423dSLydia Wang }, 3645f3db423dSLydia Wang {} /* end */ 3646f3db423dSLydia Wang }; 3647f3db423dSLydia Wang 3648f3db423dSLydia Wang 3649f3db423dSLydia Wang /* mono-out mixer elements */ 365090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3651f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3652f3db423dSLydia Wang { } /* end */ 3653f3db423dSLydia Wang }; 3654f3db423dSLydia Wang 365590dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 3656f3db423dSLydia Wang /* 3657f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3658f3db423dSLydia Wang */ 3659f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3660f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3661f3db423dSLydia Wang 3662f3db423dSLydia Wang 3663f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3664f3db423dSLydia Wang * mixer widget 3665f3db423dSLydia Wang */ 3666f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3667f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3668f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3669f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3670f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 3671f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3672f3db423dSLydia Wang 3673f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 3674f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 3675f3db423dSLydia Wang 3676f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 3677f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3678f3db423dSLydia Wang 3679f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 3680f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 3681f3db423dSLydia Wang 3682f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 3683f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3684f3db423dSLydia Wang 3685f3db423dSLydia Wang /* PW9 PW10 Output enable */ 3686f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3687f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3688f3db423dSLydia Wang 3689f3db423dSLydia Wang /* Unmute SW1, PW12 */ 3690f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3691f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 3692f3db423dSLydia Wang /* PW12 Output enable */ 3693f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3694f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 3695f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 3696f3db423dSLydia Wang /* don't bybass mixer */ 3697f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 3698f3db423dSLydia Wang /* Enable mono output */ 3699f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 3700f3db423dSLydia Wang { } 3701f3db423dSLydia Wang }; 3702f3db423dSLydia Wang 3703f3db423dSLydia Wang 370490dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 3705f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3706f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3707f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3708f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3709f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3710f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 3711f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 3712f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3713f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3714f3db423dSLydia Wang { } 3715f3db423dSLydia Wang }; 3716f3db423dSLydia Wang 3717f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 3718f3db423dSLydia Wang { 3719f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3720f3db423dSLydia Wang int err; 3721f3db423dSLydia Wang 3722f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3723f3db423dSLydia Wang if (err < 0) 3724f3db423dSLydia Wang return err; 37254a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3726f3db423dSLydia Wang if (err < 0) 3727f3db423dSLydia Wang return err; 3728f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3729f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 3730f3db423dSLydia Wang 37314a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3732f3db423dSLydia Wang if (err < 0) 3733f3db423dSLydia Wang return err; 37344a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3735f3db423dSLydia Wang if (err < 0) 3736f3db423dSLydia Wang return err; 3737620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3738f3db423dSLydia Wang if (err < 0) 3739f3db423dSLydia Wang return err; 3740f3db423dSLydia Wang 3741f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3742f3db423dSLydia Wang 3743f3db423dSLydia Wang fill_dig_outs(codec); 3744f3db423dSLydia Wang 3745f3db423dSLydia Wang if (spec->kctls.list) 3746f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3747f3db423dSLydia Wang 3748f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 3749f3db423dSLydia Wang 3750f3db423dSLydia Wang if (spec->hp_mux) 37513d83e577STakashi Iwai via_hp_build(codec); 3752f3db423dSLydia Wang 3753f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3754f4a7828bSTakashi Iwai if (err < 0) 3755f4a7828bSTakashi Iwai return err; 3756f3db423dSLydia Wang 3757f3db423dSLydia Wang return 1; 3758f3db423dSLydia Wang } 3759f3db423dSLydia Wang 3760f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 376190dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 3762f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 3763f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 3764f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 3765f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 3766f3db423dSLydia Wang { } /* end */ 3767f3db423dSLydia Wang }; 3768f3db423dSLydia Wang #endif 3769f3db423dSLydia Wang 37703e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 37713e95b9abSLydia Wang { 37723e95b9abSLydia Wang struct via_spec *spec = codec->spec; 37733e95b9abSLydia Wang int imux_is_smixer; 37743e95b9abSLydia Wang unsigned int parm; 37753e95b9abSLydia Wang unsigned int mono_out, present; 37763e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 37773e95b9abSLydia Wang imux_is_smixer = 37783e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 37793e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 37803e95b9abSLydia Wang /* inputs */ 37813e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 37823e95b9abSLydia Wang parm = AC_PWRST_D3; 37833e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 37843e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 37853e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 37863e95b9abSLydia Wang if (imux_is_smixer) 37873e95b9abSLydia Wang parm = AC_PWRST_D0; 37883e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 37893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 37903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 37913e95b9abSLydia Wang 37923e95b9abSLydia Wang parm = AC_PWRST_D3; 37933e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 37943e95b9abSLydia Wang /* PW11 (22h) */ 37953e95b9abSLydia Wang if (spec->dmic_enabled) 37963e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 37973e95b9abSLydia Wang else 37983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 37993e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 38003e95b9abSLydia Wang 38013e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 38023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 38033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 38043e95b9abSLydia Wang 38053e95b9abSLydia Wang /* outputs */ 38063e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 38073e95b9abSLydia Wang parm = AC_PWRST_D3; 38083e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 38093e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 38103e95b9abSLydia Wang if (spec->smart51_enabled) 38113e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 38123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 38133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 38143e95b9abSLydia Wang 38153e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 38163e95b9abSLydia Wang parm = AC_PWRST_D3; 38173e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 38183e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 38193e95b9abSLydia Wang if (spec->smart51_enabled) 38203e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 38213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 38223e95b9abSLydia Wang 38233e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 38243e95b9abSLydia Wang if (spec->smart51_enabled) 38253e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 38263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 38273e95b9abSLydia Wang 38283e95b9abSLydia Wang /* Mono out */ 38293e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 38303e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 38313e95b9abSLydia Wang 38323e95b9abSLydia Wang if (present) 38333e95b9abSLydia Wang mono_out = 0; 38343e95b9abSLydia Wang else { 38353e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 38363e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 38373e95b9abSLydia Wang mono_out = 0; 38383e95b9abSLydia Wang else 38393e95b9abSLydia Wang mono_out = 1; 38403e95b9abSLydia Wang } 38413e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 38423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 38433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 38443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 38453e95b9abSLydia Wang 38463e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 38473e95b9abSLydia Wang parm = AC_PWRST_D3; 38483e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 38493e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 38503e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 38513e95b9abSLydia Wang if (spec->hp_independent_mode) 38523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 38533e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 38543e95b9abSLydia Wang 38553e95b9abSLydia Wang /* force to D0 for internal Speaker */ 38563e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 38573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 38583e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 38593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 38603e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 38613e95b9abSLydia Wang } 38623e95b9abSLydia Wang 3863f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3864f3db423dSLydia Wang { 3865f3db423dSLydia Wang struct via_spec *spec; 3866f3db423dSLydia Wang int err; 3867f3db423dSLydia Wang 3868f3db423dSLydia Wang /* create a codec specific record */ 38695b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3870f3db423dSLydia Wang if (spec == NULL) 3871f3db423dSLydia Wang return -ENOMEM; 3872f3db423dSLydia Wang 3873620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3874*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3875*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3876620e2b28STakashi Iwai 3877f3db423dSLydia Wang /* automatic parse from the BIOS config */ 3878f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 3879f3db423dSLydia Wang if (err < 0) { 3880f3db423dSLydia Wang via_free(codec); 3881f3db423dSLydia Wang return err; 3882f3db423dSLydia Wang } else if (!err) { 3883f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3884f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 3885f3db423dSLydia Wang } 3886f3db423dSLydia Wang 3887f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 3888f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 3889f3db423dSLydia Wang 3890f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3891f3db423dSLydia Wang spec->num_mixers++; 3892f3db423dSLydia Wang 3893f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3894f3db423dSLydia Wang 3895f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3896f3db423dSLydia Wang 3897f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 38980f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3899f3db423dSLydia Wang 3900f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3901f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 3902f3db423dSLydia Wang #endif 3903f3db423dSLydia Wang 39043e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 3905f3db423dSLydia Wang return 0; 3906f3db423dSLydia Wang } 390725eaba2fSLydia Wang 390825eaba2fSLydia Wang /* for vt2002P */ 390925eaba2fSLydia Wang 391090dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 3911eadb9a80SLydia Wang /* Class-D speaker related verbs */ 3912eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 3913eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 3914eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 391525eaba2fSLydia Wang /* 391625eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 391725eaba2fSLydia Wang */ 391825eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 391925eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 392025eaba2fSLydia Wang 392125eaba2fSLydia Wang 392225eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 392325eaba2fSLydia Wang * mixer widget 392425eaba2fSLydia Wang */ 392525eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 392625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 392725eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 392825eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 392925eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 393025eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 393125eaba2fSLydia Wang 393225eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 393325eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 393425eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 393525eaba2fSLydia Wang 393625eaba2fSLydia Wang /* PW9 Output enable */ 393725eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 393825eaba2fSLydia Wang 393925eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 394025eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 394125eaba2fSLydia Wang 394225eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 394325eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 394425eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 394525eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 394625eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 394725eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 394825eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 394925eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 395025eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 395125eaba2fSLydia Wang 395225eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 395325eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 395425eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 395525eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 395625eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 395725eaba2fSLydia Wang 395825eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 395925eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 396025eaba2fSLydia Wang 396125eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 396225eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 396325eaba2fSLydia Wang { } 396425eaba2fSLydia Wang }; 396590dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 396611890956SLydia Wang /* 396711890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 396811890956SLydia Wang */ 396911890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 397011890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 397111890956SLydia Wang 397211890956SLydia Wang 397311890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 397411890956SLydia Wang * mixer widget 397511890956SLydia Wang */ 397611890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 397711890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 397811890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 397911890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 398011890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 398111890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 398211890956SLydia Wang 398311890956SLydia Wang /* MUX Indices: Mic = 0 */ 398411890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 398511890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 398611890956SLydia Wang 398711890956SLydia Wang /* PW9 Output enable */ 398811890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 398911890956SLydia Wang 399011890956SLydia Wang /* Enable Boost Volume backdoor */ 399111890956SLydia Wang {0x1, 0xfb9, 0x24}, 399211890956SLydia Wang 399311890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 399411890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 399511890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 399611890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 399711890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 399811890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 399911890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 400011890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 400111890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 400211890956SLydia Wang 400311890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 400411890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 400511890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 400611890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 400711890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 400811890956SLydia Wang 400911890956SLydia Wang /* set PW0 index=0 (MW0) */ 401011890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 401111890956SLydia Wang 401211890956SLydia Wang /* Enable AOW0 to MW9 */ 401311890956SLydia Wang {0x1, 0xfb8, 0x88}, 401411890956SLydia Wang { } 401511890956SLydia Wang }; 401625eaba2fSLydia Wang 401725eaba2fSLydia Wang 401890dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 401925eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 402025eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 402125eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 402225eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 402325eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 402425eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 402525eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 402625eaba2fSLydia Wang { } 402725eaba2fSLydia Wang }; 402890dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 402911890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 403011890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 403111890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 403211890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 403311890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 403411890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 403511890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 403611890956SLydia Wang { } 403711890956SLydia Wang }; 403825eaba2fSLydia Wang 403925eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 404025eaba2fSLydia Wang { 404125eaba2fSLydia Wang struct via_spec *spec = codec->spec; 404225eaba2fSLydia Wang int err; 404325eaba2fSLydia Wang 404425eaba2fSLydia Wang 404525eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 404625eaba2fSLydia Wang if (err < 0) 404725eaba2fSLydia Wang return err; 404825eaba2fSLydia Wang 40494a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 405025eaba2fSLydia Wang if (err < 0) 405125eaba2fSLydia Wang return err; 405225eaba2fSLydia Wang 405325eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 405425eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 405525eaba2fSLydia Wang 40564a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 405725eaba2fSLydia Wang if (err < 0) 405825eaba2fSLydia Wang return err; 40594a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 406025eaba2fSLydia Wang if (err < 0) 406125eaba2fSLydia Wang return err; 4062620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 406325eaba2fSLydia Wang if (err < 0) 406425eaba2fSLydia Wang return err; 406525eaba2fSLydia Wang 406625eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 406725eaba2fSLydia Wang 406825eaba2fSLydia Wang fill_dig_outs(codec); 406925eaba2fSLydia Wang 407025eaba2fSLydia Wang if (spec->kctls.list) 407125eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 407225eaba2fSLydia Wang 407325eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 407425eaba2fSLydia Wang 407525eaba2fSLydia Wang if (spec->hp_mux) 40763d83e577STakashi Iwai via_hp_build(codec); 407725eaba2fSLydia Wang 407825eaba2fSLydia Wang return 1; 407925eaba2fSLydia Wang } 408025eaba2fSLydia Wang 408125eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 408290dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 408325eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 408425eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 408525eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 408625eaba2fSLydia Wang { } /* end */ 408725eaba2fSLydia Wang }; 408825eaba2fSLydia Wang #endif 408925eaba2fSLydia Wang 40903e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 40913e95b9abSLydia Wang { 40923e95b9abSLydia Wang struct via_spec *spec = codec->spec; 40933e95b9abSLydia Wang int imux_is_smixer; 40943e95b9abSLydia Wang unsigned int parm; 40953e95b9abSLydia Wang unsigned int present; 40963e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 40973e95b9abSLydia Wang imux_is_smixer = 40983e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 40993e95b9abSLydia Wang /* inputs */ 41003e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 41013e95b9abSLydia Wang parm = AC_PWRST_D3; 41023e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 41033e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 41043e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 41053e95b9abSLydia Wang parm = AC_PWRST_D0; 41063e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 41073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 41083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 41093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 41103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 41113e95b9abSLydia Wang 41123e95b9abSLydia Wang /* outputs */ 41133e95b9abSLydia Wang /* AOW0 (8h)*/ 41143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 41153e95b9abSLydia Wang 411611890956SLydia Wang if (spec->codec_type == VT1802) { 411711890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 411811890956SLydia Wang parm = AC_PWRST_D3; 411911890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 412011890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 412111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 412211890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 412311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 412411890956SLydia Wang } else { 41253e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 41263e95b9abSLydia Wang parm = AC_PWRST_D3; 41273e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 41283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 41293e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 41303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 41313e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 413211890956SLydia Wang } 41333e95b9abSLydia Wang 413411890956SLydia Wang if (spec->codec_type == VT1802) { 413511890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 413611890956SLydia Wang parm = AC_PWRST_D3; 413711890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 413811890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 413911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 414011890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 414111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 414211890956SLydia Wang } else { 41433e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 41443e95b9abSLydia Wang parm = AC_PWRST_D3; 41453e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 41463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 41473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 41483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 41493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 415011890956SLydia Wang } 41513e95b9abSLydia Wang 41523e95b9abSLydia Wang if (spec->hp_independent_mode) 41533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 41543e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 41553e95b9abSLydia Wang 41563e95b9abSLydia Wang /* Class-D */ 41573e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 41583e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 41593e95b9abSLydia Wang 41603e95b9abSLydia Wang parm = AC_PWRST_D3; 41613e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 41623e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 416311890956SLydia Wang if (spec->codec_type == VT1802) 416411890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 416511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 416611890956SLydia Wang else 41673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 41683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 41693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 41703e95b9abSLydia Wang 41713e95b9abSLydia Wang /* Mono Out */ 41723e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 41733e95b9abSLydia Wang 41743e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 417511890956SLydia Wang if (spec->codec_type == VT1802) { 417611890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 417711890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 417811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 417911890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 418011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 418111890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 418211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 418311890956SLydia Wang } else { 41843e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 41853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 41863e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 41873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 41883e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 41893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 41903e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 419111890956SLydia Wang } 41923e95b9abSLydia Wang /* MW9 (21h) */ 41933e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 41943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 41953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 41963e95b9abSLydia Wang else 41973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 41983e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 41993e95b9abSLydia Wang } 420025eaba2fSLydia Wang 420125eaba2fSLydia Wang /* patch for vt2002P */ 420225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 420325eaba2fSLydia Wang { 420425eaba2fSLydia Wang struct via_spec *spec; 420525eaba2fSLydia Wang int err; 420625eaba2fSLydia Wang 420725eaba2fSLydia Wang /* create a codec specific record */ 42085b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 420925eaba2fSLydia Wang if (spec == NULL) 421025eaba2fSLydia Wang return -ENOMEM; 421125eaba2fSLydia Wang 4212620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4213*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 4214*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 4215620e2b28STakashi Iwai 421625eaba2fSLydia Wang /* automatic parse from the BIOS config */ 421725eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 421825eaba2fSLydia Wang if (err < 0) { 421925eaba2fSLydia Wang via_free(codec); 422025eaba2fSLydia Wang return err; 422125eaba2fSLydia Wang } else if (!err) { 422225eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 422325eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 422425eaba2fSLydia Wang } 422525eaba2fSLydia Wang 422611890956SLydia Wang if (spec->codec_type == VT1802) 422711890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 422811890956SLydia Wang vt1802_volume_init_verbs; 422911890956SLydia Wang else 423011890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 423111890956SLydia Wang vt2002P_volume_init_verbs; 423225eaba2fSLydia Wang 423311890956SLydia Wang if (spec->codec_type == VT1802) 423411890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 423511890956SLydia Wang vt1802_uniwill_init_verbs; 423611890956SLydia Wang else 423711890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 423811890956SLydia Wang vt2002P_uniwill_init_verbs; 423911890956SLydia Wang 424025eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 424125eaba2fSLydia Wang 424225eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 42430f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 424425eaba2fSLydia Wang 424525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 424625eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 424725eaba2fSLydia Wang #endif 424825eaba2fSLydia Wang 42493e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 425025eaba2fSLydia Wang return 0; 425125eaba2fSLydia Wang } 4252ab6734e7SLydia Wang 4253ab6734e7SLydia Wang /* for vt1812 */ 4254ab6734e7SLydia Wang 425590dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 4256ab6734e7SLydia Wang /* 4257ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4258ab6734e7SLydia Wang */ 4259ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4260ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4261ab6734e7SLydia Wang 4262ab6734e7SLydia Wang 4263ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4264ab6734e7SLydia Wang * mixer widget 4265ab6734e7SLydia Wang */ 4266ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4267ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4268ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4269ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4270ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4271ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4272ab6734e7SLydia Wang 4273ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 4274ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 4275ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 4276ab6734e7SLydia Wang 4277ab6734e7SLydia Wang /* PW9 Output enable */ 4278ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4279ab6734e7SLydia Wang 4280ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 4281ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 4282ab6734e7SLydia Wang 4283ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 4284ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4285ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4286ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4287ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4288ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4289ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4290ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4291ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4292ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4293ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4294ab6734e7SLydia Wang 4295ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 4296ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 4297ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 4298ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 4299ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 4300ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 4301ab6734e7SLydia Wang 4302ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 4303ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 4304ab6734e7SLydia Wang { } 4305ab6734e7SLydia Wang }; 4306ab6734e7SLydia Wang 4307ab6734e7SLydia Wang 430890dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 4309ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 4310ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4311ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 4312ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4313ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4314ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4315ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4316ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4317ab6734e7SLydia Wang { } 4318ab6734e7SLydia Wang }; 4319ab6734e7SLydia Wang 4320ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 4321ab6734e7SLydia Wang { 4322ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 4323ab6734e7SLydia Wang int err; 4324ab6734e7SLydia Wang 4325ab6734e7SLydia Wang 4326ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4327ab6734e7SLydia Wang if (err < 0) 4328ab6734e7SLydia Wang return err; 4329ab6734e7SLydia Wang fill_dig_outs(codec); 43304a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 4331ab6734e7SLydia Wang if (err < 0) 4332ab6734e7SLydia Wang return err; 4333ab6734e7SLydia Wang 4334ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 4335ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 4336ab6734e7SLydia Wang 43374a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 4338ab6734e7SLydia Wang if (err < 0) 4339ab6734e7SLydia Wang return err; 43404a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 4341ab6734e7SLydia Wang if (err < 0) 4342ab6734e7SLydia Wang return err; 4343620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 4344ab6734e7SLydia Wang if (err < 0) 4345ab6734e7SLydia Wang return err; 4346ab6734e7SLydia Wang 4347ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4348ab6734e7SLydia Wang 4349ab6734e7SLydia Wang fill_dig_outs(codec); 4350ab6734e7SLydia Wang 4351ab6734e7SLydia Wang if (spec->kctls.list) 4352ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4353ab6734e7SLydia Wang 4354ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 4355ab6734e7SLydia Wang 4356ab6734e7SLydia Wang if (spec->hp_mux) 43573d83e577STakashi Iwai via_hp_build(codec); 4358ab6734e7SLydia Wang 4359ab6734e7SLydia Wang return 1; 4360ab6734e7SLydia Wang } 4361ab6734e7SLydia Wang 4362ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 436390dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 4364ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 4365ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 4366ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 4367ab6734e7SLydia Wang { } /* end */ 4368ab6734e7SLydia Wang }; 4369ab6734e7SLydia Wang #endif 4370ab6734e7SLydia Wang 43713e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 43723e95b9abSLydia Wang { 43733e95b9abSLydia Wang struct via_spec *spec = codec->spec; 43743e95b9abSLydia Wang int imux_is_smixer = 43753e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 43763e95b9abSLydia Wang unsigned int parm; 43773e95b9abSLydia Wang unsigned int present; 43783e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 43793e95b9abSLydia Wang imux_is_smixer = 43803e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 43813e95b9abSLydia Wang /* inputs */ 43823e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 43833e95b9abSLydia Wang parm = AC_PWRST_D3; 43843e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 43853e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 43863e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 43873e95b9abSLydia Wang parm = AC_PWRST_D0; 43883e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 43893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 43903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 43913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 43923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 43933e95b9abSLydia Wang 43943e95b9abSLydia Wang /* outputs */ 43953e95b9abSLydia Wang /* AOW0 (8h)*/ 43963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 43973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 43983e95b9abSLydia Wang 43993e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 44003e95b9abSLydia Wang parm = AC_PWRST_D3; 44013e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 44023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 44033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 44043e95b9abSLydia Wang 44053e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 44063e95b9abSLydia Wang parm = AC_PWRST_D3; 44073e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 44083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 44093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 44103e95b9abSLydia Wang if (spec->hp_independent_mode) 44113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 44123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44133e95b9abSLydia Wang 44143e95b9abSLydia Wang /* Internal Speaker */ 44153e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 44163e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 44173e95b9abSLydia Wang 44183e95b9abSLydia Wang parm = AC_PWRST_D3; 44193e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 44203e95b9abSLydia Wang if (present) { 44213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 44223e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 44233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 44243e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 44253e95b9abSLydia Wang } else { 44263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 44273e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 44293e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44303e95b9abSLydia Wang } 44313e95b9abSLydia Wang 44323e95b9abSLydia Wang 44333e95b9abSLydia Wang /* Mono Out */ 44343e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 44353e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 44363e95b9abSLydia Wang 44373e95b9abSLydia Wang parm = AC_PWRST_D3; 44383e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 44393e95b9abSLydia Wang if (present) { 44403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 44413e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 44423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 44433e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 44443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 44453e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 44463e95b9abSLydia Wang } else { 44473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 44483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 44503e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 44523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 44533e95b9abSLydia Wang } 44543e95b9abSLydia Wang 44553e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 44563e95b9abSLydia Wang parm = AC_PWRST_D3; 44573e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 44583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 44593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 44603e95b9abSLydia Wang 44613e95b9abSLydia Wang } 4462ab6734e7SLydia Wang 4463ab6734e7SLydia Wang /* patch for vt1812 */ 4464ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 4465ab6734e7SLydia Wang { 4466ab6734e7SLydia Wang struct via_spec *spec; 4467ab6734e7SLydia Wang int err; 4468ab6734e7SLydia Wang 4469ab6734e7SLydia Wang /* create a codec specific record */ 44705b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4471ab6734e7SLydia Wang if (spec == NULL) 4472ab6734e7SLydia Wang return -ENOMEM; 4473ab6734e7SLydia Wang 4474620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4475*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 4476*d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 4477620e2b28STakashi Iwai 4478ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 4479ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 4480ab6734e7SLydia Wang if (err < 0) { 4481ab6734e7SLydia Wang via_free(codec); 4482ab6734e7SLydia Wang return err; 4483ab6734e7SLydia Wang } else if (!err) { 4484ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4485ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 4486ab6734e7SLydia Wang } 4487ab6734e7SLydia Wang 4488ab6734e7SLydia Wang 4489ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 4490ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 4491ab6734e7SLydia Wang 4492ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 4493ab6734e7SLydia Wang 4494ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 44950f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4496ab6734e7SLydia Wang 4497ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4498ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 4499ab6734e7SLydia Wang #endif 4500ab6734e7SLydia Wang 45013e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 4502ab6734e7SLydia Wang return 0; 4503ab6734e7SLydia Wang } 4504ab6734e7SLydia Wang 4505c577b8a1SJoseph Chan /* 4506c577b8a1SJoseph Chan * patch entries 4507c577b8a1SJoseph Chan */ 450890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 45093218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 45103218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 45113218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 45123218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 45133218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 4514f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 45153218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 4516f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 45173218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 4518f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 45193218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 4520f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 45213218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 4522f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 45233218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 4524f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 45253218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 4526f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 45273218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 4528f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 45293218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 4530f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 45313218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 4532f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 45333218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 4534f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 45353218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 4536f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 45373218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 4538f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 45393218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 4540f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 45413218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 4542f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 45433218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 4544f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 45453218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 4546d949cac1SHarald Welte .patch = patch_vt1708S}, 45473218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 4548d949cac1SHarald Welte .patch = patch_vt1708S}, 45493218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 4550d949cac1SHarald Welte .patch = patch_vt1708S}, 45513218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 4552d949cac1SHarald Welte .patch = patch_vt1708S}, 4553bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 4554d949cac1SHarald Welte .patch = patch_vt1708S}, 45553218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 4556d949cac1SHarald Welte .patch = patch_vt1708S}, 45573218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 4558d949cac1SHarald Welte .patch = patch_vt1708S}, 45593218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 4560d949cac1SHarald Welte .patch = patch_vt1708S}, 45613218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 4562d949cac1SHarald Welte .patch = patch_vt1702}, 45633218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 4564d949cac1SHarald Welte .patch = patch_vt1702}, 45653218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 4566d949cac1SHarald Welte .patch = patch_vt1702}, 45673218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 4568d949cac1SHarald Welte .patch = patch_vt1702}, 45693218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 4570d949cac1SHarald Welte .patch = patch_vt1702}, 45713218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 4572d949cac1SHarald Welte .patch = patch_vt1702}, 45733218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 4574d949cac1SHarald Welte .patch = patch_vt1702}, 45753218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 4576d949cac1SHarald Welte .patch = patch_vt1702}, 4577eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 4578eb7188caSLydia Wang .patch = patch_vt1718S}, 4579eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 4580eb7188caSLydia Wang .patch = patch_vt1718S}, 4581bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 4582bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4583bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 4584bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4585f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 4586f3db423dSLydia Wang .patch = patch_vt1716S}, 4587f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 4588f3db423dSLydia Wang .patch = patch_vt1716S}, 458925eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 459025eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 4591ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 459236dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 459336dd5c4aSLydia Wang .patch = patch_vt1708S}, 459411890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 459511890956SLydia Wang .patch = patch_vt2002P}, 459611890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 459711890956SLydia Wang .patch = patch_vt2002P}, 4598c577b8a1SJoseph Chan {} /* terminator */ 4599c577b8a1SJoseph Chan }; 46001289e9e8STakashi Iwai 46011289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 46021289e9e8STakashi Iwai 46031289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 46041289e9e8STakashi Iwai .preset = snd_hda_preset_via, 46051289e9e8STakashi Iwai .owner = THIS_MODULE, 46061289e9e8STakashi Iwai }; 46071289e9e8STakashi Iwai 46081289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 46091289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 46101289e9e8STakashi Iwai 46111289e9e8STakashi Iwai static int __init patch_via_init(void) 46121289e9e8STakashi Iwai { 46131289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 46141289e9e8STakashi Iwai } 46151289e9e8STakashi Iwai 46161289e9e8STakashi Iwai static void __exit patch_via_exit(void) 46171289e9e8STakashi Iwai { 46181289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 46191289e9e8STakashi Iwai } 46201289e9e8STakashi Iwai 46211289e9e8STakashi Iwai module_init(patch_via_init) 46221289e9e8STakashi Iwai module_exit(patch_via_exit) 4623