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]; 125*7eb56e84STakashi Iwai char stream_name_hp[32]; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12790dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1281f2e99feSLydia Wang 12982673bc8STakashi Iwai char stream_name_digital[32]; 13090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 13190dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1321f2e99feSLydia Wang 1331f2e99feSLydia Wang /* playback */ 1341f2e99feSLydia Wang struct hda_multi_out multiout; 1351f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1361f2e99feSLydia Wang 1374a79616dSTakashi Iwai struct nid_path out_path[4]; 1384a79616dSTakashi Iwai struct nid_path hp_path; 1394a79616dSTakashi Iwai struct nid_path hp_dep_path; 1404a79616dSTakashi Iwai 1411f2e99feSLydia Wang /* capture */ 1421f2e99feSLydia Wang unsigned int num_adc_nids; 143a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1441f2e99feSLydia Wang hda_nid_t mux_nids[3]; 145620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1461f2e99feSLydia Wang hda_nid_t dig_in_nid; 1471f2e99feSLydia Wang hda_nid_t dig_in_pin; 1481f2e99feSLydia Wang 1491f2e99feSLydia Wang /* capture source */ 1501f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1511f2e99feSLydia Wang unsigned int cur_mux[3]; 1521f2e99feSLydia Wang 1531f2e99feSLydia Wang /* PCM information */ 1541f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1551f2e99feSLydia Wang 1561f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1571f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1581f2e99feSLydia Wang struct snd_array kctls; 1591f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1601f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1611f2e99feSLydia Wang 1621f2e99feSLydia Wang /* HP mode source */ 1631f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1641f2e99feSLydia Wang unsigned int hp_independent_mode; 1651f2e99feSLydia Wang unsigned int hp_independent_mode_index; 166f4a7828bSTakashi Iwai unsigned int can_smart51; 1671f2e99feSLydia Wang unsigned int smart51_enabled; 168f3db423dSLydia Wang unsigned int dmic_enabled; 16924088a58STakashi Iwai unsigned int no_pin_power_ctl; 1701f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1711f2e99feSLydia Wang 1721f2e99feSLydia Wang /* work to check hp jack state */ 1731f2e99feSLydia Wang struct hda_codec *codec; 1741f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 175e06e5a29STakashi Iwai int vt1708_jack_detect; 1761f2e99feSLydia Wang int vt1708_hp_present; 1773e95b9abSLydia Wang 1783e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1793e95b9abSLydia Wang 1801f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1811f2e99feSLydia Wang struct hda_loopback_check loopback; 1821f2e99feSLydia Wang #endif 1831f2e99feSLydia Wang }; 1841f2e99feSLydia Wang 1850341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1865b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1875b0cb1d8SJaroslav Kysela { 1885b0cb1d8SJaroslav Kysela struct via_spec *spec; 1895b0cb1d8SJaroslav Kysela 1905b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1915b0cb1d8SJaroslav Kysela if (spec == NULL) 1925b0cb1d8SJaroslav Kysela return NULL; 1935b0cb1d8SJaroslav Kysela 1945b0cb1d8SJaroslav Kysela codec->spec = spec; 1955b0cb1d8SJaroslav Kysela spec->codec = codec; 1960341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1970341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1980341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1990341ccd7SLydia Wang spec->codec_type = VT1708S; 2005b0cb1d8SJaroslav Kysela return spec; 2015b0cb1d8SJaroslav Kysela } 2025b0cb1d8SJaroslav Kysela 203744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 204d7426329SHarald Welte { 205744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 206d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 207d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 208d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 209d7426329SHarald Welte 210d7426329SHarald Welte /* get codec type */ 211d7426329SHarald Welte if (ven_id != 0x1106) 212d7426329SHarald Welte codec_type = UNKNOWN; 213d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 214d7426329SHarald Welte codec_type = VT1708; 215d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 216d7426329SHarald Welte codec_type = VT1709_10CH; 217d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 218d7426329SHarald Welte codec_type = VT1709_6CH; 219518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 220d7426329SHarald Welte codec_type = VT1708B_8CH; 221518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 222518bf3baSLydia Wang codec_type = VT1708BCE; 223518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 224d7426329SHarald Welte codec_type = VT1708B_4CH; 225d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 226d7426329SHarald Welte && (dev_id >> 12) < 8) 227d7426329SHarald Welte codec_type = VT1708S; 228d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 229d7426329SHarald Welte && (dev_id >> 12) < 8) 230d7426329SHarald Welte codec_type = VT1702; 231eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 232eb7188caSLydia Wang && (dev_id >> 12) < 8) 233eb7188caSLydia Wang codec_type = VT1718S; 234f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 235f3db423dSLydia Wang codec_type = VT1716S; 236bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 237bb3c6bfcSLydia Wang codec_type = VT1718S; 23825eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 23925eaba2fSLydia Wang codec_type = VT2002P; 240ab6734e7SLydia Wang else if (dev_id == 0x0448) 241ab6734e7SLydia Wang codec_type = VT1812; 24236dd5c4aSLydia Wang else if (dev_id == 0x0440) 24336dd5c4aSLydia Wang codec_type = VT1708S; 24411890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 24511890956SLydia Wang codec_type = VT1802; 246d7426329SHarald Welte else 247d7426329SHarald Welte codec_type = UNKNOWN; 248d7426329SHarald Welte return codec_type; 249d7426329SHarald Welte }; 250d7426329SHarald Welte 251ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25269e52a80SHarald Welte #define VIA_HP_EVENT 0x01 25369e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 254ec7e7e42SLydia Wang #define VIA_MONO_EVENT 0x03 255ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT 0x04 256ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT 0x05 25769e52a80SHarald Welte 258c577b8a1SJoseph Chan enum { 259c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 260c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 261f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 26225eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 263c577b8a1SJoseph Chan }; 264c577b8a1SJoseph Chan 265c577b8a1SJoseph Chan enum { 266eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 267c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 268c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 269c577b8a1SJoseph Chan AUTO_SEQ_SIDE 270c577b8a1SJoseph Chan }; 271c577b8a1SJoseph Chan 272f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2731f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2741f2e99feSLydia Wang 2751f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2761f2e99feSLydia Wang { 2771f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2781f2e99feSLydia Wang return; 2791f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 280e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2811f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2821f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2831f2e99feSLydia Wang msecs_to_jiffies(100)); 2841f2e99feSLydia Wang } 2851f2e99feSLydia Wang 2861f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2871f2e99feSLydia Wang { 2881f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2891f2e99feSLydia Wang return; 2901f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2911f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2921f2e99feSLydia Wang return; 2931f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 294e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2955b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2961f2e99feSLydia Wang } 297f5271101SLydia Wang 2983e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2993e95b9abSLydia Wang { 3003e95b9abSLydia Wang struct via_spec *spec = codec->spec; 3013e95b9abSLydia Wang if (spec->set_widgets_power_state) 3023e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3033e95b9abSLydia Wang } 30425eaba2fSLydia Wang 305f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 306f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 307f5271101SLydia Wang { 308f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 309f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 310f5271101SLydia Wang 3113e95b9abSLydia Wang set_widgets_power_state(codec); 312f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3131f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3141f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3151f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3161f2e99feSLydia Wang else 3171f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3181f2e99feSLydia Wang } 319f5271101SLydia Wang return change; 320f5271101SLydia Wang } 321f5271101SLydia Wang 322f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 323f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 324f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 325f5271101SLydia Wang .name = NULL, \ 326f5271101SLydia Wang .index = 0, \ 327f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 328f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 329f5271101SLydia Wang .put = analog_input_switch_put, \ 330f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 331f5271101SLydia Wang 33225eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 33325eaba2fSLydia Wang 33425eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 33525eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 33625eaba2fSLydia Wang { 33725eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 33925eaba2fSLydia Wang int i; 34025eaba2fSLydia Wang int change = 0; 34125eaba2fSLydia Wang 34225eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 34325eaba2fSLydia Wang int lmute, rmute; 34425eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 34525eaba2fSLydia Wang snd_printd("Invalid control!\n"); 34625eaba2fSLydia Wang return change; 34725eaba2fSLydia Wang } 34825eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 34925eaba2fSLydia Wang ucontrol); 35025eaba2fSLydia Wang /* Get mute value */ 35125eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 35225eaba2fSLydia Wang valp++; 35325eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 35425eaba2fSLydia Wang 35525eaba2fSLydia Wang /* Set hp pins */ 35625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 35725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 35825eaba2fSLydia Wang snd_hda_codec_amp_update( 35925eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36025eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36125eaba2fSLydia Wang lmute); 36225eaba2fSLydia Wang snd_hda_codec_amp_update( 36325eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36425eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36525eaba2fSLydia Wang rmute); 36625eaba2fSLydia Wang } 36725eaba2fSLydia Wang } 36825eaba2fSLydia Wang 36925eaba2fSLydia Wang if (!lmute && !rmute) { 37025eaba2fSLydia Wang /* Line Outs */ 37125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 37225eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37325eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 37425eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37525eaba2fSLydia Wang /* Speakers */ 37625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37725eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37825eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 37925eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 38025eaba2fSLydia Wang /* unmute */ 38125eaba2fSLydia Wang via_hp_bind_automute(codec); 38225eaba2fSLydia Wang 38325eaba2fSLydia Wang } else { 38425eaba2fSLydia Wang if (lmute) { 38525eaba2fSLydia Wang /* Mute all left channels */ 38625eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38725eaba2fSLydia Wang snd_hda_codec_amp_update( 38825eaba2fSLydia Wang codec, 38925eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 39025eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39125eaba2fSLydia Wang lmute); 39225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39325eaba2fSLydia Wang snd_hda_codec_amp_update( 39425eaba2fSLydia Wang codec, 39525eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39725eaba2fSLydia Wang lmute); 39825eaba2fSLydia Wang } 39925eaba2fSLydia Wang if (rmute) { 40025eaba2fSLydia Wang /* mute all right channels */ 40125eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 40225eaba2fSLydia Wang snd_hda_codec_amp_update( 40325eaba2fSLydia Wang codec, 40425eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 40525eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 40625eaba2fSLydia Wang rmute); 40725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 40825eaba2fSLydia Wang snd_hda_codec_amp_update( 40925eaba2fSLydia Wang codec, 41025eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 41125eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 41225eaba2fSLydia Wang rmute); 41325eaba2fSLydia Wang } 41425eaba2fSLydia Wang } 41525eaba2fSLydia Wang return change; 41625eaba2fSLydia Wang } 41725eaba2fSLydia Wang 41825eaba2fSLydia Wang #define BIND_PIN_MUTE \ 41925eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 42025eaba2fSLydia Wang .name = NULL, \ 42125eaba2fSLydia Wang .index = 0, \ 42225eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 42325eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 42425eaba2fSLydia Wang .put = bind_pin_switch_put, \ 42525eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 42625eaba2fSLydia Wang 42790dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 428c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 429c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 430f5271101SLydia Wang ANALOG_INPUT_MUTE, 43125eaba2fSLydia Wang BIND_PIN_MUTE, 432c577b8a1SJoseph Chan }; 433c577b8a1SJoseph Chan 434ab6734e7SLydia Wang 435c577b8a1SJoseph Chan /* add dynamic controls */ 436291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 437291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 438291c9e33STakashi Iwai const char *name) 439c577b8a1SJoseph Chan { 440c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 441c577b8a1SJoseph Chan 442603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 443603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 444c577b8a1SJoseph Chan if (!knew) 445291c9e33STakashi Iwai return NULL; 446291c9e33STakashi Iwai *knew = *tmpl; 447291c9e33STakashi Iwai if (!name) 448291c9e33STakashi Iwai name = tmpl->name; 449291c9e33STakashi Iwai if (name) { 450c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 451c577b8a1SJoseph Chan if (!knew->name) 452291c9e33STakashi Iwai return NULL; 453291c9e33STakashi Iwai } 454291c9e33STakashi Iwai return knew; 455291c9e33STakashi Iwai } 456291c9e33STakashi Iwai 457291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 458291c9e33STakashi Iwai int idx, unsigned long val) 459291c9e33STakashi Iwai { 460291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 461291c9e33STakashi Iwai 462291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 463291c9e33STakashi Iwai if (!knew) 464c577b8a1SJoseph Chan return -ENOMEM; 4654d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4665e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 467c577b8a1SJoseph Chan knew->private_value = val; 468c577b8a1SJoseph Chan return 0; 469c577b8a1SJoseph Chan } 470c577b8a1SJoseph Chan 4717b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4727b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4737b315bb4STakashi Iwai 474291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 4755b0cb1d8SJaroslav Kysela 476603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 477603c4019STakashi Iwai { 478603c4019STakashi Iwai struct via_spec *spec = codec->spec; 479603c4019STakashi Iwai 480603c4019STakashi Iwai if (spec->kctls.list) { 481603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 482603c4019STakashi Iwai int i; 483603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 484603c4019STakashi Iwai kfree(kctl[i].name); 485603c4019STakashi Iwai } 486603c4019STakashi Iwai snd_array_free(&spec->kctls); 487603c4019STakashi Iwai } 488603c4019STakashi Iwai 489c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4909510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4917b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 492c577b8a1SJoseph Chan { 493c577b8a1SJoseph Chan char name[32]; 494c577b8a1SJoseph Chan int err; 495c577b8a1SJoseph Chan 496c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4977b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 498c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 499c577b8a1SJoseph Chan if (err < 0) 500c577b8a1SJoseph Chan return err; 501c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5027b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 503c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 504c577b8a1SJoseph Chan if (err < 0) 505c577b8a1SJoseph Chan return err; 506c577b8a1SJoseph Chan return 0; 507c577b8a1SJoseph Chan } 508c577b8a1SJoseph Chan 509c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 510c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 511c577b8a1SJoseph Chan int dac_idx) 512c577b8a1SJoseph Chan { 513c577b8a1SJoseph Chan /* set as output */ 514c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 515c577b8a1SJoseph Chan pin_type); 516c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 517c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 518d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 519d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 520d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 521c577b8a1SJoseph Chan } 522c577b8a1SJoseph Chan 523c577b8a1SJoseph Chan 524c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 525c577b8a1SJoseph Chan { 526c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 527c577b8a1SJoseph Chan int i; 528c577b8a1SJoseph Chan 529c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 530c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 531c577b8a1SJoseph Chan if (nid) 532c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 533c577b8a1SJoseph Chan } 534c577b8a1SJoseph Chan } 535c577b8a1SJoseph Chan 536c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 537c577b8a1SJoseph Chan { 538c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 539c577b8a1SJoseph Chan hda_nid_t pin; 54025eaba2fSLydia Wang int i; 541c577b8a1SJoseph Chan 54225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 54325eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 544c577b8a1SJoseph Chan if (pin) /* connect to front */ 545c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 546c577b8a1SJoseph Chan } 54725eaba2fSLydia Wang } 548c577b8a1SJoseph Chan 549f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 55032e0191dSClemens Ladisch 551c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 552c577b8a1SJoseph Chan { 553c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5547b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 55532e0191dSClemens Ladisch unsigned int ctl; 556c577b8a1SJoseph Chan int i; 557c577b8a1SJoseph Chan 5587b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5597b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 560f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 56132e0191dSClemens Ladisch ctl = PIN_OUT; 56230649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56332e0191dSClemens Ladisch ctl = PIN_VREF50; 56432e0191dSClemens Ladisch else 56532e0191dSClemens Ladisch ctl = PIN_IN; 566c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56732e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 568c577b8a1SJoseph Chan } 569c577b8a1SJoseph Chan } 570f5271101SLydia Wang 571f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 572f5271101SLydia Wang unsigned int *affected_parm) 573f5271101SLydia Wang { 574f5271101SLydia Wang unsigned parm; 575f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 576f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 577f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 578f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5791564b287SLydia Wang struct via_spec *spec = codec->spec; 58024088a58STakashi Iwai unsigned present = 0; 58124088a58STakashi Iwai 58224088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 58324088a58STakashi Iwai if (!no_presence) 58424088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 585f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 5861564b287SLydia Wang || ((no_presence || present) 5871564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 588f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 589f5271101SLydia Wang parm = AC_PWRST_D0; 590f5271101SLydia Wang } else 591f5271101SLydia Wang parm = AC_PWRST_D3; 592f5271101SLydia Wang 593f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 594f5271101SLydia Wang } 595f5271101SLydia Wang 59624088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 59724088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 59824088a58STakashi Iwai { 59924088a58STakashi Iwai static const char * const texts[] = { 60024088a58STakashi Iwai "Disabled", "Enabled" 60124088a58STakashi Iwai }; 60224088a58STakashi Iwai 60324088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 60424088a58STakashi Iwai uinfo->count = 1; 60524088a58STakashi Iwai uinfo->value.enumerated.items = 2; 60624088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 60724088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 60824088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 60924088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 61024088a58STakashi Iwai return 0; 61124088a58STakashi Iwai } 61224088a58STakashi Iwai 61324088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 61424088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61524088a58STakashi Iwai { 61624088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61724088a58STakashi Iwai struct via_spec *spec = codec->spec; 61824088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 61924088a58STakashi Iwai return 0; 62024088a58STakashi Iwai } 62124088a58STakashi Iwai 62224088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 62324088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 62424088a58STakashi Iwai { 62524088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 62624088a58STakashi Iwai struct via_spec *spec = codec->spec; 62724088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 62824088a58STakashi Iwai 62924088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 63024088a58STakashi Iwai return 0; 63124088a58STakashi Iwai spec->no_pin_power_ctl = val; 63224088a58STakashi Iwai set_widgets_power_state(codec); 63324088a58STakashi Iwai return 1; 63424088a58STakashi Iwai } 63524088a58STakashi Iwai 63624088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 63724088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 63824088a58STakashi Iwai .name = "Dynamic Power-Control", 63924088a58STakashi Iwai .info = via_pin_power_ctl_info, 64024088a58STakashi Iwai .get = via_pin_power_ctl_get, 64124088a58STakashi Iwai .put = via_pin_power_ctl_put, 64224088a58STakashi Iwai }; 64324088a58STakashi Iwai 64424088a58STakashi Iwai 645c577b8a1SJoseph Chan /* 646c577b8a1SJoseph Chan * input MUX handling 647c577b8a1SJoseph Chan */ 648c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 649c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 650c577b8a1SJoseph Chan { 651c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 652c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 653c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 654c577b8a1SJoseph Chan } 655c577b8a1SJoseph Chan 656c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 657c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 658c577b8a1SJoseph Chan { 659c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 660c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 661c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 662c577b8a1SJoseph Chan 663c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 664c577b8a1SJoseph Chan return 0; 665c577b8a1SJoseph Chan } 666c577b8a1SJoseph Chan 667c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 668c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 669c577b8a1SJoseph Chan { 670c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 671c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 672c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 673bff5fbf5SLydia Wang int ret; 674c577b8a1SJoseph Chan 675337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 676337b9d02STakashi Iwai return -EINVAL; 677a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 678a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 679a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 680a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 681a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 682bff5fbf5SLydia Wang 683bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 684bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 685bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 686a80e6e3cSLydia Wang /* update jack power state */ 6873e95b9abSLydia Wang set_widgets_power_state(codec); 688a80e6e3cSLydia Wang 689bff5fbf5SLydia Wang return ret; 690c577b8a1SJoseph Chan } 691c577b8a1SJoseph Chan 6920aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6930aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6940aa62aefSHarald Welte { 6950aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6960aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6970aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6980aa62aefSHarald Welte } 6990aa62aefSHarald Welte 7000aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7010aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7020aa62aefSHarald Welte { 7030aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7045b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 705eb7188caSLydia Wang unsigned int pinsel; 706eb7188caSLydia Wang 707eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 708eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 7090aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 7100aa62aefSHarald Welte 0x00); 7110aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 7120aa62aefSHarald Welte 7130aa62aefSHarald Welte return 0; 7140aa62aefSHarald Welte } 7150aa62aefSHarald Welte 7160713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7170713efebSLydia Wang { 7180713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7190713efebSLydia Wang if (ctl) { 7200713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7210713efebSLydia Wang ctl->vd[0].access |= active 7220713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7230713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7240713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7250713efebSLydia Wang } 7260713efebSLydia Wang } 7270713efebSLydia Wang 7285b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7295b0cb1d8SJaroslav Kysela { 7305b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7315b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7325b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7335b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7345b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 735e87885feSLydia Wang case VT2002P: return 0x19; 736e87885feSLydia Wang case VT1802: return 0x15; 737e87885feSLydia Wang case VT1812: return 0x15; 7385b0cb1d8SJaroslav Kysela default: return 0; 7395b0cb1d8SJaroslav Kysela } 7405b0cb1d8SJaroslav Kysela } 7415b0cb1d8SJaroslav Kysela 742cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 743cdc1784dSLydia Wang { 744cdc1784dSLydia Wang /* mute side channel */ 745cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 746e87885feSLydia Wang unsigned int parm; 7475b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 748cdc1784dSLydia Wang 749e87885feSLydia Wang if (sw3) { 750e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 751e87885feSLydia Wang parm = spec->hp_independent_mode ? 752e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 753e87885feSLydia Wang else 754e87885feSLydia Wang parm = spec->hp_independent_mode ? 755e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 756e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 757e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 758e87885feSLydia Wang if (spec->codec_type == VT1812) 759e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 760e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 761e87885feSLydia Wang } 762cdc1784dSLydia Wang return 0; 763cdc1784dSLydia Wang } 764cdc1784dSLydia Wang 7650aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7660aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7670aa62aefSHarald Welte { 7680aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7690aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7705b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7710aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 772cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 773cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 774cdc1784dSLydia Wang ? 1 : 0; 775ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 776ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 777ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 778ce0e5a9eSLydia Wang else 779ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 780ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7810aa62aefSHarald Welte 782ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 783ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 784ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 785cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 786cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 787cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7880aa62aefSHarald Welte 0, 0, 0); 7890aa62aefSHarald Welte 790cdc1784dSLydia Wang update_side_mute_status(codec); 7910713efebSLydia Wang /* update HP volume/swtich active state */ 7920713efebSLydia Wang if (spec->codec_type == VT1708S 793eb7188caSLydia Wang || spec->codec_type == VT1702 794f3db423dSLydia Wang || spec->codec_type == VT1718S 79525eaba2fSLydia Wang || spec->codec_type == VT1716S 79611890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7970713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7980713efebSLydia Wang spec->hp_independent_mode); 7990713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 8000713efebSLydia Wang spec->hp_independent_mode); 8010713efebSLydia Wang } 802ce0e5a9eSLydia Wang /* update jack power state */ 8033e95b9abSLydia Wang set_widgets_power_state(codec); 8040aa62aefSHarald Welte return 0; 8050aa62aefSHarald Welte } 8060aa62aefSHarald Welte 80790dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 8080aa62aefSHarald Welte { 8090aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8100aa62aefSHarald Welte .name = "Independent HP", 8110aa62aefSHarald Welte .info = via_independent_hp_info, 8120aa62aefSHarald Welte .get = via_independent_hp_get, 8130aa62aefSHarald Welte .put = via_independent_hp_put, 8140aa62aefSHarald Welte }, 8155b0cb1d8SJaroslav Kysela { 8165b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8175b0cb1d8SJaroslav Kysela .name = "Independent HP", 8185b0cb1d8SJaroslav Kysela }, 8190aa62aefSHarald Welte }; 8200aa62aefSHarald Welte 8213d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8225b0cb1d8SJaroslav Kysela { 8233d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8245b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8255b0cb1d8SJaroslav Kysela hda_nid_t nid; 8263d83e577STakashi Iwai int nums; 8273d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8285b0cb1d8SJaroslav Kysela 8295b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8305b0cb1d8SJaroslav Kysela case VT1718S: 8315b0cb1d8SJaroslav Kysela nid = 0x34; 8325b0cb1d8SJaroslav Kysela break; 8335b0cb1d8SJaroslav Kysela case VT2002P: 83411890956SLydia Wang case VT1802: 8355b0cb1d8SJaroslav Kysela nid = 0x35; 8365b0cb1d8SJaroslav Kysela break; 8375b0cb1d8SJaroslav Kysela case VT1812: 8385b0cb1d8SJaroslav Kysela nid = 0x3d; 8395b0cb1d8SJaroslav Kysela break; 8405b0cb1d8SJaroslav Kysela default: 8415b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8425b0cb1d8SJaroslav Kysela break; 8435b0cb1d8SJaroslav Kysela } 8445b0cb1d8SJaroslav Kysela 845ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 846ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 847ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8483d83e577STakashi Iwai if (nums <= 1) 8493d83e577STakashi Iwai return 0; 850ee3c35c0SLydia Wang } 8513d83e577STakashi Iwai 8523d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8533d83e577STakashi Iwai if (knew == NULL) 8543d83e577STakashi Iwai return -ENOMEM; 8553d83e577STakashi Iwai 8565b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8575b0cb1d8SJaroslav Kysela knew->private_value = nid; 8585b0cb1d8SJaroslav Kysela 8595b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8605b0cb1d8SJaroslav Kysela if (knew == NULL) 8615b0cb1d8SJaroslav Kysela return -ENOMEM; 8625b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8635b0cb1d8SJaroslav Kysela 8645b0cb1d8SJaroslav Kysela return 0; 8655b0cb1d8SJaroslav Kysela } 8665b0cb1d8SJaroslav Kysela 8671564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8681564b287SLydia Wang { 8691564b287SLydia Wang int i; 8701564b287SLydia Wang struct snd_ctl_elem_id id; 871525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 872525566cbSLydia Wang struct snd_kcontrol *ctl; 8731564b287SLydia Wang 8741564b287SLydia Wang memset(&id, 0, sizeof(id)); 8751564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8761564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8771564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 878525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 879525566cbSLydia Wang if (ctl) 880525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 881525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 882525566cbSLydia Wang &ctl->id); 8831564b287SLydia Wang } 8841564b287SLydia Wang } 8851564b287SLydia Wang 8861564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8871564b287SLydia Wang { 8881564b287SLydia Wang struct via_spec *spec = codec->spec; 8891564b287SLydia Wang int start_idx; 8901564b287SLydia Wang int end_idx; 8911564b287SLydia Wang int i; 8921564b287SLydia Wang /* get nid of MW0 and start & end index */ 8931564b287SLydia Wang switch (spec->codec_type) { 8941564b287SLydia Wang case VT1708: 8951564b287SLydia Wang start_idx = 2; 8961564b287SLydia Wang end_idx = 4; 8971564b287SLydia Wang break; 8981564b287SLydia Wang case VT1709_10CH: 8991564b287SLydia Wang case VT1709_6CH: 9001564b287SLydia Wang start_idx = 2; 9011564b287SLydia Wang end_idx = 4; 9021564b287SLydia Wang break; 9031564b287SLydia Wang case VT1708B_8CH: 9041564b287SLydia Wang case VT1708B_4CH: 9051564b287SLydia Wang case VT1708S: 906f3db423dSLydia Wang case VT1716S: 9071564b287SLydia Wang start_idx = 2; 9081564b287SLydia Wang end_idx = 4; 9091564b287SLydia Wang break; 910ab657e0cSLydia Wang case VT1718S: 911ab657e0cSLydia Wang start_idx = 1; 912ab657e0cSLydia Wang end_idx = 3; 913ab657e0cSLydia Wang break; 9141564b287SLydia Wang default: 9151564b287SLydia Wang return; 9161564b287SLydia Wang } 9171564b287SLydia Wang /* check AA path's mute status */ 9181564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9191564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 920620e2b28STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, 9211564b287SLydia Wang HDA_AMP_MUTE, val); 9221564b287SLydia Wang } 9231564b287SLydia Wang } 924f4a7828bSTakashi Iwai 925f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 9261564b287SLydia Wang { 927f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9287b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9297b315bb4STakashi Iwai int i; 9307b315bb4STakashi Iwai 9317b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 932f4a7828bSTakashi Iwai unsigned int defcfg; 933f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 934f4a7828bSTakashi Iwai continue; 935f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 936f4a7828bSTakashi Iwai return false; 937f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 938f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 939f4a7828bSTakashi Iwai return false; 940f4a7828bSTakashi Iwai return true; 9411564b287SLydia Wang } 942f4a7828bSTakashi Iwai return false; 9431564b287SLydia Wang } 9441564b287SLydia Wang 9451564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9461564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9471564b287SLydia Wang { 9481564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9491564b287SLydia Wang uinfo->count = 1; 9501564b287SLydia Wang uinfo->value.integer.min = 0; 9511564b287SLydia Wang uinfo->value.integer.max = 1; 9521564b287SLydia Wang return 0; 9531564b287SLydia Wang } 9541564b287SLydia Wang 9551564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9561564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9571564b287SLydia Wang { 9581564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9591564b287SLydia Wang struct via_spec *spec = codec->spec; 9607b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9611564b287SLydia Wang int on = 1; 9621564b287SLydia Wang int i; 9631564b287SLydia Wang 9647b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9657b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 966f4a7828bSTakashi Iwai unsigned int ctl; 96786e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9687b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9691564b287SLydia Wang continue; /* ignore FMic for independent HP */ 970f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 971f4a7828bSTakashi Iwai continue; 972f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 973f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9747b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9751564b287SLydia Wang on = 0; 9761564b287SLydia Wang } 9771564b287SLydia Wang *ucontrol->value.integer.value = on; 9781564b287SLydia Wang return 0; 9791564b287SLydia Wang } 9801564b287SLydia Wang 9811564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9821564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9831564b287SLydia Wang { 9841564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9851564b287SLydia Wang struct via_spec *spec = codec->spec; 9867b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9871564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9881564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9891564b287SLydia Wang int i; 9901564b287SLydia Wang 9917b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9927b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9937b315bb4STakashi Iwai unsigned int parm; 9947b315bb4STakashi Iwai 99586e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9967b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9971564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 998f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 999f4a7828bSTakashi Iwai continue; 10007b315bb4STakashi Iwai 10017b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 10021564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 10031564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 10041564b287SLydia Wang parm |= out_in; 10051564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 10061564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 10071564b287SLydia Wang parm); 10081564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 10091564b287SLydia Wang mute_aa_path(codec, 1); 10101564b287SLydia Wang notify_aa_path_ctls(codec); 10111564b287SLydia Wang } 10121564b287SLydia Wang } 10131564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10143e95b9abSLydia Wang set_widgets_power_state(codec); 10151564b287SLydia Wang return 1; 10161564b287SLydia Wang } 10171564b287SLydia Wang 10185f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10191564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10201564b287SLydia Wang .name = "Smart 5.1", 10211564b287SLydia Wang .count = 1, 10221564b287SLydia Wang .info = via_smart51_info, 10231564b287SLydia Wang .get = via_smart51_get, 10241564b287SLydia Wang .put = via_smart51_put, 10251564b287SLydia Wang }; 10261564b287SLydia Wang 1027f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 10285b0cb1d8SJaroslav Kysela { 1029f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 10305b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10317b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10325b0cb1d8SJaroslav Kysela hda_nid_t nid; 10335b0cb1d8SJaroslav Kysela int i; 10345b0cb1d8SJaroslav Kysela 1035f4a7828bSTakashi Iwai if (!spec->can_smart51) 1036cb34c207SLydia Wang return 0; 1037cb34c207SLydia Wang 10385f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10395b0cb1d8SJaroslav Kysela if (knew == NULL) 10405b0cb1d8SJaroslav Kysela return -ENOMEM; 10415b0cb1d8SJaroslav Kysela 10427b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10437b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 1044f4a7828bSTakashi Iwai if (is_smart51_pins(codec, nid)) { 10455f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10467b315bb4STakashi Iwai break; 10475b0cb1d8SJaroslav Kysela } 10485b0cb1d8SJaroslav Kysela } 10495b0cb1d8SJaroslav Kysela 10505b0cb1d8SJaroslav Kysela return 0; 10515b0cb1d8SJaroslav Kysela } 10525b0cb1d8SJaroslav Kysela 1053c577b8a1SJoseph Chan /* capture mixer elements */ 105490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = { 1055c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1056c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1057c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1058c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1059c577b8a1SJoseph Chan { 1060c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1061c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1062c577b8a1SJoseph Chan * So call somewhat different.. 1063c577b8a1SJoseph Chan */ 1064c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1065c577b8a1SJoseph Chan .name = "Input Source", 1066c577b8a1SJoseph Chan .count = 1, 1067c577b8a1SJoseph Chan .info = via_mux_enum_info, 1068c577b8a1SJoseph Chan .get = via_mux_enum_get, 1069c577b8a1SJoseph Chan .put = via_mux_enum_put, 1070c577b8a1SJoseph Chan }, 1071c577b8a1SJoseph Chan { } /* end */ 1072c577b8a1SJoseph Chan }; 1073f5271101SLydia Wang 1074f5271101SLydia Wang /* check AA path's mute statue */ 1075f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1076f5271101SLydia Wang { 1077f5271101SLydia Wang int mute = 1; 1078f5271101SLydia Wang int start_idx; 1079f5271101SLydia Wang int end_idx; 1080f5271101SLydia Wang int i; 1081f5271101SLydia Wang struct via_spec *spec = codec->spec; 1082f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1083f5271101SLydia Wang switch (spec->codec_type) { 1084f5271101SLydia Wang case VT1708B_8CH: 1085f5271101SLydia Wang case VT1708B_4CH: 1086f5271101SLydia Wang case VT1708S: 1087f3db423dSLydia Wang case VT1716S: 1088f5271101SLydia Wang start_idx = 2; 1089f5271101SLydia Wang end_idx = 4; 1090f5271101SLydia Wang break; 1091f5271101SLydia Wang case VT1702: 1092f5271101SLydia Wang start_idx = 1; 1093f5271101SLydia Wang end_idx = 3; 1094f5271101SLydia Wang break; 1095eb7188caSLydia Wang case VT1718S: 1096eb7188caSLydia Wang start_idx = 1; 1097eb7188caSLydia Wang end_idx = 3; 1098eb7188caSLydia Wang break; 109925eaba2fSLydia Wang case VT2002P: 1100ab6734e7SLydia Wang case VT1812: 110111890956SLydia Wang case VT1802: 110225eaba2fSLydia Wang start_idx = 0; 110325eaba2fSLydia Wang end_idx = 2; 110425eaba2fSLydia Wang break; 1105f5271101SLydia Wang default: 1106f5271101SLydia Wang return 0; 1107f5271101SLydia Wang } 1108f5271101SLydia Wang /* check AA path's mute status */ 1109f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1110f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1111620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1112f5271101SLydia Wang int shift = 8 * (i % 4); 1113f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1114f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1115f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1116f5271101SLydia Wang /* check mute status while the pin is connected */ 1117620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 1118f5271101SLydia Wang HDA_INPUT, i) >> 7; 1119620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 1120f5271101SLydia Wang HDA_INPUT, i) >> 7; 1121f5271101SLydia Wang if (!mute_l || !mute_r) { 1122f5271101SLydia Wang mute = 0; 1123f5271101SLydia Wang break; 1124f5271101SLydia Wang } 1125f5271101SLydia Wang } 1126f5271101SLydia Wang } 1127f5271101SLydia Wang return mute; 1128f5271101SLydia Wang } 1129f5271101SLydia Wang 1130f5271101SLydia Wang /* enter/exit analog low-current mode */ 1131f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1132f5271101SLydia Wang { 1133f5271101SLydia Wang struct via_spec *spec = codec->spec; 1134f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1135f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1136f5271101SLydia Wang unsigned int verb = 0; 1137f5271101SLydia Wang unsigned int parm = 0; 1138f5271101SLydia Wang 1139f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1140f5271101SLydia Wang enable = enable && saved_stream_idle; 1141f5271101SLydia Wang else { 1142f5271101SLydia Wang enable = enable && stream_idle; 1143f5271101SLydia Wang saved_stream_idle = stream_idle; 1144f5271101SLydia Wang } 1145f5271101SLydia Wang 1146f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1147f5271101SLydia Wang switch (spec->codec_type) { 1148f5271101SLydia Wang case VT1708B_8CH: 1149f5271101SLydia Wang case VT1708B_4CH: 1150f5271101SLydia Wang verb = 0xf70; 1151f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1152f5271101SLydia Wang break; 1153f5271101SLydia Wang case VT1708S: 1154eb7188caSLydia Wang case VT1718S: 1155f3db423dSLydia Wang case VT1716S: 1156f5271101SLydia Wang verb = 0xf73; 1157f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1158f5271101SLydia Wang break; 1159f5271101SLydia Wang case VT1702: 1160f5271101SLydia Wang verb = 0xf73; 1161f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1162f5271101SLydia Wang break; 116325eaba2fSLydia Wang case VT2002P: 1164ab6734e7SLydia Wang case VT1812: 116511890956SLydia Wang case VT1802: 116625eaba2fSLydia Wang verb = 0xf93; 116725eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 116825eaba2fSLydia Wang break; 1169f5271101SLydia Wang default: 1170f5271101SLydia Wang return; /* other codecs are not supported */ 1171f5271101SLydia Wang } 1172f5271101SLydia Wang /* send verb */ 1173f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1174f5271101SLydia Wang } 1175f5271101SLydia Wang 1176c577b8a1SJoseph Chan /* 1177c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1178c577b8a1SJoseph Chan */ 117990dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1180c577b8a1SJoseph Chan /* 1181c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1182c577b8a1SJoseph Chan */ 1183c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1184c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1185c577b8a1SJoseph Chan 1186c577b8a1SJoseph Chan 1187f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1188c577b8a1SJoseph Chan * mixer widget 1189c577b8a1SJoseph Chan */ 1190c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1191f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1192f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1193f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1194f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1195f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1196c577b8a1SJoseph Chan 1197c577b8a1SJoseph Chan /* 1198c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1199c577b8a1SJoseph Chan */ 1200c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1201c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1202c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1203c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1204c577b8a1SJoseph Chan 1205bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1206bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1207c577b8a1SJoseph Chan /* PW9 Output enable */ 1208c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1209aa266fccSLydia Wang /* power down jack detect function */ 1210aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1211f7278fd0SJosepch Chan { } 1212c577b8a1SJoseph Chan }; 1213c577b8a1SJoseph Chan 1214*7eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec, 1215*7eb56e84STakashi Iwai struct snd_pcm_substream *substream) 1216*7eb56e84STakashi Iwai { 1217*7eb56e84STakashi Iwai int idle = substream->pstr->substream_opened == 1 1218*7eb56e84STakashi Iwai && substream->ref_count == 0; 1219*7eb56e84STakashi Iwai analog_low_current_mode(codec, idle); 1220*7eb56e84STakashi Iwai } 1221*7eb56e84STakashi Iwai 1222c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1223c577b8a1SJoseph Chan struct hda_codec *codec, 1224c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1225c577b8a1SJoseph Chan { 1226c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1227*7eb56e84STakashi Iwai substream_set_idle(codec, substream); 12289a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12299a08160bSTakashi Iwai hinfo); 1230c577b8a1SJoseph Chan } 1231c577b8a1SJoseph Chan 12329af74210STakashi Iwai static int via_playback_pcm_close(struct hda_pcm_stream *hinfo, 12339af74210STakashi Iwai struct hda_codec *codec, 12349af74210STakashi Iwai struct snd_pcm_substream *substream) 12359af74210STakashi Iwai { 1236*7eb56e84STakashi Iwai substream_set_idle(codec, substream); 12379af74210STakashi Iwai return 0; 12389af74210STakashi Iwai } 12399af74210STakashi Iwai 1240*7eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 1241*7eb56e84STakashi Iwai struct hda_codec *codec, 1242*7eb56e84STakashi Iwai struct snd_pcm_substream *substream) 1243*7eb56e84STakashi Iwai { 1244*7eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 1245*7eb56e84STakashi Iwai struct hda_multi_out *mout = &spec->multiout; 1246*7eb56e84STakashi Iwai 1247*7eb56e84STakashi Iwai if (!mout->hp_nid || mout->hp_nid == mout->dac_nids[HDA_FRONT] || 1248*7eb56e84STakashi Iwai !spec->hp_independent_mode) 1249*7eb56e84STakashi Iwai return -EINVAL; 1250*7eb56e84STakashi Iwai substream_set_idle(codec, substream); 1251*7eb56e84STakashi Iwai return 0; 1252*7eb56e84STakashi Iwai } 1253*7eb56e84STakashi Iwai 1254*7eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 1255*7eb56e84STakashi Iwai struct hda_codec *codec, 12560aa62aefSHarald Welte unsigned int stream_tag, 12570aa62aefSHarald Welte unsigned int format, 12580aa62aefSHarald Welte struct snd_pcm_substream *substream) 12590aa62aefSHarald Welte { 12600aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12610aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1262dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12630aa62aefSHarald Welte int chs = substream->runtime->channels; 12640aa62aefSHarald Welte int i; 12657c935976SStephen Warren struct hda_spdif_out *spdif = 12667c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12670aa62aefSHarald Welte 12680aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12690aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12700aa62aefSHarald Welte if (chs == 2 && 12710aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12720aa62aefSHarald Welte format) && 12737c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12740aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12750aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12760aa62aefSHarald Welte * be updated */ 12777c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12780aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12790aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12807c935976SStephen Warren spdif->ctls & 12810aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12820aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12830aa62aefSHarald Welte stream_tag, 0, format); 12840aa62aefSHarald Welte /* turn on again (if needed) */ 12857c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12860aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12870aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12887c935976SStephen Warren spdif->ctls & 0xff); 12890aa62aefSHarald Welte } else { 12900aa62aefSHarald Welte mout->dig_out_used = 0; 12910aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12920aa62aefSHarald Welte 0, 0, 0); 12930aa62aefSHarald Welte } 12940aa62aefSHarald Welte } 12950aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12960aa62aefSHarald Welte 12970aa62aefSHarald Welte /* front */ 12980aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12990aa62aefSHarald Welte 0, format); 13000aa62aefSHarald Welte 1301eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1302eb7188caSLydia Wang && !spec->hp_independent_mode) 13030aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 13040aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 13050aa62aefSHarald Welte 0, format); 13060aa62aefSHarald Welte 13070aa62aefSHarald Welte /* extra outputs copied from front */ 13080aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13090aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13100aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13110aa62aefSHarald Welte mout->extra_out_nid[i], 13120aa62aefSHarald Welte stream_tag, 0, format); 13130aa62aefSHarald Welte 13140aa62aefSHarald Welte /* surrounds */ 13150aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 13160aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 13170aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13180aa62aefSHarald Welte i * 2, format); 13190aa62aefSHarald Welte else /* copy front */ 13200aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13210aa62aefSHarald Welte 0, format); 13220aa62aefSHarald Welte } 1323*7eb56e84STakashi Iwai vt1708_start_hp_work(spec); 1324*7eb56e84STakashi Iwai return 0; 13250aa62aefSHarald Welte } 13260aa62aefSHarald Welte 1327*7eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 13280aa62aefSHarald Welte struct hda_codec *codec, 13290aa62aefSHarald Welte unsigned int stream_tag, 13300aa62aefSHarald Welte unsigned int format, 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; 13350aa62aefSHarald Welte 1336*7eb56e84STakashi Iwai snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 0, format); 13371f2e99feSLydia Wang vt1708_start_hp_work(spec); 13380aa62aefSHarald Welte return 0; 13390aa62aefSHarald Welte } 13400aa62aefSHarald Welte 13410aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13420aa62aefSHarald Welte struct hda_codec *codec, 13430aa62aefSHarald Welte struct snd_pcm_substream *substream) 13440aa62aefSHarald Welte { 13450aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13460aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1347dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13480aa62aefSHarald Welte int i; 13490aa62aefSHarald Welte 13500aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13510aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13520aa62aefSHarald Welte 13530aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13540aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13550aa62aefSHarald Welte 0, 0, 0); 13560aa62aefSHarald Welte 13570aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13580aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13590aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13600aa62aefSHarald Welte mout->extra_out_nid[i], 13610aa62aefSHarald Welte 0, 0, 0); 13620aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13630aa62aefSHarald Welte if (mout->dig_out_nid && 13640aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13650aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13660aa62aefSHarald Welte 0, 0, 0); 13670aa62aefSHarald Welte mout->dig_out_used = 0; 13680aa62aefSHarald Welte } 13690aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 1370*7eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 1371*7eb56e84STakashi Iwai return 0; 13720aa62aefSHarald Welte } 1373*7eb56e84STakashi Iwai 1374*7eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 1375*7eb56e84STakashi Iwai struct hda_codec *codec, 1376*7eb56e84STakashi Iwai struct snd_pcm_substream *substream) 1377*7eb56e84STakashi Iwai { 1378*7eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 1379*7eb56e84STakashi Iwai struct hda_multi_out *mout = &spec->multiout; 1380*7eb56e84STakashi Iwai 1381*7eb56e84STakashi Iwai snd_hda_codec_setup_stream(codec, mout->hp_nid, 0, 0, 0); 13821f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13830aa62aefSHarald Welte return 0; 13840aa62aefSHarald Welte } 13850aa62aefSHarald Welte 1386c577b8a1SJoseph Chan /* 1387c577b8a1SJoseph Chan * Digital out 1388c577b8a1SJoseph Chan */ 1389c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1390c577b8a1SJoseph Chan struct hda_codec *codec, 1391c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1392c577b8a1SJoseph Chan { 1393c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1394c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1395c577b8a1SJoseph Chan } 1396c577b8a1SJoseph Chan 1397c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1398c577b8a1SJoseph Chan struct hda_codec *codec, 1399c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1400c577b8a1SJoseph Chan { 1401c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1402c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1403c577b8a1SJoseph Chan } 1404c577b8a1SJoseph Chan 14055691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 140698aa34c0SHarald Welte struct hda_codec *codec, 140798aa34c0SHarald Welte unsigned int stream_tag, 140898aa34c0SHarald Welte unsigned int format, 140998aa34c0SHarald Welte struct snd_pcm_substream *substream) 141098aa34c0SHarald Welte { 141198aa34c0SHarald Welte struct via_spec *spec = codec->spec; 14129da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 14139da29271STakashi Iwai stream_tag, format, substream); 14149da29271STakashi Iwai } 14155691ec7fSHarald Welte 14169da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 14179da29271STakashi Iwai struct hda_codec *codec, 14189da29271STakashi Iwai struct snd_pcm_substream *substream) 14199da29271STakashi Iwai { 14209da29271STakashi Iwai struct via_spec *spec = codec->spec; 14219da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 142298aa34c0SHarald Welte return 0; 142398aa34c0SHarald Welte } 142498aa34c0SHarald Welte 1425c577b8a1SJoseph Chan /* 1426c577b8a1SJoseph Chan * Analog capture 1427c577b8a1SJoseph Chan */ 1428c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1429c577b8a1SJoseph Chan struct hda_codec *codec, 1430c577b8a1SJoseph Chan unsigned int stream_tag, 1431c577b8a1SJoseph Chan unsigned int format, 1432c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1433c577b8a1SJoseph Chan { 1434c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1435c577b8a1SJoseph Chan 1436c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1437c577b8a1SJoseph Chan stream_tag, 0, format); 1438c577b8a1SJoseph Chan return 0; 1439c577b8a1SJoseph Chan } 1440c577b8a1SJoseph Chan 1441c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1442c577b8a1SJoseph Chan struct hda_codec *codec, 1443c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1444c577b8a1SJoseph Chan { 1445c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1446888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1447c577b8a1SJoseph Chan return 0; 1448c577b8a1SJoseph Chan } 1449c577b8a1SJoseph Chan 14509af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 1451*7eb56e84STakashi Iwai .substreams = 1, 1452c577b8a1SJoseph Chan .channels_min = 2, 1453c577b8a1SJoseph Chan .channels_max = 8, 14549af74210STakashi Iwai /* NID is set in via_build_pcms */ 1455c577b8a1SJoseph Chan .ops = { 1456c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14579af74210STakashi Iwai .close = via_playback_pcm_close, 14580aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14590aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1460c577b8a1SJoseph Chan }, 1461c577b8a1SJoseph Chan }; 1462c577b8a1SJoseph Chan 1463*7eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 1464*7eb56e84STakashi Iwai .substreams = 1, 1465*7eb56e84STakashi Iwai .channels_min = 2, 1466*7eb56e84STakashi Iwai .channels_max = 2, 1467*7eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 1468*7eb56e84STakashi Iwai .ops = { 1469*7eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1470*7eb56e84STakashi Iwai .close = via_playback_pcm_close, 1471*7eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 1472*7eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 1473*7eb56e84STakashi Iwai }, 1474*7eb56e84STakashi Iwai }; 1475*7eb56e84STakashi Iwai 147690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1477*7eb56e84STakashi Iwai .substreams = 1, 1478bc9b5623STakashi Iwai .channels_min = 2, 1479bc9b5623STakashi Iwai .channels_max = 8, 14809af74210STakashi Iwai /* NID is set in via_build_pcms */ 1481bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1482bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1483bc9b5623STakashi Iwai * disable the 24bit format, so far. 1484bc9b5623STakashi Iwai */ 1485bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1486bc9b5623STakashi Iwai .ops = { 1487bc9b5623STakashi Iwai .open = via_playback_pcm_open, 14889af74210STakashi Iwai .close = via_playback_pcm_close, 1489c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1490c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1491bc9b5623STakashi Iwai }, 1492bc9b5623STakashi Iwai }; 1493bc9b5623STakashi Iwai 14949af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 1495*7eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1496c577b8a1SJoseph Chan .channels_min = 2, 1497c577b8a1SJoseph Chan .channels_max = 2, 14989af74210STakashi Iwai /* NID is set in via_build_pcms */ 1499c577b8a1SJoseph Chan .ops = { 15009af74210STakashi Iwai .open = via_playback_pcm_open, 15019af74210STakashi Iwai .close = via_playback_pcm_close, 1502c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1503c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1504c577b8a1SJoseph Chan }, 1505c577b8a1SJoseph Chan }; 1506c577b8a1SJoseph Chan 15079af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1508c577b8a1SJoseph Chan .substreams = 1, 1509c577b8a1SJoseph Chan .channels_min = 2, 1510c577b8a1SJoseph Chan .channels_max = 2, 1511c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1512c577b8a1SJoseph Chan .ops = { 1513c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 15146b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 15159da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 15169da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1517c577b8a1SJoseph Chan }, 1518c577b8a1SJoseph Chan }; 1519c577b8a1SJoseph Chan 15209af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1521c577b8a1SJoseph Chan .substreams = 1, 1522c577b8a1SJoseph Chan .channels_min = 2, 1523c577b8a1SJoseph Chan .channels_max = 2, 1524c577b8a1SJoseph Chan }; 1525c577b8a1SJoseph Chan 1526c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1527c577b8a1SJoseph Chan { 1528c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 15295b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 153090dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 15315b0cb1d8SJaroslav Kysela int err, i; 1532c577b8a1SJoseph Chan 153324088a58STakashi Iwai if (spec->set_widgets_power_state) 153424088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 153524088a58STakashi Iwai return -ENOMEM; 153624088a58STakashi Iwai 1537c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1538c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1539c577b8a1SJoseph Chan if (err < 0) 1540c577b8a1SJoseph Chan return err; 1541c577b8a1SJoseph Chan } 1542c577b8a1SJoseph Chan 1543c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1544c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 154574b654c9SStephen Warren spec->multiout.dig_out_nid, 1546c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1547c577b8a1SJoseph Chan if (err < 0) 1548c577b8a1SJoseph Chan return err; 15499a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15509a08160bSTakashi Iwai &spec->multiout); 15519a08160bSTakashi Iwai if (err < 0) 15529a08160bSTakashi Iwai return err; 15539a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1554c577b8a1SJoseph Chan } 1555c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1556c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1557c577b8a1SJoseph Chan if (err < 0) 1558c577b8a1SJoseph Chan return err; 1559c577b8a1SJoseph Chan } 156017314379SLydia Wang 15615b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15625b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15635b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 156421949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15655b0cb1d8SJaroslav Kysela if (err < 0) 15665b0cb1d8SJaroslav Kysela return err; 15675b0cb1d8SJaroslav Kysela } 15685b0cb1d8SJaroslav Kysela 15695b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15705b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15715b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15725b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15735b0cb1d8SJaroslav Kysela continue; 15745b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15755b0cb1d8SJaroslav Kysela if (kctl == NULL) 15765b0cb1d8SJaroslav Kysela continue; 15775b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15785b0cb1d8SJaroslav Kysela knew->subdevice); 15795b0cb1d8SJaroslav Kysela } 15805b0cb1d8SJaroslav Kysela } 15815b0cb1d8SJaroslav Kysela 158217314379SLydia Wang /* init power states */ 15833e95b9abSLydia Wang set_widgets_power_state(codec); 158417314379SLydia Wang analog_low_current_mode(codec, 1); 158517314379SLydia Wang 1586603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1587c577b8a1SJoseph Chan return 0; 1588c577b8a1SJoseph Chan } 1589c577b8a1SJoseph Chan 1590c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1591c577b8a1SJoseph Chan { 1592c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1593c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1594c577b8a1SJoseph Chan 1595c577b8a1SJoseph Chan codec->num_pcms = 1; 1596c577b8a1SJoseph Chan codec->pcm_info = info; 1597c577b8a1SJoseph Chan 159882673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 159982673bc8STakashi Iwai "%s Analog", codec->chip_name); 1600c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 16019af74210STakashi Iwai 16029af74210STakashi Iwai if (!spec->stream_analog_playback) 16039af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1604377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 16059af74210STakashi Iwai *spec->stream_analog_playback; 1606377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1607377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1608c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1609c577b8a1SJoseph Chan spec->multiout.max_channels; 16109af74210STakashi Iwai 16119af74210STakashi Iwai if (!spec->stream_analog_capture) 16129af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 16139af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 16149af74210STakashi Iwai *spec->stream_analog_capture; 16159af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 16169af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 16179af74210STakashi Iwai spec->num_adc_nids; 1618c577b8a1SJoseph Chan 1619c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1620c577b8a1SJoseph Chan codec->num_pcms++; 1621c577b8a1SJoseph Chan info++; 162282673bc8STakashi Iwai snprintf(spec->stream_name_digital, 162382673bc8STakashi Iwai sizeof(spec->stream_name_digital), 162482673bc8STakashi Iwai "%s Digital", codec->chip_name); 1625c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 16267ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1627c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 16289af74210STakashi Iwai if (!spec->stream_digital_playback) 16299af74210STakashi Iwai spec->stream_digital_playback = 16309af74210STakashi Iwai &via_pcm_digital_playback; 1631c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 16329af74210STakashi Iwai *spec->stream_digital_playback; 1633c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1634c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1635c577b8a1SJoseph Chan } 1636c577b8a1SJoseph Chan if (spec->dig_in_nid) { 16379af74210STakashi Iwai if (!spec->stream_digital_capture) 16389af74210STakashi Iwai spec->stream_digital_capture = 16399af74210STakashi Iwai &via_pcm_digital_capture; 1640c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 16419af74210STakashi Iwai *spec->stream_digital_capture; 1642c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1643c577b8a1SJoseph Chan spec->dig_in_nid; 1644c577b8a1SJoseph Chan } 1645c577b8a1SJoseph Chan } 1646c577b8a1SJoseph Chan 1647*7eb56e84STakashi Iwai if (spec->multiout.hp_nid) { 1648*7eb56e84STakashi Iwai codec->num_pcms++; 1649*7eb56e84STakashi Iwai info++; 1650*7eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 1651*7eb56e84STakashi Iwai "%s HP", codec->chip_name); 1652*7eb56e84STakashi Iwai info->name = spec->stream_name_hp; 1653*7eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 1654*7eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1655*7eb56e84STakashi Iwai spec->multiout.hp_nid; 1656*7eb56e84STakashi Iwai } 1657c577b8a1SJoseph Chan return 0; 1658c577b8a1SJoseph Chan } 1659c577b8a1SJoseph Chan 1660c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1661c577b8a1SJoseph Chan { 1662c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1663c577b8a1SJoseph Chan 1664c577b8a1SJoseph Chan if (!spec) 1665c577b8a1SJoseph Chan return; 1666c577b8a1SJoseph Chan 1667603c4019STakashi Iwai via_free_kctls(codec); 16681f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1669c577b8a1SJoseph Chan kfree(codec->spec); 1670c577b8a1SJoseph Chan } 1671c577b8a1SJoseph Chan 167264be285bSTakashi Iwai /* mute/unmute outputs */ 167364be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 167464be285bSTakashi Iwai hda_nid_t *pins, bool mute) 167564be285bSTakashi Iwai { 167664be285bSTakashi Iwai int i; 167764be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 167864be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 167964be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 168064be285bSTakashi Iwai mute ? 0 : PIN_OUT); 168164be285bSTakashi Iwai } 168264be285bSTakashi Iwai 168369e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 168469e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 168569e52a80SHarald Welte { 1686dcf34c8cSLydia Wang unsigned int present = 0; 168769e52a80SHarald Welte struct via_spec *spec = codec->spec; 168869e52a80SHarald Welte 1689d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1690dcf34c8cSLydia Wang 169164be285bSTakashi Iwai if (!spec->hp_independent_mode) 169264be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 169364be285bSTakashi Iwai spec->autocfg.line_out_pins, 169464be285bSTakashi Iwai present); 169569e52a80SHarald Welte } 169669e52a80SHarald Welte 1697f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1698f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1699f3db423dSLydia Wang { 1700f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1701f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1702f3db423dSLydia Wang 1703f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1704f3db423dSLydia Wang return; 1705f3db423dSLydia Wang 1706d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1707d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1708f3db423dSLydia Wang 1709f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1710f3db423dSLydia Wang if (lineout_present) { 17113e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 17123e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17133e0693e2STakashi Iwai lineout_present ? 0 : PIN_OUT); 1714f3db423dSLydia Wang return; 1715f3db423dSLydia Wang } 1716f3db423dSLydia Wang 1717d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1718f3db423dSLydia Wang 1719f3db423dSLydia Wang if (!spec->hp_independent_mode) 17203e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 17213e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17223e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 1723f3db423dSLydia Wang } 1724f3db423dSLydia Wang 172569e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 172669e52a80SHarald Welte { 172769e52a80SHarald Welte unsigned int gpio_data; 172869e52a80SHarald Welte unsigned int vol_counter; 172969e52a80SHarald Welte unsigned int vol; 173069e52a80SHarald Welte unsigned int master_vol; 173169e52a80SHarald Welte 173269e52a80SHarald Welte struct via_spec *spec = codec->spec; 173369e52a80SHarald Welte 173469e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 173569e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 173669e52a80SHarald Welte 173769e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 173869e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 173969e52a80SHarald Welte 174069e52a80SHarald Welte vol = vol_counter & 0x1F; 174169e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 174269e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 174369e52a80SHarald Welte AC_AMP_GET_INPUT); 174469e52a80SHarald Welte 174569e52a80SHarald Welte if (gpio_data == 0x02) { 174669e52a80SHarald Welte /* unmute line out */ 17473e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17483e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17493e0693e2STakashi Iwai PIN_OUT); 175069e52a80SHarald Welte if (vol_counter & 0x20) { 175169e52a80SHarald Welte /* decrease volume */ 175269e52a80SHarald Welte if (vol > master_vol) 175369e52a80SHarald Welte vol = master_vol; 175469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 175569e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 175669e52a80SHarald Welte master_vol-vol); 175769e52a80SHarald Welte } else { 175869e52a80SHarald Welte /* increase volume */ 175969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 176069e52a80SHarald Welte HDA_AMP_VOLMASK, 176169e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 176269e52a80SHarald Welte (master_vol+vol)); 176369e52a80SHarald Welte } 176469e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 176569e52a80SHarald Welte /* mute line out */ 17663e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17673e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17683e0693e2STakashi Iwai 0); 176969e52a80SHarald Welte } 177069e52a80SHarald Welte } 177169e52a80SHarald Welte 177225eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 177325eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 177425eaba2fSLydia Wang { 177525eaba2fSLydia Wang unsigned int hp_present; 177625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 177725eaba2fSLydia Wang 177827439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 177925eaba2fSLydia Wang return; 178025eaba2fSLydia Wang 1781d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 178225eaba2fSLydia Wang 178364be285bSTakashi Iwai if (!spec->hp_independent_mode) 178464be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 178564be285bSTakashi Iwai spec->autocfg.speaker_pins, 178664be285bSTakashi Iwai hp_present); 178725eaba2fSLydia Wang } 178825eaba2fSLydia Wang 178925eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 179025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 179125eaba2fSLydia Wang { 179264be285bSTakashi Iwai int present; 179325eaba2fSLydia Wang struct via_spec *spec = codec->spec; 179425eaba2fSLydia Wang 179525eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 179625eaba2fSLydia Wang return; 179725eaba2fSLydia Wang 179864be285bSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 179964be285bSTakashi Iwai if (!spec->hp_independent_mode) 180064be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 180164be285bSTakashi Iwai spec->autocfg.line_out_pins, 180264be285bSTakashi Iwai present); 180325eaba2fSLydia Wang 180464be285bSTakashi Iwai if (!present) 180564be285bSTakashi Iwai present = snd_hda_jack_detect(codec, 180664be285bSTakashi Iwai spec->autocfg.line_out_pins[0]); 180725eaba2fSLydia Wang 180825eaba2fSLydia Wang /* Speakers */ 180964be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 181064be285bSTakashi Iwai spec->autocfg.speaker_pins, 181164be285bSTakashi Iwai present); 181225eaba2fSLydia Wang } 181325eaba2fSLydia Wang 181425eaba2fSLydia Wang 181569e52a80SHarald Welte /* unsolicited event for jack sensing */ 181669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 181769e52a80SHarald Welte unsigned int res) 181869e52a80SHarald Welte { 181969e52a80SHarald Welte res >>= 26; 1820ec7e7e42SLydia Wang 1821a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 18223e95b9abSLydia Wang set_widgets_power_state(codec); 1823ec7e7e42SLydia Wang 1824ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1825ec7e7e42SLydia Wang 1826ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1827ec7e7e42SLydia Wang via_hp_automute(codec); 1828ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1829ec7e7e42SLydia Wang via_gpio_control(codec); 1830ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1831f3db423dSLydia Wang via_mono_automute(codec); 1832ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 183325eaba2fSLydia Wang via_speaker_automute(codec); 1834ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 183525eaba2fSLydia Wang via_hp_bind_automute(codec); 183669e52a80SHarald Welte } 183769e52a80SHarald Welte 1838c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1839c577b8a1SJoseph Chan { 1840c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 184169e52a80SHarald Welte int i; 184269e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 184369e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 184469e52a80SHarald Welte 1845f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1846f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 184755d1d6c1STakashi Iwai if (spec->dig_in_pin) { 184855d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1849f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 185012b74c80STakashi Iwai PIN_OUT); 185155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1852f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1853f7278fd0SJosepch Chan } 185412b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 185512b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 185612b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1857f7278fd0SJosepch Chan 18589da29271STakashi Iwai /* assign slave outs */ 18599da29271STakashi Iwai if (spec->slave_dig_outs[0]) 18609da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 18615691ec7fSHarald Welte 1862c577b8a1SJoseph Chan return 0; 1863c577b8a1SJoseph Chan } 1864c577b8a1SJoseph Chan 18651f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18661f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18671f2e99feSLydia Wang { 18681f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18691f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18701f2e99feSLydia Wang return 0; 18711f2e99feSLydia Wang } 18721f2e99feSLydia Wang #endif 18731f2e99feSLydia Wang 1874cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1875cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1876cb53c626STakashi Iwai { 1877cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1878cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1879cb53c626STakashi Iwai } 1880cb53c626STakashi Iwai #endif 1881cb53c626STakashi Iwai 1882c577b8a1SJoseph Chan /* 1883c577b8a1SJoseph Chan */ 188490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1885c577b8a1SJoseph Chan .build_controls = via_build_controls, 1886c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1887c577b8a1SJoseph Chan .init = via_init, 1888c577b8a1SJoseph Chan .free = via_free, 18891f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18901f2e99feSLydia Wang .suspend = via_suspend, 18911f2e99feSLydia Wang #endif 1892cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1893cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1894cb53c626STakashi Iwai #endif 1895c577b8a1SJoseph Chan }; 1896c577b8a1SJoseph Chan 18974a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1898c577b8a1SJoseph Chan { 18994a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 19004a79616dSTakashi Iwai int i; 19014a79616dSTakashi Iwai 19024a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 19034a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 19044a79616dSTakashi Iwai return false; 19054a79616dSTakashi Iwai } 19064a79616dSTakashi Iwai if (spec->multiout.hp_nid == dac) 19074a79616dSTakashi Iwai return false; 19084a79616dSTakashi Iwai return true; 19094a79616dSTakashi Iwai } 19104a79616dSTakashi Iwai 19114a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 19124a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 19134a79616dSTakashi Iwai int depth, int wid_type) 19144a79616dSTakashi Iwai { 19154a79616dSTakashi Iwai hda_nid_t conn[8]; 19164a79616dSTakashi Iwai int i, nums; 19174a79616dSTakashi Iwai 19184a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 19194a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 19204a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 19214a79616dSTakashi Iwai continue; 19224a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 19234a79616dSTakashi Iwai path->path[depth] = conn[i]; 19244a79616dSTakashi Iwai path->idx[depth] = i; 19254a79616dSTakashi Iwai path->depth = ++depth; 19264a79616dSTakashi Iwai return true; 19274a79616dSTakashi Iwai } 19284a79616dSTakashi Iwai } 19294a79616dSTakashi Iwai if (depth > 4) 19304a79616dSTakashi Iwai return false; 19314a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 19324a79616dSTakashi Iwai unsigned int type; 19334a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 19344a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 19354a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 19364a79616dSTakashi Iwai continue; 19374a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 19384a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 19394a79616dSTakashi Iwai path->path[depth] = conn[i]; 19404a79616dSTakashi Iwai path->idx[depth] = i; 19414a79616dSTakashi Iwai return true; 19424a79616dSTakashi Iwai } 19434a79616dSTakashi Iwai } 19444a79616dSTakashi Iwai return false; 19454a79616dSTakashi Iwai } 19464a79616dSTakashi Iwai 19474a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 19484a79616dSTakashi Iwai { 19494a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 19504a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1951c577b8a1SJoseph Chan int i; 1952c577b8a1SJoseph Chan hda_nid_t nid; 1953c577b8a1SJoseph Chan 1954c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 19554a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 19564a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1957c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 19584a79616dSTakashi Iwai if (!nid) 19594a79616dSTakashi Iwai continue; 19604a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 19614a79616dSTakashi Iwai spec->private_dac_nids[i] = 19624a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1963c577b8a1SJoseph Chan } 1964c577b8a1SJoseph Chan return 0; 1965c577b8a1SJoseph Chan } 1966c577b8a1SJoseph Chan 19674a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 19684a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1969c577b8a1SJoseph Chan { 19704a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1971c577b8a1SJoseph Chan char name[32]; 19724a79616dSTakashi Iwai hda_nid_t nid; 19734a79616dSTakashi Iwai int err; 1974c577b8a1SJoseph Chan 19754a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19764a79616dSTakashi Iwai nid = dac; 19774a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 19784a79616dSTakashi Iwai nid = pin; 19794a79616dSTakashi Iwai else 19804a79616dSTakashi Iwai nid = 0; 19814a79616dSTakashi Iwai if (nid) { 19824a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1983c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 19844a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1985c577b8a1SJoseph Chan if (err < 0) 1986c577b8a1SJoseph Chan return err; 1987c577b8a1SJoseph Chan } 19884a79616dSTakashi Iwai 19894a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19904a79616dSTakashi Iwai nid = dac; 19914a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 19924a79616dSTakashi Iwai nid = pin; 19934a79616dSTakashi Iwai else 19944a79616dSTakashi Iwai nid = 0; 19954a79616dSTakashi Iwai if (nid) { 19964a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 19974a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 19984a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 19994a79616dSTakashi Iwai if (err < 0) 20004a79616dSTakashi Iwai return err; 20014a79616dSTakashi Iwai } 20024a79616dSTakashi Iwai return 0; 20034a79616dSTakashi Iwai } 20044a79616dSTakashi Iwai 20054a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 20064a79616dSTakashi Iwai hda_nid_t nid); 20074a79616dSTakashi Iwai 2008f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 2009f4a7828bSTakashi Iwai { 2010f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 2011f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 2012f4a7828bSTakashi Iwai int i; 2013f4a7828bSTakashi Iwai 2014f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2015f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, cfg->inputs[i].pin)) 2016f4a7828bSTakashi Iwai continue; 2017f4a7828bSTakashi Iwai spec->can_smart51 = 1; 2018f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 2019f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 2020f4a7828bSTakashi Iwai break; 2021f4a7828bSTakashi Iwai } 2022f4a7828bSTakashi Iwai } 2023f4a7828bSTakashi Iwai 20244a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 20254a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 20264a79616dSTakashi Iwai { 20274a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 2028f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 20294a79616dSTakashi Iwai static const char * const chname[4] = { 20304a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 20314a79616dSTakashi Iwai }; 20324a79616dSTakashi Iwai int i, idx, err; 2033f4a7828bSTakashi Iwai int old_line_outs; 2034f4a7828bSTakashi Iwai 2035f4a7828bSTakashi Iwai /* check smart51 */ 2036f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 2037f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 2038f4a7828bSTakashi Iwai mangle_smart51(codec); 20394a79616dSTakashi Iwai 20404a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 20414a79616dSTakashi Iwai hda_nid_t pin, dac; 20424a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 20434a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 20444a79616dSTakashi Iwai if (!pin || !dac) 20454a79616dSTakashi Iwai continue; 20464a79616dSTakashi Iwai if (i == AUTO_SEQ_CENLFE) { 20474a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 20484a79616dSTakashi Iwai if (err < 0) 20494a79616dSTakashi Iwai return err; 20504a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 20514a79616dSTakashi Iwai if (err < 0) 20524a79616dSTakashi Iwai return err; 20534a79616dSTakashi Iwai } else { 20544a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 20554a79616dSTakashi Iwai if (err < 0) 20564a79616dSTakashi Iwai return err; 20574a79616dSTakashi Iwai } 20584a79616dSTakashi Iwai } 20594a79616dSTakashi Iwai 20604a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 20614a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 20624a79616dSTakashi Iwai if (idx >= 0) { 20634a79616dSTakashi Iwai /* add control to mixer */ 20644a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 20654a79616dSTakashi Iwai "PCM Playback Volume", 20664a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20674a79616dSTakashi Iwai idx, HDA_INPUT)); 20684a79616dSTakashi Iwai if (err < 0) 20694a79616dSTakashi Iwai return err; 20704a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 20714a79616dSTakashi Iwai "PCM Playback Switch", 20724a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 20734a79616dSTakashi Iwai idx, HDA_INPUT)); 20744a79616dSTakashi Iwai if (err < 0) 20754a79616dSTakashi Iwai return err; 2076c577b8a1SJoseph Chan } 2077c577b8a1SJoseph Chan 2078f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 2079f4a7828bSTakashi Iwai 2080c577b8a1SJoseph Chan return 0; 2081c577b8a1SJoseph Chan } 2082c577b8a1SJoseph Chan 20830aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 20840aa62aefSHarald Welte { 20850aa62aefSHarald Welte int i; 20860aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2087ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 20880aa62aefSHarald Welte 20890aa62aefSHarald Welte /* for hp mode select */ 209010a20af7STakashi Iwai for (i = 0; texts[i]; i++) 209110a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 20920aa62aefSHarald Welte 20930aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 20940aa62aefSHarald Welte } 20950aa62aefSHarald Welte 20964a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 2097c577b8a1SJoseph Chan { 20984a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 20994a79616dSTakashi Iwai hda_nid_t dac = 0; 2100c577b8a1SJoseph Chan int err; 2101c577b8a1SJoseph Chan 2102c577b8a1SJoseph Chan if (!pin) 2103c577b8a1SJoseph Chan return 0; 2104c577b8a1SJoseph Chan 21054a79616dSTakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 21064a79616dSTakashi Iwai &spec->hp_dep_path, 0, -1)) 21074a79616dSTakashi Iwai return 0; 21084a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 21094a79616dSTakashi Iwai dac = spec->hp_path.path[spec->hp_path.depth - 1]; 21104a79616dSTakashi Iwai spec->multiout.hp_nid = dac; 21114a79616dSTakashi Iwai spec->hp_independent_mode_index = 21124a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 21130aa62aefSHarald Welte create_hp_imux(spec); 21144a79616dSTakashi Iwai } 21154a79616dSTakashi Iwai 21164a79616dSTakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, dac, 3); 21174a79616dSTakashi Iwai if (err < 0) 21184a79616dSTakashi Iwai return err; 21190aa62aefSHarald Welte 2120c577b8a1SJoseph Chan return 0; 2121c577b8a1SJoseph Chan } 2122c577b8a1SJoseph Chan 2123a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 2124a766d0d7STakashi Iwai hda_nid_t nid) 2125a766d0d7STakashi Iwai { 2126a766d0d7STakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 2127a766d0d7STakashi Iwai int i, nums; 2128a766d0d7STakashi Iwai 2129a766d0d7STakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 2130a766d0d7STakashi Iwai for (i = 0; i < nums; i++) 2131a766d0d7STakashi Iwai if (conn[i] == nid) 2132a766d0d7STakashi Iwai return i; 2133a766d0d7STakashi Iwai return -1; 2134a766d0d7STakashi Iwai } 2135a766d0d7STakashi Iwai 2136a766d0d7STakashi Iwai /* look for ADCs */ 2137a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2138a766d0d7STakashi Iwai { 2139a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2140a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2141a766d0d7STakashi Iwai int i; 2142a766d0d7STakashi Iwai 2143a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2144a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2145a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2146a766d0d7STakashi Iwai continue; 2147a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2148a766d0d7STakashi Iwai continue; 2149a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2150a766d0d7STakashi Iwai continue; 2151a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2152a766d0d7STakashi Iwai return -ENOMEM; 2153a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2154a766d0d7STakashi Iwai } 2155a766d0d7STakashi Iwai return 0; 2156a766d0d7STakashi Iwai } 2157a766d0d7STakashi Iwai 2158a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2159a766d0d7STakashi Iwai 2160c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2161620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 2162620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 2163c577b8a1SJoseph Chan { 216410a20af7STakashi Iwai struct via_spec *spec = codec->spec; 21650aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2166a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 2167a766d0d7STakashi Iwai hda_nid_t cap_nid; 2168a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 2169a766d0d7STakashi Iwai int num_idxs; 2170a766d0d7STakashi Iwai 2171a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2172a766d0d7STakashi Iwai if (err < 0) 2173a766d0d7STakashi Iwai return err; 2174a766d0d7STakashi Iwai err = get_mux_nids(codec); 2175a766d0d7STakashi Iwai if (err < 0) 2176a766d0d7STakashi Iwai return err; 2177a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 2178a766d0d7STakashi Iwai 2179a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 2180a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 2181a766d0d7STakashi Iwai if (num_idxs <= 0) 2182a766d0d7STakashi Iwai return 0; 2183c577b8a1SJoseph Chan 2184c577b8a1SJoseph Chan /* for internal loopback recording select */ 2185f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2186620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 218710a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2188f3268512STakashi Iwai break; 2189f3268512STakashi Iwai } 2190f3268512STakashi Iwai } 2191c577b8a1SJoseph Chan 21927b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 219310a20af7STakashi Iwai const char *label; 21947b315bb4STakashi Iwai type = cfg->inputs[i].type; 2195f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 21967b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2197c577b8a1SJoseph Chan break; 2198f3268512STakashi Iwai if (idx >= num_idxs) 2199f3268512STakashi Iwai continue; 22007b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 22017b315bb4STakashi Iwai type_idx++; 22027b315bb4STakashi Iwai else 22037b315bb4STakashi Iwai type_idx = 0; 220410a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2205620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 2206620e2b28STakashi Iwai pin_idxs[idx]); 2207a766d0d7STakashi Iwai if (idx2 >= 0) 220816922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2209620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 2210c577b8a1SJoseph Chan if (err < 0) 2211c577b8a1SJoseph Chan return err; 221210a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2213c577b8a1SJoseph Chan } 2214c577b8a1SJoseph Chan return 0; 2215c577b8a1SJoseph Chan } 2216c577b8a1SJoseph Chan 2217cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 221890dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2219cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2220cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2221cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2222cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2223cb53c626STakashi Iwai { } /* end */ 2224cb53c626STakashi Iwai }; 2225cb53c626STakashi Iwai #endif 2226cb53c626STakashi Iwai 222776d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 222876d9b0ddSHarald Welte { 222976d9b0ddSHarald Welte unsigned int def_conf; 223076d9b0ddSHarald Welte unsigned char seqassoc; 223176d9b0ddSHarald Welte 22322f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 223376d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 223476d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 223582ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 223682ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 223776d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 22382f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 223976d9b0ddSHarald Welte } 224076d9b0ddSHarald Welte 224176d9b0ddSHarald Welte return; 224276d9b0ddSHarald Welte } 224376d9b0ddSHarald Welte 2244e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 22451f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22461f2e99feSLydia Wang { 22471f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22481f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22491f2e99feSLydia Wang 22501f2e99feSLydia Wang if (spec->codec_type != VT1708) 22511f2e99feSLydia Wang return 0; 2252e06e5a29STakashi Iwai spec->vt1708_jack_detect = 22531f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2254e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 22551f2e99feSLydia Wang return 0; 22561f2e99feSLydia Wang } 22571f2e99feSLydia Wang 2258e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 22591f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22601f2e99feSLydia Wang { 22611f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22621f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22631f2e99feSLydia Wang int change; 22641f2e99feSLydia Wang 22651f2e99feSLydia Wang if (spec->codec_type != VT1708) 22661f2e99feSLydia Wang return 0; 2267e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 22681f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2269e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2270e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 22711f2e99feSLydia Wang mute_aa_path(codec, 1); 22721f2e99feSLydia Wang notify_aa_path_ctls(codec); 22731f2e99feSLydia Wang } 22741f2e99feSLydia Wang return change; 22751f2e99feSLydia Wang } 22761f2e99feSLydia Wang 2277e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 22781f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22791f2e99feSLydia Wang .name = "Jack Detect", 22801f2e99feSLydia Wang .count = 1, 22811f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2282e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2283e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 22841f2e99feSLydia Wang }; 22851f2e99feSLydia Wang 2286c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2287c577b8a1SJoseph Chan { 2288c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2289c577b8a1SJoseph Chan int err; 2290c577b8a1SJoseph Chan 229176d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 229276d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 229376d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 229476d9b0ddSHarald Welte 2295c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2296c577b8a1SJoseph Chan if (err < 0) 2297c577b8a1SJoseph Chan return err; 22984a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2299c577b8a1SJoseph Chan if (err < 0) 2300c577b8a1SJoseph Chan return err; 2301c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2302c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2303c577b8a1SJoseph Chan 23044a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2305c577b8a1SJoseph Chan if (err < 0) 2306c577b8a1SJoseph Chan return err; 23074a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2308c577b8a1SJoseph Chan if (err < 0) 2309c577b8a1SJoseph Chan return err; 2310620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2311c577b8a1SJoseph Chan if (err < 0) 2312c577b8a1SJoseph Chan return err; 23131f2e99feSLydia Wang /* add jack detect on/off control */ 2314e06e5a29STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 2315e06e5a29STakashi Iwai return -ENOMEM; 2316c577b8a1SJoseph Chan 2317c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2318c577b8a1SJoseph Chan 23190852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2320c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 232155d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2322c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2323c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2324c577b8a1SJoseph Chan 2325603c4019STakashi Iwai if (spec->kctls.list) 2326603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2327c577b8a1SJoseph Chan 232869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2329c577b8a1SJoseph Chan 23300aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 23310aa62aefSHarald Welte 2332f8fdd495SHarald Welte if (spec->hp_mux) 23333d83e577STakashi Iwai via_hp_build(codec); 2334c577b8a1SJoseph Chan 2335f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2336f4a7828bSTakashi Iwai if (err < 0) 2337f4a7828bSTakashi Iwai return err; 2338f4a7828bSTakashi Iwai 2339c577b8a1SJoseph Chan return 1; 2340c577b8a1SJoseph Chan } 2341c577b8a1SJoseph Chan 2342c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2343c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2344c577b8a1SJoseph Chan { 234525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 234625eaba2fSLydia Wang 2347c577b8a1SJoseph Chan via_init(codec); 2348c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2349c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2350c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 235111890956SLydia Wang 235211890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 235325eaba2fSLydia Wang via_hp_bind_automute(codec); 235425eaba2fSLydia Wang } else { 235525eaba2fSLydia Wang via_hp_automute(codec); 235625eaba2fSLydia Wang via_speaker_automute(codec); 235725eaba2fSLydia Wang } 235825eaba2fSLydia Wang 2359c577b8a1SJoseph Chan return 0; 2360c577b8a1SJoseph Chan } 2361c577b8a1SJoseph Chan 23621f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 23631f2e99feSLydia Wang { 23641f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 23651f2e99feSLydia Wang vt1708_hp_work.work); 23661f2e99feSLydia Wang if (spec->codec_type != VT1708) 23671f2e99feSLydia Wang return; 23681f2e99feSLydia Wang /* if jack state toggled */ 23691f2e99feSLydia Wang if (spec->vt1708_hp_present 2370d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 23711f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 23721f2e99feSLydia Wang via_hp_automute(spec->codec); 23731f2e99feSLydia Wang } 23741f2e99feSLydia Wang vt1708_start_hp_work(spec); 23751f2e99feSLydia Wang } 23761f2e99feSLydia Wang 2377337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2378337b9d02STakashi Iwai { 2379337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2380337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2381337b9d02STakashi Iwai unsigned int type; 2382337b9d02STakashi Iwai int i, n; 2383337b9d02STakashi Iwai 2384337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2385337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2386337b9d02STakashi Iwai while (nid) { 2387a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 23881c55d521STakashi Iwai if (type == AC_WID_PIN) 23891c55d521STakashi Iwai break; 2390337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2391337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2392337b9d02STakashi Iwai if (n <= 0) 2393337b9d02STakashi Iwai break; 2394337b9d02STakashi Iwai if (n > 1) { 2395337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2396337b9d02STakashi Iwai break; 2397337b9d02STakashi Iwai } 2398337b9d02STakashi Iwai nid = conn[0]; 2399337b9d02STakashi Iwai } 2400337b9d02STakashi Iwai } 24011c55d521STakashi Iwai return 0; 2402337b9d02STakashi Iwai } 2403337b9d02STakashi Iwai 2404c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2405c577b8a1SJoseph Chan { 2406c577b8a1SJoseph Chan struct via_spec *spec; 2407c577b8a1SJoseph Chan int err; 2408c577b8a1SJoseph Chan 2409c577b8a1SJoseph Chan /* create a codec specific record */ 24105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2411c577b8a1SJoseph Chan if (spec == NULL) 2412c577b8a1SJoseph Chan return -ENOMEM; 2413c577b8a1SJoseph Chan 2414620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2415620e2b28STakashi Iwai 2416c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2417c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2418c577b8a1SJoseph Chan if (err < 0) { 2419c577b8a1SJoseph Chan via_free(codec); 2420c577b8a1SJoseph Chan return err; 2421c577b8a1SJoseph Chan } else if (!err) { 2422c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2423c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2424c577b8a1SJoseph Chan } 2425c577b8a1SJoseph Chan 2426c577b8a1SJoseph Chan 2427bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2428bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2429bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2430c577b8a1SJoseph Chan 2431a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2432c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2433c577b8a1SJoseph Chan spec->num_mixers++; 2434c577b8a1SJoseph Chan } 2435c577b8a1SJoseph Chan 2436c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2437c577b8a1SJoseph Chan 2438c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2439cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2440cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2441cb53c626STakashi Iwai #endif 24421f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2443c577b8a1SJoseph Chan return 0; 2444c577b8a1SJoseph Chan } 2445c577b8a1SJoseph Chan 2446c577b8a1SJoseph Chan /* capture mixer elements */ 244790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = { 2448c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2449c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2450c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2451c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2452c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2453c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2454c577b8a1SJoseph Chan { 2455c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2456c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2457c577b8a1SJoseph Chan * So call somewhat different.. 2458c577b8a1SJoseph Chan */ 2459c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2460c577b8a1SJoseph Chan .name = "Input Source", 2461c577b8a1SJoseph Chan .count = 1, 2462c577b8a1SJoseph Chan .info = via_mux_enum_info, 2463c577b8a1SJoseph Chan .get = via_mux_enum_get, 2464c577b8a1SJoseph Chan .put = via_mux_enum_put, 2465c577b8a1SJoseph Chan }, 2466c577b8a1SJoseph Chan { } /* end */ 2467c577b8a1SJoseph Chan }; 2468c577b8a1SJoseph Chan 246990dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2470a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2471a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 247269e52a80SHarald Welte { } 247369e52a80SHarald Welte }; 247469e52a80SHarald Welte 2475c577b8a1SJoseph Chan /* 2476c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2477c577b8a1SJoseph Chan */ 247890dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2479c577b8a1SJoseph Chan /* 2480c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2481c577b8a1SJoseph Chan */ 2482c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2483c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2484c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2485c577b8a1SJoseph Chan 2486c577b8a1SJoseph Chan 2487f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2488c577b8a1SJoseph Chan * mixer widget 2489c577b8a1SJoseph Chan */ 2490c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2491f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2492f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2493f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2494f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2495f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2496c577b8a1SJoseph Chan 2497c577b8a1SJoseph Chan /* 2498c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2499c577b8a1SJoseph Chan */ 2500c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2501c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2502c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2503c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2504c577b8a1SJoseph Chan 2505c577b8a1SJoseph Chan /* 2506c577b8a1SJoseph Chan * Unmute PW3 and PW4 2507c577b8a1SJoseph Chan */ 2508c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2509c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2510c577b8a1SJoseph Chan 2511bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2512bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2513c577b8a1SJoseph Chan /* PW9 Output enable */ 2514c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2515c577b8a1SJoseph Chan { } 2516c577b8a1SJoseph Chan }; 2517c577b8a1SJoseph Chan 2518c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2519c577b8a1SJoseph Chan { 2520c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2521c577b8a1SJoseph Chan int err; 2522c577b8a1SJoseph Chan 2523c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2524c577b8a1SJoseph Chan if (err < 0) 2525c577b8a1SJoseph Chan return err; 25264a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2527c577b8a1SJoseph Chan if (err < 0) 2528c577b8a1SJoseph Chan return err; 2529c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2530c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2531c577b8a1SJoseph Chan 25324a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2533c577b8a1SJoseph Chan if (err < 0) 2534c577b8a1SJoseph Chan return err; 25354a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2536c577b8a1SJoseph Chan if (err < 0) 2537c577b8a1SJoseph Chan return err; 2538620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2539c577b8a1SJoseph Chan if (err < 0) 2540c577b8a1SJoseph Chan return err; 2541c577b8a1SJoseph Chan 2542c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2543c577b8a1SJoseph Chan 25440852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2545c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 254655d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2547c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2548c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2549c577b8a1SJoseph Chan 2550603c4019STakashi Iwai if (spec->kctls.list) 2551603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2552c577b8a1SJoseph Chan 25530aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2554c577b8a1SJoseph Chan 2555f8fdd495SHarald Welte if (spec->hp_mux) 25563d83e577STakashi Iwai via_hp_build(codec); 2557f8fdd495SHarald Welte 2558f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2559f4a7828bSTakashi Iwai if (err < 0) 2560f4a7828bSTakashi Iwai return err; 2561f4a7828bSTakashi Iwai 2562c577b8a1SJoseph Chan return 1; 2563c577b8a1SJoseph Chan } 2564c577b8a1SJoseph Chan 2565cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 256690dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2567cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2568cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2569cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2570cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2571cb53c626STakashi Iwai { } /* end */ 2572cb53c626STakashi Iwai }; 2573cb53c626STakashi Iwai #endif 2574cb53c626STakashi Iwai 2575c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2576c577b8a1SJoseph Chan { 2577c577b8a1SJoseph Chan struct via_spec *spec; 2578c577b8a1SJoseph Chan int err; 2579c577b8a1SJoseph Chan 2580c577b8a1SJoseph Chan /* create a codec specific record */ 25815b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2582c577b8a1SJoseph Chan if (spec == NULL) 2583c577b8a1SJoseph Chan return -ENOMEM; 2584c577b8a1SJoseph Chan 2585620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2586620e2b28STakashi Iwai 2587c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2588c577b8a1SJoseph Chan if (err < 0) { 2589c577b8a1SJoseph Chan via_free(codec); 2590c577b8a1SJoseph Chan return err; 2591c577b8a1SJoseph Chan } else if (!err) { 2592c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2593c577b8a1SJoseph Chan "Using genenic mode...\n"); 2594c577b8a1SJoseph Chan } 2595c577b8a1SJoseph Chan 259669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 259769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2598c577b8a1SJoseph Chan 2599a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2600c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2601c577b8a1SJoseph Chan spec->num_mixers++; 2602c577b8a1SJoseph Chan } 2603c577b8a1SJoseph Chan 2604c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2605c577b8a1SJoseph Chan 2606c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 260769e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2608cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2609cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2610cb53c626STakashi Iwai #endif 2611c577b8a1SJoseph Chan 2612c577b8a1SJoseph Chan return 0; 2613c577b8a1SJoseph Chan } 2614c577b8a1SJoseph Chan /* 2615c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2616c577b8a1SJoseph Chan */ 261790dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2618c577b8a1SJoseph Chan /* 2619c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2620c577b8a1SJoseph Chan */ 2621c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2622c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2623c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2624c577b8a1SJoseph Chan 2625c577b8a1SJoseph Chan 2626c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2627c577b8a1SJoseph Chan * mixer widget 2628c577b8a1SJoseph Chan */ 2629c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2630c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2631c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2632c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2633c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2634c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2635c577b8a1SJoseph Chan 2636c577b8a1SJoseph Chan /* 2637c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2638c577b8a1SJoseph Chan */ 2639c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2640c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2641c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2642c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2643c577b8a1SJoseph Chan 2644c577b8a1SJoseph Chan /* 2645c577b8a1SJoseph Chan * Unmute PW3 and PW4 2646c577b8a1SJoseph Chan */ 2647c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2648c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2649c577b8a1SJoseph Chan 2650c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2651c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2652c577b8a1SJoseph Chan /* PW9 Output enable */ 2653c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2654c577b8a1SJoseph Chan { } 2655c577b8a1SJoseph Chan }; 2656c577b8a1SJoseph Chan 2657c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2658c577b8a1SJoseph Chan { 2659c577b8a1SJoseph Chan struct via_spec *spec; 2660c577b8a1SJoseph Chan int err; 2661c577b8a1SJoseph Chan 2662c577b8a1SJoseph Chan /* create a codec specific record */ 26635b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2664c577b8a1SJoseph Chan if (spec == NULL) 2665c577b8a1SJoseph Chan return -ENOMEM; 2666c577b8a1SJoseph Chan 2667620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2668620e2b28STakashi Iwai 2669c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2670c577b8a1SJoseph Chan if (err < 0) { 2671c577b8a1SJoseph Chan via_free(codec); 2672c577b8a1SJoseph Chan return err; 2673c577b8a1SJoseph Chan } else if (!err) { 2674c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2675c577b8a1SJoseph Chan "Using genenic mode...\n"); 2676c577b8a1SJoseph Chan } 2677c577b8a1SJoseph Chan 267869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 267969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2680c577b8a1SJoseph Chan 2681a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2682c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2683c577b8a1SJoseph Chan spec->num_mixers++; 2684c577b8a1SJoseph Chan } 2685c577b8a1SJoseph Chan 2686c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2687c577b8a1SJoseph Chan 2688c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 268969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2690cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2691cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2692cb53c626STakashi Iwai #endif 2693f7278fd0SJosepch Chan return 0; 2694f7278fd0SJosepch Chan } 2695f7278fd0SJosepch Chan 2696f7278fd0SJosepch Chan /* capture mixer elements */ 269790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2698f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2699f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2700f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2701f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2702f7278fd0SJosepch Chan { 2703f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2704f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2705f7278fd0SJosepch Chan * So call somewhat different.. 2706f7278fd0SJosepch Chan */ 2707f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2708f7278fd0SJosepch Chan .name = "Input Source", 2709f7278fd0SJosepch Chan .count = 1, 2710f7278fd0SJosepch Chan .info = via_mux_enum_info, 2711f7278fd0SJosepch Chan .get = via_mux_enum_get, 2712f7278fd0SJosepch Chan .put = via_mux_enum_put, 2713f7278fd0SJosepch Chan }, 2714f7278fd0SJosepch Chan { } /* end */ 2715f7278fd0SJosepch Chan }; 2716f7278fd0SJosepch Chan /* 2717f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2718f7278fd0SJosepch Chan */ 271990dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2720f7278fd0SJosepch Chan /* 2721f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2722f7278fd0SJosepch Chan */ 2723f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2724f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2725f7278fd0SJosepch Chan 2726f7278fd0SJosepch Chan 2727f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2728f7278fd0SJosepch Chan * mixer widget 2729f7278fd0SJosepch Chan */ 2730f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2731f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2732f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2733f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2734f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2735f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2736f7278fd0SJosepch Chan 2737f7278fd0SJosepch Chan /* 2738f7278fd0SJosepch Chan * Set up output mixers 2739f7278fd0SJosepch Chan */ 2740f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2741f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2742f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2743f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2744f7278fd0SJosepch Chan 2745f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2746bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2747f7278fd0SJosepch Chan /* PW9 Output enable */ 2748f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2749f7278fd0SJosepch Chan /* PW10 Input enable */ 2750f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2751f7278fd0SJosepch Chan { } 2752f7278fd0SJosepch Chan }; 2753f7278fd0SJosepch Chan 275490dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2755f7278fd0SJosepch Chan /* 2756f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2757f7278fd0SJosepch Chan */ 2758f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2759f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2760f7278fd0SJosepch Chan 2761f7278fd0SJosepch Chan 2762f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2763f7278fd0SJosepch Chan * mixer widget 2764f7278fd0SJosepch Chan */ 2765f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2766f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2767f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2768f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2769f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2770f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2771f7278fd0SJosepch Chan 2772f7278fd0SJosepch Chan /* 2773f7278fd0SJosepch Chan * Set up output mixers 2774f7278fd0SJosepch Chan */ 2775f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2776f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2777f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2778f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2779f7278fd0SJosepch Chan 2780f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2781f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2782f7278fd0SJosepch Chan /* PW9 Output enable */ 2783f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2784f7278fd0SJosepch Chan /* PW10 Input enable */ 2785f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2786f7278fd0SJosepch Chan { } 2787f7278fd0SJosepch Chan }; 2788f7278fd0SJosepch Chan 278990dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2790a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2791a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2792a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2793a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2794a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2795a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2796a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2797a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2798a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 279969e52a80SHarald Welte { } 280069e52a80SHarald Welte }; 280169e52a80SHarald Welte 2802f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 2803f7278fd0SJosepch Chan { 2804f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 2805f7278fd0SJosepch Chan int err; 2806f7278fd0SJosepch Chan 2807f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2808f7278fd0SJosepch Chan if (err < 0) 2809f7278fd0SJosepch Chan return err; 28104a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2811f7278fd0SJosepch Chan if (err < 0) 2812f7278fd0SJosepch Chan return err; 2813f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2814f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 2815f7278fd0SJosepch Chan 28164a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2817f7278fd0SJosepch Chan if (err < 0) 2818f7278fd0SJosepch Chan return err; 28194a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2820f7278fd0SJosepch Chan if (err < 0) 2821f7278fd0SJosepch Chan return err; 2822620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2823f7278fd0SJosepch Chan if (err < 0) 2824f7278fd0SJosepch Chan return err; 2825f7278fd0SJosepch Chan 2826f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2827f7278fd0SJosepch Chan 28280852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2829f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 283055d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 2831f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2832f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2833f7278fd0SJosepch Chan 2834603c4019STakashi Iwai if (spec->kctls.list) 2835603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2836f7278fd0SJosepch Chan 28370aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 28380aa62aefSHarald Welte 2839f8fdd495SHarald Welte if (spec->hp_mux) 28403d83e577STakashi Iwai via_hp_build(codec); 2841f7278fd0SJosepch Chan 2842f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2843f4a7828bSTakashi Iwai if (err < 0) 2844f4a7828bSTakashi Iwai return err; 2845f4a7828bSTakashi Iwai 2846f7278fd0SJosepch Chan return 1; 2847f7278fd0SJosepch Chan } 2848f7278fd0SJosepch Chan 2849f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 285090dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2851f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2852f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2853f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2854f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2855f7278fd0SJosepch Chan { } /* end */ 2856f7278fd0SJosepch Chan }; 2857f7278fd0SJosepch Chan #endif 28583e95b9abSLydia Wang 28593e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 28603e95b9abSLydia Wang { 28613e95b9abSLydia Wang struct via_spec *spec = codec->spec; 28623e95b9abSLydia Wang int imux_is_smixer; 28633e95b9abSLydia Wang unsigned int parm; 28643e95b9abSLydia Wang int is_8ch = 0; 2865bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2866bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 28673e95b9abSLydia Wang is_8ch = 1; 28683e95b9abSLydia Wang 28693e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 28703e95b9abSLydia Wang imux_is_smixer = 28713e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 28723e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 28733e95b9abSLydia Wang /* inputs */ 28743e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 28753e95b9abSLydia Wang parm = AC_PWRST_D3; 28763e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28773e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28783e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28793e95b9abSLydia Wang if (imux_is_smixer) 28803e95b9abSLydia Wang parm = AC_PWRST_D0; 28813e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 28823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 28833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 28843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 28853e95b9abSLydia Wang 28863e95b9abSLydia Wang /* outputs */ 28873e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 28883e95b9abSLydia Wang parm = AC_PWRST_D3; 28893e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 28903e95b9abSLydia Wang if (spec->smart51_enabled) 28913e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 28933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28943e95b9abSLydia Wang 28953e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 28963e95b9abSLydia Wang if (is_8ch) { 28973e95b9abSLydia Wang parm = AC_PWRST_D3; 28983e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 28993e95b9abSLydia Wang if (spec->smart51_enabled) 29003e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 29023e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 29043e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2905bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2906bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2907bc92df7fSLydia Wang parm = AC_PWRST_D3; 2908bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2909bc92df7fSLydia Wang if (spec->smart51_enabled) 2910bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2911bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2912bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2913bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2914bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29153e95b9abSLydia Wang } 29163e95b9abSLydia Wang 29173e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 29183e95b9abSLydia Wang parm = AC_PWRST_D3; 29193e95b9abSLydia Wang /* force to D0 for internal Speaker */ 29203e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 29213e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 29223e95b9abSLydia Wang if (is_8ch) 29233e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 29243e95b9abSLydia Wang 29253e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 29263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 29273e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 29283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 29293e95b9abSLydia Wang if (is_8ch) { 29303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 29313e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 29333e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2934bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2935bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2936bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29373e95b9abSLydia Wang } 29383e95b9abSLydia Wang 2939518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2940f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2941f7278fd0SJosepch Chan { 2942f7278fd0SJosepch Chan struct via_spec *spec; 2943f7278fd0SJosepch Chan int err; 2944f7278fd0SJosepch Chan 2945518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2946518bf3baSLydia Wang return patch_vt1708S(codec); 2947f7278fd0SJosepch Chan /* create a codec specific record */ 29485b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2949f7278fd0SJosepch Chan if (spec == NULL) 2950f7278fd0SJosepch Chan return -ENOMEM; 2951f7278fd0SJosepch Chan 2952620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2953620e2b28STakashi Iwai 2954f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2955f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2956f7278fd0SJosepch Chan if (err < 0) { 2957f7278fd0SJosepch Chan via_free(codec); 2958f7278fd0SJosepch Chan return err; 2959f7278fd0SJosepch Chan } else if (!err) { 2960f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2961f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 2962f7278fd0SJosepch Chan } 2963f7278fd0SJosepch Chan 296469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 296569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 2966f7278fd0SJosepch Chan 2967a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2968f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 2969f7278fd0SJosepch Chan spec->num_mixers++; 2970f7278fd0SJosepch Chan } 2971f7278fd0SJosepch Chan 2972f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2973f7278fd0SJosepch Chan 2974f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 297569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2976f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2977f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2978f7278fd0SJosepch Chan #endif 2979f7278fd0SJosepch Chan 29803e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 29813e95b9abSLydia Wang 2982f7278fd0SJosepch Chan return 0; 2983f7278fd0SJosepch Chan } 2984f7278fd0SJosepch Chan 2985f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2986f7278fd0SJosepch Chan { 2987f7278fd0SJosepch Chan struct via_spec *spec; 2988f7278fd0SJosepch Chan int err; 2989f7278fd0SJosepch Chan 2990f7278fd0SJosepch Chan /* create a codec specific record */ 29915b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2992f7278fd0SJosepch Chan if (spec == NULL) 2993f7278fd0SJosepch Chan return -ENOMEM; 2994f7278fd0SJosepch Chan 2995f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2996f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2997f7278fd0SJosepch Chan if (err < 0) { 2998f7278fd0SJosepch Chan via_free(codec); 2999f7278fd0SJosepch Chan return err; 3000f7278fd0SJosepch Chan } else if (!err) { 3001f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3002f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3003f7278fd0SJosepch Chan } 3004f7278fd0SJosepch Chan 300569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 300669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3007f7278fd0SJosepch Chan 3008a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3009f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3010f7278fd0SJosepch Chan spec->num_mixers++; 3011f7278fd0SJosepch Chan } 3012f7278fd0SJosepch Chan 3013f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3014f7278fd0SJosepch Chan 3015f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 301669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3017f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3018f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3019f7278fd0SJosepch Chan #endif 3020c577b8a1SJoseph Chan 30213e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 30223e95b9abSLydia Wang 3023c577b8a1SJoseph Chan return 0; 3024c577b8a1SJoseph Chan } 3025c577b8a1SJoseph Chan 3026d949cac1SHarald Welte /* Patch for VT1708S */ 3027d949cac1SHarald Welte 3028d949cac1SHarald Welte /* capture mixer elements */ 302990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3030d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3031d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3032d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3033d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 30346369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 30356369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 30366369bcfcSLydia Wang HDA_INPUT), 3037d949cac1SHarald Welte { 3038d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3039d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3040d949cac1SHarald Welte * So call somewhat different.. 3041d949cac1SHarald Welte */ 3042d949cac1SHarald Welte /* .name = "Capture Source", */ 3043d949cac1SHarald Welte .name = "Input Source", 3044d949cac1SHarald Welte .count = 1, 3045d949cac1SHarald Welte .info = via_mux_enum_info, 3046d949cac1SHarald Welte .get = via_mux_enum_get, 3047d949cac1SHarald Welte .put = via_mux_enum_put, 3048d949cac1SHarald Welte }, 3049d949cac1SHarald Welte { } /* end */ 3050d949cac1SHarald Welte }; 3051d949cac1SHarald Welte 305290dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3053d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3054d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3055d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3056d949cac1SHarald Welte 3057d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3058d949cac1SHarald Welte * analog-loopback mixer widget */ 3059d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3060d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3061d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3062d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3063d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3064d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3065d949cac1SHarald Welte 3066d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3067d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 30685691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3069d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 30705691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3071d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3072d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3073bc7e7e5cSLydia Wang /* don't bybass mixer */ 3074bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3075d949cac1SHarald Welte { } 3076d949cac1SHarald Welte }; 3077d949cac1SHarald Welte 307890dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3079a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3080a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3081a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3082a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3083a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3084a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3085a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3086a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3087a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 308869e52a80SHarald Welte { } 308969e52a80SHarald Welte }; 309069e52a80SHarald Welte 309190dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3092bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3093bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3094bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3095bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3096bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3097bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3098bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3099bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3100bc92df7fSLydia Wang { } 3101bc92df7fSLydia Wang }; 3102bc92df7fSLydia Wang 31039da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 31049da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 31059da29271STakashi Iwai { 31069da29271STakashi Iwai struct via_spec *spec = codec->spec; 31079da29271STakashi Iwai int i; 31089da29271STakashi Iwai 31099da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 31109da29271STakashi Iwai hda_nid_t nid; 31119da29271STakashi Iwai int conn; 31129da29271STakashi Iwai 31139da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 31149da29271STakashi Iwai if (!nid) 31159da29271STakashi Iwai continue; 31169da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 31179da29271STakashi Iwai if (conn < 1) 31189da29271STakashi Iwai continue; 31199da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 31209da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 31219da29271STakashi Iwai else { 31229da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 31239da29271STakashi Iwai break; /* at most two dig outs */ 31249da29271STakashi Iwai } 31259da29271STakashi Iwai } 31269da29271STakashi Iwai } 31279da29271STakashi Iwai 3128d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3129d949cac1SHarald Welte { 3130d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3131d949cac1SHarald Welte int err; 3132d949cac1SHarald Welte 31339da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3134d949cac1SHarald Welte if (err < 0) 3135d949cac1SHarald Welte return err; 31364a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3137d949cac1SHarald Welte if (err < 0) 3138d949cac1SHarald Welte return err; 3139d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3140d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3141d949cac1SHarald Welte 31424a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3143d949cac1SHarald Welte if (err < 0) 3144d949cac1SHarald Welte return err; 31454a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3146d949cac1SHarald Welte if (err < 0) 3147d949cac1SHarald Welte return err; 3148620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3149d949cac1SHarald Welte if (err < 0) 3150d949cac1SHarald Welte return err; 3151d949cac1SHarald Welte 3152d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3153d949cac1SHarald Welte 31549da29271STakashi Iwai fill_dig_outs(codec); 315598aa34c0SHarald Welte 3156603c4019STakashi Iwai if (spec->kctls.list) 3157603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3158d949cac1SHarald Welte 31590aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31600aa62aefSHarald Welte 3161f8fdd495SHarald Welte if (spec->hp_mux) 31623d83e577STakashi Iwai via_hp_build(codec); 3163d949cac1SHarald Welte 3164f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3165f4a7828bSTakashi Iwai if (err < 0) 3166f4a7828bSTakashi Iwai return err; 3167f4a7828bSTakashi Iwai 3168d949cac1SHarald Welte return 1; 3169d949cac1SHarald Welte } 3170d949cac1SHarald Welte 3171d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 317290dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3173d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3174d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3175d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3176d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3177d949cac1SHarald Welte { } /* end */ 3178d949cac1SHarald Welte }; 3179d949cac1SHarald Welte #endif 3180d949cac1SHarald Welte 31816369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 31826369bcfcSLydia Wang int offset, int num_steps, int step_size) 31836369bcfcSLydia Wang { 31846369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 31856369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 31866369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 31876369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 31886369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 31896369bcfcSLydia Wang } 31906369bcfcSLydia Wang 3191d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3192d949cac1SHarald Welte { 3193d949cac1SHarald Welte struct via_spec *spec; 3194d949cac1SHarald Welte int err; 3195d949cac1SHarald Welte 3196d949cac1SHarald Welte /* create a codec specific record */ 31975b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3198d949cac1SHarald Welte if (spec == NULL) 3199d949cac1SHarald Welte return -ENOMEM; 3200d949cac1SHarald Welte 3201620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3202620e2b28STakashi Iwai 3203d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3204d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3205d949cac1SHarald Welte if (err < 0) { 3206d949cac1SHarald Welte via_free(codec); 3207d949cac1SHarald Welte return err; 3208d949cac1SHarald Welte } else if (!err) { 3209d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3210d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3211d949cac1SHarald Welte } 3212d949cac1SHarald Welte 321369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3214bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3215bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3216bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3217bc92df7fSLydia Wang else 3218bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3219bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3220d949cac1SHarald Welte 3221a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 32226369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 32236369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3224d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3225d949cac1SHarald Welte spec->num_mixers++; 3226d949cac1SHarald Welte } 3227d949cac1SHarald Welte 3228d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3229d949cac1SHarald Welte 3230d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 323169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3232d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3233d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3234d949cac1SHarald Welte #endif 3235d949cac1SHarald Welte 3236518bf3baSLydia Wang /* correct names for VT1708BCE */ 3237518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3238518bf3baSLydia Wang kfree(codec->chip_name); 3239518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3240518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3241518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3242518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3243970f630fSLydia Wang } 3244bc92df7fSLydia Wang /* correct names for VT1705 */ 3245bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3246bc92df7fSLydia Wang kfree(codec->chip_name); 3247bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3248bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3249bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3250bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3251bc92df7fSLydia Wang } 32523e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3253d949cac1SHarald Welte return 0; 3254d949cac1SHarald Welte } 3255d949cac1SHarald Welte 3256d949cac1SHarald Welte /* Patch for VT1702 */ 3257d949cac1SHarald Welte 3258d949cac1SHarald Welte /* capture mixer elements */ 325990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = { 3260d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3261d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3262d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3263d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3264d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3265d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3266d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3267d949cac1SHarald Welte HDA_INPUT), 3268d949cac1SHarald Welte { 3269d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3270d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3271d949cac1SHarald Welte * So call somewhat different.. 3272d949cac1SHarald Welte */ 3273d949cac1SHarald Welte /* .name = "Capture Source", */ 3274d949cac1SHarald Welte .name = "Input Source", 3275d949cac1SHarald Welte .count = 1, 3276d949cac1SHarald Welte .info = via_mux_enum_info, 3277d949cac1SHarald Welte .get = via_mux_enum_get, 3278d949cac1SHarald Welte .put = via_mux_enum_put, 3279d949cac1SHarald Welte }, 3280d949cac1SHarald Welte { } /* end */ 3281d949cac1SHarald Welte }; 3282d949cac1SHarald Welte 328390dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3284d949cac1SHarald Welte /* 3285d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3286d949cac1SHarald Welte */ 3287d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3288d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3289d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3290d949cac1SHarald Welte 3291d949cac1SHarald Welte 3292d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3293d949cac1SHarald Welte * mixer widget 3294d949cac1SHarald Welte */ 3295d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3296d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3297d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3298d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3299d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3300d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3301d949cac1SHarald Welte 3302d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3303d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3304d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3305d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3306d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3307bc7e7e5cSLydia Wang /* mixer enable */ 3308bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3309bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3310bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3311d949cac1SHarald Welte { } 3312d949cac1SHarald Welte }; 3313d949cac1SHarald Welte 331490dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 3315a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3316a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3317a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3318a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3319a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3320a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 332169e52a80SHarald Welte { } 332269e52a80SHarald Welte }; 332369e52a80SHarald Welte 3324d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 3325d949cac1SHarald Welte { 3326d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3327d949cac1SHarald Welte int err; 3328d949cac1SHarald Welte 33299da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3330d949cac1SHarald Welte if (err < 0) 3331d949cac1SHarald Welte return err; 33324a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3333d949cac1SHarald Welte if (err < 0) 3334d949cac1SHarald Welte return err; 3335d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3336d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3337d949cac1SHarald Welte 33384a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3339d949cac1SHarald Welte if (err < 0) 3340d949cac1SHarald Welte return err; 33414a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3342d949cac1SHarald Welte if (err < 0) 3343d949cac1SHarald Welte return err; 3344c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 3345c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 3346c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 3347c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 3348c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 3349c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 3350620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3351d949cac1SHarald Welte if (err < 0) 3352d949cac1SHarald Welte return err; 3353d949cac1SHarald Welte 3354d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3355d949cac1SHarald Welte 33569da29271STakashi Iwai fill_dig_outs(codec); 335798aa34c0SHarald Welte 3358603c4019STakashi Iwai if (spec->kctls.list) 3359603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3360d949cac1SHarald Welte 33610aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 33620aa62aefSHarald Welte 3363f8fdd495SHarald Welte if (spec->hp_mux) 33643d83e577STakashi Iwai via_hp_build(codec); 3365d949cac1SHarald Welte 3366d949cac1SHarald Welte return 1; 3367d949cac1SHarald Welte } 3368d949cac1SHarald Welte 3369d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 337090dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 3371d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 3372d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 3373d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 3374d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 3375d949cac1SHarald Welte { } /* end */ 3376d949cac1SHarald Welte }; 3377d949cac1SHarald Welte #endif 3378d949cac1SHarald Welte 33793e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 33803e95b9abSLydia Wang { 33813e95b9abSLydia Wang int imux_is_smixer = 33823e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 33833e95b9abSLydia Wang unsigned int parm; 33843e95b9abSLydia Wang /* inputs */ 33853e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 33863e95b9abSLydia Wang parm = AC_PWRST_D3; 33873e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 33883e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 33893e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 33903e95b9abSLydia Wang if (imux_is_smixer) 33913e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 33923e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 33933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 33943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 33953e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 33963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 33973e95b9abSLydia Wang 33983e95b9abSLydia Wang /* outputs */ 33993e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 34003e95b9abSLydia Wang parm = AC_PWRST_D3; 34013e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 34023e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 34033e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 34043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 34053e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 34063e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 34073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 34083e95b9abSLydia Wang } 34093e95b9abSLydia Wang 3410d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3411d949cac1SHarald Welte { 3412d949cac1SHarald Welte struct via_spec *spec; 3413d949cac1SHarald Welte int err; 3414d949cac1SHarald Welte 3415d949cac1SHarald Welte /* create a codec specific record */ 34165b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3417d949cac1SHarald Welte if (spec == NULL) 3418d949cac1SHarald Welte return -ENOMEM; 3419d949cac1SHarald Welte 3420620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 3421620e2b28STakashi Iwai 3422d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3423d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 3424d949cac1SHarald Welte if (err < 0) { 3425d949cac1SHarald Welte via_free(codec); 3426d949cac1SHarald Welte return err; 3427d949cac1SHarald Welte } else if (!err) { 3428d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3429d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3430d949cac1SHarald Welte } 3431d949cac1SHarald Welte 343269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 343369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 3434d949cac1SHarald Welte 3435a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3436d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 3437d949cac1SHarald Welte spec->num_mixers++; 3438d949cac1SHarald Welte } 3439d949cac1SHarald Welte 3440d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3441d949cac1SHarald Welte 3442d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 344369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3444d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3445d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 3446d949cac1SHarald Welte #endif 3447d949cac1SHarald Welte 34483e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 3449d949cac1SHarald Welte return 0; 3450d949cac1SHarald Welte } 3451d949cac1SHarald Welte 3452eb7188caSLydia Wang /* Patch for VT1718S */ 3453eb7188caSLydia Wang 3454eb7188caSLydia Wang /* capture mixer elements */ 345590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { 3456eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 3457eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 3458eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 3459eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 3460eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 3461eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 3462eb7188caSLydia Wang HDA_INPUT), 3463eb7188caSLydia Wang { 3464eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3465eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 3466eb7188caSLydia Wang * So call somewhat different.. 3467eb7188caSLydia Wang */ 3468eb7188caSLydia Wang .name = "Input Source", 3469eb7188caSLydia Wang .count = 2, 3470eb7188caSLydia Wang .info = via_mux_enum_info, 3471eb7188caSLydia Wang .get = via_mux_enum_get, 3472eb7188caSLydia Wang .put = via_mux_enum_put, 3473eb7188caSLydia Wang }, 3474eb7188caSLydia Wang { } /* end */ 3475eb7188caSLydia Wang }; 3476eb7188caSLydia Wang 347790dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 3478eb7188caSLydia Wang /* 3479eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3480eb7188caSLydia Wang */ 3481eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3482eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3483eb7188caSLydia Wang 34844ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 34854ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 3486eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3487eb7188caSLydia Wang * mixer widget 3488eb7188caSLydia Wang */ 3489eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3490eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3491eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3492eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3493eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 34944ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 3495eb7188caSLydia Wang 3496eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 3497eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3498eb7188caSLydia Wang /* PW9 PW10 Output enable */ 3499eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3500eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3501eb7188caSLydia Wang /* PW11 Input enable */ 3502eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 3503eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 3504eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 3505eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 3506eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3507eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3508eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3509eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3510eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3511eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3512eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3513eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3514eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3515eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3516eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 3517eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 3518eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 3519eb7188caSLydia Wang /* Unmute MW4's index 0 */ 3520eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3521eb7188caSLydia Wang { } 3522eb7188caSLydia Wang }; 3523eb7188caSLydia Wang 3524eb7188caSLydia Wang 352590dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 3526eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 3527eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3528eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3529eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3530eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3531eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3532eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3533eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3534eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3535eb7188caSLydia Wang { } 3536eb7188caSLydia Wang }; 3537eb7188caSLydia Wang 3538eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 3539eb7188caSLydia Wang { 3540eb7188caSLydia Wang struct via_spec *spec = codec->spec; 3541eb7188caSLydia Wang int err; 3542eb7188caSLydia Wang 3543eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3544eb7188caSLydia Wang 3545eb7188caSLydia Wang if (err < 0) 3546eb7188caSLydia Wang return err; 35474a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3548eb7188caSLydia Wang if (err < 0) 3549eb7188caSLydia Wang return err; 3550eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3551eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 3552eb7188caSLydia Wang 35534a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3554eb7188caSLydia Wang if (err < 0) 3555eb7188caSLydia Wang return err; 35564a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3557eb7188caSLydia Wang if (err < 0) 3558eb7188caSLydia Wang return err; 3559620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3560eb7188caSLydia Wang if (err < 0) 3561eb7188caSLydia Wang return err; 3562eb7188caSLydia Wang 3563eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3564eb7188caSLydia Wang 3565eb7188caSLydia Wang fill_dig_outs(codec); 3566eb7188caSLydia Wang 3567eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 3568eb7188caSLydia Wang spec->dig_in_nid = 0x13; 3569eb7188caSLydia Wang 3570eb7188caSLydia Wang if (spec->kctls.list) 3571eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3572eb7188caSLydia Wang 3573eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 3574eb7188caSLydia Wang 3575eb7188caSLydia Wang if (spec->hp_mux) 35763d83e577STakashi Iwai via_hp_build(codec); 3577eb7188caSLydia Wang 3578f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3579f4a7828bSTakashi Iwai if (err < 0) 3580f4a7828bSTakashi Iwai return err; 3581eb7188caSLydia Wang 3582eb7188caSLydia Wang return 1; 3583eb7188caSLydia Wang } 3584eb7188caSLydia Wang 3585eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 358690dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 3587eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 3588eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 3589eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 3590eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 3591eb7188caSLydia Wang { } /* end */ 3592eb7188caSLydia Wang }; 3593eb7188caSLydia Wang #endif 3594eb7188caSLydia Wang 35953e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 35963e95b9abSLydia Wang { 35973e95b9abSLydia Wang struct via_spec *spec = codec->spec; 35983e95b9abSLydia Wang int imux_is_smixer; 35993e95b9abSLydia Wang unsigned int parm; 36003e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 36013e95b9abSLydia Wang imux_is_smixer = 36023e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 36033e95b9abSLydia Wang /* inputs */ 36043e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 36053e95b9abSLydia Wang parm = AC_PWRST_D3; 36063e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 36073e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 36083e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 36093e95b9abSLydia Wang if (imux_is_smixer) 36103e95b9abSLydia Wang parm = AC_PWRST_D0; 36113e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 36123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 36133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 36143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 36153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 36163e95b9abSLydia Wang 36173e95b9abSLydia Wang /* outputs */ 36183e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 36193e95b9abSLydia Wang parm = AC_PWRST_D3; 36203e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 36213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 36223e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 36233e95b9abSLydia Wang 36243e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 36253e95b9abSLydia Wang parm = AC_PWRST_D3; 36263e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 36273e95b9abSLydia Wang if (spec->smart51_enabled) 36283e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 36293e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 36303e95b9abSLydia Wang 36313e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 36323e95b9abSLydia Wang parm = AC_PWRST_D3; 36333e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 36343e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 36353e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 36363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 36373e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 36383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 36393e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 36403e95b9abSLydia Wang 36413e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 36423e95b9abSLydia Wang parm = AC_PWRST_D3; 36433e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 36443e95b9abSLydia Wang if (spec->smart51_enabled) 36453e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 36463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 36473e95b9abSLydia Wang 36483e95b9abSLydia Wang if (spec->hp_independent_mode) { 36493e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 36503e95b9abSLydia Wang parm = AC_PWRST_D3; 36513e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 36523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 36533e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 36553e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36563e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 36573e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 36583e95b9abSLydia Wang } 36593e95b9abSLydia Wang } 36603e95b9abSLydia Wang 3661eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 3662eb7188caSLydia Wang { 3663eb7188caSLydia Wang struct via_spec *spec; 3664eb7188caSLydia Wang int err; 3665eb7188caSLydia Wang 3666eb7188caSLydia Wang /* create a codec specific record */ 36675b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3668eb7188caSLydia Wang if (spec == NULL) 3669eb7188caSLydia Wang return -ENOMEM; 3670eb7188caSLydia Wang 3671620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3672620e2b28STakashi Iwai 3673eb7188caSLydia Wang /* automatic parse from the BIOS config */ 3674eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 3675eb7188caSLydia Wang if (err < 0) { 3676eb7188caSLydia Wang via_free(codec); 3677eb7188caSLydia Wang return err; 3678eb7188caSLydia Wang } else if (!err) { 3679eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3680eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 3681eb7188caSLydia Wang } 3682eb7188caSLydia Wang 3683eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 3684eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 3685eb7188caSLydia Wang 3686a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3687bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 3688bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 3689eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 3690eb7188caSLydia Wang spec->num_mixers++; 3691eb7188caSLydia Wang } 3692eb7188caSLydia Wang 3693eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 3694eb7188caSLydia Wang 3695eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 36960f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3697eb7188caSLydia Wang 3698eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3699eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 3700eb7188caSLydia Wang #endif 3701eb7188caSLydia Wang 37023e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 37033e95b9abSLydia Wang 3704eb7188caSLydia Wang return 0; 3705eb7188caSLydia Wang } 3706f3db423dSLydia Wang 3707f3db423dSLydia Wang /* Patch for VT1716S */ 3708f3db423dSLydia Wang 3709f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 3710f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 3711f3db423dSLydia Wang { 3712f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 3713f3db423dSLydia Wang uinfo->count = 1; 3714f3db423dSLydia Wang uinfo->value.integer.min = 0; 3715f3db423dSLydia Wang uinfo->value.integer.max = 1; 3716f3db423dSLydia Wang return 0; 3717f3db423dSLydia Wang } 3718f3db423dSLydia Wang 3719f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 3720f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3721f3db423dSLydia Wang { 3722f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3723f3db423dSLydia Wang int index = 0; 3724f3db423dSLydia Wang 3725f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 3726f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 3727f3db423dSLydia Wang if (index != -1) 3728f3db423dSLydia Wang *ucontrol->value.integer.value = index; 3729f3db423dSLydia Wang 3730f3db423dSLydia Wang return 0; 3731f3db423dSLydia Wang } 3732f3db423dSLydia Wang 3733f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 3734f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3735f3db423dSLydia Wang { 3736f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3737f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3738f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 3739f3db423dSLydia Wang 3740f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 3741f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 3742f3db423dSLydia Wang spec->dmic_enabled = index; 37433e95b9abSLydia Wang set_widgets_power_state(codec); 3744f3db423dSLydia Wang return 1; 3745f3db423dSLydia Wang } 3746f3db423dSLydia Wang 3747f3db423dSLydia Wang /* capture mixer elements */ 374890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { 3749f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3750f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3751f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3752f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3753f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 3754f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 3755f3db423dSLydia Wang HDA_INPUT), 3756f3db423dSLydia Wang { 3757f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3758f3db423dSLydia Wang .name = "Input Source", 3759f3db423dSLydia Wang .count = 1, 3760f3db423dSLydia Wang .info = via_mux_enum_info, 3761f3db423dSLydia Wang .get = via_mux_enum_get, 3762f3db423dSLydia Wang .put = via_mux_enum_put, 3763f3db423dSLydia Wang }, 3764f3db423dSLydia Wang { } /* end */ 3765f3db423dSLydia Wang }; 3766f3db423dSLydia Wang 376790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 3768f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 3769f3db423dSLydia Wang { 3770f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3771f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 37725b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 3773f3db423dSLydia Wang .count = 1, 3774f3db423dSLydia Wang .info = vt1716s_dmic_info, 3775f3db423dSLydia Wang .get = vt1716s_dmic_get, 3776f3db423dSLydia Wang .put = vt1716s_dmic_put, 3777f3db423dSLydia Wang }, 3778f3db423dSLydia Wang {} /* end */ 3779f3db423dSLydia Wang }; 3780f3db423dSLydia Wang 3781f3db423dSLydia Wang 3782f3db423dSLydia Wang /* mono-out mixer elements */ 378390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3784f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3785f3db423dSLydia Wang { } /* end */ 3786f3db423dSLydia Wang }; 3787f3db423dSLydia Wang 378890dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 3789f3db423dSLydia Wang /* 3790f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3791f3db423dSLydia Wang */ 3792f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3793f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3794f3db423dSLydia Wang 3795f3db423dSLydia Wang 3796f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3797f3db423dSLydia Wang * mixer widget 3798f3db423dSLydia Wang */ 3799f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3800f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3801f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3802f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3803f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 3804f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3805f3db423dSLydia Wang 3806f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 3807f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 3808f3db423dSLydia Wang 3809f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 3810f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3811f3db423dSLydia Wang 3812f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 3813f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 3814f3db423dSLydia Wang 3815f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 3816f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3817f3db423dSLydia Wang 3818f3db423dSLydia Wang /* PW9 PW10 Output enable */ 3819f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3820f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3821f3db423dSLydia Wang 3822f3db423dSLydia Wang /* Unmute SW1, PW12 */ 3823f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3824f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 3825f3db423dSLydia Wang /* PW12 Output enable */ 3826f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3827f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 3828f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 3829f3db423dSLydia Wang /* don't bybass mixer */ 3830f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 3831f3db423dSLydia Wang /* Enable mono output */ 3832f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 3833f3db423dSLydia Wang { } 3834f3db423dSLydia Wang }; 3835f3db423dSLydia Wang 3836f3db423dSLydia Wang 383790dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 3838f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3839f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3840f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3841f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3842f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3843f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 3844f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 3845f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3846f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3847f3db423dSLydia Wang { } 3848f3db423dSLydia Wang }; 3849f3db423dSLydia Wang 3850f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 3851f3db423dSLydia Wang { 3852f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3853f3db423dSLydia Wang int err; 3854f3db423dSLydia Wang 3855f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3856f3db423dSLydia Wang if (err < 0) 3857f3db423dSLydia Wang return err; 38584a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3859f3db423dSLydia Wang if (err < 0) 3860f3db423dSLydia Wang return err; 3861f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3862f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 3863f3db423dSLydia Wang 38644a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3865f3db423dSLydia Wang if (err < 0) 3866f3db423dSLydia Wang return err; 38674a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3868f3db423dSLydia Wang if (err < 0) 3869f3db423dSLydia Wang return err; 3870620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3871f3db423dSLydia Wang if (err < 0) 3872f3db423dSLydia Wang return err; 3873f3db423dSLydia Wang 3874f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3875f3db423dSLydia Wang 3876f3db423dSLydia Wang fill_dig_outs(codec); 3877f3db423dSLydia Wang 3878f3db423dSLydia Wang if (spec->kctls.list) 3879f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3880f3db423dSLydia Wang 3881f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 3882f3db423dSLydia Wang 3883f3db423dSLydia Wang if (spec->hp_mux) 38843d83e577STakashi Iwai via_hp_build(codec); 3885f3db423dSLydia Wang 3886f4a7828bSTakashi Iwai err = via_smart51_build(codec); 3887f4a7828bSTakashi Iwai if (err < 0) 3888f4a7828bSTakashi Iwai return err; 3889f3db423dSLydia Wang 3890f3db423dSLydia Wang return 1; 3891f3db423dSLydia Wang } 3892f3db423dSLydia Wang 3893f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 389490dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 3895f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 3896f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 3897f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 3898f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 3899f3db423dSLydia Wang { } /* end */ 3900f3db423dSLydia Wang }; 3901f3db423dSLydia Wang #endif 3902f3db423dSLydia Wang 39033e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 39043e95b9abSLydia Wang { 39053e95b9abSLydia Wang struct via_spec *spec = codec->spec; 39063e95b9abSLydia Wang int imux_is_smixer; 39073e95b9abSLydia Wang unsigned int parm; 39083e95b9abSLydia Wang unsigned int mono_out, present; 39093e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 39103e95b9abSLydia Wang imux_is_smixer = 39113e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 39123e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 39133e95b9abSLydia Wang /* inputs */ 39143e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 39153e95b9abSLydia Wang parm = AC_PWRST_D3; 39163e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 39173e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 39183e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 39193e95b9abSLydia Wang if (imux_is_smixer) 39203e95b9abSLydia Wang parm = AC_PWRST_D0; 39213e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 39223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 39233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 39243e95b9abSLydia Wang 39253e95b9abSLydia Wang parm = AC_PWRST_D3; 39263e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 39273e95b9abSLydia Wang /* PW11 (22h) */ 39283e95b9abSLydia Wang if (spec->dmic_enabled) 39293e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 39303e95b9abSLydia Wang else 39313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 39323e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 39333e95b9abSLydia Wang 39343e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 39353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 39363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 39373e95b9abSLydia Wang 39383e95b9abSLydia Wang /* outputs */ 39393e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 39403e95b9abSLydia Wang parm = AC_PWRST_D3; 39413e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 39423e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 39433e95b9abSLydia Wang if (spec->smart51_enabled) 39443e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 39453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 39463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 39473e95b9abSLydia Wang 39483e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 39493e95b9abSLydia Wang parm = AC_PWRST_D3; 39503e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 39513e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 39523e95b9abSLydia Wang if (spec->smart51_enabled) 39533e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 39543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 39553e95b9abSLydia Wang 39563e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 39573e95b9abSLydia Wang if (spec->smart51_enabled) 39583e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 39593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 39603e95b9abSLydia Wang 39613e95b9abSLydia Wang /* Mono out */ 39623e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 39633e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 39643e95b9abSLydia Wang 39653e95b9abSLydia Wang if (present) 39663e95b9abSLydia Wang mono_out = 0; 39673e95b9abSLydia Wang else { 39683e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 39693e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 39703e95b9abSLydia Wang mono_out = 0; 39713e95b9abSLydia Wang else 39723e95b9abSLydia Wang mono_out = 1; 39733e95b9abSLydia Wang } 39743e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 39753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 39763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 39773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 39783e95b9abSLydia Wang 39793e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 39803e95b9abSLydia Wang parm = AC_PWRST_D3; 39813e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 39823e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 39833e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 39843e95b9abSLydia Wang if (spec->hp_independent_mode) 39853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 39863e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 39873e95b9abSLydia Wang 39883e95b9abSLydia Wang /* force to D0 for internal Speaker */ 39893e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 39903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 39913e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 39923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 39933e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 39943e95b9abSLydia Wang } 39953e95b9abSLydia Wang 3996f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3997f3db423dSLydia Wang { 3998f3db423dSLydia Wang struct via_spec *spec; 3999f3db423dSLydia Wang int err; 4000f3db423dSLydia Wang 4001f3db423dSLydia Wang /* create a codec specific record */ 40025b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4003f3db423dSLydia Wang if (spec == NULL) 4004f3db423dSLydia Wang return -ENOMEM; 4005f3db423dSLydia Wang 4006620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 4007620e2b28STakashi Iwai 4008f3db423dSLydia Wang /* automatic parse from the BIOS config */ 4009f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 4010f3db423dSLydia Wang if (err < 0) { 4011f3db423dSLydia Wang via_free(codec); 4012f3db423dSLydia Wang return err; 4013f3db423dSLydia Wang } else if (!err) { 4014f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4015f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 4016f3db423dSLydia Wang } 4017f3db423dSLydia Wang 4018f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 4019f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 4020f3db423dSLydia Wang 4021a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4022f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 4023f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4024f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 4025f3db423dSLydia Wang spec->num_mixers++; 4026f3db423dSLydia Wang } 4027f3db423dSLydia Wang 4028f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 4029f3db423dSLydia Wang spec->num_mixers++; 4030f3db423dSLydia Wang 4031f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 4032f3db423dSLydia Wang 4033f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 4034f3db423dSLydia Wang 4035f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 40360f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4037f3db423dSLydia Wang 4038f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4039f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 4040f3db423dSLydia Wang #endif 4041f3db423dSLydia Wang 40423e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 4043f3db423dSLydia Wang return 0; 4044f3db423dSLydia Wang } 404525eaba2fSLydia Wang 404625eaba2fSLydia Wang /* for vt2002P */ 404725eaba2fSLydia Wang 404825eaba2fSLydia Wang /* capture mixer elements */ 404990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { 405025eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 405125eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 405225eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 405325eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 405425eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 405525eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 405625eaba2fSLydia Wang HDA_INPUT), 405725eaba2fSLydia Wang { 405825eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 405925eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 406025eaba2fSLydia Wang * So call somewhat different.. 406125eaba2fSLydia Wang */ 406225eaba2fSLydia Wang /* .name = "Capture Source", */ 406325eaba2fSLydia Wang .name = "Input Source", 406425eaba2fSLydia Wang .count = 2, 406525eaba2fSLydia Wang .info = via_mux_enum_info, 406625eaba2fSLydia Wang .get = via_mux_enum_get, 406725eaba2fSLydia Wang .put = via_mux_enum_put, 406825eaba2fSLydia Wang }, 406925eaba2fSLydia Wang { } /* end */ 407025eaba2fSLydia Wang }; 407125eaba2fSLydia Wang 407290dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 4073eadb9a80SLydia Wang /* Class-D speaker related verbs */ 4074eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 4075eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 4076eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 407725eaba2fSLydia Wang /* 407825eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 407925eaba2fSLydia Wang */ 408025eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 408125eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 408225eaba2fSLydia Wang 408325eaba2fSLydia Wang 408425eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 408525eaba2fSLydia Wang * mixer widget 408625eaba2fSLydia Wang */ 408725eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 408825eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 408925eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 409025eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 409125eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 409225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 409325eaba2fSLydia Wang 409425eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 409525eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 409625eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 409725eaba2fSLydia Wang 409825eaba2fSLydia Wang /* PW9 Output enable */ 409925eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 410025eaba2fSLydia Wang 410125eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 410225eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 410325eaba2fSLydia Wang 410425eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 410525eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 410625eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 410725eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 410825eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 410925eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 411025eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 411125eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 411225eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 411325eaba2fSLydia Wang 411425eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 411525eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 411625eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 411725eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 411825eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 411925eaba2fSLydia Wang 412025eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 412125eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 412225eaba2fSLydia Wang 412325eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 412425eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 412525eaba2fSLydia Wang { } 412625eaba2fSLydia Wang }; 412790dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 412811890956SLydia Wang /* 412911890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 413011890956SLydia Wang */ 413111890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 413211890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 413311890956SLydia Wang 413411890956SLydia Wang 413511890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 413611890956SLydia Wang * mixer widget 413711890956SLydia Wang */ 413811890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 413911890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 414011890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 414111890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 414211890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 414311890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 414411890956SLydia Wang 414511890956SLydia Wang /* MUX Indices: Mic = 0 */ 414611890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 414711890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 414811890956SLydia Wang 414911890956SLydia Wang /* PW9 Output enable */ 415011890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 415111890956SLydia Wang 415211890956SLydia Wang /* Enable Boost Volume backdoor */ 415311890956SLydia Wang {0x1, 0xfb9, 0x24}, 415411890956SLydia Wang 415511890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 415611890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 415711890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 415811890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 415911890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 416011890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 416111890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 416211890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 416311890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 416411890956SLydia Wang 416511890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 416611890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 416711890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 416811890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 416911890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 417011890956SLydia Wang 417111890956SLydia Wang /* set PW0 index=0 (MW0) */ 417211890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 417311890956SLydia Wang 417411890956SLydia Wang /* Enable AOW0 to MW9 */ 417511890956SLydia Wang {0x1, 0xfb8, 0x88}, 417611890956SLydia Wang { } 417711890956SLydia Wang }; 417825eaba2fSLydia Wang 417925eaba2fSLydia Wang 418090dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 418125eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 418225eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 418325eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 418425eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 418525eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 418625eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 418725eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 418825eaba2fSLydia Wang { } 418925eaba2fSLydia Wang }; 419090dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 419111890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 419211890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 419311890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 419411890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 419511890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 419611890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 419711890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 419811890956SLydia Wang { } 419911890956SLydia Wang }; 420025eaba2fSLydia Wang 420125eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 420225eaba2fSLydia Wang { 420325eaba2fSLydia Wang struct via_spec *spec = codec->spec; 420425eaba2fSLydia Wang int err; 420525eaba2fSLydia Wang 420625eaba2fSLydia Wang 420725eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 420825eaba2fSLydia Wang if (err < 0) 420925eaba2fSLydia Wang return err; 421025eaba2fSLydia Wang 42114a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 421225eaba2fSLydia Wang if (err < 0) 421325eaba2fSLydia Wang return err; 421425eaba2fSLydia Wang 421525eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 421625eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 421725eaba2fSLydia Wang 42184a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 421925eaba2fSLydia Wang if (err < 0) 422025eaba2fSLydia Wang return err; 42214a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 422225eaba2fSLydia Wang if (err < 0) 422325eaba2fSLydia Wang return err; 4224620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 422525eaba2fSLydia Wang if (err < 0) 422625eaba2fSLydia Wang return err; 422725eaba2fSLydia Wang 422825eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 422925eaba2fSLydia Wang 423025eaba2fSLydia Wang fill_dig_outs(codec); 423125eaba2fSLydia Wang 423225eaba2fSLydia Wang if (spec->kctls.list) 423325eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 423425eaba2fSLydia Wang 423525eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 423625eaba2fSLydia Wang 423725eaba2fSLydia Wang if (spec->hp_mux) 42383d83e577STakashi Iwai via_hp_build(codec); 423925eaba2fSLydia Wang 424025eaba2fSLydia Wang return 1; 424125eaba2fSLydia Wang } 424225eaba2fSLydia Wang 424325eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 424490dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 424525eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 424625eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 424725eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 424825eaba2fSLydia Wang { } /* end */ 424925eaba2fSLydia Wang }; 425025eaba2fSLydia Wang #endif 425125eaba2fSLydia Wang 42523e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 42533e95b9abSLydia Wang { 42543e95b9abSLydia Wang struct via_spec *spec = codec->spec; 42553e95b9abSLydia Wang int imux_is_smixer; 42563e95b9abSLydia Wang unsigned int parm; 42573e95b9abSLydia Wang unsigned int present; 42583e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 42593e95b9abSLydia Wang imux_is_smixer = 42603e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 42613e95b9abSLydia Wang /* inputs */ 42623e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 42633e95b9abSLydia Wang parm = AC_PWRST_D3; 42643e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 42653e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 42663e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 42673e95b9abSLydia Wang parm = AC_PWRST_D0; 42683e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 42693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 42703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 42713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 42723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 42733e95b9abSLydia Wang 42743e95b9abSLydia Wang /* outputs */ 42753e95b9abSLydia Wang /* AOW0 (8h)*/ 42763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 42773e95b9abSLydia Wang 427811890956SLydia Wang if (spec->codec_type == VT1802) { 427911890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 428011890956SLydia Wang parm = AC_PWRST_D3; 428111890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 428211890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 428311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 428411890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 428511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 428611890956SLydia Wang } else { 42873e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 42883e95b9abSLydia Wang parm = AC_PWRST_D3; 42893e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 42903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 42913e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 42923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 42933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 429411890956SLydia Wang } 42953e95b9abSLydia Wang 429611890956SLydia Wang if (spec->codec_type == VT1802) { 429711890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 429811890956SLydia Wang parm = AC_PWRST_D3; 429911890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 430011890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 430111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 430211890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 430311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 430411890956SLydia Wang } else { 43053e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 43063e95b9abSLydia Wang parm = AC_PWRST_D3; 43073e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 43083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 43093e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 43113e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 431211890956SLydia Wang } 43133e95b9abSLydia Wang 43143e95b9abSLydia Wang if (spec->hp_independent_mode) 43153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 43163e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 43173e95b9abSLydia Wang 43183e95b9abSLydia Wang /* Class-D */ 43193e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 43203e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 43213e95b9abSLydia Wang 43223e95b9abSLydia Wang parm = AC_PWRST_D3; 43233e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 43243e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 432511890956SLydia Wang if (spec->codec_type == VT1802) 432611890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 432711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 432811890956SLydia Wang else 43293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 43303e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 43323e95b9abSLydia Wang 43333e95b9abSLydia Wang /* Mono Out */ 43343e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 43353e95b9abSLydia Wang 43363e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 433711890956SLydia Wang if (spec->codec_type == VT1802) { 433811890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 433911890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 434011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 434111890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 434211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 434311890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 434411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 434511890956SLydia Wang } else { 43463e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 43473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 43483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 43503e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 43513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 43523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 435311890956SLydia Wang } 43543e95b9abSLydia Wang /* MW9 (21h) */ 43553e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 43563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 43573e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 43583e95b9abSLydia Wang else 43593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 43603e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 43613e95b9abSLydia Wang } 436225eaba2fSLydia Wang 436325eaba2fSLydia Wang /* patch for vt2002P */ 436425eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 436525eaba2fSLydia Wang { 436625eaba2fSLydia Wang struct via_spec *spec; 436725eaba2fSLydia Wang int err; 436825eaba2fSLydia Wang 436925eaba2fSLydia Wang /* create a codec specific record */ 43705b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 437125eaba2fSLydia Wang if (spec == NULL) 437225eaba2fSLydia Wang return -ENOMEM; 437325eaba2fSLydia Wang 4374620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4375620e2b28STakashi Iwai 437625eaba2fSLydia Wang /* automatic parse from the BIOS config */ 437725eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 437825eaba2fSLydia Wang if (err < 0) { 437925eaba2fSLydia Wang via_free(codec); 438025eaba2fSLydia Wang return err; 438125eaba2fSLydia Wang } else if (!err) { 438225eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 438325eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 438425eaba2fSLydia Wang } 438525eaba2fSLydia Wang 438611890956SLydia Wang if (spec->codec_type == VT1802) 438711890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 438811890956SLydia Wang vt1802_volume_init_verbs; 438911890956SLydia Wang else 439011890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 439111890956SLydia Wang vt2002P_volume_init_verbs; 439225eaba2fSLydia Wang 439311890956SLydia Wang if (spec->codec_type == VT1802) 439411890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 439511890956SLydia Wang vt1802_uniwill_init_verbs; 439611890956SLydia Wang else 439711890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 439811890956SLydia Wang vt2002P_uniwill_init_verbs; 439911890956SLydia Wang 4400a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 440125eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 440225eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 440325eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 440425eaba2fSLydia Wang spec->num_mixers++; 440525eaba2fSLydia Wang } 440625eaba2fSLydia Wang 440725eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 440825eaba2fSLydia Wang 440925eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 44100f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 441125eaba2fSLydia Wang 441225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 441325eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 441425eaba2fSLydia Wang #endif 441525eaba2fSLydia Wang 44163e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 441725eaba2fSLydia Wang return 0; 441825eaba2fSLydia Wang } 4419ab6734e7SLydia Wang 4420ab6734e7SLydia Wang /* for vt1812 */ 4421ab6734e7SLydia Wang 4422ab6734e7SLydia Wang /* capture mixer elements */ 442390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = { 4424ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4425ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4426ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4427ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4428ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4429ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 4430ab6734e7SLydia Wang HDA_INPUT), 4431ab6734e7SLydia Wang { 4432ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4433ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4434ab6734e7SLydia Wang * So call somewhat different.. 4435ab6734e7SLydia Wang */ 4436ab6734e7SLydia Wang .name = "Input Source", 4437ab6734e7SLydia Wang .count = 2, 4438ab6734e7SLydia Wang .info = via_mux_enum_info, 4439ab6734e7SLydia Wang .get = via_mux_enum_get, 4440ab6734e7SLydia Wang .put = via_mux_enum_put, 4441ab6734e7SLydia Wang }, 4442ab6734e7SLydia Wang { } /* end */ 4443ab6734e7SLydia Wang }; 4444ab6734e7SLydia Wang 444590dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 4446ab6734e7SLydia Wang /* 4447ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4448ab6734e7SLydia Wang */ 4449ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4450ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4451ab6734e7SLydia Wang 4452ab6734e7SLydia Wang 4453ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4454ab6734e7SLydia Wang * mixer widget 4455ab6734e7SLydia Wang */ 4456ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4457ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4458ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4459ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4460ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4461ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4462ab6734e7SLydia Wang 4463ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 4464ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 4465ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 4466ab6734e7SLydia Wang 4467ab6734e7SLydia Wang /* PW9 Output enable */ 4468ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4469ab6734e7SLydia Wang 4470ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 4471ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 4472ab6734e7SLydia Wang 4473ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 4474ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4475ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4476ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4477ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4478ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4479ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4480ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4481ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4482ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4483ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4484ab6734e7SLydia Wang 4485ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 4486ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 4487ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 4488ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 4489ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 4490ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 4491ab6734e7SLydia Wang 4492ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 4493ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 4494ab6734e7SLydia Wang { } 4495ab6734e7SLydia Wang }; 4496ab6734e7SLydia Wang 4497ab6734e7SLydia Wang 449890dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 4499ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 4500ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4501ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 4502ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4503ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4504ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4505ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4506ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4507ab6734e7SLydia Wang { } 4508ab6734e7SLydia Wang }; 4509ab6734e7SLydia Wang 4510ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 4511ab6734e7SLydia Wang { 4512ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 4513ab6734e7SLydia Wang int err; 4514ab6734e7SLydia Wang 4515ab6734e7SLydia Wang 4516ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4517ab6734e7SLydia Wang if (err < 0) 4518ab6734e7SLydia Wang return err; 4519ab6734e7SLydia Wang fill_dig_outs(codec); 45204a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 4521ab6734e7SLydia Wang if (err < 0) 4522ab6734e7SLydia Wang return err; 4523ab6734e7SLydia Wang 4524ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 4525ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 4526ab6734e7SLydia Wang 45274a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 4528ab6734e7SLydia Wang if (err < 0) 4529ab6734e7SLydia Wang return err; 45304a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 4531ab6734e7SLydia Wang if (err < 0) 4532ab6734e7SLydia Wang return err; 4533620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 4534ab6734e7SLydia Wang if (err < 0) 4535ab6734e7SLydia Wang return err; 4536ab6734e7SLydia Wang 4537ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4538ab6734e7SLydia Wang 4539ab6734e7SLydia Wang fill_dig_outs(codec); 4540ab6734e7SLydia Wang 4541ab6734e7SLydia Wang if (spec->kctls.list) 4542ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4543ab6734e7SLydia Wang 4544ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 4545ab6734e7SLydia Wang 4546ab6734e7SLydia Wang if (spec->hp_mux) 45473d83e577STakashi Iwai via_hp_build(codec); 4548ab6734e7SLydia Wang 4549ab6734e7SLydia Wang return 1; 4550ab6734e7SLydia Wang } 4551ab6734e7SLydia Wang 4552ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 455390dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 4554ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 4555ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 4556ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 4557ab6734e7SLydia Wang { } /* end */ 4558ab6734e7SLydia Wang }; 4559ab6734e7SLydia Wang #endif 4560ab6734e7SLydia Wang 45613e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 45623e95b9abSLydia Wang { 45633e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45643e95b9abSLydia Wang int imux_is_smixer = 45653e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 45663e95b9abSLydia Wang unsigned int parm; 45673e95b9abSLydia Wang unsigned int present; 45683e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 45693e95b9abSLydia Wang imux_is_smixer = 45703e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 45713e95b9abSLydia Wang /* inputs */ 45723e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45733e95b9abSLydia Wang parm = AC_PWRST_D3; 45743e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45753e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45763e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45773e95b9abSLydia Wang parm = AC_PWRST_D0; 45783e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 45793e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45833e95b9abSLydia Wang 45843e95b9abSLydia Wang /* outputs */ 45853e95b9abSLydia Wang /* AOW0 (8h)*/ 45863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 45873e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 45883e95b9abSLydia Wang 45893e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 45903e95b9abSLydia Wang parm = AC_PWRST_D3; 45913e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 45933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 45943e95b9abSLydia Wang 45953e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 45963e95b9abSLydia Wang parm = AC_PWRST_D3; 45973e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 45983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 45993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 46003e95b9abSLydia Wang if (spec->hp_independent_mode) 46013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 46023e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46033e95b9abSLydia Wang 46043e95b9abSLydia Wang /* Internal Speaker */ 46053e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 46063e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 46073e95b9abSLydia Wang 46083e95b9abSLydia Wang parm = AC_PWRST_D3; 46093e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 46103e95b9abSLydia Wang if (present) { 46113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 46123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 46143e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46153e95b9abSLydia Wang } else { 46163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 46173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 46193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46203e95b9abSLydia Wang } 46213e95b9abSLydia Wang 46223e95b9abSLydia Wang 46233e95b9abSLydia Wang /* Mono Out */ 46243e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 46253e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 46263e95b9abSLydia Wang 46273e95b9abSLydia Wang parm = AC_PWRST_D3; 46283e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 46293e95b9abSLydia Wang if (present) { 46303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 46313e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 46333e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 46353e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46363e95b9abSLydia Wang } else { 46373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 46383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 46403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 46423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46433e95b9abSLydia Wang } 46443e95b9abSLydia Wang 46453e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 46463e95b9abSLydia Wang parm = AC_PWRST_D3; 46473e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 46483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 46493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 46503e95b9abSLydia Wang 46513e95b9abSLydia Wang } 4652ab6734e7SLydia Wang 4653ab6734e7SLydia Wang /* patch for vt1812 */ 4654ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 4655ab6734e7SLydia Wang { 4656ab6734e7SLydia Wang struct via_spec *spec; 4657ab6734e7SLydia Wang int err; 4658ab6734e7SLydia Wang 4659ab6734e7SLydia Wang /* create a codec specific record */ 46605b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4661ab6734e7SLydia Wang if (spec == NULL) 4662ab6734e7SLydia Wang return -ENOMEM; 4663ab6734e7SLydia Wang 4664620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4665620e2b28STakashi Iwai 4666ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 4667ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 4668ab6734e7SLydia Wang if (err < 0) { 4669ab6734e7SLydia Wang via_free(codec); 4670ab6734e7SLydia Wang return err; 4671ab6734e7SLydia Wang } else if (!err) { 4672ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4673ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 4674ab6734e7SLydia Wang } 4675ab6734e7SLydia Wang 4676ab6734e7SLydia Wang 4677ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 4678ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 4679ab6734e7SLydia Wang 4680a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4681ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4682ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4683ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 4684ab6734e7SLydia Wang spec->num_mixers++; 4685ab6734e7SLydia Wang } 4686ab6734e7SLydia Wang 4687ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 4688ab6734e7SLydia Wang 4689ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 46900f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4691ab6734e7SLydia Wang 4692ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4693ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 4694ab6734e7SLydia Wang #endif 4695ab6734e7SLydia Wang 46963e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 4697ab6734e7SLydia Wang return 0; 4698ab6734e7SLydia Wang } 4699ab6734e7SLydia Wang 4700c577b8a1SJoseph Chan /* 4701c577b8a1SJoseph Chan * patch entries 4702c577b8a1SJoseph Chan */ 470390dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 47043218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 47053218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 47063218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 47073218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 47083218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 4709f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 47103218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 4711f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 47123218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 4713f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 47143218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 4715f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 47163218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 4717f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 47183218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 4719f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 47203218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 4721f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 47223218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 4723f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 47243218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 4725f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 47263218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 4727f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 47283218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 4729f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 47303218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 4731f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 47323218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 4733f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47343218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 4735f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47363218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 4737f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47383218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 4739f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 47403218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 4741d949cac1SHarald Welte .patch = patch_vt1708S}, 47423218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 4743d949cac1SHarald Welte .patch = patch_vt1708S}, 47443218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 4745d949cac1SHarald Welte .patch = patch_vt1708S}, 47463218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 4747d949cac1SHarald Welte .patch = patch_vt1708S}, 4748bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 4749d949cac1SHarald Welte .patch = patch_vt1708S}, 47503218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 4751d949cac1SHarald Welte .patch = patch_vt1708S}, 47523218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 4753d949cac1SHarald Welte .patch = patch_vt1708S}, 47543218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 4755d949cac1SHarald Welte .patch = patch_vt1708S}, 47563218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 4757d949cac1SHarald Welte .patch = patch_vt1702}, 47583218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 4759d949cac1SHarald Welte .patch = patch_vt1702}, 47603218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 4761d949cac1SHarald Welte .patch = patch_vt1702}, 47623218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 4763d949cac1SHarald Welte .patch = patch_vt1702}, 47643218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 4765d949cac1SHarald Welte .patch = patch_vt1702}, 47663218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 4767d949cac1SHarald Welte .patch = patch_vt1702}, 47683218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 4769d949cac1SHarald Welte .patch = patch_vt1702}, 47703218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 4771d949cac1SHarald Welte .patch = patch_vt1702}, 4772eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 4773eb7188caSLydia Wang .patch = patch_vt1718S}, 4774eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 4775eb7188caSLydia Wang .patch = patch_vt1718S}, 4776bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 4777bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4778bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 4779bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 4780f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 4781f3db423dSLydia Wang .patch = patch_vt1716S}, 4782f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 4783f3db423dSLydia Wang .patch = patch_vt1716S}, 478425eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 478525eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 4786ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 478736dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 478836dd5c4aSLydia Wang .patch = patch_vt1708S}, 478911890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 479011890956SLydia Wang .patch = patch_vt2002P}, 479111890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 479211890956SLydia Wang .patch = patch_vt2002P}, 4793c577b8a1SJoseph Chan {} /* terminator */ 4794c577b8a1SJoseph Chan }; 47951289e9e8STakashi Iwai 47961289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 47971289e9e8STakashi Iwai 47981289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 47991289e9e8STakashi Iwai .preset = snd_hda_preset_via, 48001289e9e8STakashi Iwai .owner = THIS_MODULE, 48011289e9e8STakashi Iwai }; 48021289e9e8STakashi Iwai 48031289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 48041289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 48051289e9e8STakashi Iwai 48061289e9e8STakashi Iwai static int __init patch_via_init(void) 48071289e9e8STakashi Iwai { 48081289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 48091289e9e8STakashi Iwai } 48101289e9e8STakashi Iwai 48111289e9e8STakashi Iwai static void __exit patch_via_exit(void) 48121289e9e8STakashi Iwai { 48131289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 48141289e9e8STakashi Iwai } 48151289e9e8STakashi Iwai 48161289e9e8STakashi Iwai module_init(patch_via_init) 48171289e9e8STakashi Iwai module_exit(patch_via_exit) 4818