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]; 12590dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1271f2e99feSLydia Wang 12882673bc8STakashi Iwai char stream_name_digital[32]; 12990dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 13090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1311f2e99feSLydia Wang 1321f2e99feSLydia Wang /* playback */ 1331f2e99feSLydia Wang struct hda_multi_out multiout; 1341f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1351f2e99feSLydia Wang 1364a79616dSTakashi Iwai struct nid_path out_path[4]; 1374a79616dSTakashi Iwai struct nid_path hp_path; 1384a79616dSTakashi Iwai struct nid_path hp_dep_path; 1394a79616dSTakashi Iwai 1401f2e99feSLydia Wang /* capture */ 1411f2e99feSLydia Wang unsigned int num_adc_nids; 142a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1431f2e99feSLydia Wang hda_nid_t mux_nids[3]; 144620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1451f2e99feSLydia Wang hda_nid_t dig_in_nid; 1461f2e99feSLydia Wang hda_nid_t dig_in_pin; 1471f2e99feSLydia Wang 1481f2e99feSLydia Wang /* capture source */ 1491f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1501f2e99feSLydia Wang unsigned int cur_mux[3]; 1511f2e99feSLydia Wang 1521f2e99feSLydia Wang /* PCM information */ 1531f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1541f2e99feSLydia Wang 1551f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1561f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1571f2e99feSLydia Wang struct snd_array kctls; 1581f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1591f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1601f2e99feSLydia Wang 1611f2e99feSLydia Wang /* HP mode source */ 1621f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1631f2e99feSLydia Wang unsigned int hp_independent_mode; 1641f2e99feSLydia Wang unsigned int hp_independent_mode_index; 165f4a7828bSTakashi Iwai unsigned int can_smart51; 1661f2e99feSLydia Wang unsigned int smart51_enabled; 167f3db423dSLydia Wang unsigned int dmic_enabled; 16824088a58STakashi Iwai unsigned int no_pin_power_ctl; 1691f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1701f2e99feSLydia Wang 1711f2e99feSLydia Wang /* work to check hp jack state */ 1721f2e99feSLydia Wang struct hda_codec *codec; 1731f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 174e06e5a29STakashi Iwai int vt1708_jack_detect; 1751f2e99feSLydia Wang int vt1708_hp_present; 1763e95b9abSLydia Wang 1773e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1783e95b9abSLydia Wang 1791f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1801f2e99feSLydia Wang struct hda_loopback_check loopback; 1811f2e99feSLydia Wang #endif 1821f2e99feSLydia Wang }; 1831f2e99feSLydia Wang 1840341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1855b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1865b0cb1d8SJaroslav Kysela { 1875b0cb1d8SJaroslav Kysela struct via_spec *spec; 1885b0cb1d8SJaroslav Kysela 1895b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1905b0cb1d8SJaroslav Kysela if (spec == NULL) 1915b0cb1d8SJaroslav Kysela return NULL; 1925b0cb1d8SJaroslav Kysela 1935b0cb1d8SJaroslav Kysela codec->spec = spec; 1945b0cb1d8SJaroslav Kysela spec->codec = codec; 1950341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1960341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1970341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1980341ccd7SLydia Wang spec->codec_type = VT1708S; 1995b0cb1d8SJaroslav Kysela return spec; 2005b0cb1d8SJaroslav Kysela } 2015b0cb1d8SJaroslav Kysela 202744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 203d7426329SHarald Welte { 204744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 205d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 206d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 207d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 208d7426329SHarald Welte 209d7426329SHarald Welte /* get codec type */ 210d7426329SHarald Welte if (ven_id != 0x1106) 211d7426329SHarald Welte codec_type = UNKNOWN; 212d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 213d7426329SHarald Welte codec_type = VT1708; 214d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 215d7426329SHarald Welte codec_type = VT1709_10CH; 216d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 217d7426329SHarald Welte codec_type = VT1709_6CH; 218518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 219d7426329SHarald Welte codec_type = VT1708B_8CH; 220518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 221518bf3baSLydia Wang codec_type = VT1708BCE; 222518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 223d7426329SHarald Welte codec_type = VT1708B_4CH; 224d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 225d7426329SHarald Welte && (dev_id >> 12) < 8) 226d7426329SHarald Welte codec_type = VT1708S; 227d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 228d7426329SHarald Welte && (dev_id >> 12) < 8) 229d7426329SHarald Welte codec_type = VT1702; 230eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 231eb7188caSLydia Wang && (dev_id >> 12) < 8) 232eb7188caSLydia Wang codec_type = VT1718S; 233f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 234f3db423dSLydia Wang codec_type = VT1716S; 235bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 236bb3c6bfcSLydia Wang codec_type = VT1718S; 23725eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 23825eaba2fSLydia Wang codec_type = VT2002P; 239ab6734e7SLydia Wang else if (dev_id == 0x0448) 240ab6734e7SLydia Wang codec_type = VT1812; 24136dd5c4aSLydia Wang else if (dev_id == 0x0440) 24236dd5c4aSLydia Wang codec_type = VT1708S; 24311890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 24411890956SLydia Wang codec_type = VT1802; 245d7426329SHarald Welte else 246d7426329SHarald Welte codec_type = UNKNOWN; 247d7426329SHarald Welte return codec_type; 248d7426329SHarald Welte }; 249d7426329SHarald Welte 250ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25169e52a80SHarald Welte #define VIA_HP_EVENT 0x01 25269e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 253ec7e7e42SLydia Wang #define VIA_MONO_EVENT 0x03 254ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT 0x04 255ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT 0x05 25669e52a80SHarald Welte 257c577b8a1SJoseph Chan enum { 258c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 259c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 260f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 26125eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 262c577b8a1SJoseph Chan }; 263c577b8a1SJoseph Chan 264c577b8a1SJoseph Chan enum { 265eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 266c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 267c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 268c577b8a1SJoseph Chan AUTO_SEQ_SIDE 269c577b8a1SJoseph Chan }; 270c577b8a1SJoseph Chan 271f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2721f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2731f2e99feSLydia Wang 2741f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2751f2e99feSLydia Wang { 2761f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2771f2e99feSLydia Wang return; 2781f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 279e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2801f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2811f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2821f2e99feSLydia Wang msecs_to_jiffies(100)); 2831f2e99feSLydia Wang } 2841f2e99feSLydia Wang 2851f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2861f2e99feSLydia Wang { 2871f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2881f2e99feSLydia Wang return; 2891f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2901f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2911f2e99feSLydia Wang return; 2921f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 293e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2945b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2951f2e99feSLydia Wang } 296f5271101SLydia Wang 2973e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2983e95b9abSLydia Wang { 2993e95b9abSLydia Wang struct via_spec *spec = codec->spec; 3003e95b9abSLydia Wang if (spec->set_widgets_power_state) 3013e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3023e95b9abSLydia Wang } 30325eaba2fSLydia Wang 304f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 305f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 306f5271101SLydia Wang { 307f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 308f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 309f5271101SLydia Wang 3103e95b9abSLydia Wang set_widgets_power_state(codec); 311f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3121f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3131f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3141f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3151f2e99feSLydia Wang else 3161f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3171f2e99feSLydia Wang } 318f5271101SLydia Wang return change; 319f5271101SLydia Wang } 320f5271101SLydia Wang 321f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 322f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 323f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 324f5271101SLydia Wang .name = NULL, \ 325f5271101SLydia Wang .index = 0, \ 326f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 327f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 328f5271101SLydia Wang .put = analog_input_switch_put, \ 329f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 330f5271101SLydia Wang 33125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 33225eaba2fSLydia Wang 33325eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 33425eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 33525eaba2fSLydia Wang { 33625eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 33825eaba2fSLydia Wang int i; 33925eaba2fSLydia Wang int change = 0; 34025eaba2fSLydia Wang 34125eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 34225eaba2fSLydia Wang int lmute, rmute; 34325eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 34425eaba2fSLydia Wang snd_printd("Invalid control!\n"); 34525eaba2fSLydia Wang return change; 34625eaba2fSLydia Wang } 34725eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 34825eaba2fSLydia Wang ucontrol); 34925eaba2fSLydia Wang /* Get mute value */ 35025eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 35125eaba2fSLydia Wang valp++; 35225eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 35325eaba2fSLydia Wang 35425eaba2fSLydia Wang /* Set hp pins */ 35525eaba2fSLydia Wang if (!spec->hp_independent_mode) { 35625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 35725eaba2fSLydia Wang snd_hda_codec_amp_update( 35825eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 35925eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36025eaba2fSLydia Wang lmute); 36125eaba2fSLydia Wang snd_hda_codec_amp_update( 36225eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36325eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36425eaba2fSLydia Wang rmute); 36525eaba2fSLydia Wang } 36625eaba2fSLydia Wang } 36725eaba2fSLydia Wang 36825eaba2fSLydia Wang if (!lmute && !rmute) { 36925eaba2fSLydia Wang /* Line Outs */ 37025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 37125eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37225eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 37325eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37425eaba2fSLydia Wang /* Speakers */ 37525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37625eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37725eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 37825eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37925eaba2fSLydia Wang /* unmute */ 38025eaba2fSLydia Wang via_hp_bind_automute(codec); 38125eaba2fSLydia Wang 38225eaba2fSLydia Wang } else { 38325eaba2fSLydia Wang if (lmute) { 38425eaba2fSLydia Wang /* Mute all left channels */ 38525eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38625eaba2fSLydia Wang snd_hda_codec_amp_update( 38725eaba2fSLydia Wang codec, 38825eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 38925eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39025eaba2fSLydia Wang lmute); 39125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39225eaba2fSLydia Wang snd_hda_codec_amp_update( 39325eaba2fSLydia Wang codec, 39425eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39525eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39625eaba2fSLydia Wang lmute); 39725eaba2fSLydia Wang } 39825eaba2fSLydia Wang if (rmute) { 39925eaba2fSLydia Wang /* mute all right channels */ 40025eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 40125eaba2fSLydia Wang snd_hda_codec_amp_update( 40225eaba2fSLydia Wang codec, 40325eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 40425eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 40525eaba2fSLydia Wang rmute); 40625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 40725eaba2fSLydia Wang snd_hda_codec_amp_update( 40825eaba2fSLydia Wang codec, 40925eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 41025eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 41125eaba2fSLydia Wang rmute); 41225eaba2fSLydia Wang } 41325eaba2fSLydia Wang } 41425eaba2fSLydia Wang return change; 41525eaba2fSLydia Wang } 41625eaba2fSLydia Wang 41725eaba2fSLydia Wang #define BIND_PIN_MUTE \ 41825eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 41925eaba2fSLydia Wang .name = NULL, \ 42025eaba2fSLydia Wang .index = 0, \ 42125eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 42225eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 42325eaba2fSLydia Wang .put = bind_pin_switch_put, \ 42425eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 42525eaba2fSLydia Wang 42690dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 427c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 428c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 429f5271101SLydia Wang ANALOG_INPUT_MUTE, 43025eaba2fSLydia Wang BIND_PIN_MUTE, 431c577b8a1SJoseph Chan }; 432c577b8a1SJoseph Chan 433ab6734e7SLydia Wang 434c577b8a1SJoseph Chan /* add dynamic controls */ 435291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 436291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 437291c9e33STakashi Iwai const char *name) 438c577b8a1SJoseph Chan { 439c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 440c577b8a1SJoseph Chan 441603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 442603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 443c577b8a1SJoseph Chan if (!knew) 444291c9e33STakashi Iwai return NULL; 445291c9e33STakashi Iwai *knew = *tmpl; 446291c9e33STakashi Iwai if (!name) 447291c9e33STakashi Iwai name = tmpl->name; 448291c9e33STakashi Iwai if (name) { 449c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 450c577b8a1SJoseph Chan if (!knew->name) 451291c9e33STakashi Iwai return NULL; 452291c9e33STakashi Iwai } 453291c9e33STakashi Iwai return knew; 454291c9e33STakashi Iwai } 455291c9e33STakashi Iwai 456291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 457291c9e33STakashi Iwai int idx, unsigned long val) 458291c9e33STakashi Iwai { 459291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 460291c9e33STakashi Iwai 461291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 462291c9e33STakashi Iwai if (!knew) 463c577b8a1SJoseph Chan return -ENOMEM; 4644d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4655e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 466c577b8a1SJoseph Chan knew->private_value = val; 467c577b8a1SJoseph Chan return 0; 468c577b8a1SJoseph Chan } 469c577b8a1SJoseph Chan 4707b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4717b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4727b315bb4STakashi Iwai 473291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 4745b0cb1d8SJaroslav Kysela 475603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 476603c4019STakashi Iwai { 477603c4019STakashi Iwai struct via_spec *spec = codec->spec; 478603c4019STakashi Iwai 479603c4019STakashi Iwai if (spec->kctls.list) { 480603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 481603c4019STakashi Iwai int i; 482603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 483603c4019STakashi Iwai kfree(kctl[i].name); 484603c4019STakashi Iwai } 485603c4019STakashi Iwai snd_array_free(&spec->kctls); 486603c4019STakashi Iwai } 487603c4019STakashi Iwai 488c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4899510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4907b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 491c577b8a1SJoseph Chan { 492c577b8a1SJoseph Chan char name[32]; 493c577b8a1SJoseph Chan int err; 494c577b8a1SJoseph Chan 495c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4967b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 497c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 498c577b8a1SJoseph Chan if (err < 0) 499c577b8a1SJoseph Chan return err; 500c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5017b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 502c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 503c577b8a1SJoseph Chan if (err < 0) 504c577b8a1SJoseph Chan return err; 505c577b8a1SJoseph Chan return 0; 506c577b8a1SJoseph Chan } 507c577b8a1SJoseph Chan 508c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 509c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 510c577b8a1SJoseph Chan int dac_idx) 511c577b8a1SJoseph Chan { 512c577b8a1SJoseph Chan /* set as output */ 513c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 514c577b8a1SJoseph Chan pin_type); 515c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 516c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 517d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 518d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 519d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 520c577b8a1SJoseph Chan } 521c577b8a1SJoseph Chan 522c577b8a1SJoseph Chan 523c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 524c577b8a1SJoseph Chan { 525c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 526c577b8a1SJoseph Chan int i; 527c577b8a1SJoseph Chan 528c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 529c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 530c577b8a1SJoseph Chan if (nid) 531c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 532c577b8a1SJoseph Chan } 533c577b8a1SJoseph Chan } 534c577b8a1SJoseph Chan 535c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 536c577b8a1SJoseph Chan { 537c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 538c577b8a1SJoseph Chan hda_nid_t pin; 53925eaba2fSLydia Wang int i; 540c577b8a1SJoseph Chan 54125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 54225eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 543c577b8a1SJoseph Chan if (pin) /* connect to front */ 544c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 545c577b8a1SJoseph Chan } 54625eaba2fSLydia Wang } 547c577b8a1SJoseph Chan 548f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 54932e0191dSClemens Ladisch 550c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 551c577b8a1SJoseph Chan { 552c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5537b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 55432e0191dSClemens Ladisch unsigned int ctl; 555c577b8a1SJoseph Chan int i; 556c577b8a1SJoseph Chan 5577b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5587b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 559f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 56032e0191dSClemens Ladisch ctl = PIN_OUT; 56130649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56232e0191dSClemens Ladisch ctl = PIN_VREF50; 56332e0191dSClemens Ladisch else 56432e0191dSClemens Ladisch ctl = PIN_IN; 565c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56632e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 567c577b8a1SJoseph Chan } 568c577b8a1SJoseph Chan } 569f5271101SLydia Wang 570f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 571f5271101SLydia Wang unsigned int *affected_parm) 572f5271101SLydia Wang { 573f5271101SLydia Wang unsigned parm; 574f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 575f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 576f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 577f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5781564b287SLydia Wang struct via_spec *spec = codec->spec; 57924088a58STakashi Iwai unsigned present = 0; 58024088a58STakashi Iwai 58124088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 58224088a58STakashi Iwai if (!no_presence) 58324088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 584f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 5851564b287SLydia Wang || ((no_presence || present) 5861564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 587f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 588f5271101SLydia Wang parm = AC_PWRST_D0; 589f5271101SLydia Wang } else 590f5271101SLydia Wang parm = AC_PWRST_D3; 591f5271101SLydia Wang 592f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 593f5271101SLydia Wang } 594f5271101SLydia Wang 59524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 59624088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 59724088a58STakashi Iwai { 59824088a58STakashi Iwai static const char * const texts[] = { 59924088a58STakashi Iwai "Disabled", "Enabled" 60024088a58STakashi Iwai }; 60124088a58STakashi Iwai 60224088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 60324088a58STakashi Iwai uinfo->count = 1; 60424088a58STakashi Iwai uinfo->value.enumerated.items = 2; 60524088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 60624088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 60724088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 60824088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 60924088a58STakashi Iwai return 0; 61024088a58STakashi Iwai } 61124088a58STakashi Iwai 61224088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 61324088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61424088a58STakashi Iwai { 61524088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61624088a58STakashi Iwai struct via_spec *spec = codec->spec; 61724088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 61824088a58STakashi Iwai return 0; 61924088a58STakashi Iwai } 62024088a58STakashi Iwai 62124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 62224088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 62324088a58STakashi Iwai { 62424088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 62524088a58STakashi Iwai struct via_spec *spec = codec->spec; 62624088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 62724088a58STakashi Iwai 62824088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 62924088a58STakashi Iwai return 0; 63024088a58STakashi Iwai spec->no_pin_power_ctl = val; 63124088a58STakashi Iwai set_widgets_power_state(codec); 63224088a58STakashi Iwai return 1; 63324088a58STakashi Iwai } 63424088a58STakashi Iwai 63524088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 63624088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 63724088a58STakashi Iwai .name = "Dynamic Power-Control", 63824088a58STakashi Iwai .info = via_pin_power_ctl_info, 63924088a58STakashi Iwai .get = via_pin_power_ctl_get, 64024088a58STakashi Iwai .put = via_pin_power_ctl_put, 64124088a58STakashi Iwai }; 64224088a58STakashi Iwai 64324088a58STakashi Iwai 644c577b8a1SJoseph Chan /* 645c577b8a1SJoseph Chan * input MUX handling 646c577b8a1SJoseph Chan */ 647c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 648c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 649c577b8a1SJoseph Chan { 650c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 651c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 652c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 653c577b8a1SJoseph Chan } 654c577b8a1SJoseph Chan 655c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 656c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 657c577b8a1SJoseph Chan { 658c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 659c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 660c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 661c577b8a1SJoseph Chan 662c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 663c577b8a1SJoseph Chan return 0; 664c577b8a1SJoseph Chan } 665c577b8a1SJoseph Chan 666c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 667c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 668c577b8a1SJoseph Chan { 669c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 670c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 671c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 672bff5fbf5SLydia Wang int ret; 673c577b8a1SJoseph Chan 674337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 675337b9d02STakashi Iwai return -EINVAL; 676a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 677a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 678a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 679a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 680a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 681bff5fbf5SLydia Wang 682bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 683bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 684bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 685a80e6e3cSLydia Wang /* update jack power state */ 6863e95b9abSLydia Wang set_widgets_power_state(codec); 687a80e6e3cSLydia Wang 688bff5fbf5SLydia Wang return ret; 689c577b8a1SJoseph Chan } 690c577b8a1SJoseph Chan 6910aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6920aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6930aa62aefSHarald Welte { 6940aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6950aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6960aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6970aa62aefSHarald Welte } 6980aa62aefSHarald Welte 6990aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7000aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7010aa62aefSHarald Welte { 7020aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7035b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 704eb7188caSLydia Wang unsigned int pinsel; 705eb7188caSLydia Wang 706eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 707eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 7080aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 7090aa62aefSHarald Welte 0x00); 7100aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 7110aa62aefSHarald Welte 7120aa62aefSHarald Welte return 0; 7130aa62aefSHarald Welte } 7140aa62aefSHarald Welte 7150713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7160713efebSLydia Wang { 7170713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7180713efebSLydia Wang if (ctl) { 7190713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7200713efebSLydia Wang ctl->vd[0].access |= active 7210713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7220713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7230713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7240713efebSLydia Wang } 7250713efebSLydia Wang } 7260713efebSLydia Wang 7275b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7285b0cb1d8SJaroslav Kysela { 7295b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7305b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7315b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7325b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7335b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 734e87885feSLydia Wang case VT2002P: return 0x19; 735e87885feSLydia Wang case VT1802: return 0x15; 736e87885feSLydia Wang case VT1812: return 0x15; 7375b0cb1d8SJaroslav Kysela default: return 0; 7385b0cb1d8SJaroslav Kysela } 7395b0cb1d8SJaroslav Kysela } 7405b0cb1d8SJaroslav Kysela 741cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 742cdc1784dSLydia Wang { 743cdc1784dSLydia Wang /* mute side channel */ 744cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 745e87885feSLydia Wang unsigned int parm; 7465b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 747cdc1784dSLydia Wang 748e87885feSLydia Wang if (sw3) { 749e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 750e87885feSLydia Wang parm = spec->hp_independent_mode ? 751e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 752e87885feSLydia Wang else 753e87885feSLydia Wang parm = spec->hp_independent_mode ? 754e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 755e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 756e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 757e87885feSLydia Wang if (spec->codec_type == VT1812) 758e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 759e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 760e87885feSLydia Wang } 761cdc1784dSLydia Wang return 0; 762cdc1784dSLydia Wang } 763cdc1784dSLydia Wang 7640aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7650aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7660aa62aefSHarald Welte { 7670aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7680aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7695b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7700aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 771cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 772cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 773cdc1784dSLydia Wang ? 1 : 0; 774ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 775ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 776ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 777ce0e5a9eSLydia Wang else 778ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 779ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7800aa62aefSHarald Welte 781ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 782ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 783ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 784cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 785cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 786cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7870aa62aefSHarald Welte 0, 0, 0); 7880aa62aefSHarald Welte 789cdc1784dSLydia Wang update_side_mute_status(codec); 7900713efebSLydia Wang /* update HP volume/swtich active state */ 7910713efebSLydia Wang if (spec->codec_type == VT1708S 792eb7188caSLydia Wang || spec->codec_type == VT1702 793f3db423dSLydia Wang || spec->codec_type == VT1718S 79425eaba2fSLydia Wang || spec->codec_type == VT1716S 79511890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7960713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7970713efebSLydia Wang spec->hp_independent_mode); 7980713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 7990713efebSLydia Wang spec->hp_independent_mode); 8000713efebSLydia Wang } 801ce0e5a9eSLydia Wang /* update jack power state */ 8023e95b9abSLydia Wang set_widgets_power_state(codec); 8030aa62aefSHarald Welte return 0; 8040aa62aefSHarald Welte } 8050aa62aefSHarald Welte 80690dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 8070aa62aefSHarald Welte { 8080aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8090aa62aefSHarald Welte .name = "Independent HP", 8100aa62aefSHarald Welte .info = via_independent_hp_info, 8110aa62aefSHarald Welte .get = via_independent_hp_get, 8120aa62aefSHarald Welte .put = via_independent_hp_put, 8130aa62aefSHarald Welte }, 8145b0cb1d8SJaroslav Kysela { 8155b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8165b0cb1d8SJaroslav Kysela .name = "Independent HP", 8175b0cb1d8SJaroslav Kysela }, 8180aa62aefSHarald Welte }; 8190aa62aefSHarald Welte 8203d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8215b0cb1d8SJaroslav Kysela { 8223d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8235b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8245b0cb1d8SJaroslav Kysela hda_nid_t nid; 8253d83e577STakashi Iwai int nums; 8263d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8275b0cb1d8SJaroslav Kysela 8285b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8295b0cb1d8SJaroslav Kysela case VT1718S: 8305b0cb1d8SJaroslav Kysela nid = 0x34; 8315b0cb1d8SJaroslav Kysela break; 8325b0cb1d8SJaroslav Kysela case VT2002P: 83311890956SLydia Wang case VT1802: 8345b0cb1d8SJaroslav Kysela nid = 0x35; 8355b0cb1d8SJaroslav Kysela break; 8365b0cb1d8SJaroslav Kysela case VT1812: 8375b0cb1d8SJaroslav Kysela nid = 0x3d; 8385b0cb1d8SJaroslav Kysela break; 8395b0cb1d8SJaroslav Kysela default: 8405b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8415b0cb1d8SJaroslav Kysela break; 8425b0cb1d8SJaroslav Kysela } 8435b0cb1d8SJaroslav Kysela 844ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 845ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 846ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8473d83e577STakashi Iwai if (nums <= 1) 8483d83e577STakashi Iwai return 0; 849ee3c35c0SLydia Wang } 8503d83e577STakashi Iwai 8513d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8523d83e577STakashi Iwai if (knew == NULL) 8533d83e577STakashi Iwai return -ENOMEM; 8543d83e577STakashi Iwai 8555b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8565b0cb1d8SJaroslav Kysela knew->private_value = nid; 8575b0cb1d8SJaroslav Kysela 8585b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8595b0cb1d8SJaroslav Kysela if (knew == NULL) 8605b0cb1d8SJaroslav Kysela return -ENOMEM; 8615b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8625b0cb1d8SJaroslav Kysela 8635b0cb1d8SJaroslav Kysela return 0; 8645b0cb1d8SJaroslav Kysela } 8655b0cb1d8SJaroslav Kysela 8661564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8671564b287SLydia Wang { 8681564b287SLydia Wang int i; 8691564b287SLydia Wang struct snd_ctl_elem_id id; 870525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 871525566cbSLydia Wang struct snd_kcontrol *ctl; 8721564b287SLydia Wang 8731564b287SLydia Wang memset(&id, 0, sizeof(id)); 8741564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8751564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8761564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 877525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 878525566cbSLydia Wang if (ctl) 879525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 880525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 881525566cbSLydia Wang &ctl->id); 8821564b287SLydia Wang } 8831564b287SLydia Wang } 8841564b287SLydia Wang 8851564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8861564b287SLydia Wang { 8871564b287SLydia Wang struct via_spec *spec = codec->spec; 8881564b287SLydia Wang int start_idx; 8891564b287SLydia Wang int end_idx; 8901564b287SLydia Wang int i; 8911564b287SLydia Wang /* get nid of MW0 and start & end index */ 8921564b287SLydia Wang switch (spec->codec_type) { 8931564b287SLydia Wang case VT1708: 8941564b287SLydia Wang start_idx = 2; 8951564b287SLydia Wang end_idx = 4; 8961564b287SLydia Wang break; 8971564b287SLydia Wang case VT1709_10CH: 8981564b287SLydia Wang case VT1709_6CH: 8991564b287SLydia Wang start_idx = 2; 9001564b287SLydia Wang end_idx = 4; 9011564b287SLydia Wang break; 9021564b287SLydia Wang case VT1708B_8CH: 9031564b287SLydia Wang case VT1708B_4CH: 9041564b287SLydia Wang case VT1708S: 905f3db423dSLydia Wang case VT1716S: 9061564b287SLydia Wang start_idx = 2; 9071564b287SLydia Wang end_idx = 4; 9081564b287SLydia Wang break; 909ab657e0cSLydia Wang case VT1718S: 910ab657e0cSLydia Wang start_idx = 1; 911ab657e0cSLydia Wang end_idx = 3; 912ab657e0cSLydia Wang break; 9131564b287SLydia Wang default: 9141564b287SLydia Wang return; 9151564b287SLydia Wang } 9161564b287SLydia Wang /* check AA path's mute status */ 9171564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9181564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 919620e2b28STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, 9201564b287SLydia Wang HDA_AMP_MUTE, val); 9211564b287SLydia Wang } 9221564b287SLydia Wang } 923f4a7828bSTakashi Iwai 924f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 9251564b287SLydia Wang { 926f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9277b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9287b315bb4STakashi Iwai int i; 9297b315bb4STakashi Iwai 9307b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 931f4a7828bSTakashi Iwai unsigned int defcfg; 932f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 933f4a7828bSTakashi Iwai continue; 934f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 935f4a7828bSTakashi Iwai return false; 936f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 937f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 938f4a7828bSTakashi Iwai return false; 939f4a7828bSTakashi Iwai return true; 9401564b287SLydia Wang } 941f4a7828bSTakashi Iwai return false; 9421564b287SLydia Wang } 9431564b287SLydia Wang 9441564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9451564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9461564b287SLydia Wang { 9471564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9481564b287SLydia Wang uinfo->count = 1; 9491564b287SLydia Wang uinfo->value.integer.min = 0; 9501564b287SLydia Wang uinfo->value.integer.max = 1; 9511564b287SLydia Wang return 0; 9521564b287SLydia Wang } 9531564b287SLydia Wang 9541564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9551564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9561564b287SLydia Wang { 9571564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9581564b287SLydia Wang struct via_spec *spec = codec->spec; 9597b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9601564b287SLydia Wang int on = 1; 9611564b287SLydia Wang int i; 9621564b287SLydia Wang 9637b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9647b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 965f4a7828bSTakashi Iwai unsigned int ctl; 96686e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9677b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9681564b287SLydia Wang continue; /* ignore FMic for independent HP */ 969f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 970f4a7828bSTakashi Iwai continue; 971f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 972f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9737b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9741564b287SLydia Wang on = 0; 9751564b287SLydia Wang } 9761564b287SLydia Wang *ucontrol->value.integer.value = on; 9771564b287SLydia Wang return 0; 9781564b287SLydia Wang } 9791564b287SLydia Wang 9801564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9811564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9821564b287SLydia Wang { 9831564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9841564b287SLydia Wang struct via_spec *spec = codec->spec; 9857b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9861564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9871564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9881564b287SLydia Wang int i; 9891564b287SLydia Wang 9907b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9917b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9927b315bb4STakashi Iwai unsigned int parm; 9937b315bb4STakashi Iwai 99486e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9957b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9961564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 997f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 998f4a7828bSTakashi Iwai continue; 9997b315bb4STakashi Iwai 10007b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 10011564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 10021564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 10031564b287SLydia Wang parm |= out_in; 10041564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 10051564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 10061564b287SLydia Wang parm); 10071564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 10081564b287SLydia Wang mute_aa_path(codec, 1); 10091564b287SLydia Wang notify_aa_path_ctls(codec); 10101564b287SLydia Wang } 10111564b287SLydia Wang } 10121564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10133e95b9abSLydia Wang set_widgets_power_state(codec); 10141564b287SLydia Wang return 1; 10151564b287SLydia Wang } 10161564b287SLydia Wang 10175f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10181564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10191564b287SLydia Wang .name = "Smart 5.1", 10201564b287SLydia Wang .count = 1, 10211564b287SLydia Wang .info = via_smart51_info, 10221564b287SLydia Wang .get = via_smart51_get, 10231564b287SLydia Wang .put = via_smart51_put, 10241564b287SLydia Wang }; 10251564b287SLydia Wang 1026f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 10275b0cb1d8SJaroslav Kysela { 1028f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 10295b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10307b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10315b0cb1d8SJaroslav Kysela hda_nid_t nid; 10325b0cb1d8SJaroslav Kysela int i; 10335b0cb1d8SJaroslav Kysela 1034f4a7828bSTakashi Iwai if (!spec->can_smart51) 1035cb34c207SLydia Wang return 0; 1036cb34c207SLydia Wang 10375f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10385b0cb1d8SJaroslav Kysela if (knew == NULL) 10395b0cb1d8SJaroslav Kysela return -ENOMEM; 10405b0cb1d8SJaroslav Kysela 10417b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10427b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 1043f4a7828bSTakashi Iwai if (is_smart51_pins(codec, nid)) { 10445f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10457b315bb4STakashi Iwai break; 10465b0cb1d8SJaroslav Kysela } 10475b0cb1d8SJaroslav Kysela } 10485b0cb1d8SJaroslav Kysela 10495b0cb1d8SJaroslav Kysela return 0; 10505b0cb1d8SJaroslav Kysela } 10515b0cb1d8SJaroslav Kysela 1052c577b8a1SJoseph Chan /* capture mixer elements */ 105390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = { 1054c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1055c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1056c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1057c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1058c577b8a1SJoseph Chan { 1059c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1060c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1061c577b8a1SJoseph Chan * So call somewhat different.. 1062c577b8a1SJoseph Chan */ 1063c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1064c577b8a1SJoseph Chan .name = "Input Source", 1065c577b8a1SJoseph Chan .count = 1, 1066c577b8a1SJoseph Chan .info = via_mux_enum_info, 1067c577b8a1SJoseph Chan .get = via_mux_enum_get, 1068c577b8a1SJoseph Chan .put = via_mux_enum_put, 1069c577b8a1SJoseph Chan }, 1070c577b8a1SJoseph Chan { } /* end */ 1071c577b8a1SJoseph Chan }; 1072f5271101SLydia Wang 1073f5271101SLydia Wang /* check AA path's mute statue */ 1074f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1075f5271101SLydia Wang { 1076f5271101SLydia Wang int mute = 1; 1077f5271101SLydia Wang int start_idx; 1078f5271101SLydia Wang int end_idx; 1079f5271101SLydia Wang int i; 1080f5271101SLydia Wang struct via_spec *spec = codec->spec; 1081f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1082f5271101SLydia Wang switch (spec->codec_type) { 1083f5271101SLydia Wang case VT1708B_8CH: 1084f5271101SLydia Wang case VT1708B_4CH: 1085f5271101SLydia Wang case VT1708S: 1086f3db423dSLydia Wang case VT1716S: 1087f5271101SLydia Wang start_idx = 2; 1088f5271101SLydia Wang end_idx = 4; 1089f5271101SLydia Wang break; 1090f5271101SLydia Wang case VT1702: 1091f5271101SLydia Wang start_idx = 1; 1092f5271101SLydia Wang end_idx = 3; 1093f5271101SLydia Wang break; 1094eb7188caSLydia Wang case VT1718S: 1095eb7188caSLydia Wang start_idx = 1; 1096eb7188caSLydia Wang end_idx = 3; 1097eb7188caSLydia Wang break; 109825eaba2fSLydia Wang case VT2002P: 1099ab6734e7SLydia Wang case VT1812: 110011890956SLydia Wang case VT1802: 110125eaba2fSLydia Wang start_idx = 0; 110225eaba2fSLydia Wang end_idx = 2; 110325eaba2fSLydia Wang break; 1104f5271101SLydia Wang default: 1105f5271101SLydia Wang return 0; 1106f5271101SLydia Wang } 1107f5271101SLydia Wang /* check AA path's mute status */ 1108f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1109f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1110620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1111f5271101SLydia Wang int shift = 8 * (i % 4); 1112f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1113f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1114f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1115f5271101SLydia Wang /* check mute status while the pin is connected */ 1116620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 1117f5271101SLydia Wang HDA_INPUT, i) >> 7; 1118620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 1119f5271101SLydia Wang HDA_INPUT, i) >> 7; 1120f5271101SLydia Wang if (!mute_l || !mute_r) { 1121f5271101SLydia Wang mute = 0; 1122f5271101SLydia Wang break; 1123f5271101SLydia Wang } 1124f5271101SLydia Wang } 1125f5271101SLydia Wang } 1126f5271101SLydia Wang return mute; 1127f5271101SLydia Wang } 1128f5271101SLydia Wang 1129f5271101SLydia Wang /* enter/exit analog low-current mode */ 1130f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1131f5271101SLydia Wang { 1132f5271101SLydia Wang struct via_spec *spec = codec->spec; 1133f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1134f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1135f5271101SLydia Wang unsigned int verb = 0; 1136f5271101SLydia Wang unsigned int parm = 0; 1137f5271101SLydia Wang 1138f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1139f5271101SLydia Wang enable = enable && saved_stream_idle; 1140f5271101SLydia Wang else { 1141f5271101SLydia Wang enable = enable && stream_idle; 1142f5271101SLydia Wang saved_stream_idle = stream_idle; 1143f5271101SLydia Wang } 1144f5271101SLydia Wang 1145f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1146f5271101SLydia Wang switch (spec->codec_type) { 1147f5271101SLydia Wang case VT1708B_8CH: 1148f5271101SLydia Wang case VT1708B_4CH: 1149f5271101SLydia Wang verb = 0xf70; 1150f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1151f5271101SLydia Wang break; 1152f5271101SLydia Wang case VT1708S: 1153eb7188caSLydia Wang case VT1718S: 1154f3db423dSLydia Wang case VT1716S: 1155f5271101SLydia Wang verb = 0xf73; 1156f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1157f5271101SLydia Wang break; 1158f5271101SLydia Wang case VT1702: 1159f5271101SLydia Wang verb = 0xf73; 1160f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1161f5271101SLydia Wang break; 116225eaba2fSLydia Wang case VT2002P: 1163ab6734e7SLydia Wang case VT1812: 116411890956SLydia Wang case VT1802: 116525eaba2fSLydia Wang verb = 0xf93; 116625eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 116725eaba2fSLydia Wang break; 1168f5271101SLydia Wang default: 1169f5271101SLydia Wang return; /* other codecs are not supported */ 1170f5271101SLydia Wang } 1171f5271101SLydia Wang /* send verb */ 1172f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1173f5271101SLydia Wang } 1174f5271101SLydia Wang 1175c577b8a1SJoseph Chan /* 1176c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1177c577b8a1SJoseph Chan */ 117890dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1179c577b8a1SJoseph Chan /* 1180c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1181c577b8a1SJoseph Chan */ 1182c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1183c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1184c577b8a1SJoseph Chan 1185c577b8a1SJoseph Chan 1186f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1187c577b8a1SJoseph Chan * mixer widget 1188c577b8a1SJoseph Chan */ 1189c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1190f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1191f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1192f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1193f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1194f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1195c577b8a1SJoseph Chan 1196c577b8a1SJoseph Chan /* 1197c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1198c577b8a1SJoseph Chan */ 1199c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1200c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1201c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1202c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1203c577b8a1SJoseph Chan 1204bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1205bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1206c577b8a1SJoseph Chan /* PW9 Output enable */ 1207c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1208aa266fccSLydia Wang /* power down jack detect function */ 1209aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1210f7278fd0SJosepch Chan { } 1211c577b8a1SJoseph Chan }; 1212c577b8a1SJoseph Chan 1213c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1214c577b8a1SJoseph Chan struct hda_codec *codec, 1215c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1216c577b8a1SJoseph Chan { 1217c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 121817314379SLydia Wang int idle = substream->pstr->substream_opened == 1 121917314379SLydia Wang && substream->ref_count == 0; 122017314379SLydia Wang analog_low_current_mode(codec, idle); 12219a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12229a08160bSTakashi Iwai hinfo); 1223c577b8a1SJoseph Chan } 1224c577b8a1SJoseph Chan 1225*9af74210STakashi Iwai static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, 1226*9af74210STakashi Iwai struct hda_codec *codec, 1227*9af74210STakashi Iwai struct snd_pcm_substream *substream) 1228*9af74210STakashi Iwai { 1229*9af74210STakashi Iwai int idle = substream->pstr->substream_opened == 1 1230*9af74210STakashi Iwai && substream->ref_count == 0; 1231*9af74210STakashi Iwai 1232*9af74210STakashi Iwai analog_low_current_mode(codec, idle); 1233*9af74210STakashi Iwai return 0; 1234*9af74210STakashi Iwai } 1235*9af74210STakashi Iwai 12360aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 12370aa62aefSHarald Welte unsigned int stream_tag, 12380aa62aefSHarald Welte unsigned int format, 12390aa62aefSHarald Welte struct snd_pcm_substream *substream) 12400aa62aefSHarald Welte { 12410aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12420aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1243dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12440aa62aefSHarald Welte int chs = substream->runtime->channels; 12450aa62aefSHarald Welte int i; 12467c935976SStephen Warren struct hda_spdif_out *spdif = 12477c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12480aa62aefSHarald Welte 12490aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12500aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12510aa62aefSHarald Welte if (chs == 2 && 12520aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12530aa62aefSHarald Welte format) && 12547c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12550aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12560aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12570aa62aefSHarald Welte * be updated */ 12587c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12590aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12600aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12617c935976SStephen Warren spdif->ctls & 12620aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12630aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12640aa62aefSHarald Welte stream_tag, 0, format); 12650aa62aefSHarald Welte /* turn on again (if needed) */ 12667c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12670aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12680aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12697c935976SStephen Warren spdif->ctls & 0xff); 12700aa62aefSHarald Welte } else { 12710aa62aefSHarald Welte mout->dig_out_used = 0; 12720aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12730aa62aefSHarald Welte 0, 0, 0); 12740aa62aefSHarald Welte } 12750aa62aefSHarald Welte } 12760aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12770aa62aefSHarald Welte 12780aa62aefSHarald Welte /* front */ 12790aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12800aa62aefSHarald Welte 0, format); 12810aa62aefSHarald Welte 1282eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1283eb7188caSLydia Wang && !spec->hp_independent_mode) 12840aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12850aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12860aa62aefSHarald Welte 0, format); 12870aa62aefSHarald Welte 12880aa62aefSHarald Welte /* extra outputs copied from front */ 12890aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12900aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12910aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12920aa62aefSHarald Welte mout->extra_out_nid[i], 12930aa62aefSHarald Welte stream_tag, 0, format); 12940aa62aefSHarald Welte 12950aa62aefSHarald Welte /* surrounds */ 12960aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12970aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12980aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12990aa62aefSHarald Welte i * 2, format); 13000aa62aefSHarald Welte else /* copy front */ 13010aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13020aa62aefSHarald Welte 0, format); 13030aa62aefSHarald Welte } 13040aa62aefSHarald Welte } 13050aa62aefSHarald Welte 13060aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 13070aa62aefSHarald Welte struct hda_codec *codec, 13080aa62aefSHarald Welte unsigned int stream_tag, 13090aa62aefSHarald Welte unsigned int format, 13100aa62aefSHarald Welte struct snd_pcm_substream *substream) 13110aa62aefSHarald Welte { 13120aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13130aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1314dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13150aa62aefSHarald Welte 13160aa62aefSHarald Welte if (substream->number == 0) 13170aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 13180aa62aefSHarald Welte substream); 13190aa62aefSHarald Welte else { 13200aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13210aa62aefSHarald Welte spec->hp_independent_mode) 13220aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13230aa62aefSHarald Welte stream_tag, 0, format); 13240aa62aefSHarald Welte } 13251f2e99feSLydia Wang vt1708_start_hp_work(spec); 13260aa62aefSHarald Welte return 0; 13270aa62aefSHarald Welte } 13280aa62aefSHarald Welte 13290aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13300aa62aefSHarald Welte struct hda_codec *codec, 13310aa62aefSHarald Welte struct snd_pcm_substream *substream) 13320aa62aefSHarald Welte { 13330aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13340aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1335dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13360aa62aefSHarald Welte int i; 13370aa62aefSHarald Welte 13380aa62aefSHarald Welte if (substream->number == 0) { 13390aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13400aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13410aa62aefSHarald Welte 13420aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13430aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13440aa62aefSHarald Welte 0, 0, 0); 13450aa62aefSHarald Welte 13460aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13470aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13480aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13490aa62aefSHarald Welte mout->extra_out_nid[i], 13500aa62aefSHarald Welte 0, 0, 0); 13510aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13520aa62aefSHarald Welte if (mout->dig_out_nid && 13530aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13540aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13550aa62aefSHarald Welte 0, 0, 0); 13560aa62aefSHarald Welte mout->dig_out_used = 0; 13570aa62aefSHarald Welte } 13580aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13590aa62aefSHarald Welte } else { 13600aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13610aa62aefSHarald Welte spec->hp_independent_mode) 13620aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13630aa62aefSHarald Welte 0, 0, 0); 13640aa62aefSHarald Welte } 13651f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13660aa62aefSHarald Welte return 0; 13670aa62aefSHarald Welte } 13680aa62aefSHarald Welte 1369c577b8a1SJoseph Chan /* 1370c577b8a1SJoseph Chan * Digital out 1371c577b8a1SJoseph Chan */ 1372c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1373c577b8a1SJoseph Chan struct hda_codec *codec, 1374c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1375c577b8a1SJoseph Chan { 1376c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1377c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1378c577b8a1SJoseph Chan } 1379c577b8a1SJoseph Chan 1380c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1381c577b8a1SJoseph Chan struct hda_codec *codec, 1382c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1383c577b8a1SJoseph Chan { 1384c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1385c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1386c577b8a1SJoseph Chan } 1387c577b8a1SJoseph Chan 13885691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 138998aa34c0SHarald Welte struct hda_codec *codec, 139098aa34c0SHarald Welte unsigned int stream_tag, 139198aa34c0SHarald Welte unsigned int format, 139298aa34c0SHarald Welte struct snd_pcm_substream *substream) 139398aa34c0SHarald Welte { 139498aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13959da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13969da29271STakashi Iwai stream_tag, format, substream); 13979da29271STakashi Iwai } 13985691ec7fSHarald Welte 13999da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 14009da29271STakashi Iwai struct hda_codec *codec, 14019da29271STakashi Iwai struct snd_pcm_substream *substream) 14029da29271STakashi Iwai { 14039da29271STakashi Iwai struct via_spec *spec = codec->spec; 14049da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 140598aa34c0SHarald Welte return 0; 140698aa34c0SHarald Welte } 140798aa34c0SHarald Welte 1408c577b8a1SJoseph Chan /* 1409c577b8a1SJoseph Chan * Analog capture 1410c577b8a1SJoseph Chan */ 1411c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1412c577b8a1SJoseph Chan struct hda_codec *codec, 1413c577b8a1SJoseph Chan unsigned int stream_tag, 1414c577b8a1SJoseph Chan unsigned int format, 1415c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1416c577b8a1SJoseph Chan { 1417c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1418c577b8a1SJoseph Chan 1419c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1420c577b8a1SJoseph Chan stream_tag, 0, format); 1421c577b8a1SJoseph Chan return 0; 1422c577b8a1SJoseph Chan } 1423c577b8a1SJoseph Chan 1424c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1425c577b8a1SJoseph Chan struct hda_codec *codec, 1426c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1427c577b8a1SJoseph Chan { 1428c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1429888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1430c577b8a1SJoseph Chan return 0; 1431c577b8a1SJoseph Chan } 1432c577b8a1SJoseph Chan 1433*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 1434*9af74210STakashi Iwai .substreams = 2, /* will be changed in via_build_pcms() */ 1435c577b8a1SJoseph Chan .channels_min = 2, 1436c577b8a1SJoseph Chan .channels_max = 8, 1437*9af74210STakashi Iwai /* NID is set in via_build_pcms */ 1438c577b8a1SJoseph Chan .ops = { 1439c577b8a1SJoseph Chan .open = via_playback_pcm_open, 1440*9af74210STakashi Iwai .close = via_playback_pcm_close, 14410aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14420aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1443c577b8a1SJoseph Chan }, 1444c577b8a1SJoseph Chan }; 1445c577b8a1SJoseph Chan 144690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1447*9af74210STakashi Iwai .substreams = 2, /* will be changed in via_build_pcms() */ 1448bc9b5623STakashi Iwai .channels_min = 2, 1449bc9b5623STakashi Iwai .channels_max = 8, 1450*9af74210STakashi Iwai /* NID is set in via_build_pcms */ 1451bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1452bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1453bc9b5623STakashi Iwai * disable the 24bit format, so far. 1454bc9b5623STakashi Iwai */ 1455bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1456bc9b5623STakashi Iwai .ops = { 1457bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1458*9af74210STakashi Iwai .close = via_playback_pcm_close, 1459c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1460c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1461bc9b5623STakashi Iwai }, 1462bc9b5623STakashi Iwai }; 1463bc9b5623STakashi Iwai 1464*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 1465*9af74210STakashi Iwai .substreams = 2, /* will be changed in via_build_pcms() */ 1466c577b8a1SJoseph Chan .channels_min = 2, 1467c577b8a1SJoseph Chan .channels_max = 2, 1468*9af74210STakashi Iwai /* NID is set in via_build_pcms */ 1469c577b8a1SJoseph Chan .ops = { 1470*9af74210STakashi Iwai .open = via_playback_pcm_open, 1471*9af74210STakashi Iwai .close = via_playback_pcm_close, 1472c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1473c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1474c577b8a1SJoseph Chan }, 1475c577b8a1SJoseph Chan }; 1476c577b8a1SJoseph Chan 1477*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1478c577b8a1SJoseph Chan .substreams = 1, 1479c577b8a1SJoseph Chan .channels_min = 2, 1480c577b8a1SJoseph Chan .channels_max = 2, 1481c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1482c577b8a1SJoseph Chan .ops = { 1483c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14846b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14859da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14869da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1487c577b8a1SJoseph Chan }, 1488c577b8a1SJoseph Chan }; 1489c577b8a1SJoseph Chan 1490*9af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1491c577b8a1SJoseph Chan .substreams = 1, 1492c577b8a1SJoseph Chan .channels_min = 2, 1493c577b8a1SJoseph Chan .channels_max = 2, 1494c577b8a1SJoseph Chan }; 1495c577b8a1SJoseph Chan 1496c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1497c577b8a1SJoseph Chan { 1498c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 14995b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 150090dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 15015b0cb1d8SJaroslav Kysela int err, i; 1502c577b8a1SJoseph Chan 150324088a58STakashi Iwai if (spec->set_widgets_power_state) 150424088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 150524088a58STakashi Iwai return -ENOMEM; 150624088a58STakashi Iwai 1507c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1508c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1509c577b8a1SJoseph Chan if (err < 0) 1510c577b8a1SJoseph Chan return err; 1511c577b8a1SJoseph Chan } 1512c577b8a1SJoseph Chan 1513c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1514c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 151574b654c9SStephen Warren spec->multiout.dig_out_nid, 1516c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1517c577b8a1SJoseph Chan if (err < 0) 1518c577b8a1SJoseph Chan return err; 15199a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15209a08160bSTakashi Iwai &spec->multiout); 15219a08160bSTakashi Iwai if (err < 0) 15229a08160bSTakashi Iwai return err; 15239a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1524c577b8a1SJoseph Chan } 1525c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1526c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1527c577b8a1SJoseph Chan if (err < 0) 1528c577b8a1SJoseph Chan return err; 1529c577b8a1SJoseph Chan } 153017314379SLydia Wang 15315b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15325b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15335b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 153421949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15355b0cb1d8SJaroslav Kysela if (err < 0) 15365b0cb1d8SJaroslav Kysela return err; 15375b0cb1d8SJaroslav Kysela } 15385b0cb1d8SJaroslav Kysela 15395b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15405b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15415b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15425b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15435b0cb1d8SJaroslav Kysela continue; 15445b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15455b0cb1d8SJaroslav Kysela if (kctl == NULL) 15465b0cb1d8SJaroslav Kysela continue; 15475b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15485b0cb1d8SJaroslav Kysela knew->subdevice); 15495b0cb1d8SJaroslav Kysela } 15505b0cb1d8SJaroslav Kysela } 15515b0cb1d8SJaroslav Kysela 155217314379SLydia Wang /* init power states */ 15533e95b9abSLydia Wang set_widgets_power_state(codec); 155417314379SLydia Wang analog_low_current_mode(codec, 1); 155517314379SLydia Wang 1556603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1557c577b8a1SJoseph Chan return 0; 1558c577b8a1SJoseph Chan } 1559c577b8a1SJoseph Chan 1560c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1561c577b8a1SJoseph Chan { 1562c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1563c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1564c577b8a1SJoseph Chan 1565c577b8a1SJoseph Chan codec->num_pcms = 1; 1566c577b8a1SJoseph Chan codec->pcm_info = info; 1567c577b8a1SJoseph Chan 156882673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 156982673bc8STakashi Iwai "%s Analog", codec->chip_name); 1570c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1571*9af74210STakashi Iwai 1572*9af74210STakashi Iwai if (!spec->stream_analog_playback) 1573*9af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1574377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1575*9af74210STakashi Iwai *spec->stream_analog_playback; 1576377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1577377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1578c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1579c577b8a1SJoseph Chan spec->multiout.max_channels; 1580*9af74210STakashi Iwai if (!spec->multiout.hp_nid) 1581*9af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams = 1; 1582*9af74210STakashi Iwai 1583*9af74210STakashi Iwai if (!spec->stream_analog_capture) 1584*9af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 1585*9af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1586*9af74210STakashi Iwai *spec->stream_analog_capture; 1587*9af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1588*9af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1589*9af74210STakashi Iwai spec->num_adc_nids; 1590c577b8a1SJoseph Chan 1591c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1592c577b8a1SJoseph Chan codec->num_pcms++; 1593c577b8a1SJoseph Chan info++; 159482673bc8STakashi Iwai snprintf(spec->stream_name_digital, 159582673bc8STakashi Iwai sizeof(spec->stream_name_digital), 159682673bc8STakashi Iwai "%s Digital", codec->chip_name); 1597c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 15987ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1599c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1600*9af74210STakashi Iwai if (!spec->stream_digital_playback) 1601*9af74210STakashi Iwai spec->stream_digital_playback = 1602*9af74210STakashi Iwai &via_pcm_digital_playback; 1603c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1604*9af74210STakashi Iwai *spec->stream_digital_playback; 1605c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1606c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1607c577b8a1SJoseph Chan } 1608c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1609*9af74210STakashi Iwai if (!spec->stream_digital_capture) 1610*9af74210STakashi Iwai spec->stream_digital_capture = 1611*9af74210STakashi Iwai &via_pcm_digital_capture; 1612c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1613*9af74210STakashi Iwai *spec->stream_digital_capture; 1614c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1615c577b8a1SJoseph Chan spec->dig_in_nid; 1616c577b8a1SJoseph Chan } 1617c577b8a1SJoseph Chan } 1618c577b8a1SJoseph Chan 1619c577b8a1SJoseph Chan return 0; 1620c577b8a1SJoseph Chan } 1621c577b8a1SJoseph Chan 1622c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1623c577b8a1SJoseph Chan { 1624c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1625c577b8a1SJoseph Chan 1626c577b8a1SJoseph Chan if (!spec) 1627c577b8a1SJoseph Chan return; 1628c577b8a1SJoseph Chan 1629603c4019STakashi Iwai via_free_kctls(codec); 16301f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1631c577b8a1SJoseph Chan kfree(codec->spec); 1632c577b8a1SJoseph Chan } 1633c577b8a1SJoseph Chan 163464be285bSTakashi Iwai /* mute/unmute outputs */ 163564be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 163664be285bSTakashi Iwai hda_nid_t *pins, bool mute) 163764be285bSTakashi Iwai { 163864be285bSTakashi Iwai int i; 163964be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 164064be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 164164be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 164264be285bSTakashi Iwai mute ? 0 : PIN_OUT); 164364be285bSTakashi Iwai } 164464be285bSTakashi Iwai 164569e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 164669e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 164769e52a80SHarald Welte { 1648dcf34c8cSLydia Wang unsigned int present = 0; 164969e52a80SHarald Welte struct via_spec *spec = codec->spec; 165069e52a80SHarald Welte 1651d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1652dcf34c8cSLydia Wang 165364be285bSTakashi Iwai if (!spec->hp_independent_mode) 165464be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 165564be285bSTakashi Iwai spec->autocfg.line_out_pins, 165664be285bSTakashi Iwai present); 165769e52a80SHarald Welte } 165869e52a80SHarald Welte 1659f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1660f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1661f3db423dSLydia Wang { 1662f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1663f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1664f3db423dSLydia Wang 1665f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1666f3db423dSLydia Wang return; 1667f3db423dSLydia Wang 1668d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1669d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1670f3db423dSLydia Wang 1671f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1672f3db423dSLydia Wang if (lineout_present) { 16733e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 16743e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16753e0693e2STakashi Iwai lineout_present ? 0 : PIN_OUT); 1676f3db423dSLydia Wang return; 1677f3db423dSLydia Wang } 1678f3db423dSLydia Wang 1679d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1680f3db423dSLydia Wang 1681f3db423dSLydia Wang if (!spec->hp_independent_mode) 16823e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 16833e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16843e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 1685f3db423dSLydia Wang } 1686f3db423dSLydia Wang 168769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 168869e52a80SHarald Welte { 168969e52a80SHarald Welte unsigned int gpio_data; 169069e52a80SHarald Welte unsigned int vol_counter; 169169e52a80SHarald Welte unsigned int vol; 169269e52a80SHarald Welte unsigned int master_vol; 169369e52a80SHarald Welte 169469e52a80SHarald Welte struct via_spec *spec = codec->spec; 169569e52a80SHarald Welte 169669e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 169769e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 169869e52a80SHarald Welte 169969e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 170069e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 170169e52a80SHarald Welte 170269e52a80SHarald Welte vol = vol_counter & 0x1F; 170369e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 170469e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 170569e52a80SHarald Welte AC_AMP_GET_INPUT); 170669e52a80SHarald Welte 170769e52a80SHarald Welte if (gpio_data == 0x02) { 170869e52a80SHarald Welte /* unmute line out */ 17093e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17103e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17113e0693e2STakashi Iwai PIN_OUT); 171269e52a80SHarald Welte if (vol_counter & 0x20) { 171369e52a80SHarald Welte /* decrease volume */ 171469e52a80SHarald Welte if (vol > master_vol) 171569e52a80SHarald Welte vol = master_vol; 171669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 171769e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 171869e52a80SHarald Welte master_vol-vol); 171969e52a80SHarald Welte } else { 172069e52a80SHarald Welte /* increase volume */ 172169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 172269e52a80SHarald Welte HDA_AMP_VOLMASK, 172369e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 172469e52a80SHarald Welte (master_vol+vol)); 172569e52a80SHarald Welte } 172669e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 172769e52a80SHarald Welte /* mute line out */ 17283e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17293e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17303e0693e2STakashi Iwai 0); 173169e52a80SHarald Welte } 173269e52a80SHarald Welte } 173369e52a80SHarald Welte 173425eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 173525eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 173625eaba2fSLydia Wang { 173725eaba2fSLydia Wang unsigned int hp_present; 173825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 173925eaba2fSLydia Wang 174027439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 174125eaba2fSLydia Wang return; 174225eaba2fSLydia Wang 1743d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 174425eaba2fSLydia Wang 174564be285bSTakashi Iwai if (!spec->hp_independent_mode) 174664be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 174764be285bSTakashi Iwai spec->autocfg.speaker_pins, 174864be285bSTakashi Iwai hp_present); 174925eaba2fSLydia Wang } 175025eaba2fSLydia Wang 175125eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 175225eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 175325eaba2fSLydia Wang { 175464be285bSTakashi Iwai int present; 175525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 175625eaba2fSLydia Wang 175725eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 175825eaba2fSLydia Wang return; 175925eaba2fSLydia Wang 176064be285bSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 176164be285bSTakashi Iwai if (!spec->hp_independent_mode) 176264be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 176364be285bSTakashi Iwai spec->autocfg.line_out_pins, 176464be285bSTakashi Iwai present); 176525eaba2fSLydia Wang 176664be285bSTakashi Iwai if (!present) 176764be285bSTakashi Iwai present = snd_hda_jack_detect(codec, 176864be285bSTakashi Iwai spec->autocfg.line_out_pins[0]); 176925eaba2fSLydia Wang 177025eaba2fSLydia Wang /* Speakers */ 177164be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 177264be285bSTakashi Iwai spec->autocfg.speaker_pins, 177364be285bSTakashi Iwai present); 177425eaba2fSLydia Wang } 177525eaba2fSLydia Wang 177625eaba2fSLydia Wang 177769e52a80SHarald Welte /* unsolicited event for jack sensing */ 177869e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 177969e52a80SHarald Welte unsigned int res) 178069e52a80SHarald Welte { 178169e52a80SHarald Welte res >>= 26; 1782ec7e7e42SLydia Wang 1783a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 17843e95b9abSLydia Wang set_widgets_power_state(codec); 1785ec7e7e42SLydia Wang 1786ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1787ec7e7e42SLydia Wang 1788ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1789ec7e7e42SLydia Wang via_hp_automute(codec); 1790ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1791ec7e7e42SLydia Wang via_gpio_control(codec); 1792ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1793f3db423dSLydia Wang via_mono_automute(codec); 1794ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 179525eaba2fSLydia Wang via_speaker_automute(codec); 1796ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 179725eaba2fSLydia Wang via_hp_bind_automute(codec); 179869e52a80SHarald Welte } 179969e52a80SHarald Welte 1800c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1801c577b8a1SJoseph Chan { 1802c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 180369e52a80SHarald Welte int i; 180469e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 180569e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 180669e52a80SHarald Welte 1807f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1808f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 180955d1d6c1STakashi Iwai if (spec->dig_in_pin) { 181055d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1811f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 181212b74c80STakashi Iwai PIN_OUT); 181355d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1814f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1815f7278fd0SJosepch Chan } 181612b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 181712b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 181812b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1819f7278fd0SJosepch Chan 18209da29271STakashi Iwai /* assign slave outs */ 18219da29271STakashi Iwai if (spec->slave_dig_outs[0]) 18229da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 18235691ec7fSHarald Welte 1824c577b8a1SJoseph Chan return 0; 1825c577b8a1SJoseph Chan } 1826c577b8a1SJoseph Chan 18271f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18281f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18291f2e99feSLydia Wang { 18301f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18311f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18321f2e99feSLydia Wang return 0; 18331f2e99feSLydia Wang } 18341f2e99feSLydia Wang #endif 18351f2e99feSLydia Wang 1836cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1837cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1838cb53c626STakashi Iwai { 1839cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1840cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1841cb53c626STakashi Iwai } 1842cb53c626STakashi Iwai #endif 1843cb53c626STakashi Iwai 1844c577b8a1SJoseph Chan /* 1845c577b8a1SJoseph Chan */ 184690dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1847c577b8a1SJoseph Chan .build_controls = via_build_controls, 1848c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1849c577b8a1SJoseph Chan .init = via_init, 1850c577b8a1SJoseph Chan .free = via_free, 18511f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18521f2e99feSLydia Wang .suspend = via_suspend, 18531f2e99feSLydia Wang #endif 1854cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1855cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1856cb53c626STakashi Iwai #endif 1857c577b8a1SJoseph Chan }; 1858c577b8a1SJoseph Chan 18594a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1860c577b8a1SJoseph Chan { 18614a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 18624a79616dSTakashi Iwai int i; 18634a79616dSTakashi Iwai 18644a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 18654a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 18664a79616dSTakashi Iwai return false; 18674a79616dSTakashi Iwai } 18684a79616dSTakashi Iwai if (spec->multiout.hp_nid == dac) 18694a79616dSTakashi Iwai return false; 18704a79616dSTakashi Iwai return true; 18714a79616dSTakashi Iwai } 18724a79616dSTakashi Iwai 18734a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 18744a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 18754a79616dSTakashi Iwai int depth, int wid_type) 18764a79616dSTakashi Iwai { 18774a79616dSTakashi Iwai hda_nid_t conn[8]; 18784a79616dSTakashi Iwai int i, nums; 18794a79616dSTakashi Iwai 18804a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 18814a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 18824a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 18834a79616dSTakashi Iwai continue; 18844a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 18854a79616dSTakashi Iwai path->path[depth] = conn[i]; 18864a79616dSTakashi Iwai path->idx[depth] = i; 18874a79616dSTakashi Iwai path->depth = ++depth; 18884a79616dSTakashi Iwai return true; 18894a79616dSTakashi Iwai } 18904a79616dSTakashi Iwai } 18914a79616dSTakashi Iwai if (depth > 4) 18924a79616dSTakashi Iwai return false; 18934a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 18944a79616dSTakashi Iwai unsigned int type; 18954a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 18964a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 18974a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 18984a79616dSTakashi Iwai continue; 18994a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 19004a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 19014a79616dSTakashi Iwai path->path[depth] = conn[i]; 19024a79616dSTakashi Iwai path->idx[depth] = i; 19034a79616dSTakashi Iwai return true; 19044a79616dSTakashi Iwai } 19054a79616dSTakashi Iwai } 19064a79616dSTakashi Iwai return false; 19074a79616dSTakashi Iwai } 19084a79616dSTakashi Iwai 19094a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 19104a79616dSTakashi Iwai { 19114a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 19124a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1913c577b8a1SJoseph Chan int i; 1914c577b8a1SJoseph Chan hda_nid_t nid; 1915c577b8a1SJoseph Chan 1916c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 19174a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 19184a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1919c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 19204a79616dSTakashi Iwai if (!nid) 19214a79616dSTakashi Iwai continue; 19224a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 19234a79616dSTakashi Iwai spec->private_dac_nids[i] = 19244a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1925c577b8a1SJoseph Chan } 1926c577b8a1SJoseph Chan return 0; 1927c577b8a1SJoseph Chan } 1928c577b8a1SJoseph Chan 19294a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 19304a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1931c577b8a1SJoseph Chan { 19324a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1933c577b8a1SJoseph Chan char name[32]; 19344a79616dSTakashi Iwai hda_nid_t nid; 19354a79616dSTakashi Iwai int err; 1936c577b8a1SJoseph Chan 19374a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19384a79616dSTakashi Iwai nid = dac; 19394a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19404a79616dSTakashi Iwai nid = pin; 19414a79616dSTakashi Iwai else 19424a79616dSTakashi Iwai nid = 0; 19434a79616dSTakashi Iwai if (nid) { 19444a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1945c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 19464a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1947c577b8a1SJoseph Chan if (err < 0) 1948c577b8a1SJoseph Chan return err; 1949c577b8a1SJoseph Chan } 19504a79616dSTakashi Iwai 19514a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19524a79616dSTakashi Iwai nid = dac; 19534a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19544a79616dSTakashi Iwai nid = pin; 19554a79616dSTakashi Iwai else 19564a79616dSTakashi Iwai nid = 0; 19574a79616dSTakashi Iwai if (nid) { 19584a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 19594a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 19604a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 19614a79616dSTakashi Iwai if (err < 0) 19624a79616dSTakashi Iwai return err; 19634a79616dSTakashi Iwai } 19644a79616dSTakashi Iwai return 0; 19654a79616dSTakashi Iwai } 19664a79616dSTakashi Iwai 19674a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 19684a79616dSTakashi Iwai hda_nid_t nid); 19694a79616dSTakashi Iwai 1970f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1971f4a7828bSTakashi Iwai { 1972f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1973f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1974f4a7828bSTakashi Iwai int i; 1975f4a7828bSTakashi Iwai 1976f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1977f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, cfg->inputs[i].pin)) 1978f4a7828bSTakashi Iwai continue; 1979f4a7828bSTakashi Iwai spec->can_smart51 = 1; 1980f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 1981f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1982f4a7828bSTakashi Iwai break; 1983f4a7828bSTakashi Iwai } 1984f4a7828bSTakashi Iwai } 1985f4a7828bSTakashi Iwai 19864a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 19874a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 19884a79616dSTakashi Iwai { 19894a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1990f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 19914a79616dSTakashi Iwai static const char * const chname[4] = { 19924a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 19934a79616dSTakashi Iwai }; 19944a79616dSTakashi Iwai int i, idx, err; 1995f4a7828bSTakashi Iwai int old_line_outs; 1996f4a7828bSTakashi Iwai 1997f4a7828bSTakashi Iwai /* check smart51 */ 1998f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 1999f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 2000f4a7828bSTakashi Iwai mangle_smart51(codec); 20014a79616dSTakashi Iwai 20024a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 20034a79616dSTakashi Iwai hda_nid_t pin, dac; 20044a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 20054a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 20064a79616dSTakashi Iwai if (!pin || !dac) 20074a79616dSTakashi Iwai continue; 20084a79616dSTakashi Iwai if (i == AUTO_SEQ_CENLFE) { 20094a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 20104a79616dSTakashi Iwai if (err < 0) 20114a79616dSTakashi Iwai return err; 20124a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 20134a79616dSTakashi Iwai if (err < 0) 20144a79616dSTakashi Iwai return err; 20154a79616dSTakashi Iwai } else { 20164a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 20174a79616dSTakashi Iwai if (err < 0) 20184a79616dSTakashi Iwai return err; 20194a79616dSTakashi Iwai } 20204a79616dSTakashi Iwai } 20214a79616dSTakashi Iwai 20224a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 20234a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 20244a79616dSTakashi Iwai if (idx >= 0) { 20254a79616dSTakashi Iwai /* add control to mixer */ 20264a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 20274a79616dSTakashi Iwai "PCM Playback Volume", 20284a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20294a79616dSTakashi Iwai idx, HDA_INPUT)); 20304a79616dSTakashi Iwai if (err < 0) 20314a79616dSTakashi Iwai return err; 20324a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 20334a79616dSTakashi Iwai "PCM Playback Switch", 20344a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20354a79616dSTakashi Iwai idx, HDA_INPUT)); 20364a79616dSTakashi Iwai if (err < 0) 20374a79616dSTakashi Iwai return err; 2038c577b8a1SJoseph Chan } 2039c577b8a1SJoseph Chan 2040f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 2041f4a7828bSTakashi Iwai 2042c577b8a1SJoseph Chan return 0; 2043c577b8a1SJoseph Chan } 2044c577b8a1SJoseph Chan 20450aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 20460aa62aefSHarald Welte { 20470aa62aefSHarald Welte int i; 20480aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2049ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 20500aa62aefSHarald Welte 20510aa62aefSHarald Welte /* for hp mode select */ 205210a20af7STakashi Iwai for (i = 0; texts[i]; i++) 205310a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 20540aa62aefSHarald Welte 20550aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 20560aa62aefSHarald Welte } 20570aa62aefSHarald Welte 20584a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 2059c577b8a1SJoseph Chan { 20604a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 20614a79616dSTakashi Iwai hda_nid_t dac = 0; 2062c577b8a1SJoseph Chan int err; 2063c577b8a1SJoseph Chan 2064c577b8a1SJoseph Chan if (!pin) 2065c577b8a1SJoseph Chan return 0; 2066c577b8a1SJoseph Chan 20674a79616dSTakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 20684a79616dSTakashi Iwai &spec->hp_dep_path, 0, -1)) 20694a79616dSTakashi Iwai return 0; 20704a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 20714a79616dSTakashi Iwai dac = spec->hp_path.path[spec->hp_path.depth - 1]; 20724a79616dSTakashi Iwai spec->multiout.hp_nid = dac; 20734a79616dSTakashi Iwai spec->hp_independent_mode_index = 20744a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 20750aa62aefSHarald Welte create_hp_imux(spec); 20764a79616dSTakashi Iwai } 20774a79616dSTakashi Iwai 20784a79616dSTakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, dac, 3); 20794a79616dSTakashi Iwai if (err < 0) 20804a79616dSTakashi Iwai return err; 20810aa62aefSHarald Welte 2082c577b8a1SJoseph Chan return 0; 2083c577b8a1SJoseph Chan } 2084c577b8a1SJoseph Chan 2085a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 2086a766d0d7STakashi Iwai hda_nid_t nid) 2087a766d0d7STakashi Iwai { 2088a766d0d7STakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 2089a766d0d7STakashi Iwai int i, nums; 2090a766d0d7STakashi Iwai 2091a766d0d7STakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 2092a766d0d7STakashi Iwai for (i = 0; i < nums; i++) 2093a766d0d7STakashi Iwai if (conn[i] == nid) 2094a766d0d7STakashi Iwai return i; 2095a766d0d7STakashi Iwai return -1; 2096a766d0d7STakashi Iwai } 2097a766d0d7STakashi Iwai 2098a766d0d7STakashi Iwai /* look for ADCs */ 2099a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2100a766d0d7STakashi Iwai { 2101a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2102a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2103a766d0d7STakashi Iwai int i; 2104a766d0d7STakashi Iwai 2105a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2106a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2107a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2108a766d0d7STakashi Iwai continue; 2109a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2110a766d0d7STakashi Iwai continue; 2111a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2112a766d0d7STakashi Iwai continue; 2113a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2114a766d0d7STakashi Iwai return -ENOMEM; 2115a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2116a766d0d7STakashi Iwai } 2117a766d0d7STakashi Iwai return 0; 2118a766d0d7STakashi Iwai } 2119a766d0d7STakashi Iwai 2120a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2121a766d0d7STakashi Iwai 2122c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2123620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 2124620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 2125c577b8a1SJoseph Chan { 212610a20af7STakashi Iwai struct via_spec *spec = codec->spec; 21270aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2128a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 2129a766d0d7STakashi Iwai hda_nid_t cap_nid; 2130a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 2131a766d0d7STakashi Iwai int num_idxs; 2132a766d0d7STakashi Iwai 2133a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2134a766d0d7STakashi Iwai if (err < 0) 2135a766d0d7STakashi Iwai return err; 2136a766d0d7STakashi Iwai err = get_mux_nids(codec); 2137a766d0d7STakashi Iwai if (err < 0) 2138a766d0d7STakashi Iwai return err; 2139a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 2140a766d0d7STakashi Iwai 2141a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 2142a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 2143a766d0d7STakashi Iwai if (num_idxs <= 0) 2144a766d0d7STakashi Iwai return 0; 2145c577b8a1SJoseph Chan 2146c577b8a1SJoseph Chan /* for internal loopback recording select */ 2147f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2148620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 214910a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2150f3268512STakashi Iwai break; 2151f3268512STakashi Iwai } 2152f3268512STakashi Iwai } 2153c577b8a1SJoseph Chan 21547b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 215510a20af7STakashi Iwai const char *label; 21567b315bb4STakashi Iwai type = cfg->inputs[i].type; 2157f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 21587b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2159c577b8a1SJoseph Chan break; 2160f3268512STakashi Iwai if (idx >= num_idxs) 2161f3268512STakashi Iwai continue; 21627b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 21637b315bb4STakashi Iwai type_idx++; 21647b315bb4STakashi Iwai else 21657b315bb4STakashi Iwai type_idx = 0; 216610a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2167620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 2168620e2b28STakashi Iwai pin_idxs[idx]); 2169a766d0d7STakashi Iwai if (idx2 >= 0) 217016922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2171620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 2172c577b8a1SJoseph Chan if (err < 0) 2173c577b8a1SJoseph Chan return err; 217410a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2175c577b8a1SJoseph Chan } 2176c577b8a1SJoseph Chan return 0; 2177c577b8a1SJoseph Chan } 2178c577b8a1SJoseph Chan 2179cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 218090dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2181cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2182cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2183cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2184cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2185cb53c626STakashi Iwai { } /* end */ 2186cb53c626STakashi Iwai }; 2187cb53c626STakashi Iwai #endif 2188cb53c626STakashi Iwai 218976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 219076d9b0ddSHarald Welte { 219176d9b0ddSHarald Welte unsigned int def_conf; 219276d9b0ddSHarald Welte unsigned char seqassoc; 219376d9b0ddSHarald Welte 21942f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 219576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 219676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 219782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 219882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 219976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 22002f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 220176d9b0ddSHarald Welte } 220276d9b0ddSHarald Welte 220376d9b0ddSHarald Welte return; 220476d9b0ddSHarald Welte } 220576d9b0ddSHarald Welte 2206e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 22071f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22081f2e99feSLydia Wang { 22091f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22101f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22111f2e99feSLydia Wang 22121f2e99feSLydia Wang if (spec->codec_type != VT1708) 22131f2e99feSLydia Wang return 0; 2214e06e5a29STakashi Iwai spec->vt1708_jack_detect = 22151f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2216e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 22171f2e99feSLydia Wang return 0; 22181f2e99feSLydia Wang } 22191f2e99feSLydia Wang 2220e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 22211f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22221f2e99feSLydia Wang { 22231f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22241f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22251f2e99feSLydia Wang int change; 22261f2e99feSLydia Wang 22271f2e99feSLydia Wang if (spec->codec_type != VT1708) 22281f2e99feSLydia Wang return 0; 2229e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 22301f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2231e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2232e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 22331f2e99feSLydia Wang mute_aa_path(codec, 1); 22341f2e99feSLydia Wang notify_aa_path_ctls(codec); 22351f2e99feSLydia Wang } 22361f2e99feSLydia Wang return change; 22371f2e99feSLydia Wang } 22381f2e99feSLydia Wang 2239e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 22401f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22411f2e99feSLydia Wang .name = "Jack Detect", 22421f2e99feSLydia Wang .count = 1, 22431f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2244e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2245e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 22461f2e99feSLydia Wang }; 22471f2e99feSLydia Wang 2248c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2249c577b8a1SJoseph Chan { 2250c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2251c577b8a1SJoseph Chan int err; 2252c577b8a1SJoseph Chan 225376d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 225476d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 225576d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 225676d9b0ddSHarald Welte 2257c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2258c577b8a1SJoseph Chan if (err < 0) 2259c577b8a1SJoseph Chan return err; 22604a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2261c577b8a1SJoseph Chan if (err < 0) 2262c577b8a1SJoseph Chan return err; 2263c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2264c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2265c577b8a1SJoseph Chan 22664a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2267c577b8a1SJoseph Chan if (err < 0) 2268c577b8a1SJoseph Chan return err; 22694a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2270c577b8a1SJoseph Chan if (err < 0) 2271c577b8a1SJoseph Chan return err; 2272620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2273c577b8a1SJoseph Chan if (err < 0) 2274c577b8a1SJoseph Chan return err; 22751f2e99feSLydia Wang /* add jack detect on/off control */ 2276e06e5a29STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 2277e06e5a29STakashi Iwai return -ENOMEM; 2278c577b8a1SJoseph Chan 2279c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2280c577b8a1SJoseph Chan 22810852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2282c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 228355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2284c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2285c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2286c577b8a1SJoseph Chan 2287603c4019STakashi Iwai if (spec->kctls.list) 2288603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2289c577b8a1SJoseph Chan 229069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2291c577b8a1SJoseph Chan 22920aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 22930aa62aefSHarald Welte 2294f8fdd495SHarald Welte if (spec->hp_mux) 22953d83e577STakashi Iwai via_hp_build(codec); 2296c577b8a1SJoseph Chan 2297f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2298f4a7828bSTakashi Iwai if (err < 0) 2299f4a7828bSTakashi Iwai return err; 2300f4a7828bSTakashi Iwai 2301c577b8a1SJoseph Chan return 1; 2302c577b8a1SJoseph Chan } 2303c577b8a1SJoseph Chan 2304c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2305c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2306c577b8a1SJoseph Chan { 230725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 230825eaba2fSLydia Wang 2309c577b8a1SJoseph Chan via_init(codec); 2310c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2311c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2312c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 231311890956SLydia Wang 231411890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 231525eaba2fSLydia Wang via_hp_bind_automute(codec); 231625eaba2fSLydia Wang } else { 231725eaba2fSLydia Wang via_hp_automute(codec); 231825eaba2fSLydia Wang via_speaker_automute(codec); 231925eaba2fSLydia Wang } 232025eaba2fSLydia Wang 2321c577b8a1SJoseph Chan return 0; 2322c577b8a1SJoseph Chan } 2323c577b8a1SJoseph Chan 23241f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 23251f2e99feSLydia Wang { 23261f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 23271f2e99feSLydia Wang vt1708_hp_work.work); 23281f2e99feSLydia Wang if (spec->codec_type != VT1708) 23291f2e99feSLydia Wang return; 23301f2e99feSLydia Wang /* if jack state toggled */ 23311f2e99feSLydia Wang if (spec->vt1708_hp_present 2332d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 23331f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 23341f2e99feSLydia Wang via_hp_automute(spec->codec); 23351f2e99feSLydia Wang } 23361f2e99feSLydia Wang vt1708_start_hp_work(spec); 23371f2e99feSLydia Wang } 23381f2e99feSLydia Wang 2339337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2340337b9d02STakashi Iwai { 2341337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2342337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2343337b9d02STakashi Iwai unsigned int type; 2344337b9d02STakashi Iwai int i, n; 2345337b9d02STakashi Iwai 2346337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2347337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2348337b9d02STakashi Iwai while (nid) { 2349a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 23501c55d521STakashi Iwai if (type == AC_WID_PIN) 23511c55d521STakashi Iwai break; 2352337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2353337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2354337b9d02STakashi Iwai if (n <= 0) 2355337b9d02STakashi Iwai break; 2356337b9d02STakashi Iwai if (n > 1) { 2357337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2358337b9d02STakashi Iwai break; 2359337b9d02STakashi Iwai } 2360337b9d02STakashi Iwai nid = conn[0]; 2361337b9d02STakashi Iwai } 2362337b9d02STakashi Iwai } 23631c55d521STakashi Iwai return 0; 2364337b9d02STakashi Iwai } 2365337b9d02STakashi Iwai 2366c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2367c577b8a1SJoseph Chan { 2368c577b8a1SJoseph Chan struct via_spec *spec; 2369c577b8a1SJoseph Chan int err; 2370c577b8a1SJoseph Chan 2371c577b8a1SJoseph Chan /* create a codec specific record */ 23725b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2373c577b8a1SJoseph Chan if (spec == NULL) 2374c577b8a1SJoseph Chan return -ENOMEM; 2375c577b8a1SJoseph Chan 2376620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2377620e2b28STakashi Iwai 2378c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2379c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2380c577b8a1SJoseph Chan if (err < 0) { 2381c577b8a1SJoseph Chan via_free(codec); 2382c577b8a1SJoseph Chan return err; 2383c577b8a1SJoseph Chan } else if (!err) { 2384c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2385c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2386c577b8a1SJoseph Chan } 2387c577b8a1SJoseph Chan 2388c577b8a1SJoseph Chan 2389bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2390bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2391bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2392c577b8a1SJoseph Chan 2393a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2394c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2395c577b8a1SJoseph Chan spec->num_mixers++; 2396c577b8a1SJoseph Chan } 2397c577b8a1SJoseph Chan 2398c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2399c577b8a1SJoseph Chan 2400c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2401cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2402cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2403cb53c626STakashi Iwai #endif 24041f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2405c577b8a1SJoseph Chan return 0; 2406c577b8a1SJoseph Chan } 2407c577b8a1SJoseph Chan 2408c577b8a1SJoseph Chan /* capture mixer elements */ 240990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = { 2410c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2411c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2412c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2413c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2414c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2415c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2416c577b8a1SJoseph Chan { 2417c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2418c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2419c577b8a1SJoseph Chan * So call somewhat different.. 2420c577b8a1SJoseph Chan */ 2421c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2422c577b8a1SJoseph Chan .name = "Input Source", 2423c577b8a1SJoseph Chan .count = 1, 2424c577b8a1SJoseph Chan .info = via_mux_enum_info, 2425c577b8a1SJoseph Chan .get = via_mux_enum_get, 2426c577b8a1SJoseph Chan .put = via_mux_enum_put, 2427c577b8a1SJoseph Chan }, 2428c577b8a1SJoseph Chan { } /* end */ 2429c577b8a1SJoseph Chan }; 2430c577b8a1SJoseph Chan 243190dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2432a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2433a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 243469e52a80SHarald Welte { } 243569e52a80SHarald Welte }; 243669e52a80SHarald Welte 2437c577b8a1SJoseph Chan /* 2438c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2439c577b8a1SJoseph Chan */ 244090dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2441c577b8a1SJoseph Chan /* 2442c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2443c577b8a1SJoseph Chan */ 2444c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2445c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2446c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2447c577b8a1SJoseph Chan 2448c577b8a1SJoseph Chan 2449f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2450c577b8a1SJoseph Chan * mixer widget 2451c577b8a1SJoseph Chan */ 2452c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2453f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2454f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2455f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2456f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2457f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2458c577b8a1SJoseph Chan 2459c577b8a1SJoseph Chan /* 2460c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2461c577b8a1SJoseph Chan */ 2462c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2463c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2464c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2465c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2466c577b8a1SJoseph Chan 2467c577b8a1SJoseph Chan /* 2468c577b8a1SJoseph Chan * Unmute PW3 and PW4 2469c577b8a1SJoseph Chan */ 2470c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2471c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2472c577b8a1SJoseph Chan 2473bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2474bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2475c577b8a1SJoseph Chan /* PW9 Output enable */ 2476c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2477c577b8a1SJoseph Chan { } 2478c577b8a1SJoseph Chan }; 2479c577b8a1SJoseph Chan 2480c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2481c577b8a1SJoseph Chan { 2482c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2483c577b8a1SJoseph Chan int err; 2484c577b8a1SJoseph Chan 2485c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2486c577b8a1SJoseph Chan if (err < 0) 2487c577b8a1SJoseph Chan return err; 24884a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2489c577b8a1SJoseph Chan if (err < 0) 2490c577b8a1SJoseph Chan return err; 2491c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2492c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2493c577b8a1SJoseph Chan 24944a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2495c577b8a1SJoseph Chan if (err < 0) 2496c577b8a1SJoseph Chan return err; 24974a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2498c577b8a1SJoseph Chan if (err < 0) 2499c577b8a1SJoseph Chan return err; 2500620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2501c577b8a1SJoseph Chan if (err < 0) 2502c577b8a1SJoseph Chan return err; 2503c577b8a1SJoseph Chan 2504c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2505c577b8a1SJoseph Chan 25060852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2507c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 250855d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2509c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2510c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2511c577b8a1SJoseph Chan 2512603c4019STakashi Iwai if (spec->kctls.list) 2513603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2514c577b8a1SJoseph Chan 25150aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2516c577b8a1SJoseph Chan 2517f8fdd495SHarald Welte if (spec->hp_mux) 25183d83e577STakashi Iwai via_hp_build(codec); 2519f8fdd495SHarald Welte 2520f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2521f4a7828bSTakashi Iwai if (err < 0) 2522f4a7828bSTakashi Iwai return err; 2523f4a7828bSTakashi Iwai 2524c577b8a1SJoseph Chan return 1; 2525c577b8a1SJoseph Chan } 2526c577b8a1SJoseph Chan 2527cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 252890dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2529cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2530cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2531cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2532cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2533cb53c626STakashi Iwai { } /* end */ 2534cb53c626STakashi Iwai }; 2535cb53c626STakashi Iwai #endif 2536cb53c626STakashi Iwai 2537c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2538c577b8a1SJoseph Chan { 2539c577b8a1SJoseph Chan struct via_spec *spec; 2540c577b8a1SJoseph Chan int err; 2541c577b8a1SJoseph Chan 2542c577b8a1SJoseph Chan /* create a codec specific record */ 25435b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2544c577b8a1SJoseph Chan if (spec == NULL) 2545c577b8a1SJoseph Chan return -ENOMEM; 2546c577b8a1SJoseph Chan 2547620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2548620e2b28STakashi Iwai 2549c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2550c577b8a1SJoseph Chan if (err < 0) { 2551c577b8a1SJoseph Chan via_free(codec); 2552c577b8a1SJoseph Chan return err; 2553c577b8a1SJoseph Chan } else if (!err) { 2554c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2555c577b8a1SJoseph Chan "Using genenic mode...\n"); 2556c577b8a1SJoseph Chan } 2557c577b8a1SJoseph Chan 255869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 255969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2560c577b8a1SJoseph Chan 2561a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2562c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2563c577b8a1SJoseph Chan spec->num_mixers++; 2564c577b8a1SJoseph Chan } 2565c577b8a1SJoseph Chan 2566c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2567c577b8a1SJoseph Chan 2568c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 256969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2570cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2571cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2572cb53c626STakashi Iwai #endif 2573c577b8a1SJoseph Chan 2574c577b8a1SJoseph Chan return 0; 2575c577b8a1SJoseph Chan } 2576c577b8a1SJoseph Chan /* 2577c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2578c577b8a1SJoseph Chan */ 257990dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2580c577b8a1SJoseph Chan /* 2581c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2582c577b8a1SJoseph Chan */ 2583c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2584c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2585c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2586c577b8a1SJoseph Chan 2587c577b8a1SJoseph Chan 2588c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2589c577b8a1SJoseph Chan * mixer widget 2590c577b8a1SJoseph Chan */ 2591c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2592c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2593c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2594c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2595c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2596c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2597c577b8a1SJoseph Chan 2598c577b8a1SJoseph Chan /* 2599c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2600c577b8a1SJoseph Chan */ 2601c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2602c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2603c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2604c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2605c577b8a1SJoseph Chan 2606c577b8a1SJoseph Chan /* 2607c577b8a1SJoseph Chan * Unmute PW3 and PW4 2608c577b8a1SJoseph Chan */ 2609c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2610c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2611c577b8a1SJoseph Chan 2612c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2613c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2614c577b8a1SJoseph Chan /* PW9 Output enable */ 2615c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2616c577b8a1SJoseph Chan { } 2617c577b8a1SJoseph Chan }; 2618c577b8a1SJoseph Chan 2619c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2620c577b8a1SJoseph Chan { 2621c577b8a1SJoseph Chan struct via_spec *spec; 2622c577b8a1SJoseph Chan int err; 2623c577b8a1SJoseph Chan 2624c577b8a1SJoseph Chan /* create a codec specific record */ 26255b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2626c577b8a1SJoseph Chan if (spec == NULL) 2627c577b8a1SJoseph Chan return -ENOMEM; 2628c577b8a1SJoseph Chan 2629620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2630620e2b28STakashi Iwai 2631c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2632c577b8a1SJoseph Chan if (err < 0) { 2633c577b8a1SJoseph Chan via_free(codec); 2634c577b8a1SJoseph Chan return err; 2635c577b8a1SJoseph Chan } else if (!err) { 2636c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2637c577b8a1SJoseph Chan "Using genenic mode...\n"); 2638c577b8a1SJoseph Chan } 2639c577b8a1SJoseph Chan 264069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 264169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2642c577b8a1SJoseph Chan 2643a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2644c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2645c577b8a1SJoseph Chan spec->num_mixers++; 2646c577b8a1SJoseph Chan } 2647c577b8a1SJoseph Chan 2648c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2649c577b8a1SJoseph Chan 2650c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 265169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2652cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2653cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2654cb53c626STakashi Iwai #endif 2655f7278fd0SJosepch Chan return 0; 2656f7278fd0SJosepch Chan } 2657f7278fd0SJosepch Chan 2658f7278fd0SJosepch Chan /* capture mixer elements */ 265990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2660f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2661f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2662f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2663f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2664f7278fd0SJosepch Chan { 2665f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2666f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2667f7278fd0SJosepch Chan * So call somewhat different.. 2668f7278fd0SJosepch Chan */ 2669f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2670f7278fd0SJosepch Chan .name = "Input Source", 2671f7278fd0SJosepch Chan .count = 1, 2672f7278fd0SJosepch Chan .info = via_mux_enum_info, 2673f7278fd0SJosepch Chan .get = via_mux_enum_get, 2674f7278fd0SJosepch Chan .put = via_mux_enum_put, 2675f7278fd0SJosepch Chan }, 2676f7278fd0SJosepch Chan { } /* end */ 2677f7278fd0SJosepch Chan }; 2678f7278fd0SJosepch Chan /* 2679f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2680f7278fd0SJosepch Chan */ 268190dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2682f7278fd0SJosepch Chan /* 2683f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2684f7278fd0SJosepch Chan */ 2685f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2686f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2687f7278fd0SJosepch Chan 2688f7278fd0SJosepch Chan 2689f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2690f7278fd0SJosepch Chan * mixer widget 2691f7278fd0SJosepch Chan */ 2692f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2693f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2694f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2695f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2696f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2697f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2698f7278fd0SJosepch Chan 2699f7278fd0SJosepch Chan /* 2700f7278fd0SJosepch Chan * Set up output mixers 2701f7278fd0SJosepch Chan */ 2702f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2703f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2704f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2705f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2706f7278fd0SJosepch Chan 2707f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2708bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2709f7278fd0SJosepch Chan /* PW9 Output enable */ 2710f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2711f7278fd0SJosepch Chan /* PW10 Input enable */ 2712f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2713f7278fd0SJosepch Chan { } 2714f7278fd0SJosepch Chan }; 2715f7278fd0SJosepch Chan 271690dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2717f7278fd0SJosepch Chan /* 2718f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2719f7278fd0SJosepch Chan */ 2720f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2721f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2722f7278fd0SJosepch Chan 2723f7278fd0SJosepch Chan 2724f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2725f7278fd0SJosepch Chan * mixer widget 2726f7278fd0SJosepch Chan */ 2727f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2728f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2729f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2730f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2731f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2732f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2733f7278fd0SJosepch Chan 2734f7278fd0SJosepch Chan /* 2735f7278fd0SJosepch Chan * Set up output mixers 2736f7278fd0SJosepch Chan */ 2737f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2738f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2739f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2740f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2741f7278fd0SJosepch Chan 2742f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2743f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2744f7278fd0SJosepch Chan /* PW9 Output enable */ 2745f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2746f7278fd0SJosepch Chan /* PW10 Input enable */ 2747f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2748f7278fd0SJosepch Chan { } 2749f7278fd0SJosepch Chan }; 2750f7278fd0SJosepch Chan 275190dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2752a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2753a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2754a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2755a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2756a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2757a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2758a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2759a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2760a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 276169e52a80SHarald Welte { } 276269e52a80SHarald Welte }; 276369e52a80SHarald Welte 2764f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 2765f7278fd0SJosepch Chan { 2766f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 2767f7278fd0SJosepch Chan int err; 2768f7278fd0SJosepch Chan 2769f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2770f7278fd0SJosepch Chan if (err < 0) 2771f7278fd0SJosepch Chan return err; 27724a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2773f7278fd0SJosepch Chan if (err < 0) 2774f7278fd0SJosepch Chan return err; 2775f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2776f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 2777f7278fd0SJosepch Chan 27784a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2779f7278fd0SJosepch Chan if (err < 0) 2780f7278fd0SJosepch Chan return err; 27814a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2782f7278fd0SJosepch Chan if (err < 0) 2783f7278fd0SJosepch Chan return err; 2784620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2785f7278fd0SJosepch Chan if (err < 0) 2786f7278fd0SJosepch Chan return err; 2787f7278fd0SJosepch Chan 2788f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2789f7278fd0SJosepch Chan 27900852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2791f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 279255d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 2793f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2794f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2795f7278fd0SJosepch Chan 2796603c4019STakashi Iwai if (spec->kctls.list) 2797603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2798f7278fd0SJosepch Chan 27990aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 28000aa62aefSHarald Welte 2801f8fdd495SHarald Welte if (spec->hp_mux) 28023d83e577STakashi Iwai via_hp_build(codec); 2803f7278fd0SJosepch Chan 2804f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2805f4a7828bSTakashi Iwai if (err < 0) 2806f4a7828bSTakashi Iwai return err; 2807f4a7828bSTakashi Iwai 2808f7278fd0SJosepch Chan return 1; 2809f7278fd0SJosepch Chan } 2810f7278fd0SJosepch Chan 2811f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 281290dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2813f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2814f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2815f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2816f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2817f7278fd0SJosepch Chan { } /* end */ 2818f7278fd0SJosepch Chan }; 2819f7278fd0SJosepch Chan #endif 28203e95b9abSLydia Wang 28213e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 28223e95b9abSLydia Wang { 28233e95b9abSLydia Wang struct via_spec *spec = codec->spec; 28243e95b9abSLydia Wang int imux_is_smixer; 28253e95b9abSLydia Wang unsigned int parm; 28263e95b9abSLydia Wang int is_8ch = 0; 2827bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2828bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 28293e95b9abSLydia Wang is_8ch = 1; 28303e95b9abSLydia Wang 28313e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 28323e95b9abSLydia Wang imux_is_smixer = 28333e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 28343e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 28353e95b9abSLydia Wang /* inputs */ 28363e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 28373e95b9abSLydia Wang parm = AC_PWRST_D3; 28383e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28393e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28403e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28413e95b9abSLydia Wang if (imux_is_smixer) 28423e95b9abSLydia Wang parm = AC_PWRST_D0; 28433e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 28443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 28453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 28463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 28473e95b9abSLydia Wang 28483e95b9abSLydia Wang /* outputs */ 28493e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 28503e95b9abSLydia Wang parm = AC_PWRST_D3; 28513e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 28523e95b9abSLydia Wang if (spec->smart51_enabled) 28533e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 28553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28563e95b9abSLydia Wang 28573e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 28583e95b9abSLydia Wang if (is_8ch) { 28593e95b9abSLydia Wang parm = AC_PWRST_D3; 28603e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 28613e95b9abSLydia Wang if (spec->smart51_enabled) 28623e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 28643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 28663e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2867bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2868bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2869bc92df7fSLydia Wang parm = AC_PWRST_D3; 2870bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2871bc92df7fSLydia Wang if (spec->smart51_enabled) 2872bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2873bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2874bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2875bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2876bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28773e95b9abSLydia Wang } 28783e95b9abSLydia Wang 28793e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 28803e95b9abSLydia Wang parm = AC_PWRST_D3; 28813e95b9abSLydia Wang /* force to D0 for internal Speaker */ 28823e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 28833e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 28843e95b9abSLydia Wang if (is_8ch) 28853e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 28863e95b9abSLydia Wang 28873e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 28883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 28893e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 28913e95b9abSLydia Wang if (is_8ch) { 28923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 28933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 28953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2896bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2897bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2898bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28993e95b9abSLydia Wang } 29003e95b9abSLydia Wang 2901518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2902f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2903f7278fd0SJosepch Chan { 2904f7278fd0SJosepch Chan struct via_spec *spec; 2905f7278fd0SJosepch Chan int err; 2906f7278fd0SJosepch Chan 2907518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2908518bf3baSLydia Wang return patch_vt1708S(codec); 2909f7278fd0SJosepch Chan /* create a codec specific record */ 29105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2911f7278fd0SJosepch Chan if (spec == NULL) 2912f7278fd0SJosepch Chan return -ENOMEM; 2913f7278fd0SJosepch Chan 2914620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2915620e2b28STakashi Iwai 2916f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2917f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2918f7278fd0SJosepch Chan if (err < 0) { 2919f7278fd0SJosepch Chan via_free(codec); 2920f7278fd0SJosepch Chan return err; 2921f7278fd0SJosepch Chan } else if (!err) { 2922f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2923f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2924f7278fd0SJosepch Chan } 2925f7278fd0SJosepch Chan 292669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 292769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2928f7278fd0SJosepch Chan 2929a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2930f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2931f7278fd0SJosepch Chan spec->num_mixers++; 2932f7278fd0SJosepch Chan } 2933f7278fd0SJosepch Chan 2934f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2935f7278fd0SJosepch Chan 2936f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 293769e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2938f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2939f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2940f7278fd0SJosepch Chan #endif 2941f7278fd0SJosepch Chan 29423e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 29433e95b9abSLydia Wang 2944f7278fd0SJosepch Chan return 0; 2945f7278fd0SJosepch Chan } 2946f7278fd0SJosepch Chan 2947f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2948f7278fd0SJosepch Chan { 2949f7278fd0SJosepch Chan struct via_spec *spec; 2950f7278fd0SJosepch Chan int err; 2951f7278fd0SJosepch Chan 2952f7278fd0SJosepch Chan /* create a codec specific record */ 29535b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2954f7278fd0SJosepch Chan if (spec == NULL) 2955f7278fd0SJosepch Chan return -ENOMEM; 2956f7278fd0SJosepch Chan 2957f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2958f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2959f7278fd0SJosepch Chan if (err < 0) { 2960f7278fd0SJosepch Chan via_free(codec); 2961f7278fd0SJosepch Chan return err; 2962f7278fd0SJosepch Chan } else if (!err) { 2963f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2964f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2965f7278fd0SJosepch Chan } 2966f7278fd0SJosepch Chan 296769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 296869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2969f7278fd0SJosepch Chan 2970a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2971f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2972f7278fd0SJosepch Chan spec->num_mixers++; 2973f7278fd0SJosepch Chan } 2974f7278fd0SJosepch Chan 2975f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2976f7278fd0SJosepch Chan 2977f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 297869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2979f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2980f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2981f7278fd0SJosepch Chan #endif 2982c577b8a1SJoseph Chan 29833e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 29843e95b9abSLydia Wang 2985c577b8a1SJoseph Chan return 0; 2986c577b8a1SJoseph Chan } 2987c577b8a1SJoseph Chan 2988d949cac1SHarald Welte /* Patch for VT1708S */ 2989d949cac1SHarald Welte 2990d949cac1SHarald Welte /* capture mixer elements */ 299190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { 2992d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2993d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2994d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2995d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 29966369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 29976369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 29986369bcfcSLydia Wang HDA_INPUT), 2999d949cac1SHarald Welte { 3000d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3001d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3002d949cac1SHarald Welte * So call somewhat different.. 3003d949cac1SHarald Welte */ 3004d949cac1SHarald Welte /* .name = "Capture Source", */ 3005d949cac1SHarald Welte .name = "Input Source", 3006d949cac1SHarald Welte .count = 1, 3007d949cac1SHarald Welte .info = via_mux_enum_info, 3008d949cac1SHarald Welte .get = via_mux_enum_get, 3009d949cac1SHarald Welte .put = via_mux_enum_put, 3010d949cac1SHarald Welte }, 3011d949cac1SHarald Welte { } /* end */ 3012d949cac1SHarald Welte }; 3013d949cac1SHarald Welte 301490dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3015d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3016d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3017d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3018d949cac1SHarald Welte 3019d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3020d949cac1SHarald Welte * analog-loopback mixer widget */ 3021d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3022d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3023d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3024d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3025d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3026d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3027d949cac1SHarald Welte 3028d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3029d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 30305691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3031d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 30325691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3033d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3034d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3035bc7e7e5cSLydia Wang /* don't bybass mixer */ 3036bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3037d949cac1SHarald Welte { } 3038d949cac1SHarald Welte }; 3039d949cac1SHarald Welte 304090dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3041a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3042a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3043a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3044a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3045a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3046a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3047a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3048a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3049a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 305069e52a80SHarald Welte { } 305169e52a80SHarald Welte }; 305269e52a80SHarald Welte 305390dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3054bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3055bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3056bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3057bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3058bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3059bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3060bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3061bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3062bc92df7fSLydia Wang { } 3063bc92df7fSLydia Wang }; 3064bc92df7fSLydia Wang 30659da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 30669da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 30679da29271STakashi Iwai { 30689da29271STakashi Iwai struct via_spec *spec = codec->spec; 30699da29271STakashi Iwai int i; 30709da29271STakashi Iwai 30719da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 30729da29271STakashi Iwai hda_nid_t nid; 30739da29271STakashi Iwai int conn; 30749da29271STakashi Iwai 30759da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 30769da29271STakashi Iwai if (!nid) 30779da29271STakashi Iwai continue; 30789da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 30799da29271STakashi Iwai if (conn < 1) 30809da29271STakashi Iwai continue; 30819da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 30829da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 30839da29271STakashi Iwai else { 30849da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 30859da29271STakashi Iwai break; /* at most two dig outs */ 30869da29271STakashi Iwai } 30879da29271STakashi Iwai } 30889da29271STakashi Iwai } 30899da29271STakashi Iwai 3090d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3091d949cac1SHarald Welte { 3092d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3093d949cac1SHarald Welte int err; 3094d949cac1SHarald Welte 30959da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3096d949cac1SHarald Welte if (err < 0) 3097d949cac1SHarald Welte return err; 30984a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3099d949cac1SHarald Welte if (err < 0) 3100d949cac1SHarald Welte return err; 3101d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3102d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3103d949cac1SHarald Welte 31044a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3105d949cac1SHarald Welte if (err < 0) 3106d949cac1SHarald Welte return err; 31074a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3108d949cac1SHarald Welte if (err < 0) 3109d949cac1SHarald Welte return err; 3110620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3111d949cac1SHarald Welte if (err < 0) 3112d949cac1SHarald Welte return err; 3113d949cac1SHarald Welte 3114d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3115d949cac1SHarald Welte 31169da29271STakashi Iwai fill_dig_outs(codec); 311798aa34c0SHarald Welte 3118603c4019STakashi Iwai if (spec->kctls.list) 3119603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3120d949cac1SHarald Welte 31210aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31220aa62aefSHarald Welte 3123f8fdd495SHarald Welte if (spec->hp_mux) 31243d83e577STakashi Iwai via_hp_build(codec); 3125d949cac1SHarald Welte 3126f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3127f4a7828bSTakashi Iwai if (err < 0) 3128f4a7828bSTakashi Iwai return err; 3129f4a7828bSTakashi Iwai 3130d949cac1SHarald Welte return 1; 3131d949cac1SHarald Welte } 3132d949cac1SHarald Welte 3133d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 313490dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3135d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3136d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3137d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3138d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3139d949cac1SHarald Welte { } /* end */ 3140d949cac1SHarald Welte }; 3141d949cac1SHarald Welte #endif 3142d949cac1SHarald Welte 31436369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 31446369bcfcSLydia Wang int offset, int num_steps, int step_size) 31456369bcfcSLydia Wang { 31466369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 31476369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 31486369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 31496369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 31506369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 31516369bcfcSLydia Wang } 31526369bcfcSLydia Wang 3153d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3154d949cac1SHarald Welte { 3155d949cac1SHarald Welte struct via_spec *spec; 3156d949cac1SHarald Welte int err; 3157d949cac1SHarald Welte 3158d949cac1SHarald Welte /* create a codec specific record */ 31595b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3160d949cac1SHarald Welte if (spec == NULL) 3161d949cac1SHarald Welte return -ENOMEM; 3162d949cac1SHarald Welte 3163620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3164620e2b28STakashi Iwai 3165d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3166d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3167d949cac1SHarald Welte if (err < 0) { 3168d949cac1SHarald Welte via_free(codec); 3169d949cac1SHarald Welte return err; 3170d949cac1SHarald Welte } else if (!err) { 3171d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3172d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3173d949cac1SHarald Welte } 3174d949cac1SHarald Welte 317569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3176bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3177bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3178bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3179bc92df7fSLydia Wang else 3180bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3181bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3182d949cac1SHarald Welte 3183a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 31846369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 31856369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3186d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3187d949cac1SHarald Welte spec->num_mixers++; 3188d949cac1SHarald Welte } 3189d949cac1SHarald Welte 3190d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3191d949cac1SHarald Welte 3192d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 319369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3194d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3195d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3196d949cac1SHarald Welte #endif 3197d949cac1SHarald Welte 3198518bf3baSLydia Wang /* correct names for VT1708BCE */ 3199518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3200518bf3baSLydia Wang kfree(codec->chip_name); 3201518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3202518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3203518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3204518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3205970f630fSLydia Wang } 3206bc92df7fSLydia Wang /* correct names for VT1705 */ 3207bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3208bc92df7fSLydia Wang kfree(codec->chip_name); 3209bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3210bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3211bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3212bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3213bc92df7fSLydia Wang } 32143e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3215d949cac1SHarald Welte return 0; 3216d949cac1SHarald Welte } 3217d949cac1SHarald Welte 3218d949cac1SHarald Welte /* Patch for VT1702 */ 3219d949cac1SHarald Welte 3220d949cac1SHarald Welte /* capture mixer elements */ 322190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = { 3222d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3223d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3224d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3225d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3226d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3227d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3228d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3229d949cac1SHarald Welte HDA_INPUT), 3230d949cac1SHarald Welte { 3231d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3232d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3233d949cac1SHarald Welte * So call somewhat different.. 3234d949cac1SHarald Welte */ 3235d949cac1SHarald Welte /* .name = "Capture Source", */ 3236d949cac1SHarald Welte .name = "Input Source", 3237d949cac1SHarald Welte .count = 1, 3238d949cac1SHarald Welte .info = via_mux_enum_info, 3239d949cac1SHarald Welte .get = via_mux_enum_get, 3240d949cac1SHarald Welte .put = via_mux_enum_put, 3241d949cac1SHarald Welte }, 3242d949cac1SHarald Welte { } /* end */ 3243d949cac1SHarald Welte }; 3244d949cac1SHarald Welte 324590dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3246d949cac1SHarald Welte /* 3247d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3248d949cac1SHarald Welte */ 3249d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3250d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3251d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3252d949cac1SHarald Welte 3253d949cac1SHarald Welte 3254d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3255d949cac1SHarald Welte * mixer widget 3256d949cac1SHarald Welte */ 3257d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3258d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3259d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3260d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3261d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3262d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3263d949cac1SHarald Welte 3264d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3265d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3266d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3267d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3268d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3269bc7e7e5cSLydia Wang /* mixer enable */ 3270bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3271bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3272bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3273d949cac1SHarald Welte { } 3274d949cac1SHarald Welte }; 3275d949cac1SHarald Welte 327690dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 3277a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3278a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3279a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3280a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3281a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3282a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 328369e52a80SHarald Welte { } 328469e52a80SHarald Welte }; 328569e52a80SHarald Welte 3286d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 3287d949cac1SHarald Welte { 3288d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3289d949cac1SHarald Welte int err; 3290d949cac1SHarald Welte 32919da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3292d949cac1SHarald Welte if (err < 0) 3293d949cac1SHarald Welte return err; 32944a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3295d949cac1SHarald Welte if (err < 0) 3296d949cac1SHarald Welte return err; 3297d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3298d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3299d949cac1SHarald Welte 33004a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3301d949cac1SHarald Welte if (err < 0) 3302d949cac1SHarald Welte return err; 33034a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3304d949cac1SHarald Welte if (err < 0) 3305d949cac1SHarald Welte return err; 3306c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 3307c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 3308c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 3309c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 3310c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 3311c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 3312620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3313d949cac1SHarald Welte if (err < 0) 3314d949cac1SHarald Welte return err; 3315d949cac1SHarald Welte 3316d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3317d949cac1SHarald Welte 33189da29271STakashi Iwai fill_dig_outs(codec); 331998aa34c0SHarald Welte 3320603c4019STakashi Iwai if (spec->kctls.list) 3321603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3322d949cac1SHarald Welte 33230aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 33240aa62aefSHarald Welte 3325f8fdd495SHarald Welte if (spec->hp_mux) 33263d83e577STakashi Iwai via_hp_build(codec); 3327d949cac1SHarald Welte 3328d949cac1SHarald Welte return 1; 3329d949cac1SHarald Welte } 3330d949cac1SHarald Welte 3331d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 333290dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 3333d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 3334d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 3335d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 3336d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 3337d949cac1SHarald Welte { } /* end */ 3338d949cac1SHarald Welte }; 3339d949cac1SHarald Welte #endif 3340d949cac1SHarald Welte 33413e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 33423e95b9abSLydia Wang { 33433e95b9abSLydia Wang int imux_is_smixer = 33443e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 33453e95b9abSLydia Wang unsigned int parm; 33463e95b9abSLydia Wang /* inputs */ 33473e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 33483e95b9abSLydia Wang parm = AC_PWRST_D3; 33493e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 33503e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 33513e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 33523e95b9abSLydia Wang if (imux_is_smixer) 33533e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 33543e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 33553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 33563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 33573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 33583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 33593e95b9abSLydia Wang 33603e95b9abSLydia Wang /* outputs */ 33613e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 33623e95b9abSLydia Wang parm = AC_PWRST_D3; 33633e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 33643e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 33653e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 33663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 33673e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 33683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 33703e95b9abSLydia Wang } 33713e95b9abSLydia Wang 3372d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3373d949cac1SHarald Welte { 3374d949cac1SHarald Welte struct via_spec *spec; 3375d949cac1SHarald Welte int err; 3376d949cac1SHarald Welte 3377d949cac1SHarald Welte /* create a codec specific record */ 33785b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3379d949cac1SHarald Welte if (spec == NULL) 3380d949cac1SHarald Welte return -ENOMEM; 3381d949cac1SHarald Welte 3382620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 3383620e2b28STakashi Iwai 3384d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3385d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 3386d949cac1SHarald Welte if (err < 0) { 3387d949cac1SHarald Welte via_free(codec); 3388d949cac1SHarald Welte return err; 3389d949cac1SHarald Welte } else if (!err) { 3390d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3391d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3392d949cac1SHarald Welte } 3393d949cac1SHarald Welte 339469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 339569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 3396d949cac1SHarald Welte 3397a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3398d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 3399d949cac1SHarald Welte spec->num_mixers++; 3400d949cac1SHarald Welte } 3401d949cac1SHarald Welte 3402d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3403d949cac1SHarald Welte 3404d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 340569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3406d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3407d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 3408d949cac1SHarald Welte #endif 3409d949cac1SHarald Welte 34103e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 3411d949cac1SHarald Welte return 0; 3412d949cac1SHarald Welte } 3413d949cac1SHarald Welte 3414eb7188caSLydia Wang /* Patch for VT1718S */ 3415eb7188caSLydia Wang 3416eb7188caSLydia Wang /* capture mixer elements */ 341790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { 3418eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 3419eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 3420eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 3421eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 3422eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 3423eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 3424eb7188caSLydia Wang HDA_INPUT), 3425eb7188caSLydia Wang { 3426eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3427eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 3428eb7188caSLydia Wang * So call somewhat different.. 3429eb7188caSLydia Wang */ 3430eb7188caSLydia Wang .name = "Input Source", 3431eb7188caSLydia Wang .count = 2, 3432eb7188caSLydia Wang .info = via_mux_enum_info, 3433eb7188caSLydia Wang .get = via_mux_enum_get, 3434eb7188caSLydia Wang .put = via_mux_enum_put, 3435eb7188caSLydia Wang }, 3436eb7188caSLydia Wang { } /* end */ 3437eb7188caSLydia Wang }; 3438eb7188caSLydia Wang 343990dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 3440eb7188caSLydia Wang /* 3441eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3442eb7188caSLydia Wang */ 3443eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3444eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3445eb7188caSLydia Wang 34464ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 34474ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 3448eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3449eb7188caSLydia Wang * mixer widget 3450eb7188caSLydia Wang */ 3451eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3452eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3453eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3454eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3455eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 34564ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 3457eb7188caSLydia Wang 3458eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 3459eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3460eb7188caSLydia Wang /* PW9 PW10 Output enable */ 3461eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3462eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3463eb7188caSLydia Wang /* PW11 Input enable */ 3464eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 3465eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 3466eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 3467eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 3468eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3469eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3470eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3471eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3472eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3473eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3474eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3475eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3476eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3477eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3478eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 3479eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 3480eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 3481eb7188caSLydia Wang /* Unmute MW4's index 0 */ 3482eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3483eb7188caSLydia Wang { } 3484eb7188caSLydia Wang }; 3485eb7188caSLydia Wang 3486eb7188caSLydia Wang 348790dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 3488eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 3489eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3490eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3491eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3492eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3493eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3494eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3495eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3496eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3497eb7188caSLydia Wang { } 3498eb7188caSLydia Wang }; 3499eb7188caSLydia Wang 3500eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 3501eb7188caSLydia Wang { 3502eb7188caSLydia Wang struct via_spec *spec = codec->spec; 3503eb7188caSLydia Wang int err; 3504eb7188caSLydia Wang 3505eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3506eb7188caSLydia Wang 3507eb7188caSLydia Wang if (err < 0) 3508eb7188caSLydia Wang return err; 35094a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3510eb7188caSLydia Wang if (err < 0) 3511eb7188caSLydia Wang return err; 3512eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3513eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 3514eb7188caSLydia Wang 35154a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3516eb7188caSLydia Wang if (err < 0) 3517eb7188caSLydia Wang return err; 35184a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3519eb7188caSLydia Wang if (err < 0) 3520eb7188caSLydia Wang return err; 3521620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3522eb7188caSLydia Wang if (err < 0) 3523eb7188caSLydia Wang return err; 3524eb7188caSLydia Wang 3525eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3526eb7188caSLydia Wang 3527eb7188caSLydia Wang fill_dig_outs(codec); 3528eb7188caSLydia Wang 3529eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 3530eb7188caSLydia Wang spec->dig_in_nid = 0x13; 3531eb7188caSLydia Wang 3532eb7188caSLydia Wang if (spec->kctls.list) 3533eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3534eb7188caSLydia Wang 3535eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 3536eb7188caSLydia Wang 3537eb7188caSLydia Wang if (spec->hp_mux) 35383d83e577STakashi Iwai via_hp_build(codec); 3539eb7188caSLydia Wang 3540f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3541f4a7828bSTakashi Iwai if (err < 0) 3542f4a7828bSTakashi Iwai return err; 3543eb7188caSLydia Wang 3544eb7188caSLydia Wang return 1; 3545eb7188caSLydia Wang } 3546eb7188caSLydia Wang 3547eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 354890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 3549eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 3550eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 3551eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 3552eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 3553eb7188caSLydia Wang { } /* end */ 3554eb7188caSLydia Wang }; 3555eb7188caSLydia Wang #endif 3556eb7188caSLydia Wang 35573e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 35583e95b9abSLydia Wang { 35593e95b9abSLydia Wang struct via_spec *spec = codec->spec; 35603e95b9abSLydia Wang int imux_is_smixer; 35613e95b9abSLydia Wang unsigned int parm; 35623e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 35633e95b9abSLydia Wang imux_is_smixer = 35643e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 35653e95b9abSLydia Wang /* inputs */ 35663e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 35673e95b9abSLydia Wang parm = AC_PWRST_D3; 35683e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 35693e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 35703e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 35713e95b9abSLydia Wang if (imux_is_smixer) 35723e95b9abSLydia Wang parm = AC_PWRST_D0; 35733e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 35743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 35753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 35763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 35773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 35783e95b9abSLydia Wang 35793e95b9abSLydia Wang /* outputs */ 35803e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 35813e95b9abSLydia Wang parm = AC_PWRST_D3; 35823e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 35833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 35843e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 35853e95b9abSLydia Wang 35863e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 35873e95b9abSLydia Wang parm = AC_PWRST_D3; 35883e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 35893e95b9abSLydia Wang if (spec->smart51_enabled) 35903e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 35913e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 35923e95b9abSLydia Wang 35933e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 35943e95b9abSLydia Wang parm = AC_PWRST_D3; 35953e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 35963e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 35973e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 35983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 35993e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 36003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 36013e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 36023e95b9abSLydia Wang 36033e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 36043e95b9abSLydia Wang parm = AC_PWRST_D3; 36053e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 36063e95b9abSLydia Wang if (spec->smart51_enabled) 36073e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 36083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 36093e95b9abSLydia Wang 36103e95b9abSLydia Wang if (spec->hp_independent_mode) { 36113e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 36123e95b9abSLydia Wang parm = AC_PWRST_D3; 36133e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 36143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 36153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 36173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36183e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 36193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36203e95b9abSLydia Wang } 36213e95b9abSLydia Wang } 36223e95b9abSLydia Wang 3623eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 3624eb7188caSLydia Wang { 3625eb7188caSLydia Wang struct via_spec *spec; 3626eb7188caSLydia Wang int err; 3627eb7188caSLydia Wang 3628eb7188caSLydia Wang /* create a codec specific record */ 36295b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3630eb7188caSLydia Wang if (spec == NULL) 3631eb7188caSLydia Wang return -ENOMEM; 3632eb7188caSLydia Wang 3633620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3634620e2b28STakashi Iwai 3635eb7188caSLydia Wang /* automatic parse from the BIOS config */ 3636eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 3637eb7188caSLydia Wang if (err < 0) { 3638eb7188caSLydia Wang via_free(codec); 3639eb7188caSLydia Wang return err; 3640eb7188caSLydia Wang } else if (!err) { 3641eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3642eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 3643eb7188caSLydia Wang } 3644eb7188caSLydia Wang 3645eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 3646eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 3647eb7188caSLydia Wang 3648a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3649bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 3650bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 3651eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 3652eb7188caSLydia Wang spec->num_mixers++; 3653eb7188caSLydia Wang } 3654eb7188caSLydia Wang 3655eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 3656eb7188caSLydia Wang 3657eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 36580f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3659eb7188caSLydia Wang 3660eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3661eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 3662eb7188caSLydia Wang #endif 3663eb7188caSLydia Wang 36643e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 36653e95b9abSLydia Wang 3666eb7188caSLydia Wang return 0; 3667eb7188caSLydia Wang } 3668f3db423dSLydia Wang 3669f3db423dSLydia Wang /* Patch for VT1716S */ 3670f3db423dSLydia Wang 3671f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 3672f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 3673f3db423dSLydia Wang { 3674f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 3675f3db423dSLydia Wang uinfo->count = 1; 3676f3db423dSLydia Wang uinfo->value.integer.min = 0; 3677f3db423dSLydia Wang uinfo->value.integer.max = 1; 3678f3db423dSLydia Wang return 0; 3679f3db423dSLydia Wang } 3680f3db423dSLydia Wang 3681f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 3682f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3683f3db423dSLydia Wang { 3684f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3685f3db423dSLydia Wang int index = 0; 3686f3db423dSLydia Wang 3687f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 3688f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 3689f3db423dSLydia Wang if (index != -1) 3690f3db423dSLydia Wang *ucontrol->value.integer.value = index; 3691f3db423dSLydia Wang 3692f3db423dSLydia Wang return 0; 3693f3db423dSLydia Wang } 3694f3db423dSLydia Wang 3695f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 3696f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3697f3db423dSLydia Wang { 3698f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3699f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3700f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 3701f3db423dSLydia Wang 3702f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 3703f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 3704f3db423dSLydia Wang spec->dmic_enabled = index; 37053e95b9abSLydia Wang set_widgets_power_state(codec); 3706f3db423dSLydia Wang return 1; 3707f3db423dSLydia Wang } 3708f3db423dSLydia Wang 3709f3db423dSLydia Wang /* capture mixer elements */ 371090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { 3711f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3712f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3713f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3714f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3715f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 3716f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 3717f3db423dSLydia Wang HDA_INPUT), 3718f3db423dSLydia Wang { 3719f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3720f3db423dSLydia Wang .name = "Input Source", 3721f3db423dSLydia Wang .count = 1, 3722f3db423dSLydia Wang .info = via_mux_enum_info, 3723f3db423dSLydia Wang .get = via_mux_enum_get, 3724f3db423dSLydia Wang .put = via_mux_enum_put, 3725f3db423dSLydia Wang }, 3726f3db423dSLydia Wang { } /* end */ 3727f3db423dSLydia Wang }; 3728f3db423dSLydia Wang 372990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 3730f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 3731f3db423dSLydia Wang { 3732f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3733f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 37345b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 3735f3db423dSLydia Wang .count = 1, 3736f3db423dSLydia Wang .info = vt1716s_dmic_info, 3737f3db423dSLydia Wang .get = vt1716s_dmic_get, 3738f3db423dSLydia Wang .put = vt1716s_dmic_put, 3739f3db423dSLydia Wang }, 3740f3db423dSLydia Wang {} /* end */ 3741f3db423dSLydia Wang }; 3742f3db423dSLydia Wang 3743f3db423dSLydia Wang 3744f3db423dSLydia Wang /* mono-out mixer elements */ 374590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3746f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3747f3db423dSLydia Wang { } /* end */ 3748f3db423dSLydia Wang }; 3749f3db423dSLydia Wang 375090dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 3751f3db423dSLydia Wang /* 3752f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3753f3db423dSLydia Wang */ 3754f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3755f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3756f3db423dSLydia Wang 3757f3db423dSLydia Wang 3758f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3759f3db423dSLydia Wang * mixer widget 3760f3db423dSLydia Wang */ 3761f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3762f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3763f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3764f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3765f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 3766f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3767f3db423dSLydia Wang 3768f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 3769f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 3770f3db423dSLydia Wang 3771f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 3772f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3773f3db423dSLydia Wang 3774f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 3775f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 3776f3db423dSLydia Wang 3777f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 3778f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3779f3db423dSLydia Wang 3780f3db423dSLydia Wang /* PW9 PW10 Output enable */ 3781f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3782f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3783f3db423dSLydia Wang 3784f3db423dSLydia Wang /* Unmute SW1, PW12 */ 3785f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3786f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 3787f3db423dSLydia Wang /* PW12 Output enable */ 3788f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3789f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 3790f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 3791f3db423dSLydia Wang /* don't bybass mixer */ 3792f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 3793f3db423dSLydia Wang /* Enable mono output */ 3794f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 3795f3db423dSLydia Wang { } 3796f3db423dSLydia Wang }; 3797f3db423dSLydia Wang 3798f3db423dSLydia Wang 379990dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 3800f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3801f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3802f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3803f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3804f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3805f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 3806f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 3807f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3808f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3809f3db423dSLydia Wang { } 3810f3db423dSLydia Wang }; 3811f3db423dSLydia Wang 3812f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 3813f3db423dSLydia Wang { 3814f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3815f3db423dSLydia Wang int err; 3816f3db423dSLydia Wang 3817f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3818f3db423dSLydia Wang if (err < 0) 3819f3db423dSLydia Wang return err; 38204a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3821f3db423dSLydia Wang if (err < 0) 3822f3db423dSLydia Wang return err; 3823f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3824f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 3825f3db423dSLydia Wang 38264a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3827f3db423dSLydia Wang if (err < 0) 3828f3db423dSLydia Wang return err; 38294a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3830f3db423dSLydia Wang if (err < 0) 3831f3db423dSLydia Wang return err; 3832620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3833f3db423dSLydia Wang if (err < 0) 3834f3db423dSLydia Wang return err; 3835f3db423dSLydia Wang 3836f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3837f3db423dSLydia Wang 3838f3db423dSLydia Wang fill_dig_outs(codec); 3839f3db423dSLydia Wang 3840f3db423dSLydia Wang if (spec->kctls.list) 3841f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3842f3db423dSLydia Wang 3843f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 3844f3db423dSLydia Wang 3845f3db423dSLydia Wang if (spec->hp_mux) 38463d83e577STakashi Iwai via_hp_build(codec); 3847f3db423dSLydia Wang 3848f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3849f4a7828bSTakashi Iwai if (err < 0) 3850f4a7828bSTakashi Iwai return err; 3851f3db423dSLydia Wang 3852f3db423dSLydia Wang return 1; 3853f3db423dSLydia Wang } 3854f3db423dSLydia Wang 3855f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 385690dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 3857f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 3858f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 3859f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 3860f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 3861f3db423dSLydia Wang { } /* end */ 3862f3db423dSLydia Wang }; 3863f3db423dSLydia Wang #endif 3864f3db423dSLydia Wang 38653e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 38663e95b9abSLydia Wang { 38673e95b9abSLydia Wang struct via_spec *spec = codec->spec; 38683e95b9abSLydia Wang int imux_is_smixer; 38693e95b9abSLydia Wang unsigned int parm; 38703e95b9abSLydia Wang unsigned int mono_out, present; 38713e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 38723e95b9abSLydia Wang imux_is_smixer = 38733e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 38743e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 38753e95b9abSLydia Wang /* inputs */ 38763e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 38773e95b9abSLydia Wang parm = AC_PWRST_D3; 38783e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 38793e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 38803e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 38813e95b9abSLydia Wang if (imux_is_smixer) 38823e95b9abSLydia Wang parm = AC_PWRST_D0; 38833e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 38843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 38853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 38863e95b9abSLydia Wang 38873e95b9abSLydia Wang parm = AC_PWRST_D3; 38883e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 38893e95b9abSLydia Wang /* PW11 (22h) */ 38903e95b9abSLydia Wang if (spec->dmic_enabled) 38913e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 38923e95b9abSLydia Wang else 38933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 38943e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 38953e95b9abSLydia Wang 38963e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 38973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 38983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 38993e95b9abSLydia Wang 39003e95b9abSLydia Wang /* outputs */ 39013e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 39023e95b9abSLydia Wang parm = AC_PWRST_D3; 39033e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 39043e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 39053e95b9abSLydia Wang if (spec->smart51_enabled) 39063e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 39073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 39083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 39093e95b9abSLydia Wang 39103e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 39113e95b9abSLydia Wang parm = AC_PWRST_D3; 39123e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 39133e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 39143e95b9abSLydia Wang if (spec->smart51_enabled) 39153e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 39163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 39173e95b9abSLydia Wang 39183e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 39193e95b9abSLydia Wang if (spec->smart51_enabled) 39203e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 39213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 39223e95b9abSLydia Wang 39233e95b9abSLydia Wang /* Mono out */ 39243e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 39253e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 39263e95b9abSLydia Wang 39273e95b9abSLydia Wang if (present) 39283e95b9abSLydia Wang mono_out = 0; 39293e95b9abSLydia Wang else { 39303e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 39313e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 39323e95b9abSLydia Wang mono_out = 0; 39333e95b9abSLydia Wang else 39343e95b9abSLydia Wang mono_out = 1; 39353e95b9abSLydia Wang } 39363e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 39373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 39383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 39393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 39403e95b9abSLydia Wang 39413e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 39423e95b9abSLydia Wang parm = AC_PWRST_D3; 39433e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 39443e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 39453e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 39463e95b9abSLydia Wang if (spec->hp_independent_mode) 39473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 39483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 39493e95b9abSLydia Wang 39503e95b9abSLydia Wang /* force to D0 for internal Speaker */ 39513e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 39523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 39533e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 39543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 39553e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 39563e95b9abSLydia Wang } 39573e95b9abSLydia Wang 3958f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3959f3db423dSLydia Wang { 3960f3db423dSLydia Wang struct via_spec *spec; 3961f3db423dSLydia Wang int err; 3962f3db423dSLydia Wang 3963f3db423dSLydia Wang /* create a codec specific record */ 39645b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3965f3db423dSLydia Wang if (spec == NULL) 3966f3db423dSLydia Wang return -ENOMEM; 3967f3db423dSLydia Wang 3968620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3969620e2b28STakashi Iwai 3970f3db423dSLydia Wang /* automatic parse from the BIOS config */ 3971f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 3972f3db423dSLydia Wang if (err < 0) { 3973f3db423dSLydia Wang via_free(codec); 3974f3db423dSLydia Wang return err; 3975f3db423dSLydia Wang } else if (!err) { 3976f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3977f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 3978f3db423dSLydia Wang } 3979f3db423dSLydia Wang 3980f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 3981f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 3982f3db423dSLydia Wang 3983a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3984f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 3985f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3986f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 3987f3db423dSLydia Wang spec->num_mixers++; 3988f3db423dSLydia Wang } 3989f3db423dSLydia Wang 3990f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3991f3db423dSLydia Wang spec->num_mixers++; 3992f3db423dSLydia Wang 3993f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3994f3db423dSLydia Wang 3995f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3996f3db423dSLydia Wang 3997f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 39980f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3999f3db423dSLydia Wang 4000f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4001f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 4002f3db423dSLydia Wang #endif 4003f3db423dSLydia Wang 40043e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 4005f3db423dSLydia Wang return 0; 4006f3db423dSLydia Wang } 400725eaba2fSLydia Wang 400825eaba2fSLydia Wang /* for vt2002P */ 400925eaba2fSLydia Wang 401025eaba2fSLydia Wang /* capture mixer elements */ 401190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { 401225eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 401325eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 401425eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 401525eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 401625eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 401725eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 401825eaba2fSLydia Wang HDA_INPUT), 401925eaba2fSLydia Wang { 402025eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 402125eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 402225eaba2fSLydia Wang * So call somewhat different.. 402325eaba2fSLydia Wang */ 402425eaba2fSLydia Wang /* .name = "Capture Source", */ 402525eaba2fSLydia Wang .name = "Input Source", 402625eaba2fSLydia Wang .count = 2, 402725eaba2fSLydia Wang .info = via_mux_enum_info, 402825eaba2fSLydia Wang .get = via_mux_enum_get, 402925eaba2fSLydia Wang .put = via_mux_enum_put, 403025eaba2fSLydia Wang }, 403125eaba2fSLydia Wang { } /* end */ 403225eaba2fSLydia Wang }; 403325eaba2fSLydia Wang 403490dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 4035eadb9a80SLydia Wang /* Class-D speaker related verbs */ 4036eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 4037eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 4038eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 403925eaba2fSLydia Wang /* 404025eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 404125eaba2fSLydia Wang */ 404225eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 404325eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 404425eaba2fSLydia Wang 404525eaba2fSLydia Wang 404625eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 404725eaba2fSLydia Wang * mixer widget 404825eaba2fSLydia Wang */ 404925eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 405025eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 405125eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 405225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 405325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 405425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 405525eaba2fSLydia Wang 405625eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 405725eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 405825eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 405925eaba2fSLydia Wang 406025eaba2fSLydia Wang /* PW9 Output enable */ 406125eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 406225eaba2fSLydia Wang 406325eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 406425eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 406525eaba2fSLydia Wang 406625eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 406725eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 406825eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 406925eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 407025eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 407125eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 407225eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 407325eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 407425eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 407525eaba2fSLydia Wang 407625eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 407725eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 407825eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 407925eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 408025eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 408125eaba2fSLydia Wang 408225eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 408325eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 408425eaba2fSLydia Wang 408525eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 408625eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 408725eaba2fSLydia Wang { } 408825eaba2fSLydia Wang }; 408990dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 409011890956SLydia Wang /* 409111890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 409211890956SLydia Wang */ 409311890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 409411890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 409511890956SLydia Wang 409611890956SLydia Wang 409711890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 409811890956SLydia Wang * mixer widget 409911890956SLydia Wang */ 410011890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 410111890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 410211890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 410311890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 410411890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 410511890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 410611890956SLydia Wang 410711890956SLydia Wang /* MUX Indices: Mic = 0 */ 410811890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 410911890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 411011890956SLydia Wang 411111890956SLydia Wang /* PW9 Output enable */ 411211890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 411311890956SLydia Wang 411411890956SLydia Wang /* Enable Boost Volume backdoor */ 411511890956SLydia Wang {0x1, 0xfb9, 0x24}, 411611890956SLydia Wang 411711890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 411811890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 411911890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 412011890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 412111890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 412211890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 412311890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 412411890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 412511890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 412611890956SLydia Wang 412711890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 412811890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 412911890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 413011890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 413111890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 413211890956SLydia Wang 413311890956SLydia Wang /* set PW0 index=0 (MW0) */ 413411890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 413511890956SLydia Wang 413611890956SLydia Wang /* Enable AOW0 to MW9 */ 413711890956SLydia Wang {0x1, 0xfb8, 0x88}, 413811890956SLydia Wang { } 413911890956SLydia Wang }; 414025eaba2fSLydia Wang 414125eaba2fSLydia Wang 414290dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 414325eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 414425eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 414525eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 414625eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 414725eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 414825eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 414925eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 415025eaba2fSLydia Wang { } 415125eaba2fSLydia Wang }; 415290dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 415311890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 415411890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 415511890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 415611890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 415711890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 415811890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 415911890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 416011890956SLydia Wang { } 416111890956SLydia Wang }; 416225eaba2fSLydia Wang 416325eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 416425eaba2fSLydia Wang { 416525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 416625eaba2fSLydia Wang int err; 416725eaba2fSLydia Wang 416825eaba2fSLydia Wang 416925eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 417025eaba2fSLydia Wang if (err < 0) 417125eaba2fSLydia Wang return err; 417225eaba2fSLydia Wang 41734a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 417425eaba2fSLydia Wang if (err < 0) 417525eaba2fSLydia Wang return err; 417625eaba2fSLydia Wang 417725eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 417825eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 417925eaba2fSLydia Wang 41804a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 418125eaba2fSLydia Wang if (err < 0) 418225eaba2fSLydia Wang return err; 41834a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 418425eaba2fSLydia Wang if (err < 0) 418525eaba2fSLydia Wang return err; 4186620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 418725eaba2fSLydia Wang if (err < 0) 418825eaba2fSLydia Wang return err; 418925eaba2fSLydia Wang 419025eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 419125eaba2fSLydia Wang 419225eaba2fSLydia Wang fill_dig_outs(codec); 419325eaba2fSLydia Wang 419425eaba2fSLydia Wang if (spec->kctls.list) 419525eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 419625eaba2fSLydia Wang 419725eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 419825eaba2fSLydia Wang 419925eaba2fSLydia Wang if (spec->hp_mux) 42003d83e577STakashi Iwai via_hp_build(codec); 420125eaba2fSLydia Wang 420225eaba2fSLydia Wang return 1; 420325eaba2fSLydia Wang } 420425eaba2fSLydia Wang 420525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 420690dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 420725eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 420825eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 420925eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 421025eaba2fSLydia Wang { } /* end */ 421125eaba2fSLydia Wang }; 421225eaba2fSLydia Wang #endif 421325eaba2fSLydia Wang 42143e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 42153e95b9abSLydia Wang { 42163e95b9abSLydia Wang struct via_spec *spec = codec->spec; 42173e95b9abSLydia Wang int imux_is_smixer; 42183e95b9abSLydia Wang unsigned int parm; 42193e95b9abSLydia Wang unsigned int present; 42203e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 42213e95b9abSLydia Wang imux_is_smixer = 42223e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 42233e95b9abSLydia Wang /* inputs */ 42243e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 42253e95b9abSLydia Wang parm = AC_PWRST_D3; 42263e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 42273e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 42283e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 42293e95b9abSLydia Wang parm = AC_PWRST_D0; 42303e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 42313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 42323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 42333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 42343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 42353e95b9abSLydia Wang 42363e95b9abSLydia Wang /* outputs */ 42373e95b9abSLydia Wang /* AOW0 (8h)*/ 42383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 42393e95b9abSLydia Wang 424011890956SLydia Wang if (spec->codec_type == VT1802) { 424111890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 424211890956SLydia Wang parm = AC_PWRST_D3; 424311890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 424411890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 424511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 424611890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 424711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 424811890956SLydia Wang } else { 42493e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 42503e95b9abSLydia Wang parm = AC_PWRST_D3; 42513e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 42523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 42533e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 42543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 42553e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 425611890956SLydia Wang } 42573e95b9abSLydia Wang 425811890956SLydia Wang if (spec->codec_type == VT1802) { 425911890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 426011890956SLydia Wang parm = AC_PWRST_D3; 426111890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 426211890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 426311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 426411890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 426511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 426611890956SLydia Wang } else { 42673e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 42683e95b9abSLydia Wang parm = AC_PWRST_D3; 42693e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 42703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 42713e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 42723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 42733e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 427411890956SLydia Wang } 42753e95b9abSLydia Wang 42763e95b9abSLydia Wang if (spec->hp_independent_mode) 42773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 42783e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 42793e95b9abSLydia Wang 42803e95b9abSLydia Wang /* Class-D */ 42813e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 42823e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 42833e95b9abSLydia Wang 42843e95b9abSLydia Wang parm = AC_PWRST_D3; 42853e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 42863e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 428711890956SLydia Wang if (spec->codec_type == VT1802) 428811890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 428911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 429011890956SLydia Wang else 42913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 42923e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 42933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 42943e95b9abSLydia Wang 42953e95b9abSLydia Wang /* Mono Out */ 42963e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 42973e95b9abSLydia Wang 42983e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 429911890956SLydia Wang if (spec->codec_type == VT1802) { 430011890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 430111890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 430211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 430311890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 430411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 430511890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 430611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 430711890956SLydia Wang } else { 43083e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 43093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 43103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 43123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 43143e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 431511890956SLydia Wang } 43163e95b9abSLydia Wang /* MW9 (21h) */ 43173e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 43183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 43193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 43203e95b9abSLydia Wang else 43213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 43223e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 43233e95b9abSLydia Wang } 432425eaba2fSLydia Wang 432525eaba2fSLydia Wang /* patch for vt2002P */ 432625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 432725eaba2fSLydia Wang { 432825eaba2fSLydia Wang struct via_spec *spec; 432925eaba2fSLydia Wang int err; 433025eaba2fSLydia Wang 433125eaba2fSLydia Wang /* create a codec specific record */ 43325b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 433325eaba2fSLydia Wang if (spec == NULL) 433425eaba2fSLydia Wang return -ENOMEM; 433525eaba2fSLydia Wang 4336620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4337620e2b28STakashi Iwai 433825eaba2fSLydia Wang /* automatic parse from the BIOS config */ 433925eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 434025eaba2fSLydia Wang if (err < 0) { 434125eaba2fSLydia Wang via_free(codec); 434225eaba2fSLydia Wang return err; 434325eaba2fSLydia Wang } else if (!err) { 434425eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 434525eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 434625eaba2fSLydia Wang } 434725eaba2fSLydia Wang 434811890956SLydia Wang if (spec->codec_type == VT1802) 434911890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 435011890956SLydia Wang vt1802_volume_init_verbs; 435111890956SLydia Wang else 435211890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 435311890956SLydia Wang vt2002P_volume_init_verbs; 435425eaba2fSLydia Wang 435511890956SLydia Wang if (spec->codec_type == VT1802) 435611890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 435711890956SLydia Wang vt1802_uniwill_init_verbs; 435811890956SLydia Wang else 435911890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 436011890956SLydia Wang vt2002P_uniwill_init_verbs; 436111890956SLydia Wang 4362a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 436325eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 436425eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 436525eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 436625eaba2fSLydia Wang spec->num_mixers++; 436725eaba2fSLydia Wang } 436825eaba2fSLydia Wang 436925eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 437025eaba2fSLydia Wang 437125eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 43720f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 437325eaba2fSLydia Wang 437425eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 437525eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 437625eaba2fSLydia Wang #endif 437725eaba2fSLydia Wang 43783e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 437925eaba2fSLydia Wang return 0; 438025eaba2fSLydia Wang } 4381ab6734e7SLydia Wang 4382ab6734e7SLydia Wang /* for vt1812 */ 4383ab6734e7SLydia Wang 4384ab6734e7SLydia Wang /* capture mixer elements */ 438590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = { 4386ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4387ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4388ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4389ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4390ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4391ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 4392ab6734e7SLydia Wang HDA_INPUT), 4393ab6734e7SLydia Wang { 4394ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4395ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4396ab6734e7SLydia Wang * So call somewhat different.. 4397ab6734e7SLydia Wang */ 4398ab6734e7SLydia Wang .name = "Input Source", 4399ab6734e7SLydia Wang .count = 2, 4400ab6734e7SLydia Wang .info = via_mux_enum_info, 4401ab6734e7SLydia Wang .get = via_mux_enum_get, 4402ab6734e7SLydia Wang .put = via_mux_enum_put, 4403ab6734e7SLydia Wang }, 4404ab6734e7SLydia Wang { } /* end */ 4405ab6734e7SLydia Wang }; 4406ab6734e7SLydia Wang 440790dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 4408ab6734e7SLydia Wang /* 4409ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4410ab6734e7SLydia Wang */ 4411ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4412ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4413ab6734e7SLydia Wang 4414ab6734e7SLydia Wang 4415ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4416ab6734e7SLydia Wang * mixer widget 4417ab6734e7SLydia Wang */ 4418ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4419ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4420ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4421ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4422ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4423ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4424ab6734e7SLydia Wang 4425ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 4426ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 4427ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 4428ab6734e7SLydia Wang 4429ab6734e7SLydia Wang /* PW9 Output enable */ 4430ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4431ab6734e7SLydia Wang 4432ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 4433ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 4434ab6734e7SLydia Wang 4435ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 4436ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4437ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4438ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4439ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4440ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4441ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4442ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4443ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4444ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4445ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4446ab6734e7SLydia Wang 4447ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 4448ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 4449ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 4450ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 4451ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 4452ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 4453ab6734e7SLydia Wang 4454ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 4455ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 4456ab6734e7SLydia Wang { } 4457ab6734e7SLydia Wang }; 4458ab6734e7SLydia Wang 4459ab6734e7SLydia Wang 446090dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 4461ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 4462ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4463ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 4464ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4465ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4466ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4467ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4468ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4469ab6734e7SLydia Wang { } 4470ab6734e7SLydia Wang }; 4471ab6734e7SLydia Wang 4472ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 4473ab6734e7SLydia Wang { 4474ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 4475ab6734e7SLydia Wang int err; 4476ab6734e7SLydia Wang 4477ab6734e7SLydia Wang 4478ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4479ab6734e7SLydia Wang if (err < 0) 4480ab6734e7SLydia Wang return err; 4481ab6734e7SLydia Wang fill_dig_outs(codec); 44824a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 4483ab6734e7SLydia Wang if (err < 0) 4484ab6734e7SLydia Wang return err; 4485ab6734e7SLydia Wang 4486ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 4487ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 4488ab6734e7SLydia Wang 44894a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 4490ab6734e7SLydia Wang if (err < 0) 4491ab6734e7SLydia Wang return err; 44924a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 4493ab6734e7SLydia Wang if (err < 0) 4494ab6734e7SLydia Wang return err; 4495620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 4496ab6734e7SLydia Wang if (err < 0) 4497ab6734e7SLydia Wang return err; 4498ab6734e7SLydia Wang 4499ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4500ab6734e7SLydia Wang 4501ab6734e7SLydia Wang fill_dig_outs(codec); 4502ab6734e7SLydia Wang 4503ab6734e7SLydia Wang if (spec->kctls.list) 4504ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4505ab6734e7SLydia Wang 4506ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 4507ab6734e7SLydia Wang 4508ab6734e7SLydia Wang if (spec->hp_mux) 45093d83e577STakashi Iwai via_hp_build(codec); 4510ab6734e7SLydia Wang 4511ab6734e7SLydia Wang return 1; 4512ab6734e7SLydia Wang } 4513ab6734e7SLydia Wang 4514ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 451590dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 4516ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 4517ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 4518ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 4519ab6734e7SLydia Wang { } /* end */ 4520ab6734e7SLydia Wang }; 4521ab6734e7SLydia Wang #endif 4522ab6734e7SLydia Wang 45233e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 45243e95b9abSLydia Wang { 45253e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45263e95b9abSLydia Wang int imux_is_smixer = 45273e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 45283e95b9abSLydia Wang unsigned int parm; 45293e95b9abSLydia Wang unsigned int present; 45303e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 45313e95b9abSLydia Wang imux_is_smixer = 45323e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 45333e95b9abSLydia Wang /* inputs */ 45343e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45353e95b9abSLydia Wang parm = AC_PWRST_D3; 45363e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45373e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45383e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45393e95b9abSLydia Wang parm = AC_PWRST_D0; 45403e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 45413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45453e95b9abSLydia Wang 45463e95b9abSLydia Wang /* outputs */ 45473e95b9abSLydia Wang /* AOW0 (8h)*/ 45483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 45493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 45503e95b9abSLydia Wang 45513e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 45523e95b9abSLydia Wang parm = AC_PWRST_D3; 45533e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 45553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 45563e95b9abSLydia Wang 45573e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 45583e95b9abSLydia Wang parm = AC_PWRST_D3; 45593e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 45603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 45613e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 45623e95b9abSLydia Wang if (spec->hp_independent_mode) 45633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 45643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 45653e95b9abSLydia Wang 45663e95b9abSLydia Wang /* Internal Speaker */ 45673e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 45683e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 45693e95b9abSLydia Wang 45703e95b9abSLydia Wang parm = AC_PWRST_D3; 45713e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 45723e95b9abSLydia Wang if (present) { 45733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 45743e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 45753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 45763e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 45773e95b9abSLydia Wang } else { 45783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 45793e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 45803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 45813e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 45823e95b9abSLydia Wang } 45833e95b9abSLydia Wang 45843e95b9abSLydia Wang 45853e95b9abSLydia Wang /* Mono Out */ 45863e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 45873e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 45883e95b9abSLydia Wang 45893e95b9abSLydia Wang parm = AC_PWRST_D3; 45903e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 45913e95b9abSLydia Wang if (present) { 45923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 45933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 45943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 45953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 45963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 45973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 45983e95b9abSLydia Wang } else { 45993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 46003e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 46023e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 46043e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46053e95b9abSLydia Wang } 46063e95b9abSLydia Wang 46073e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 46083e95b9abSLydia Wang parm = AC_PWRST_D3; 46093e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 46103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 46113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 46123e95b9abSLydia Wang 46133e95b9abSLydia Wang } 4614ab6734e7SLydia Wang 4615ab6734e7SLydia Wang /* patch for vt1812 */ 4616ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 4617ab6734e7SLydia Wang { 4618ab6734e7SLydia Wang struct via_spec *spec; 4619ab6734e7SLydia Wang int err; 4620ab6734e7SLydia Wang 4621ab6734e7SLydia Wang /* create a codec specific record */ 46225b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4623ab6734e7SLydia Wang if (spec == NULL) 4624ab6734e7SLydia Wang return -ENOMEM; 4625ab6734e7SLydia Wang 4626620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4627620e2b28STakashi Iwai 4628ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 4629ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 4630ab6734e7SLydia Wang if (err < 0) { 4631ab6734e7SLydia Wang via_free(codec); 4632ab6734e7SLydia Wang return err; 4633ab6734e7SLydia Wang } else if (!err) { 4634ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4635ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 4636ab6734e7SLydia Wang } 4637ab6734e7SLydia Wang 4638ab6734e7SLydia Wang 4639ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 4640ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 4641ab6734e7SLydia Wang 4642a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4643ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4644ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4645ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 4646ab6734e7SLydia Wang spec->num_mixers++; 4647ab6734e7SLydia Wang } 4648ab6734e7SLydia Wang 4649ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 4650ab6734e7SLydia Wang 4651ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 46520f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4653ab6734e7SLydia Wang 4654ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4655ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 4656ab6734e7SLydia Wang #endif 4657ab6734e7SLydia Wang 46583e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 4659ab6734e7SLydia Wang return 0; 4660ab6734e7SLydia Wang } 4661ab6734e7SLydia Wang 4662c577b8a1SJoseph Chan /* 4663c577b8a1SJoseph Chan * patch entries 4664c577b8a1SJoseph Chan */ 466590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 46663218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 46673218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 46683218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 46693218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 46703218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 4671f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 46723218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 4673f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 46743218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 4675f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 46763218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 4677f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 46783218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 4679f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 46803218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 4681f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 46823218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 4683f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 46843218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 4685f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 46863218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 4687f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 46883218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 4689f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 46903218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 4691f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 46923218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 4693f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 46943218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 4695f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 46963218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 4697f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 46983218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 4699f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47003218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 4701f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47023218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 4703d949cac1SHarald Welte .patch = patch_vt1708S}, 47043218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 4705d949cac1SHarald Welte .patch = patch_vt1708S}, 47063218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 4707d949cac1SHarald Welte .patch = patch_vt1708S}, 47083218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 4709d949cac1SHarald Welte .patch = patch_vt1708S}, 4710bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 4711d949cac1SHarald Welte .patch = patch_vt1708S}, 47123218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 4713d949cac1SHarald Welte .patch = patch_vt1708S}, 47143218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 4715d949cac1SHarald Welte .patch = patch_vt1708S}, 47163218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 4717d949cac1SHarald Welte .patch = patch_vt1708S}, 47183218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 4719d949cac1SHarald Welte .patch = patch_vt1702}, 47203218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 4721d949cac1SHarald Welte .patch = patch_vt1702}, 47223218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 4723d949cac1SHarald Welte .patch = patch_vt1702}, 47243218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 4725d949cac1SHarald Welte .patch = patch_vt1702}, 47263218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 4727d949cac1SHarald Welte .patch = patch_vt1702}, 47283218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 4729d949cac1SHarald Welte .patch = patch_vt1702}, 47303218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 4731d949cac1SHarald Welte .patch = patch_vt1702}, 47323218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 4733d949cac1SHarald Welte .patch = patch_vt1702}, 4734eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 4735eb7188caSLydia Wang .patch = patch_vt1718S}, 4736eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 4737eb7188caSLydia Wang .patch = patch_vt1718S}, 4738bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 4739bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4740bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 4741bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4742f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 4743f3db423dSLydia Wang .patch = patch_vt1716S}, 4744f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 4745f3db423dSLydia Wang .patch = patch_vt1716S}, 474625eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 474725eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 4748ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 474936dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 475036dd5c4aSLydia Wang .patch = patch_vt1708S}, 475111890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 475211890956SLydia Wang .patch = patch_vt2002P}, 475311890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 475411890956SLydia Wang .patch = patch_vt2002P}, 4755c577b8a1SJoseph Chan {} /* terminator */ 4756c577b8a1SJoseph Chan }; 47571289e9e8STakashi Iwai 47581289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 47591289e9e8STakashi Iwai 47601289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 47611289e9e8STakashi Iwai .preset = snd_hda_preset_via, 47621289e9e8STakashi Iwai .owner = THIS_MODULE, 47631289e9e8STakashi Iwai }; 47641289e9e8STakashi Iwai 47651289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 47661289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 47671289e9e8STakashi Iwai 47681289e9e8STakashi Iwai static int __init patch_via_init(void) 47691289e9e8STakashi Iwai { 47701289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 47711289e9e8STakashi Iwai } 47721289e9e8STakashi Iwai 47731289e9e8STakashi Iwai static void __exit patch_via_exit(void) 47741289e9e8STakashi Iwai { 47751289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 47761289e9e8STakashi Iwai } 47771289e9e8STakashi Iwai 47781289e9e8STakashi Iwai module_init(patch_via_init) 47791289e9e8STakashi Iwai module_exit(patch_via_exit) 4780