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 110*4a79616dSTakashi Iwai struct nid_path { 111*4a79616dSTakashi Iwai int depth; 112*4a79616dSTakashi Iwai hda_nid_t path[5]; 113*4a79616dSTakashi Iwai short idx[5]; 114*4a79616dSTakashi Iwai }; 115*4a79616dSTakashi Iwai 1161f2e99feSLydia Wang struct via_spec { 1171f2e99feSLydia Wang /* codec parameterization */ 11890dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1191f2e99feSLydia Wang unsigned int num_mixers; 1201f2e99feSLydia Wang 12190dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1221f2e99feSLydia Wang unsigned int num_iverbs; 1231f2e99feSLydia Wang 12482673bc8STakashi Iwai char stream_name_analog[32]; 12590dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1271f2e99feSLydia Wang 12882673bc8STakashi Iwai char stream_name_digital[32]; 12990dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 13090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1311f2e99feSLydia Wang 1321f2e99feSLydia Wang /* playback */ 1331f2e99feSLydia Wang struct hda_multi_out multiout; 1341f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1351f2e99feSLydia Wang 136*4a79616dSTakashi Iwai struct nid_path out_path[4]; 137*4a79616dSTakashi Iwai struct nid_path hp_path; 138*4a79616dSTakashi Iwai struct nid_path hp_dep_path; 139*4a79616dSTakashi Iwai 1401f2e99feSLydia Wang /* capture */ 1411f2e99feSLydia Wang unsigned int num_adc_nids; 142a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1431f2e99feSLydia Wang hda_nid_t mux_nids[3]; 144620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1451f2e99feSLydia Wang hda_nid_t dig_in_nid; 1461f2e99feSLydia Wang hda_nid_t dig_in_pin; 1471f2e99feSLydia Wang 1481f2e99feSLydia Wang /* capture source */ 1491f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1501f2e99feSLydia Wang unsigned int cur_mux[3]; 1511f2e99feSLydia Wang 1521f2e99feSLydia Wang /* PCM information */ 1531f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1541f2e99feSLydia Wang 1551f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1561f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1571f2e99feSLydia Wang struct snd_array kctls; 1581f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1591f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1601f2e99feSLydia Wang 1611f2e99feSLydia Wang /* HP mode source */ 1621f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1631f2e99feSLydia Wang unsigned int hp_independent_mode; 1641f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1651f2e99feSLydia Wang unsigned int smart51_enabled; 166f3db423dSLydia Wang unsigned int dmic_enabled; 16724088a58STakashi Iwai unsigned int no_pin_power_ctl; 1681f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1691f2e99feSLydia Wang 1701f2e99feSLydia Wang /* work to check hp jack state */ 1711f2e99feSLydia Wang struct hda_codec *codec; 1721f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 173e06e5a29STakashi Iwai int vt1708_jack_detect; 1741f2e99feSLydia Wang int vt1708_hp_present; 1753e95b9abSLydia Wang 1763e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1773e95b9abSLydia Wang 1781f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1791f2e99feSLydia Wang struct hda_loopback_check loopback; 1801f2e99feSLydia Wang #endif 1811f2e99feSLydia Wang }; 1821f2e99feSLydia Wang 1830341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1845b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1855b0cb1d8SJaroslav Kysela { 1865b0cb1d8SJaroslav Kysela struct via_spec *spec; 1875b0cb1d8SJaroslav Kysela 1885b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1895b0cb1d8SJaroslav Kysela if (spec == NULL) 1905b0cb1d8SJaroslav Kysela return NULL; 1915b0cb1d8SJaroslav Kysela 1925b0cb1d8SJaroslav Kysela codec->spec = spec; 1935b0cb1d8SJaroslav Kysela spec->codec = codec; 1940341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1950341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1960341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1970341ccd7SLydia Wang spec->codec_type = VT1708S; 1985b0cb1d8SJaroslav Kysela return spec; 1995b0cb1d8SJaroslav Kysela } 2005b0cb1d8SJaroslav Kysela 201744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 202d7426329SHarald Welte { 203744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 204d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 205d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 206d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 207d7426329SHarald Welte 208d7426329SHarald Welte /* get codec type */ 209d7426329SHarald Welte if (ven_id != 0x1106) 210d7426329SHarald Welte codec_type = UNKNOWN; 211d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 212d7426329SHarald Welte codec_type = VT1708; 213d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 214d7426329SHarald Welte codec_type = VT1709_10CH; 215d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 216d7426329SHarald Welte codec_type = VT1709_6CH; 217518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 218d7426329SHarald Welte codec_type = VT1708B_8CH; 219518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 220518bf3baSLydia Wang codec_type = VT1708BCE; 221518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 222d7426329SHarald Welte codec_type = VT1708B_4CH; 223d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 224d7426329SHarald Welte && (dev_id >> 12) < 8) 225d7426329SHarald Welte codec_type = VT1708S; 226d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 227d7426329SHarald Welte && (dev_id >> 12) < 8) 228d7426329SHarald Welte codec_type = VT1702; 229eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 230eb7188caSLydia Wang && (dev_id >> 12) < 8) 231eb7188caSLydia Wang codec_type = VT1718S; 232f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 233f3db423dSLydia Wang codec_type = VT1716S; 234bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 235bb3c6bfcSLydia Wang codec_type = VT1718S; 23625eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 23725eaba2fSLydia Wang codec_type = VT2002P; 238ab6734e7SLydia Wang else if (dev_id == 0x0448) 239ab6734e7SLydia Wang codec_type = VT1812; 24036dd5c4aSLydia Wang else if (dev_id == 0x0440) 24136dd5c4aSLydia Wang codec_type = VT1708S; 24211890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 24311890956SLydia Wang codec_type = VT1802; 244d7426329SHarald Welte else 245d7426329SHarald Welte codec_type = UNKNOWN; 246d7426329SHarald Welte return codec_type; 247d7426329SHarald Welte }; 248d7426329SHarald Welte 249ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25069e52a80SHarald Welte #define VIA_HP_EVENT 0x01 25169e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 252ec7e7e42SLydia Wang #define VIA_MONO_EVENT 0x03 253ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT 0x04 254ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT 0x05 25569e52a80SHarald Welte 256c577b8a1SJoseph Chan enum { 257c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 258c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 259f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 26025eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 261c577b8a1SJoseph Chan }; 262c577b8a1SJoseph Chan 263c577b8a1SJoseph Chan enum { 264eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 265c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 266c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 267c577b8a1SJoseph Chan AUTO_SEQ_SIDE 268c577b8a1SJoseph Chan }; 269c577b8a1SJoseph Chan 270f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2711f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2721f2e99feSLydia Wang 2731f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2741f2e99feSLydia Wang { 2751f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2761f2e99feSLydia Wang return; 2771f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 278e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2791f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2801f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2811f2e99feSLydia Wang msecs_to_jiffies(100)); 2821f2e99feSLydia Wang } 2831f2e99feSLydia Wang 2841f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2851f2e99feSLydia Wang { 2861f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2871f2e99feSLydia Wang return; 2881f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2891f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2901f2e99feSLydia Wang return; 2911f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 292e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2935b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2941f2e99feSLydia Wang } 295f5271101SLydia Wang 2963e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2973e95b9abSLydia Wang { 2983e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2993e95b9abSLydia Wang if (spec->set_widgets_power_state) 3003e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3013e95b9abSLydia Wang } 30225eaba2fSLydia Wang 303f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 304f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 305f5271101SLydia Wang { 306f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 307f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 308f5271101SLydia Wang 3093e95b9abSLydia Wang set_widgets_power_state(codec); 310f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3111f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3121f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3131f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3141f2e99feSLydia Wang else 3151f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3161f2e99feSLydia Wang } 317f5271101SLydia Wang return change; 318f5271101SLydia Wang } 319f5271101SLydia Wang 320f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 321f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 322f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 323f5271101SLydia Wang .name = NULL, \ 324f5271101SLydia Wang .index = 0, \ 325f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 326f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 327f5271101SLydia Wang .put = analog_input_switch_put, \ 328f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 329f5271101SLydia Wang 33025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 33125eaba2fSLydia Wang 33225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 33325eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 33425eaba2fSLydia Wang { 33525eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 33625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 33725eaba2fSLydia Wang int i; 33825eaba2fSLydia Wang int change = 0; 33925eaba2fSLydia Wang 34025eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 34125eaba2fSLydia Wang int lmute, rmute; 34225eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 34325eaba2fSLydia Wang snd_printd("Invalid control!\n"); 34425eaba2fSLydia Wang return change; 34525eaba2fSLydia Wang } 34625eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 34725eaba2fSLydia Wang ucontrol); 34825eaba2fSLydia Wang /* Get mute value */ 34925eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 35025eaba2fSLydia Wang valp++; 35125eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 35225eaba2fSLydia Wang 35325eaba2fSLydia Wang /* Set hp pins */ 35425eaba2fSLydia Wang if (!spec->hp_independent_mode) { 35525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 35625eaba2fSLydia Wang snd_hda_codec_amp_update( 35725eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 35825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35925eaba2fSLydia Wang lmute); 36025eaba2fSLydia Wang snd_hda_codec_amp_update( 36125eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 36225eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36325eaba2fSLydia Wang rmute); 36425eaba2fSLydia Wang } 36525eaba2fSLydia Wang } 36625eaba2fSLydia Wang 36725eaba2fSLydia Wang if (!lmute && !rmute) { 36825eaba2fSLydia Wang /* Line Outs */ 36925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 37025eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37125eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 37225eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37325eaba2fSLydia Wang /* Speakers */ 37425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37525eaba2fSLydia Wang snd_hda_codec_amp_stereo( 37625eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 37725eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 37825eaba2fSLydia Wang /* unmute */ 37925eaba2fSLydia Wang via_hp_bind_automute(codec); 38025eaba2fSLydia Wang 38125eaba2fSLydia Wang } else { 38225eaba2fSLydia Wang if (lmute) { 38325eaba2fSLydia Wang /* Mute all left channels */ 38425eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38525eaba2fSLydia Wang snd_hda_codec_amp_update( 38625eaba2fSLydia Wang codec, 38725eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 38825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 38925eaba2fSLydia Wang lmute); 39025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39125eaba2fSLydia Wang snd_hda_codec_amp_update( 39225eaba2fSLydia Wang codec, 39325eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39425eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39525eaba2fSLydia Wang lmute); 39625eaba2fSLydia Wang } 39725eaba2fSLydia Wang if (rmute) { 39825eaba2fSLydia Wang /* mute all right channels */ 39925eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 40025eaba2fSLydia Wang snd_hda_codec_amp_update( 40125eaba2fSLydia Wang codec, 40225eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 40325eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 40425eaba2fSLydia Wang rmute); 40525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 40625eaba2fSLydia Wang snd_hda_codec_amp_update( 40725eaba2fSLydia Wang codec, 40825eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 40925eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 41025eaba2fSLydia Wang rmute); 41125eaba2fSLydia Wang } 41225eaba2fSLydia Wang } 41325eaba2fSLydia Wang return change; 41425eaba2fSLydia Wang } 41525eaba2fSLydia Wang 41625eaba2fSLydia Wang #define BIND_PIN_MUTE \ 41725eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 41825eaba2fSLydia Wang .name = NULL, \ 41925eaba2fSLydia Wang .index = 0, \ 42025eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 42125eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 42225eaba2fSLydia Wang .put = bind_pin_switch_put, \ 42325eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 42425eaba2fSLydia Wang 42590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 426c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 427c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 428f5271101SLydia Wang ANALOG_INPUT_MUTE, 42925eaba2fSLydia Wang BIND_PIN_MUTE, 430c577b8a1SJoseph Chan }; 431c577b8a1SJoseph Chan 432ab6734e7SLydia Wang 433c577b8a1SJoseph Chan /* add dynamic controls */ 434291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 435291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 436291c9e33STakashi Iwai const char *name) 437c577b8a1SJoseph Chan { 438c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 439c577b8a1SJoseph Chan 440603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 441603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 442c577b8a1SJoseph Chan if (!knew) 443291c9e33STakashi Iwai return NULL; 444291c9e33STakashi Iwai *knew = *tmpl; 445291c9e33STakashi Iwai if (!name) 446291c9e33STakashi Iwai name = tmpl->name; 447291c9e33STakashi Iwai if (name) { 448c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 449c577b8a1SJoseph Chan if (!knew->name) 450291c9e33STakashi Iwai return NULL; 451291c9e33STakashi Iwai } 452291c9e33STakashi Iwai return knew; 453291c9e33STakashi Iwai } 454291c9e33STakashi Iwai 455291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 456291c9e33STakashi Iwai int idx, unsigned long val) 457291c9e33STakashi Iwai { 458291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 459291c9e33STakashi Iwai 460291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 461291c9e33STakashi Iwai if (!knew) 462c577b8a1SJoseph Chan return -ENOMEM; 4634d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4645e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 465c577b8a1SJoseph Chan knew->private_value = val; 466c577b8a1SJoseph Chan return 0; 467c577b8a1SJoseph Chan } 468c577b8a1SJoseph Chan 4697b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4707b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4717b315bb4STakashi Iwai 472291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 4735b0cb1d8SJaroslav Kysela 474603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 475603c4019STakashi Iwai { 476603c4019STakashi Iwai struct via_spec *spec = codec->spec; 477603c4019STakashi Iwai 478603c4019STakashi Iwai if (spec->kctls.list) { 479603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 480603c4019STakashi Iwai int i; 481603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 482603c4019STakashi Iwai kfree(kctl[i].name); 483603c4019STakashi Iwai } 484603c4019STakashi Iwai snd_array_free(&spec->kctls); 485603c4019STakashi Iwai } 486603c4019STakashi Iwai 487c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4889510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4897b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 490c577b8a1SJoseph Chan { 491c577b8a1SJoseph Chan char name[32]; 492c577b8a1SJoseph Chan int err; 493c577b8a1SJoseph Chan 494c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4957b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 496c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 497c577b8a1SJoseph Chan if (err < 0) 498c577b8a1SJoseph Chan return err; 499c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5007b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 501c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 502c577b8a1SJoseph Chan if (err < 0) 503c577b8a1SJoseph Chan return err; 504c577b8a1SJoseph Chan return 0; 505c577b8a1SJoseph Chan } 506c577b8a1SJoseph Chan 507c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 508c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 509c577b8a1SJoseph Chan int dac_idx) 510c577b8a1SJoseph Chan { 511c577b8a1SJoseph Chan /* set as output */ 512c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 513c577b8a1SJoseph Chan pin_type); 514c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 515c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 516d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 517d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 518d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 519c577b8a1SJoseph Chan } 520c577b8a1SJoseph Chan 521c577b8a1SJoseph Chan 522c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 523c577b8a1SJoseph Chan { 524c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 525c577b8a1SJoseph Chan int i; 526c577b8a1SJoseph Chan 527c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 528c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 529c577b8a1SJoseph Chan if (nid) 530c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 531c577b8a1SJoseph Chan } 532c577b8a1SJoseph Chan } 533c577b8a1SJoseph Chan 534c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 535c577b8a1SJoseph Chan { 536c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 537c577b8a1SJoseph Chan hda_nid_t pin; 53825eaba2fSLydia Wang int i; 539c577b8a1SJoseph Chan 54025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 54125eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 542c577b8a1SJoseph Chan if (pin) /* connect to front */ 543c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 544c577b8a1SJoseph Chan } 54525eaba2fSLydia Wang } 546c577b8a1SJoseph Chan 54732e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 54832e0191dSClemens Ladisch 549c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 550c577b8a1SJoseph Chan { 551c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5527b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 55332e0191dSClemens Ladisch unsigned int ctl; 554c577b8a1SJoseph Chan int i; 555c577b8a1SJoseph Chan 5567b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5577b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 55832e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 55932e0191dSClemens Ladisch ctl = PIN_OUT; 56030649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56132e0191dSClemens Ladisch ctl = PIN_VREF50; 56232e0191dSClemens Ladisch else 56332e0191dSClemens Ladisch ctl = PIN_IN; 564c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56532e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 566c577b8a1SJoseph Chan } 567c577b8a1SJoseph Chan } 568f5271101SLydia Wang 569f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 570f5271101SLydia Wang unsigned int *affected_parm) 571f5271101SLydia Wang { 572f5271101SLydia Wang unsigned parm; 573f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 574f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 575f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 576f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5771564b287SLydia Wang struct via_spec *spec = codec->spec; 57824088a58STakashi Iwai unsigned present = 0; 57924088a58STakashi Iwai 58024088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 58124088a58STakashi Iwai if (!no_presence) 58224088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 5831564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5841564b287SLydia Wang || ((no_presence || present) 5851564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 586f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 587f5271101SLydia Wang parm = AC_PWRST_D0; 588f5271101SLydia Wang } else 589f5271101SLydia Wang parm = AC_PWRST_D3; 590f5271101SLydia Wang 591f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 592f5271101SLydia Wang } 593f5271101SLydia Wang 59424088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 59524088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 59624088a58STakashi Iwai { 59724088a58STakashi Iwai static const char * const texts[] = { 59824088a58STakashi Iwai "Disabled", "Enabled" 59924088a58STakashi Iwai }; 60024088a58STakashi Iwai 60124088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 60224088a58STakashi Iwai uinfo->count = 1; 60324088a58STakashi Iwai uinfo->value.enumerated.items = 2; 60424088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 60524088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 60624088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 60724088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 60824088a58STakashi Iwai return 0; 60924088a58STakashi Iwai } 61024088a58STakashi Iwai 61124088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 61224088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61324088a58STakashi Iwai { 61424088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61524088a58STakashi Iwai struct via_spec *spec = codec->spec; 61624088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 61724088a58STakashi Iwai return 0; 61824088a58STakashi Iwai } 61924088a58STakashi Iwai 62024088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 62124088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 62224088a58STakashi Iwai { 62324088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 62424088a58STakashi Iwai struct via_spec *spec = codec->spec; 62524088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 62624088a58STakashi Iwai 62724088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 62824088a58STakashi Iwai return 0; 62924088a58STakashi Iwai spec->no_pin_power_ctl = val; 63024088a58STakashi Iwai set_widgets_power_state(codec); 63124088a58STakashi Iwai return 1; 63224088a58STakashi Iwai } 63324088a58STakashi Iwai 63424088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 63524088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 63624088a58STakashi Iwai .name = "Dynamic Power-Control", 63724088a58STakashi Iwai .info = via_pin_power_ctl_info, 63824088a58STakashi Iwai .get = via_pin_power_ctl_get, 63924088a58STakashi Iwai .put = via_pin_power_ctl_put, 64024088a58STakashi Iwai }; 64124088a58STakashi Iwai 64224088a58STakashi Iwai 643c577b8a1SJoseph Chan /* 644c577b8a1SJoseph Chan * input MUX handling 645c577b8a1SJoseph Chan */ 646c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 647c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 648c577b8a1SJoseph Chan { 649c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 650c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 651c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 652c577b8a1SJoseph Chan } 653c577b8a1SJoseph Chan 654c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 655c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 656c577b8a1SJoseph Chan { 657c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 658c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 659c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 660c577b8a1SJoseph Chan 661c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 662c577b8a1SJoseph Chan return 0; 663c577b8a1SJoseph Chan } 664c577b8a1SJoseph Chan 665c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 666c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 667c577b8a1SJoseph Chan { 668c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 669c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 670c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 671bff5fbf5SLydia Wang int ret; 672c577b8a1SJoseph Chan 673337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 674337b9d02STakashi Iwai return -EINVAL; 675a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 676a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 677a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 678a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 679a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 680bff5fbf5SLydia Wang 681bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 682bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 683bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 684a80e6e3cSLydia Wang /* update jack power state */ 6853e95b9abSLydia Wang set_widgets_power_state(codec); 686a80e6e3cSLydia Wang 687bff5fbf5SLydia Wang return ret; 688c577b8a1SJoseph Chan } 689c577b8a1SJoseph Chan 6900aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6910aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6920aa62aefSHarald Welte { 6930aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6940aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6950aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6960aa62aefSHarald Welte } 6970aa62aefSHarald Welte 6980aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 6990aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7000aa62aefSHarald Welte { 7010aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7025b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 703eb7188caSLydia Wang unsigned int pinsel; 704eb7188caSLydia Wang 705eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 706eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 7070aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 7080aa62aefSHarald Welte 0x00); 7090aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 7100aa62aefSHarald Welte 7110aa62aefSHarald Welte return 0; 7120aa62aefSHarald Welte } 7130aa62aefSHarald Welte 7140713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7150713efebSLydia Wang { 7160713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7170713efebSLydia Wang if (ctl) { 7180713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7190713efebSLydia Wang ctl->vd[0].access |= active 7200713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7210713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7220713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7230713efebSLydia Wang } 7240713efebSLydia Wang } 7250713efebSLydia Wang 7265b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7275b0cb1d8SJaroslav Kysela { 7285b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7295b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7305b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7315b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7325b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 733e87885feSLydia Wang case VT2002P: return 0x19; 734e87885feSLydia Wang case VT1802: return 0x15; 735e87885feSLydia Wang case VT1812: return 0x15; 7365b0cb1d8SJaroslav Kysela default: return 0; 7375b0cb1d8SJaroslav Kysela } 7385b0cb1d8SJaroslav Kysela } 7395b0cb1d8SJaroslav Kysela 740cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 741cdc1784dSLydia Wang { 742cdc1784dSLydia Wang /* mute side channel */ 743cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 744e87885feSLydia Wang unsigned int parm; 7455b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 746cdc1784dSLydia Wang 747e87885feSLydia Wang if (sw3) { 748e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 749e87885feSLydia Wang parm = spec->hp_independent_mode ? 750e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 751e87885feSLydia Wang else 752e87885feSLydia Wang parm = spec->hp_independent_mode ? 753e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 754e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 755e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 756e87885feSLydia Wang if (spec->codec_type == VT1812) 757e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 758e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 759e87885feSLydia Wang } 760cdc1784dSLydia Wang return 0; 761cdc1784dSLydia Wang } 762cdc1784dSLydia Wang 7630aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7640aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7650aa62aefSHarald Welte { 7660aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7670aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7685b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7690aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 770cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 771cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 772cdc1784dSLydia Wang ? 1 : 0; 773ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 774ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 775ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 776ce0e5a9eSLydia Wang else 777ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 778ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7790aa62aefSHarald Welte 780ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 781ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 782ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 783cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 784cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 785cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7860aa62aefSHarald Welte 0, 0, 0); 7870aa62aefSHarald Welte 788cdc1784dSLydia Wang update_side_mute_status(codec); 7890713efebSLydia Wang /* update HP volume/swtich active state */ 7900713efebSLydia Wang if (spec->codec_type == VT1708S 791eb7188caSLydia Wang || spec->codec_type == VT1702 792f3db423dSLydia Wang || spec->codec_type == VT1718S 79325eaba2fSLydia Wang || spec->codec_type == VT1716S 79411890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7950713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7960713efebSLydia Wang spec->hp_independent_mode); 7970713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 7980713efebSLydia Wang spec->hp_independent_mode); 7990713efebSLydia Wang } 800ce0e5a9eSLydia Wang /* update jack power state */ 8013e95b9abSLydia Wang set_widgets_power_state(codec); 8020aa62aefSHarald Welte return 0; 8030aa62aefSHarald Welte } 8040aa62aefSHarald Welte 80590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 8060aa62aefSHarald Welte { 8070aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8080aa62aefSHarald Welte .name = "Independent HP", 8090aa62aefSHarald Welte .info = via_independent_hp_info, 8100aa62aefSHarald Welte .get = via_independent_hp_get, 8110aa62aefSHarald Welte .put = via_independent_hp_put, 8120aa62aefSHarald Welte }, 8135b0cb1d8SJaroslav Kysela { 8145b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8155b0cb1d8SJaroslav Kysela .name = "Independent HP", 8165b0cb1d8SJaroslav Kysela }, 8170aa62aefSHarald Welte }; 8180aa62aefSHarald Welte 8193d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8205b0cb1d8SJaroslav Kysela { 8213d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8225b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8235b0cb1d8SJaroslav Kysela hda_nid_t nid; 8243d83e577STakashi Iwai int nums; 8253d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8265b0cb1d8SJaroslav Kysela 8275b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8285b0cb1d8SJaroslav Kysela case VT1718S: 8295b0cb1d8SJaroslav Kysela nid = 0x34; 8305b0cb1d8SJaroslav Kysela break; 8315b0cb1d8SJaroslav Kysela case VT2002P: 83211890956SLydia Wang case VT1802: 8335b0cb1d8SJaroslav Kysela nid = 0x35; 8345b0cb1d8SJaroslav Kysela break; 8355b0cb1d8SJaroslav Kysela case VT1812: 8365b0cb1d8SJaroslav Kysela nid = 0x3d; 8375b0cb1d8SJaroslav Kysela break; 8385b0cb1d8SJaroslav Kysela default: 8395b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8405b0cb1d8SJaroslav Kysela break; 8415b0cb1d8SJaroslav Kysela } 8425b0cb1d8SJaroslav Kysela 843ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 844ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 845ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8463d83e577STakashi Iwai if (nums <= 1) 8473d83e577STakashi Iwai return 0; 848ee3c35c0SLydia Wang } 8493d83e577STakashi Iwai 8503d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8513d83e577STakashi Iwai if (knew == NULL) 8523d83e577STakashi Iwai return -ENOMEM; 8533d83e577STakashi Iwai 8545b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8555b0cb1d8SJaroslav Kysela knew->private_value = nid; 8565b0cb1d8SJaroslav Kysela 8575b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8585b0cb1d8SJaroslav Kysela if (knew == NULL) 8595b0cb1d8SJaroslav Kysela return -ENOMEM; 8605b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8615b0cb1d8SJaroslav Kysela 8625b0cb1d8SJaroslav Kysela return 0; 8635b0cb1d8SJaroslav Kysela } 8645b0cb1d8SJaroslav Kysela 8651564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8661564b287SLydia Wang { 8671564b287SLydia Wang int i; 8681564b287SLydia Wang struct snd_ctl_elem_id id; 869525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 870525566cbSLydia Wang struct snd_kcontrol *ctl; 8711564b287SLydia Wang 8721564b287SLydia Wang memset(&id, 0, sizeof(id)); 8731564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8741564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8751564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 876525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 877525566cbSLydia Wang if (ctl) 878525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 879525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 880525566cbSLydia Wang &ctl->id); 8811564b287SLydia Wang } 8821564b287SLydia Wang } 8831564b287SLydia Wang 8841564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8851564b287SLydia Wang { 8861564b287SLydia Wang struct via_spec *spec = codec->spec; 8871564b287SLydia Wang int start_idx; 8881564b287SLydia Wang int end_idx; 8891564b287SLydia Wang int i; 8901564b287SLydia Wang /* get nid of MW0 and start & end index */ 8911564b287SLydia Wang switch (spec->codec_type) { 8921564b287SLydia Wang case VT1708: 8931564b287SLydia Wang start_idx = 2; 8941564b287SLydia Wang end_idx = 4; 8951564b287SLydia Wang break; 8961564b287SLydia Wang case VT1709_10CH: 8971564b287SLydia Wang case VT1709_6CH: 8981564b287SLydia Wang start_idx = 2; 8991564b287SLydia Wang end_idx = 4; 9001564b287SLydia Wang break; 9011564b287SLydia Wang case VT1708B_8CH: 9021564b287SLydia Wang case VT1708B_4CH: 9031564b287SLydia Wang case VT1708S: 904f3db423dSLydia Wang case VT1716S: 9051564b287SLydia Wang start_idx = 2; 9061564b287SLydia Wang end_idx = 4; 9071564b287SLydia Wang break; 908ab657e0cSLydia Wang case VT1718S: 909ab657e0cSLydia Wang start_idx = 1; 910ab657e0cSLydia Wang end_idx = 3; 911ab657e0cSLydia Wang break; 9121564b287SLydia Wang default: 9131564b287SLydia Wang return; 9141564b287SLydia Wang } 9151564b287SLydia Wang /* check AA path's mute status */ 9161564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9171564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 918620e2b28STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, 9191564b287SLydia Wang HDA_AMP_MUTE, val); 9201564b287SLydia Wang } 9211564b287SLydia Wang } 9221564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 9231564b287SLydia Wang { 9247b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9257b315bb4STakashi Iwai int i; 9267b315bb4STakashi Iwai 9277b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9287b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 92986e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 9301564b287SLydia Wang } 9317b315bb4STakashi Iwai return 0; 9321564b287SLydia Wang } 9331564b287SLydia Wang 9341564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9351564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9361564b287SLydia Wang { 9371564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9381564b287SLydia Wang uinfo->count = 1; 9391564b287SLydia Wang uinfo->value.integer.min = 0; 9401564b287SLydia Wang uinfo->value.integer.max = 1; 9411564b287SLydia Wang return 0; 9421564b287SLydia Wang } 9431564b287SLydia Wang 9441564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9451564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9461564b287SLydia Wang { 9471564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9481564b287SLydia Wang struct via_spec *spec = codec->spec; 9497b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9501564b287SLydia Wang int on = 1; 9511564b287SLydia Wang int i; 9521564b287SLydia Wang 9537b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9547b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9557b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 9567b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 95786e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9587b315bb4STakashi Iwai continue; 95986e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9607b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9611564b287SLydia Wang continue; /* ignore FMic for independent HP */ 9627b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9631564b287SLydia Wang on = 0; 9641564b287SLydia Wang } 9651564b287SLydia Wang *ucontrol->value.integer.value = on; 9661564b287SLydia Wang return 0; 9671564b287SLydia Wang } 9681564b287SLydia Wang 9691564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9701564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9711564b287SLydia Wang { 9721564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9731564b287SLydia Wang struct via_spec *spec = codec->spec; 9747b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9751564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9761564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9771564b287SLydia Wang int i; 9781564b287SLydia Wang 9797b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9807b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9817b315bb4STakashi Iwai unsigned int parm; 9827b315bb4STakashi Iwai 98386e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9847b315bb4STakashi Iwai continue; 98586e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9867b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9871564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 9887b315bb4STakashi Iwai 9897b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 9901564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9911564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 9921564b287SLydia Wang parm |= out_in; 9931564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 9941564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 9951564b287SLydia Wang parm); 9961564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 9971564b287SLydia Wang mute_aa_path(codec, 1); 9981564b287SLydia Wang notify_aa_path_ctls(codec); 9991564b287SLydia Wang } 10007b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 1001eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1002eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1003eb7188caSLydia Wang HDA_AMP_UNMUTE); 10041564b287SLydia Wang } 100586e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 1006f3db423dSLydia Wang if (spec->codec_type == VT1708S 1007f3db423dSLydia Wang || spec->codec_type == VT1716S) { 10081564b287SLydia Wang /* input = index 1 (AOW3) */ 10091564b287SLydia Wang snd_hda_codec_write( 10101564b287SLydia Wang codec, nid, 0, 10111564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 10121564b287SLydia Wang snd_hda_codec_amp_stereo( 10131564b287SLydia Wang codec, nid, HDA_OUTPUT, 10141564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 10151564b287SLydia Wang } 10161564b287SLydia Wang } 10171564b287SLydia Wang } 10181564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10193e95b9abSLydia Wang set_widgets_power_state(codec); 10201564b287SLydia Wang return 1; 10211564b287SLydia Wang } 10221564b287SLydia Wang 10235f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10241564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10251564b287SLydia Wang .name = "Smart 5.1", 10261564b287SLydia Wang .count = 1, 10271564b287SLydia Wang .info = via_smart51_info, 10281564b287SLydia Wang .get = via_smart51_get, 10291564b287SLydia Wang .put = via_smart51_put, 10301564b287SLydia Wang }; 10311564b287SLydia Wang 10325b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 10335b0cb1d8SJaroslav Kysela { 10345b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10357b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10365b0cb1d8SJaroslav Kysela hda_nid_t nid; 10375b0cb1d8SJaroslav Kysela int i; 10385b0cb1d8SJaroslav Kysela 1039cb34c207SLydia Wang if (!cfg) 1040cb34c207SLydia Wang return 0; 1041cb34c207SLydia Wang if (cfg->line_outs > 2) 1042cb34c207SLydia Wang return 0; 1043cb34c207SLydia Wang 10445f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10455b0cb1d8SJaroslav Kysela if (knew == NULL) 10465b0cb1d8SJaroslav Kysela return -ENOMEM; 10475b0cb1d8SJaroslav Kysela 10487b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10497b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 105086e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 10515f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10527b315bb4STakashi Iwai break; 10535b0cb1d8SJaroslav Kysela } 10545b0cb1d8SJaroslav Kysela } 10555b0cb1d8SJaroslav Kysela 10565b0cb1d8SJaroslav Kysela return 0; 10575b0cb1d8SJaroslav Kysela } 10585b0cb1d8SJaroslav Kysela 1059c577b8a1SJoseph Chan /* capture mixer elements */ 106090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = { 1061c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1062c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1063c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1064c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1065c577b8a1SJoseph Chan { 1066c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1067c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1068c577b8a1SJoseph Chan * So call somewhat different.. 1069c577b8a1SJoseph Chan */ 1070c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1071c577b8a1SJoseph Chan .name = "Input Source", 1072c577b8a1SJoseph Chan .count = 1, 1073c577b8a1SJoseph Chan .info = via_mux_enum_info, 1074c577b8a1SJoseph Chan .get = via_mux_enum_get, 1075c577b8a1SJoseph Chan .put = via_mux_enum_put, 1076c577b8a1SJoseph Chan }, 1077c577b8a1SJoseph Chan { } /* end */ 1078c577b8a1SJoseph Chan }; 1079f5271101SLydia Wang 1080f5271101SLydia Wang /* check AA path's mute statue */ 1081f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1082f5271101SLydia Wang { 1083f5271101SLydia Wang int mute = 1; 1084f5271101SLydia Wang int start_idx; 1085f5271101SLydia Wang int end_idx; 1086f5271101SLydia Wang int i; 1087f5271101SLydia Wang struct via_spec *spec = codec->spec; 1088f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1089f5271101SLydia Wang switch (spec->codec_type) { 1090f5271101SLydia Wang case VT1708B_8CH: 1091f5271101SLydia Wang case VT1708B_4CH: 1092f5271101SLydia Wang case VT1708S: 1093f3db423dSLydia Wang case VT1716S: 1094f5271101SLydia Wang start_idx = 2; 1095f5271101SLydia Wang end_idx = 4; 1096f5271101SLydia Wang break; 1097f5271101SLydia Wang case VT1702: 1098f5271101SLydia Wang start_idx = 1; 1099f5271101SLydia Wang end_idx = 3; 1100f5271101SLydia Wang break; 1101eb7188caSLydia Wang case VT1718S: 1102eb7188caSLydia Wang start_idx = 1; 1103eb7188caSLydia Wang end_idx = 3; 1104eb7188caSLydia Wang break; 110525eaba2fSLydia Wang case VT2002P: 1106ab6734e7SLydia Wang case VT1812: 110711890956SLydia Wang case VT1802: 110825eaba2fSLydia Wang start_idx = 0; 110925eaba2fSLydia Wang end_idx = 2; 111025eaba2fSLydia Wang break; 1111f5271101SLydia Wang default: 1112f5271101SLydia Wang return 0; 1113f5271101SLydia Wang } 1114f5271101SLydia Wang /* check AA path's mute status */ 1115f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1116f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1117620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1118f5271101SLydia Wang int shift = 8 * (i % 4); 1119f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1120f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1121f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1122f5271101SLydia Wang /* check mute status while the pin is connected */ 1123620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 1124f5271101SLydia Wang HDA_INPUT, i) >> 7; 1125620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 1126f5271101SLydia Wang HDA_INPUT, i) >> 7; 1127f5271101SLydia Wang if (!mute_l || !mute_r) { 1128f5271101SLydia Wang mute = 0; 1129f5271101SLydia Wang break; 1130f5271101SLydia Wang } 1131f5271101SLydia Wang } 1132f5271101SLydia Wang } 1133f5271101SLydia Wang return mute; 1134f5271101SLydia Wang } 1135f5271101SLydia Wang 1136f5271101SLydia Wang /* enter/exit analog low-current mode */ 1137f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1138f5271101SLydia Wang { 1139f5271101SLydia Wang struct via_spec *spec = codec->spec; 1140f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1141f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1142f5271101SLydia Wang unsigned int verb = 0; 1143f5271101SLydia Wang unsigned int parm = 0; 1144f5271101SLydia Wang 1145f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1146f5271101SLydia Wang enable = enable && saved_stream_idle; 1147f5271101SLydia Wang else { 1148f5271101SLydia Wang enable = enable && stream_idle; 1149f5271101SLydia Wang saved_stream_idle = stream_idle; 1150f5271101SLydia Wang } 1151f5271101SLydia Wang 1152f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1153f5271101SLydia Wang switch (spec->codec_type) { 1154f5271101SLydia Wang case VT1708B_8CH: 1155f5271101SLydia Wang case VT1708B_4CH: 1156f5271101SLydia Wang verb = 0xf70; 1157f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1158f5271101SLydia Wang break; 1159f5271101SLydia Wang case VT1708S: 1160eb7188caSLydia Wang case VT1718S: 1161f3db423dSLydia Wang case VT1716S: 1162f5271101SLydia Wang verb = 0xf73; 1163f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1164f5271101SLydia Wang break; 1165f5271101SLydia Wang case VT1702: 1166f5271101SLydia Wang verb = 0xf73; 1167f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1168f5271101SLydia Wang break; 116925eaba2fSLydia Wang case VT2002P: 1170ab6734e7SLydia Wang case VT1812: 117111890956SLydia Wang case VT1802: 117225eaba2fSLydia Wang verb = 0xf93; 117325eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 117425eaba2fSLydia Wang break; 1175f5271101SLydia Wang default: 1176f5271101SLydia Wang return; /* other codecs are not supported */ 1177f5271101SLydia Wang } 1178f5271101SLydia Wang /* send verb */ 1179f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1180f5271101SLydia Wang } 1181f5271101SLydia Wang 1182c577b8a1SJoseph Chan /* 1183c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1184c577b8a1SJoseph Chan */ 118590dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1186c577b8a1SJoseph Chan /* 1187c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1188c577b8a1SJoseph Chan */ 1189c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1190c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1191c577b8a1SJoseph Chan 1192c577b8a1SJoseph Chan 1193f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1194c577b8a1SJoseph Chan * mixer widget 1195c577b8a1SJoseph Chan */ 1196c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1197f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1198f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1199f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1200f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1201f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1202c577b8a1SJoseph Chan 1203c577b8a1SJoseph Chan /* 1204c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1205c577b8a1SJoseph Chan */ 1206c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1207c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1208c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1209c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1210c577b8a1SJoseph Chan 1211bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1212bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1213c577b8a1SJoseph Chan /* PW9 Output enable */ 1214c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1215aa266fccSLydia Wang /* power down jack detect function */ 1216aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1217f7278fd0SJosepch Chan { } 1218c577b8a1SJoseph Chan }; 1219c577b8a1SJoseph Chan 1220c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1221c577b8a1SJoseph Chan struct hda_codec *codec, 1222c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1223c577b8a1SJoseph Chan { 1224c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 122517314379SLydia Wang int idle = substream->pstr->substream_opened == 1 122617314379SLydia Wang && substream->ref_count == 0; 122717314379SLydia Wang analog_low_current_mode(codec, idle); 12289a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12299a08160bSTakashi Iwai hinfo); 1230c577b8a1SJoseph Chan } 1231c577b8a1SJoseph Chan 12320aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 12330aa62aefSHarald Welte unsigned int stream_tag, 12340aa62aefSHarald Welte unsigned int format, 12350aa62aefSHarald Welte struct snd_pcm_substream *substream) 12360aa62aefSHarald Welte { 12370aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12380aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1239dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12400aa62aefSHarald Welte int chs = substream->runtime->channels; 12410aa62aefSHarald Welte int i; 12427c935976SStephen Warren struct hda_spdif_out *spdif = 12437c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12440aa62aefSHarald Welte 12450aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12460aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12470aa62aefSHarald Welte if (chs == 2 && 12480aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12490aa62aefSHarald Welte format) && 12507c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12510aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12520aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12530aa62aefSHarald Welte * be updated */ 12547c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12550aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12560aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12577c935976SStephen Warren spdif->ctls & 12580aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12590aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12600aa62aefSHarald Welte stream_tag, 0, format); 12610aa62aefSHarald Welte /* turn on again (if needed) */ 12627c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12630aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12640aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12657c935976SStephen Warren spdif->ctls & 0xff); 12660aa62aefSHarald Welte } else { 12670aa62aefSHarald Welte mout->dig_out_used = 0; 12680aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12690aa62aefSHarald Welte 0, 0, 0); 12700aa62aefSHarald Welte } 12710aa62aefSHarald Welte } 12720aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12730aa62aefSHarald Welte 12740aa62aefSHarald Welte /* front */ 12750aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12760aa62aefSHarald Welte 0, format); 12770aa62aefSHarald Welte 1278eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1279eb7188caSLydia Wang && !spec->hp_independent_mode) 12800aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12810aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12820aa62aefSHarald Welte 0, format); 12830aa62aefSHarald Welte 12840aa62aefSHarald Welte /* extra outputs copied from front */ 12850aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12860aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12870aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12880aa62aefSHarald Welte mout->extra_out_nid[i], 12890aa62aefSHarald Welte stream_tag, 0, format); 12900aa62aefSHarald Welte 12910aa62aefSHarald Welte /* surrounds */ 12920aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12930aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12940aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12950aa62aefSHarald Welte i * 2, format); 12960aa62aefSHarald Welte else /* copy front */ 12970aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12980aa62aefSHarald Welte 0, format); 12990aa62aefSHarald Welte } 13000aa62aefSHarald Welte } 13010aa62aefSHarald Welte 13020aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 13030aa62aefSHarald Welte struct hda_codec *codec, 13040aa62aefSHarald Welte unsigned int stream_tag, 13050aa62aefSHarald Welte unsigned int format, 13060aa62aefSHarald Welte struct snd_pcm_substream *substream) 13070aa62aefSHarald Welte { 13080aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13090aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1310dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13110aa62aefSHarald Welte 13120aa62aefSHarald Welte if (substream->number == 0) 13130aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 13140aa62aefSHarald Welte substream); 13150aa62aefSHarald Welte else { 13160aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13170aa62aefSHarald Welte spec->hp_independent_mode) 13180aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13190aa62aefSHarald Welte stream_tag, 0, format); 13200aa62aefSHarald Welte } 13211f2e99feSLydia Wang vt1708_start_hp_work(spec); 13220aa62aefSHarald Welte return 0; 13230aa62aefSHarald Welte } 13240aa62aefSHarald Welte 13250aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13260aa62aefSHarald Welte struct hda_codec *codec, 13270aa62aefSHarald Welte struct snd_pcm_substream *substream) 13280aa62aefSHarald Welte { 13290aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13300aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1331dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13320aa62aefSHarald Welte int i; 13330aa62aefSHarald Welte 13340aa62aefSHarald Welte if (substream->number == 0) { 13350aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13360aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13370aa62aefSHarald Welte 13380aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13390aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13400aa62aefSHarald Welte 0, 0, 0); 13410aa62aefSHarald Welte 13420aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13430aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13440aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13450aa62aefSHarald Welte mout->extra_out_nid[i], 13460aa62aefSHarald Welte 0, 0, 0); 13470aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13480aa62aefSHarald Welte if (mout->dig_out_nid && 13490aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13500aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13510aa62aefSHarald Welte 0, 0, 0); 13520aa62aefSHarald Welte mout->dig_out_used = 0; 13530aa62aefSHarald Welte } 13540aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13550aa62aefSHarald Welte } else { 13560aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13570aa62aefSHarald Welte spec->hp_independent_mode) 13580aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13590aa62aefSHarald Welte 0, 0, 0); 13600aa62aefSHarald Welte } 13611f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13620aa62aefSHarald Welte return 0; 13630aa62aefSHarald Welte } 13640aa62aefSHarald Welte 1365c577b8a1SJoseph Chan /* 1366c577b8a1SJoseph Chan * Digital out 1367c577b8a1SJoseph Chan */ 1368c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1369c577b8a1SJoseph Chan struct hda_codec *codec, 1370c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1371c577b8a1SJoseph Chan { 1372c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1373c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1374c577b8a1SJoseph Chan } 1375c577b8a1SJoseph Chan 1376c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1377c577b8a1SJoseph Chan struct hda_codec *codec, 1378c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1379c577b8a1SJoseph Chan { 1380c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1381c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1382c577b8a1SJoseph Chan } 1383c577b8a1SJoseph Chan 13845691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 138598aa34c0SHarald Welte struct hda_codec *codec, 138698aa34c0SHarald Welte unsigned int stream_tag, 138798aa34c0SHarald Welte unsigned int format, 138898aa34c0SHarald Welte struct snd_pcm_substream *substream) 138998aa34c0SHarald Welte { 139098aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13919da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13929da29271STakashi Iwai stream_tag, format, substream); 13939da29271STakashi Iwai } 13945691ec7fSHarald Welte 13959da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 13969da29271STakashi Iwai struct hda_codec *codec, 13979da29271STakashi Iwai struct snd_pcm_substream *substream) 13989da29271STakashi Iwai { 13999da29271STakashi Iwai struct via_spec *spec = codec->spec; 14009da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 140198aa34c0SHarald Welte return 0; 140298aa34c0SHarald Welte } 140398aa34c0SHarald Welte 1404c577b8a1SJoseph Chan /* 1405c577b8a1SJoseph Chan * Analog capture 1406c577b8a1SJoseph Chan */ 1407c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1408c577b8a1SJoseph Chan struct hda_codec *codec, 1409c577b8a1SJoseph Chan unsigned int stream_tag, 1410c577b8a1SJoseph Chan unsigned int format, 1411c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1412c577b8a1SJoseph Chan { 1413c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1414c577b8a1SJoseph Chan 1415c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1416c577b8a1SJoseph Chan stream_tag, 0, format); 1417c577b8a1SJoseph Chan return 0; 1418c577b8a1SJoseph Chan } 1419c577b8a1SJoseph Chan 1420c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1421c577b8a1SJoseph Chan struct hda_codec *codec, 1422c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1423c577b8a1SJoseph Chan { 1424c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1425888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1426c577b8a1SJoseph Chan return 0; 1427c577b8a1SJoseph Chan } 1428c577b8a1SJoseph Chan 142990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_playback = { 14300aa62aefSHarald Welte .substreams = 2, 1431c577b8a1SJoseph Chan .channels_min = 2, 1432c577b8a1SJoseph Chan .channels_max = 8, 1433c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1434c577b8a1SJoseph Chan .ops = { 1435c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14360aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14370aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1438c577b8a1SJoseph Chan }, 1439c577b8a1SJoseph Chan }; 1440c577b8a1SJoseph Chan 144190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1442c873cc25SLydia Wang .substreams = 2, 1443bc9b5623STakashi Iwai .channels_min = 2, 1444bc9b5623STakashi Iwai .channels_max = 8, 1445bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1446bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1447bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1448bc9b5623STakashi Iwai * disable the 24bit format, so far. 1449bc9b5623STakashi Iwai */ 1450bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1451bc9b5623STakashi Iwai .ops = { 1452bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1453c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1454c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1455bc9b5623STakashi Iwai }, 1456bc9b5623STakashi Iwai }; 1457bc9b5623STakashi Iwai 145890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_capture = { 1459c577b8a1SJoseph Chan .substreams = 2, 1460c577b8a1SJoseph Chan .channels_min = 2, 1461c577b8a1SJoseph Chan .channels_max = 2, 1462c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1463c577b8a1SJoseph Chan .ops = { 1464c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1465c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1466c577b8a1SJoseph Chan }, 1467c577b8a1SJoseph Chan }; 1468c577b8a1SJoseph Chan 146990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_playback = { 1470c577b8a1SJoseph Chan .substreams = 1, 1471c577b8a1SJoseph Chan .channels_min = 2, 1472c577b8a1SJoseph Chan .channels_max = 2, 1473c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1474c577b8a1SJoseph Chan .ops = { 1475c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14766b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14779da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14789da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1479c577b8a1SJoseph Chan }, 1480c577b8a1SJoseph Chan }; 1481c577b8a1SJoseph Chan 148290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_capture = { 1483c577b8a1SJoseph Chan .substreams = 1, 1484c577b8a1SJoseph Chan .channels_min = 2, 1485c577b8a1SJoseph Chan .channels_max = 2, 1486c577b8a1SJoseph Chan }; 1487c577b8a1SJoseph Chan 1488c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1489c577b8a1SJoseph Chan { 1490c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 14915b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 149290dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 14935b0cb1d8SJaroslav Kysela int err, i; 1494c577b8a1SJoseph Chan 149524088a58STakashi Iwai if (spec->set_widgets_power_state) 149624088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 149724088a58STakashi Iwai return -ENOMEM; 149824088a58STakashi Iwai 1499c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1500c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1501c577b8a1SJoseph Chan if (err < 0) 1502c577b8a1SJoseph Chan return err; 1503c577b8a1SJoseph Chan } 1504c577b8a1SJoseph Chan 1505c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1506c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 150774b654c9SStephen Warren spec->multiout.dig_out_nid, 1508c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1509c577b8a1SJoseph Chan if (err < 0) 1510c577b8a1SJoseph Chan return err; 15119a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15129a08160bSTakashi Iwai &spec->multiout); 15139a08160bSTakashi Iwai if (err < 0) 15149a08160bSTakashi Iwai return err; 15159a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1516c577b8a1SJoseph Chan } 1517c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1518c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1519c577b8a1SJoseph Chan if (err < 0) 1520c577b8a1SJoseph Chan return err; 1521c577b8a1SJoseph Chan } 152217314379SLydia Wang 15235b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15245b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15255b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 152621949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15275b0cb1d8SJaroslav Kysela if (err < 0) 15285b0cb1d8SJaroslav Kysela return err; 15295b0cb1d8SJaroslav Kysela } 15305b0cb1d8SJaroslav Kysela 15315b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15325b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15335b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15345b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15355b0cb1d8SJaroslav Kysela continue; 15365b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15375b0cb1d8SJaroslav Kysela if (kctl == NULL) 15385b0cb1d8SJaroslav Kysela continue; 15395b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15405b0cb1d8SJaroslav Kysela knew->subdevice); 15415b0cb1d8SJaroslav Kysela } 15425b0cb1d8SJaroslav Kysela } 15435b0cb1d8SJaroslav Kysela 154417314379SLydia Wang /* init power states */ 15453e95b9abSLydia Wang set_widgets_power_state(codec); 154617314379SLydia Wang analog_low_current_mode(codec, 1); 154717314379SLydia Wang 1548603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1549c577b8a1SJoseph Chan return 0; 1550c577b8a1SJoseph Chan } 1551c577b8a1SJoseph Chan 1552c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1553c577b8a1SJoseph Chan { 1554c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1555c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1556c577b8a1SJoseph Chan 1557c577b8a1SJoseph Chan codec->num_pcms = 1; 1558c577b8a1SJoseph Chan codec->pcm_info = info; 1559c577b8a1SJoseph Chan 156082673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 156182673bc8STakashi Iwai "%s Analog", codec->chip_name); 1562c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1563377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1564377ff31aSLydia Wang *(spec->stream_analog_playback); 1565377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1566377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1567c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1568c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1569c577b8a1SJoseph Chan 1570c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1571c577b8a1SJoseph Chan spec->multiout.max_channels; 1572c577b8a1SJoseph Chan 1573c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1574c577b8a1SJoseph Chan codec->num_pcms++; 1575c577b8a1SJoseph Chan info++; 157682673bc8STakashi Iwai snprintf(spec->stream_name_digital, 157782673bc8STakashi Iwai sizeof(spec->stream_name_digital), 157882673bc8STakashi Iwai "%s Digital", codec->chip_name); 1579c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 15807ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1581c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1582c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1583c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1584c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1585c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1586c577b8a1SJoseph Chan } 1587c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1588c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1589c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1590c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1591c577b8a1SJoseph Chan spec->dig_in_nid; 1592c577b8a1SJoseph Chan } 1593c577b8a1SJoseph Chan } 1594c577b8a1SJoseph Chan 1595c577b8a1SJoseph Chan return 0; 1596c577b8a1SJoseph Chan } 1597c577b8a1SJoseph Chan 1598c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1599c577b8a1SJoseph Chan { 1600c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1601c577b8a1SJoseph Chan 1602c577b8a1SJoseph Chan if (!spec) 1603c577b8a1SJoseph Chan return; 1604c577b8a1SJoseph Chan 1605603c4019STakashi Iwai via_free_kctls(codec); 16061f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1607c577b8a1SJoseph Chan kfree(codec->spec); 1608c577b8a1SJoseph Chan } 1609c577b8a1SJoseph Chan 161064be285bSTakashi Iwai /* mute/unmute outputs */ 161164be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 161264be285bSTakashi Iwai hda_nid_t *pins, bool mute) 161364be285bSTakashi Iwai { 161464be285bSTakashi Iwai int i; 161564be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 161664be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 161764be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 161864be285bSTakashi Iwai mute ? 0 : PIN_OUT); 161964be285bSTakashi Iwai } 162064be285bSTakashi Iwai 162169e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 162269e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 162369e52a80SHarald Welte { 1624dcf34c8cSLydia Wang unsigned int present = 0; 162569e52a80SHarald Welte struct via_spec *spec = codec->spec; 162669e52a80SHarald Welte 1627d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1628dcf34c8cSLydia Wang 162964be285bSTakashi Iwai if (!spec->hp_independent_mode) 163064be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 163164be285bSTakashi Iwai spec->autocfg.line_out_pins, 163264be285bSTakashi Iwai present); 163369e52a80SHarald Welte } 163469e52a80SHarald Welte 1635f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1636f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1637f3db423dSLydia Wang { 1638f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1639f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1640f3db423dSLydia Wang 1641f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1642f3db423dSLydia Wang return; 1643f3db423dSLydia Wang 1644d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1645d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1646f3db423dSLydia Wang 1647f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1648f3db423dSLydia Wang if (lineout_present) { 16493e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 16503e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16513e0693e2STakashi Iwai lineout_present ? 0 : PIN_OUT); 1652f3db423dSLydia Wang return; 1653f3db423dSLydia Wang } 1654f3db423dSLydia Wang 1655d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1656f3db423dSLydia Wang 1657f3db423dSLydia Wang if (!spec->hp_independent_mode) 16583e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 16593e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16603e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 1661f3db423dSLydia Wang } 1662f3db423dSLydia Wang 166369e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 166469e52a80SHarald Welte { 166569e52a80SHarald Welte unsigned int gpio_data; 166669e52a80SHarald Welte unsigned int vol_counter; 166769e52a80SHarald Welte unsigned int vol; 166869e52a80SHarald Welte unsigned int master_vol; 166969e52a80SHarald Welte 167069e52a80SHarald Welte struct via_spec *spec = codec->spec; 167169e52a80SHarald Welte 167269e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 167369e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 167469e52a80SHarald Welte 167569e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 167669e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 167769e52a80SHarald Welte 167869e52a80SHarald Welte vol = vol_counter & 0x1F; 167969e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 168069e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 168169e52a80SHarald Welte AC_AMP_GET_INPUT); 168269e52a80SHarald Welte 168369e52a80SHarald Welte if (gpio_data == 0x02) { 168469e52a80SHarald Welte /* unmute line out */ 16853e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 16863e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 16873e0693e2STakashi Iwai PIN_OUT); 168869e52a80SHarald Welte if (vol_counter & 0x20) { 168969e52a80SHarald Welte /* decrease volume */ 169069e52a80SHarald Welte if (vol > master_vol) 169169e52a80SHarald Welte vol = master_vol; 169269e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 169369e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 169469e52a80SHarald Welte master_vol-vol); 169569e52a80SHarald Welte } else { 169669e52a80SHarald Welte /* increase volume */ 169769e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 169869e52a80SHarald Welte HDA_AMP_VOLMASK, 169969e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 170069e52a80SHarald Welte (master_vol+vol)); 170169e52a80SHarald Welte } 170269e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 170369e52a80SHarald Welte /* mute line out */ 17043e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 17053e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 17063e0693e2STakashi Iwai 0); 170769e52a80SHarald Welte } 170869e52a80SHarald Welte } 170969e52a80SHarald Welte 171025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 171125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 171225eaba2fSLydia Wang { 171325eaba2fSLydia Wang unsigned int hp_present; 171425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 171525eaba2fSLydia Wang 171627439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 171725eaba2fSLydia Wang return; 171825eaba2fSLydia Wang 1719d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 172025eaba2fSLydia Wang 172164be285bSTakashi Iwai if (!spec->hp_independent_mode) 172264be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 172364be285bSTakashi Iwai spec->autocfg.speaker_pins, 172464be285bSTakashi Iwai hp_present); 172525eaba2fSLydia Wang } 172625eaba2fSLydia Wang 172725eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 172825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 172925eaba2fSLydia Wang { 173064be285bSTakashi Iwai int present; 173125eaba2fSLydia Wang struct via_spec *spec = codec->spec; 173225eaba2fSLydia Wang 173325eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 173425eaba2fSLydia Wang return; 173525eaba2fSLydia Wang 173664be285bSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 173764be285bSTakashi Iwai if (!spec->hp_independent_mode) 173864be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 173964be285bSTakashi Iwai spec->autocfg.line_out_pins, 174064be285bSTakashi Iwai present); 174125eaba2fSLydia Wang 174264be285bSTakashi Iwai if (!present) 174364be285bSTakashi Iwai present = snd_hda_jack_detect(codec, 174464be285bSTakashi Iwai spec->autocfg.line_out_pins[0]); 174525eaba2fSLydia Wang 174625eaba2fSLydia Wang /* Speakers */ 174764be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 174864be285bSTakashi Iwai spec->autocfg.speaker_pins, 174964be285bSTakashi Iwai present); 175025eaba2fSLydia Wang } 175125eaba2fSLydia Wang 175225eaba2fSLydia Wang 175369e52a80SHarald Welte /* unsolicited event for jack sensing */ 175469e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 175569e52a80SHarald Welte unsigned int res) 175669e52a80SHarald Welte { 175769e52a80SHarald Welte res >>= 26; 1758ec7e7e42SLydia Wang 1759a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 17603e95b9abSLydia Wang set_widgets_power_state(codec); 1761ec7e7e42SLydia Wang 1762ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1763ec7e7e42SLydia Wang 1764ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1765ec7e7e42SLydia Wang via_hp_automute(codec); 1766ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1767ec7e7e42SLydia Wang via_gpio_control(codec); 1768ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1769f3db423dSLydia Wang via_mono_automute(codec); 1770ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 177125eaba2fSLydia Wang via_speaker_automute(codec); 1772ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 177325eaba2fSLydia Wang via_hp_bind_automute(codec); 177469e52a80SHarald Welte } 177569e52a80SHarald Welte 1776c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1777c577b8a1SJoseph Chan { 1778c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 177969e52a80SHarald Welte int i; 178069e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 178169e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 178269e52a80SHarald Welte 1783f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1784f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 178555d1d6c1STakashi Iwai if (spec->dig_in_pin) { 178655d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1787f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 178812b74c80STakashi Iwai PIN_OUT); 178955d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1790f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1791f7278fd0SJosepch Chan } 179212b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 179312b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 179412b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1795f7278fd0SJosepch Chan 17969da29271STakashi Iwai /* assign slave outs */ 17979da29271STakashi Iwai if (spec->slave_dig_outs[0]) 17989da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 17995691ec7fSHarald Welte 1800c577b8a1SJoseph Chan return 0; 1801c577b8a1SJoseph Chan } 1802c577b8a1SJoseph Chan 18031f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18041f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18051f2e99feSLydia Wang { 18061f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18071f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18081f2e99feSLydia Wang return 0; 18091f2e99feSLydia Wang } 18101f2e99feSLydia Wang #endif 18111f2e99feSLydia Wang 1812cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1813cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1814cb53c626STakashi Iwai { 1815cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1816cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1817cb53c626STakashi Iwai } 1818cb53c626STakashi Iwai #endif 1819cb53c626STakashi Iwai 1820c577b8a1SJoseph Chan /* 1821c577b8a1SJoseph Chan */ 182290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1823c577b8a1SJoseph Chan .build_controls = via_build_controls, 1824c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1825c577b8a1SJoseph Chan .init = via_init, 1826c577b8a1SJoseph Chan .free = via_free, 18271f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18281f2e99feSLydia Wang .suspend = via_suspend, 18291f2e99feSLydia Wang #endif 1830cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1831cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1832cb53c626STakashi Iwai #endif 1833c577b8a1SJoseph Chan }; 1834c577b8a1SJoseph Chan 1835*4a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1836c577b8a1SJoseph Chan { 1837*4a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1838*4a79616dSTakashi Iwai int i; 1839*4a79616dSTakashi Iwai 1840*4a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 1841*4a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 1842*4a79616dSTakashi Iwai return false; 1843*4a79616dSTakashi Iwai } 1844*4a79616dSTakashi Iwai if (spec->multiout.hp_nid == dac) 1845*4a79616dSTakashi Iwai return false; 1846*4a79616dSTakashi Iwai return true; 1847*4a79616dSTakashi Iwai } 1848*4a79616dSTakashi Iwai 1849*4a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 1850*4a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 1851*4a79616dSTakashi Iwai int depth, int wid_type) 1852*4a79616dSTakashi Iwai { 1853*4a79616dSTakashi Iwai hda_nid_t conn[8]; 1854*4a79616dSTakashi Iwai int i, nums; 1855*4a79616dSTakashi Iwai 1856*4a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 1857*4a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 1858*4a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 1859*4a79616dSTakashi Iwai continue; 1860*4a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 1861*4a79616dSTakashi Iwai path->path[depth] = conn[i]; 1862*4a79616dSTakashi Iwai path->idx[depth] = i; 1863*4a79616dSTakashi Iwai path->depth = ++depth; 1864*4a79616dSTakashi Iwai return true; 1865*4a79616dSTakashi Iwai } 1866*4a79616dSTakashi Iwai } 1867*4a79616dSTakashi Iwai if (depth > 4) 1868*4a79616dSTakashi Iwai return false; 1869*4a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 1870*4a79616dSTakashi Iwai unsigned int type; 1871*4a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 1872*4a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 1873*4a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 1874*4a79616dSTakashi Iwai continue; 1875*4a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 1876*4a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 1877*4a79616dSTakashi Iwai path->path[depth] = conn[i]; 1878*4a79616dSTakashi Iwai path->idx[depth] = i; 1879*4a79616dSTakashi Iwai return true; 1880*4a79616dSTakashi Iwai } 1881*4a79616dSTakashi Iwai } 1882*4a79616dSTakashi Iwai return false; 1883*4a79616dSTakashi Iwai } 1884*4a79616dSTakashi Iwai 1885*4a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 1886*4a79616dSTakashi Iwai { 1887*4a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1888*4a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1889c577b8a1SJoseph Chan int i; 1890c577b8a1SJoseph Chan hda_nid_t nid; 1891c577b8a1SJoseph Chan 1892c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1893*4a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 1894*4a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1895c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1896*4a79616dSTakashi Iwai if (!nid) 1897*4a79616dSTakashi Iwai continue; 1898*4a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 1899*4a79616dSTakashi Iwai spec->private_dac_nids[i] = 1900*4a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1901c577b8a1SJoseph Chan } 1902c577b8a1SJoseph Chan return 0; 1903c577b8a1SJoseph Chan } 1904c577b8a1SJoseph Chan 1905*4a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 1906*4a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1907c577b8a1SJoseph Chan { 1908*4a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1909c577b8a1SJoseph Chan char name[32]; 1910*4a79616dSTakashi Iwai hda_nid_t nid; 1911*4a79616dSTakashi Iwai int err; 1912c577b8a1SJoseph Chan 1913*4a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 1914*4a79616dSTakashi Iwai nid = dac; 1915*4a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 1916*4a79616dSTakashi Iwai nid = pin; 1917*4a79616dSTakashi Iwai else 1918*4a79616dSTakashi Iwai nid = 0; 1919*4a79616dSTakashi Iwai if (nid) { 1920*4a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1921c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1922*4a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1923c577b8a1SJoseph Chan if (err < 0) 1924c577b8a1SJoseph Chan return err; 1925c577b8a1SJoseph Chan } 1926*4a79616dSTakashi Iwai 1927*4a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 1928*4a79616dSTakashi Iwai nid = dac; 1929*4a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 1930*4a79616dSTakashi Iwai nid = pin; 1931*4a79616dSTakashi Iwai else 1932*4a79616dSTakashi Iwai nid = 0; 1933*4a79616dSTakashi Iwai if (nid) { 1934*4a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 1935*4a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1936*4a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 1937*4a79616dSTakashi Iwai if (err < 0) 1938*4a79616dSTakashi Iwai return err; 1939*4a79616dSTakashi Iwai } 1940*4a79616dSTakashi Iwai return 0; 1941*4a79616dSTakashi Iwai } 1942*4a79616dSTakashi Iwai 1943*4a79616dSTakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 1944*4a79616dSTakashi Iwai hda_nid_t nid); 1945*4a79616dSTakashi Iwai 1946*4a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 1947*4a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 1948*4a79616dSTakashi Iwai { 1949*4a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1950*4a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1951*4a79616dSTakashi Iwai static const char * const chname[4] = { 1952*4a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 1953*4a79616dSTakashi Iwai }; 1954*4a79616dSTakashi Iwai int i, idx, err; 1955*4a79616dSTakashi Iwai 1956*4a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1957*4a79616dSTakashi Iwai hda_nid_t pin, dac; 1958*4a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 1959*4a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 1960*4a79616dSTakashi Iwai if (!pin || !dac) 1961*4a79616dSTakashi Iwai continue; 1962*4a79616dSTakashi Iwai if (i == AUTO_SEQ_CENLFE) { 1963*4a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 1964*4a79616dSTakashi Iwai if (err < 0) 1965*4a79616dSTakashi Iwai return err; 1966*4a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 1967*4a79616dSTakashi Iwai if (err < 0) 1968*4a79616dSTakashi Iwai return err; 1969*4a79616dSTakashi Iwai } else { 1970*4a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 1971*4a79616dSTakashi Iwai if (err < 0) 1972*4a79616dSTakashi Iwai return err; 1973*4a79616dSTakashi Iwai } 1974*4a79616dSTakashi Iwai } 1975*4a79616dSTakashi Iwai 1976*4a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 1977*4a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 1978*4a79616dSTakashi Iwai if (idx >= 0) { 1979*4a79616dSTakashi Iwai /* add control to mixer */ 1980*4a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1981*4a79616dSTakashi Iwai "PCM Playback Volume", 1982*4a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 1983*4a79616dSTakashi Iwai idx, HDA_INPUT)); 1984*4a79616dSTakashi Iwai if (err < 0) 1985*4a79616dSTakashi Iwai return err; 1986*4a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1987*4a79616dSTakashi Iwai "PCM Playback Switch", 1988*4a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 1989*4a79616dSTakashi Iwai idx, HDA_INPUT)); 1990*4a79616dSTakashi Iwai if (err < 0) 1991*4a79616dSTakashi Iwai return err; 1992c577b8a1SJoseph Chan } 1993c577b8a1SJoseph Chan 1994c577b8a1SJoseph Chan return 0; 1995c577b8a1SJoseph Chan } 1996c577b8a1SJoseph Chan 19970aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 19980aa62aefSHarald Welte { 19990aa62aefSHarald Welte int i; 20000aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2001ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 20020aa62aefSHarald Welte 20030aa62aefSHarald Welte /* for hp mode select */ 200410a20af7STakashi Iwai for (i = 0; texts[i]; i++) 200510a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 20060aa62aefSHarald Welte 20070aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 20080aa62aefSHarald Welte } 20090aa62aefSHarald Welte 2010*4a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 2011c577b8a1SJoseph Chan { 2012*4a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 2013*4a79616dSTakashi Iwai hda_nid_t dac = 0; 2014c577b8a1SJoseph Chan int err; 2015c577b8a1SJoseph Chan 2016c577b8a1SJoseph Chan if (!pin) 2017c577b8a1SJoseph Chan return 0; 2018c577b8a1SJoseph Chan 2019*4a79616dSTakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 2020*4a79616dSTakashi Iwai &spec->hp_dep_path, 0, -1)) 2021*4a79616dSTakashi Iwai return 0; 2022*4a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 2023*4a79616dSTakashi Iwai dac = spec->hp_path.path[spec->hp_path.depth - 1]; 2024*4a79616dSTakashi Iwai spec->multiout.hp_nid = dac; 2025*4a79616dSTakashi Iwai spec->hp_independent_mode_index = 2026*4a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 20270aa62aefSHarald Welte create_hp_imux(spec); 2028*4a79616dSTakashi Iwai } 2029*4a79616dSTakashi Iwai 2030*4a79616dSTakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, dac, 3); 2031*4a79616dSTakashi Iwai if (err < 0) 2032*4a79616dSTakashi Iwai return err; 20330aa62aefSHarald Welte 2034c577b8a1SJoseph Chan return 0; 2035c577b8a1SJoseph Chan } 2036c577b8a1SJoseph Chan 2037a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 2038a766d0d7STakashi Iwai hda_nid_t nid) 2039a766d0d7STakashi Iwai { 2040a766d0d7STakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 2041a766d0d7STakashi Iwai int i, nums; 2042a766d0d7STakashi Iwai 2043a766d0d7STakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 2044a766d0d7STakashi Iwai for (i = 0; i < nums; i++) 2045a766d0d7STakashi Iwai if (conn[i] == nid) 2046a766d0d7STakashi Iwai return i; 2047a766d0d7STakashi Iwai return -1; 2048a766d0d7STakashi Iwai } 2049a766d0d7STakashi Iwai 2050a766d0d7STakashi Iwai /* look for ADCs */ 2051a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2052a766d0d7STakashi Iwai { 2053a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2054a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2055a766d0d7STakashi Iwai int i; 2056a766d0d7STakashi Iwai 2057a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2058a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2059a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2060a766d0d7STakashi Iwai continue; 2061a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2062a766d0d7STakashi Iwai continue; 2063a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2064a766d0d7STakashi Iwai continue; 2065a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2066a766d0d7STakashi Iwai return -ENOMEM; 2067a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2068a766d0d7STakashi Iwai } 2069a766d0d7STakashi Iwai return 0; 2070a766d0d7STakashi Iwai } 2071a766d0d7STakashi Iwai 2072a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2073a766d0d7STakashi Iwai 2074c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2075620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 2076620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 2077c577b8a1SJoseph Chan { 207810a20af7STakashi Iwai struct via_spec *spec = codec->spec; 20790aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2080a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 2081a766d0d7STakashi Iwai hda_nid_t cap_nid; 2082a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 2083a766d0d7STakashi Iwai int num_idxs; 2084a766d0d7STakashi Iwai 2085a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2086a766d0d7STakashi Iwai if (err < 0) 2087a766d0d7STakashi Iwai return err; 2088a766d0d7STakashi Iwai err = get_mux_nids(codec); 2089a766d0d7STakashi Iwai if (err < 0) 2090a766d0d7STakashi Iwai return err; 2091a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 2092a766d0d7STakashi Iwai 2093a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 2094a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 2095a766d0d7STakashi Iwai if (num_idxs <= 0) 2096a766d0d7STakashi Iwai return 0; 2097c577b8a1SJoseph Chan 2098c577b8a1SJoseph Chan /* for internal loopback recording select */ 2099f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2100620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 210110a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2102f3268512STakashi Iwai break; 2103f3268512STakashi Iwai } 2104f3268512STakashi Iwai } 2105c577b8a1SJoseph Chan 21067b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 210710a20af7STakashi Iwai const char *label; 21087b315bb4STakashi Iwai type = cfg->inputs[i].type; 2109f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 21107b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2111c577b8a1SJoseph Chan break; 2112f3268512STakashi Iwai if (idx >= num_idxs) 2113f3268512STakashi Iwai continue; 21147b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 21157b315bb4STakashi Iwai type_idx++; 21167b315bb4STakashi Iwai else 21177b315bb4STakashi Iwai type_idx = 0; 211810a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2119620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 2120620e2b28STakashi Iwai pin_idxs[idx]); 2121a766d0d7STakashi Iwai if (idx2 >= 0) 212216922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2123620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 2124c577b8a1SJoseph Chan if (err < 0) 2125c577b8a1SJoseph Chan return err; 212610a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2127c577b8a1SJoseph Chan } 2128c577b8a1SJoseph Chan return 0; 2129c577b8a1SJoseph Chan } 2130c577b8a1SJoseph Chan 2131cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 213290dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2133cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2134cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2135cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2136cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2137cb53c626STakashi Iwai { } /* end */ 2138cb53c626STakashi Iwai }; 2139cb53c626STakashi Iwai #endif 2140cb53c626STakashi Iwai 214176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 214276d9b0ddSHarald Welte { 214376d9b0ddSHarald Welte unsigned int def_conf; 214476d9b0ddSHarald Welte unsigned char seqassoc; 214576d9b0ddSHarald Welte 21462f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 214776d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 214876d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 214982ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 215082ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 215176d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 21522f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 215376d9b0ddSHarald Welte } 215476d9b0ddSHarald Welte 215576d9b0ddSHarald Welte return; 215676d9b0ddSHarald Welte } 215776d9b0ddSHarald Welte 2158e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 21591f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21601f2e99feSLydia Wang { 21611f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21621f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21631f2e99feSLydia Wang 21641f2e99feSLydia Wang if (spec->codec_type != VT1708) 21651f2e99feSLydia Wang return 0; 2166e06e5a29STakashi Iwai spec->vt1708_jack_detect = 21671f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2168e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 21691f2e99feSLydia Wang return 0; 21701f2e99feSLydia Wang } 21711f2e99feSLydia Wang 2172e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 21731f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21741f2e99feSLydia Wang { 21751f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21761f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21771f2e99feSLydia Wang int change; 21781f2e99feSLydia Wang 21791f2e99feSLydia Wang if (spec->codec_type != VT1708) 21801f2e99feSLydia Wang return 0; 2181e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 21821f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2183e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2184e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 21851f2e99feSLydia Wang mute_aa_path(codec, 1); 21861f2e99feSLydia Wang notify_aa_path_ctls(codec); 21871f2e99feSLydia Wang } 21881f2e99feSLydia Wang return change; 21891f2e99feSLydia Wang } 21901f2e99feSLydia Wang 2191e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 21921f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 21931f2e99feSLydia Wang .name = "Jack Detect", 21941f2e99feSLydia Wang .count = 1, 21951f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2196e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2197e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 21981f2e99feSLydia Wang }; 21991f2e99feSLydia Wang 2200c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2201c577b8a1SJoseph Chan { 2202c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2203c577b8a1SJoseph Chan int err; 2204c577b8a1SJoseph Chan 220576d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 220676d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 220776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 220876d9b0ddSHarald Welte 2209c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2210c577b8a1SJoseph Chan if (err < 0) 2211c577b8a1SJoseph Chan return err; 2212*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2213c577b8a1SJoseph Chan if (err < 0) 2214c577b8a1SJoseph Chan return err; 2215c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2216c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2217c577b8a1SJoseph Chan 2218*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2219c577b8a1SJoseph Chan if (err < 0) 2220c577b8a1SJoseph Chan return err; 2221*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2222c577b8a1SJoseph Chan if (err < 0) 2223c577b8a1SJoseph Chan return err; 2224620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2225c577b8a1SJoseph Chan if (err < 0) 2226c577b8a1SJoseph Chan return err; 22271f2e99feSLydia Wang /* add jack detect on/off control */ 2228e06e5a29STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 2229e06e5a29STakashi Iwai return -ENOMEM; 2230c577b8a1SJoseph Chan 2231c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2232c577b8a1SJoseph Chan 22330852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2234c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 223555d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2236c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2237c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2238c577b8a1SJoseph Chan 2239603c4019STakashi Iwai if (spec->kctls.list) 2240603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2241c577b8a1SJoseph Chan 224269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2243c577b8a1SJoseph Chan 22440aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 22450aa62aefSHarald Welte 2246f8fdd495SHarald Welte if (spec->hp_mux) 22473d83e577STakashi Iwai via_hp_build(codec); 2248c577b8a1SJoseph Chan 22495b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2250c577b8a1SJoseph Chan return 1; 2251c577b8a1SJoseph Chan } 2252c577b8a1SJoseph Chan 2253c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2254c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2255c577b8a1SJoseph Chan { 225625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 225725eaba2fSLydia Wang 2258c577b8a1SJoseph Chan via_init(codec); 2259c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2260c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2261c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 226211890956SLydia Wang 226311890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 226425eaba2fSLydia Wang via_hp_bind_automute(codec); 226525eaba2fSLydia Wang } else { 226625eaba2fSLydia Wang via_hp_automute(codec); 226725eaba2fSLydia Wang via_speaker_automute(codec); 226825eaba2fSLydia Wang } 226925eaba2fSLydia Wang 2270c577b8a1SJoseph Chan return 0; 2271c577b8a1SJoseph Chan } 2272c577b8a1SJoseph Chan 22731f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 22741f2e99feSLydia Wang { 22751f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 22761f2e99feSLydia Wang vt1708_hp_work.work); 22771f2e99feSLydia Wang if (spec->codec_type != VT1708) 22781f2e99feSLydia Wang return; 22791f2e99feSLydia Wang /* if jack state toggled */ 22801f2e99feSLydia Wang if (spec->vt1708_hp_present 2281d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 22821f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 22831f2e99feSLydia Wang via_hp_automute(spec->codec); 22841f2e99feSLydia Wang } 22851f2e99feSLydia Wang vt1708_start_hp_work(spec); 22861f2e99feSLydia Wang } 22871f2e99feSLydia Wang 2288337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2289337b9d02STakashi Iwai { 2290337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2291337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2292337b9d02STakashi Iwai unsigned int type; 2293337b9d02STakashi Iwai int i, n; 2294337b9d02STakashi Iwai 2295337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2296337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2297337b9d02STakashi Iwai while (nid) { 2298a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22991c55d521STakashi Iwai if (type == AC_WID_PIN) 23001c55d521STakashi Iwai break; 2301337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2302337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2303337b9d02STakashi Iwai if (n <= 0) 2304337b9d02STakashi Iwai break; 2305337b9d02STakashi Iwai if (n > 1) { 2306337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2307337b9d02STakashi Iwai break; 2308337b9d02STakashi Iwai } 2309337b9d02STakashi Iwai nid = conn[0]; 2310337b9d02STakashi Iwai } 2311337b9d02STakashi Iwai } 23121c55d521STakashi Iwai return 0; 2313337b9d02STakashi Iwai } 2314337b9d02STakashi Iwai 2315c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2316c577b8a1SJoseph Chan { 2317c577b8a1SJoseph Chan struct via_spec *spec; 2318c577b8a1SJoseph Chan int err; 2319c577b8a1SJoseph Chan 2320c577b8a1SJoseph Chan /* create a codec specific record */ 23215b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2322c577b8a1SJoseph Chan if (spec == NULL) 2323c577b8a1SJoseph Chan return -ENOMEM; 2324c577b8a1SJoseph Chan 2325620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2326620e2b28STakashi Iwai 2327c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2328c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2329c577b8a1SJoseph Chan if (err < 0) { 2330c577b8a1SJoseph Chan via_free(codec); 2331c577b8a1SJoseph Chan return err; 2332c577b8a1SJoseph Chan } else if (!err) { 2333c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2334c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2335c577b8a1SJoseph Chan } 2336c577b8a1SJoseph Chan 2337c577b8a1SJoseph Chan 2338c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2339bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2340bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2341bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2342c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2343c577b8a1SJoseph Chan 2344c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2345c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2346c577b8a1SJoseph Chan 2347a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2348c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2349c577b8a1SJoseph Chan spec->num_mixers++; 2350c577b8a1SJoseph Chan } 2351c577b8a1SJoseph Chan 2352c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2353c577b8a1SJoseph Chan 2354c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2355cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2356cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2357cb53c626STakashi Iwai #endif 23581f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2359c577b8a1SJoseph Chan return 0; 2360c577b8a1SJoseph Chan } 2361c577b8a1SJoseph Chan 2362c577b8a1SJoseph Chan /* capture mixer elements */ 236390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = { 2364c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2365c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2366c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2367c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2368c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2369c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2370c577b8a1SJoseph Chan { 2371c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2372c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2373c577b8a1SJoseph Chan * So call somewhat different.. 2374c577b8a1SJoseph Chan */ 2375c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2376c577b8a1SJoseph Chan .name = "Input Source", 2377c577b8a1SJoseph Chan .count = 1, 2378c577b8a1SJoseph Chan .info = via_mux_enum_info, 2379c577b8a1SJoseph Chan .get = via_mux_enum_get, 2380c577b8a1SJoseph Chan .put = via_mux_enum_put, 2381c577b8a1SJoseph Chan }, 2382c577b8a1SJoseph Chan { } /* end */ 2383c577b8a1SJoseph Chan }; 2384c577b8a1SJoseph Chan 238590dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2386a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2387a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 238869e52a80SHarald Welte { } 238969e52a80SHarald Welte }; 239069e52a80SHarald Welte 2391c577b8a1SJoseph Chan /* 2392c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2393c577b8a1SJoseph Chan */ 239490dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2395c577b8a1SJoseph Chan /* 2396c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2397c577b8a1SJoseph Chan */ 2398c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2399c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2400c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2401c577b8a1SJoseph Chan 2402c577b8a1SJoseph Chan 2403f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2404c577b8a1SJoseph Chan * mixer widget 2405c577b8a1SJoseph Chan */ 2406c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2407f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2408f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2409f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2410f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2411f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2412c577b8a1SJoseph Chan 2413c577b8a1SJoseph Chan /* 2414c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2415c577b8a1SJoseph Chan */ 2416c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2417c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2418c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2419c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2420c577b8a1SJoseph Chan 2421c577b8a1SJoseph Chan /* 2422c577b8a1SJoseph Chan * Unmute PW3 and PW4 2423c577b8a1SJoseph Chan */ 2424c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2425c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2426c577b8a1SJoseph Chan 2427bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2428bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2429c577b8a1SJoseph Chan /* PW9 Output enable */ 2430c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2431c577b8a1SJoseph Chan { } 2432c577b8a1SJoseph Chan }; 2433c577b8a1SJoseph Chan 243490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2435c577b8a1SJoseph Chan .substreams = 1, 2436c577b8a1SJoseph Chan .channels_min = 2, 2437c577b8a1SJoseph Chan .channels_max = 10, 2438c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2439c577b8a1SJoseph Chan .ops = { 2440c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2441c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2442c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2443c577b8a1SJoseph Chan }, 2444c577b8a1SJoseph Chan }; 2445c577b8a1SJoseph Chan 244690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2447c577b8a1SJoseph Chan .substreams = 1, 2448c577b8a1SJoseph Chan .channels_min = 2, 2449c577b8a1SJoseph Chan .channels_max = 6, 2450c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2451c577b8a1SJoseph Chan .ops = { 2452c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2453c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2454c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2455c577b8a1SJoseph Chan }, 2456c577b8a1SJoseph Chan }; 2457c577b8a1SJoseph Chan 245890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_analog_capture = { 2459c577b8a1SJoseph Chan .substreams = 2, 2460c577b8a1SJoseph Chan .channels_min = 2, 2461c577b8a1SJoseph Chan .channels_max = 2, 2462c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2463c577b8a1SJoseph Chan .ops = { 2464c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2465c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2466c577b8a1SJoseph Chan }, 2467c577b8a1SJoseph Chan }; 2468c577b8a1SJoseph Chan 246990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_playback = { 2470c577b8a1SJoseph Chan .substreams = 1, 2471c577b8a1SJoseph Chan .channels_min = 2, 2472c577b8a1SJoseph Chan .channels_max = 2, 2473c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2474c577b8a1SJoseph Chan .ops = { 2475c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2476c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2477c577b8a1SJoseph Chan }, 2478c577b8a1SJoseph Chan }; 2479c577b8a1SJoseph Chan 248090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_capture = { 2481c577b8a1SJoseph Chan .substreams = 1, 2482c577b8a1SJoseph Chan .channels_min = 2, 2483c577b8a1SJoseph Chan .channels_max = 2, 2484c577b8a1SJoseph Chan }; 2485c577b8a1SJoseph Chan 2486c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2487c577b8a1SJoseph Chan { 2488c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2489c577b8a1SJoseph Chan int err; 2490c577b8a1SJoseph Chan 2491c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2492c577b8a1SJoseph Chan if (err < 0) 2493c577b8a1SJoseph Chan return err; 2494*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2495c577b8a1SJoseph Chan if (err < 0) 2496c577b8a1SJoseph Chan return err; 2497c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2498c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2499c577b8a1SJoseph Chan 2500*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2501c577b8a1SJoseph Chan if (err < 0) 2502c577b8a1SJoseph Chan return err; 2503*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2504c577b8a1SJoseph Chan if (err < 0) 2505c577b8a1SJoseph Chan return err; 2506620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2507c577b8a1SJoseph Chan if (err < 0) 2508c577b8a1SJoseph Chan return err; 2509c577b8a1SJoseph Chan 2510c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2511c577b8a1SJoseph Chan 25120852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2513c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 251455d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2515c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2516c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2517c577b8a1SJoseph Chan 2518603c4019STakashi Iwai if (spec->kctls.list) 2519603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2520c577b8a1SJoseph Chan 25210aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2522c577b8a1SJoseph Chan 2523f8fdd495SHarald Welte if (spec->hp_mux) 25243d83e577STakashi Iwai via_hp_build(codec); 2525f8fdd495SHarald Welte 25265b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2527c577b8a1SJoseph Chan return 1; 2528c577b8a1SJoseph Chan } 2529c577b8a1SJoseph Chan 2530cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 253190dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2532cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2533cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2534cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2535cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2536cb53c626STakashi Iwai { } /* end */ 2537cb53c626STakashi Iwai }; 2538cb53c626STakashi Iwai #endif 2539cb53c626STakashi Iwai 2540c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2541c577b8a1SJoseph Chan { 2542c577b8a1SJoseph Chan struct via_spec *spec; 2543c577b8a1SJoseph Chan int err; 2544c577b8a1SJoseph Chan 2545c577b8a1SJoseph Chan /* create a codec specific record */ 25465b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2547c577b8a1SJoseph Chan if (spec == NULL) 2548c577b8a1SJoseph Chan return -ENOMEM; 2549c577b8a1SJoseph Chan 2550620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2551620e2b28STakashi Iwai 2552c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2553c577b8a1SJoseph Chan if (err < 0) { 2554c577b8a1SJoseph Chan via_free(codec); 2555c577b8a1SJoseph Chan return err; 2556c577b8a1SJoseph Chan } else if (!err) { 2557c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2558c577b8a1SJoseph Chan "Using genenic mode...\n"); 2559c577b8a1SJoseph Chan } 2560c577b8a1SJoseph Chan 256169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 256269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2563c577b8a1SJoseph Chan 2564c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2565c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2566c577b8a1SJoseph Chan 2567c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2568c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2569c577b8a1SJoseph Chan 2570a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2571c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2572c577b8a1SJoseph Chan spec->num_mixers++; 2573c577b8a1SJoseph Chan } 2574c577b8a1SJoseph Chan 2575c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2576c577b8a1SJoseph Chan 2577c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 257869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2579cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2580cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2581cb53c626STakashi Iwai #endif 2582c577b8a1SJoseph Chan 2583c577b8a1SJoseph Chan return 0; 2584c577b8a1SJoseph Chan } 2585c577b8a1SJoseph Chan /* 2586c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2587c577b8a1SJoseph Chan */ 258890dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2589c577b8a1SJoseph Chan /* 2590c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2591c577b8a1SJoseph Chan */ 2592c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2593c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2594c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2595c577b8a1SJoseph Chan 2596c577b8a1SJoseph Chan 2597c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2598c577b8a1SJoseph Chan * mixer widget 2599c577b8a1SJoseph Chan */ 2600c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2601c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2602c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2603c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2604c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2605c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2606c577b8a1SJoseph Chan 2607c577b8a1SJoseph Chan /* 2608c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2609c577b8a1SJoseph Chan */ 2610c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2611c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2612c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2613c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2614c577b8a1SJoseph Chan 2615c577b8a1SJoseph Chan /* 2616c577b8a1SJoseph Chan * Unmute PW3 and PW4 2617c577b8a1SJoseph Chan */ 2618c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2619c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2620c577b8a1SJoseph Chan 2621c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2622c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2623c577b8a1SJoseph Chan /* PW9 Output enable */ 2624c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2625c577b8a1SJoseph Chan { } 2626c577b8a1SJoseph Chan }; 2627c577b8a1SJoseph Chan 2628c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2629c577b8a1SJoseph Chan { 2630c577b8a1SJoseph Chan struct via_spec *spec; 2631c577b8a1SJoseph Chan int err; 2632c577b8a1SJoseph Chan 2633c577b8a1SJoseph Chan /* create a codec specific record */ 26345b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2635c577b8a1SJoseph Chan if (spec == NULL) 2636c577b8a1SJoseph Chan return -ENOMEM; 2637c577b8a1SJoseph Chan 2638620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2639620e2b28STakashi Iwai 2640c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2641c577b8a1SJoseph Chan if (err < 0) { 2642c577b8a1SJoseph Chan via_free(codec); 2643c577b8a1SJoseph Chan return err; 2644c577b8a1SJoseph Chan } else if (!err) { 2645c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2646c577b8a1SJoseph Chan "Using genenic mode...\n"); 2647c577b8a1SJoseph Chan } 2648c577b8a1SJoseph Chan 264969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 265069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2651c577b8a1SJoseph Chan 2652c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2653c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2654c577b8a1SJoseph Chan 2655c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2656c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2657c577b8a1SJoseph Chan 2658a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2659c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2660c577b8a1SJoseph Chan spec->num_mixers++; 2661c577b8a1SJoseph Chan } 2662c577b8a1SJoseph Chan 2663c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2664c577b8a1SJoseph Chan 2665c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 266669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2667cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2668cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2669cb53c626STakashi Iwai #endif 2670f7278fd0SJosepch Chan return 0; 2671f7278fd0SJosepch Chan } 2672f7278fd0SJosepch Chan 2673f7278fd0SJosepch Chan /* capture mixer elements */ 267490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2675f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2676f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2677f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2678f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2679f7278fd0SJosepch Chan { 2680f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2681f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2682f7278fd0SJosepch Chan * So call somewhat different.. 2683f7278fd0SJosepch Chan */ 2684f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2685f7278fd0SJosepch Chan .name = "Input Source", 2686f7278fd0SJosepch Chan .count = 1, 2687f7278fd0SJosepch Chan .info = via_mux_enum_info, 2688f7278fd0SJosepch Chan .get = via_mux_enum_get, 2689f7278fd0SJosepch Chan .put = via_mux_enum_put, 2690f7278fd0SJosepch Chan }, 2691f7278fd0SJosepch Chan { } /* end */ 2692f7278fd0SJosepch Chan }; 2693f7278fd0SJosepch Chan /* 2694f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2695f7278fd0SJosepch Chan */ 269690dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2697f7278fd0SJosepch Chan /* 2698f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2699f7278fd0SJosepch Chan */ 2700f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2701f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2702f7278fd0SJosepch Chan 2703f7278fd0SJosepch Chan 2704f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2705f7278fd0SJosepch Chan * mixer widget 2706f7278fd0SJosepch Chan */ 2707f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2708f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2709f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2710f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2711f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2712f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2713f7278fd0SJosepch Chan 2714f7278fd0SJosepch Chan /* 2715f7278fd0SJosepch Chan * Set up output mixers 2716f7278fd0SJosepch Chan */ 2717f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2718f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2719f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2720f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2721f7278fd0SJosepch Chan 2722f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2723bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2724f7278fd0SJosepch Chan /* PW9 Output enable */ 2725f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2726f7278fd0SJosepch Chan /* PW10 Input enable */ 2727f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2728f7278fd0SJosepch Chan { } 2729f7278fd0SJosepch Chan }; 2730f7278fd0SJosepch Chan 273190dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2732f7278fd0SJosepch Chan /* 2733f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2734f7278fd0SJosepch Chan */ 2735f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2736f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2737f7278fd0SJosepch Chan 2738f7278fd0SJosepch Chan 2739f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2740f7278fd0SJosepch Chan * mixer widget 2741f7278fd0SJosepch Chan */ 2742f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2743f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2744f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2745f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2746f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2747f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2748f7278fd0SJosepch Chan 2749f7278fd0SJosepch Chan /* 2750f7278fd0SJosepch Chan * Set up output mixers 2751f7278fd0SJosepch Chan */ 2752f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2753f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2754f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2755f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2756f7278fd0SJosepch Chan 2757f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2758f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2759f7278fd0SJosepch Chan /* PW9 Output enable */ 2760f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2761f7278fd0SJosepch Chan /* PW10 Input enable */ 2762f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2763f7278fd0SJosepch Chan { } 2764f7278fd0SJosepch Chan }; 2765f7278fd0SJosepch Chan 276690dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2767a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2768a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2769a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2770a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2771a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2772a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2773a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2774a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2775a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 277669e52a80SHarald Welte { } 277769e52a80SHarald Welte }; 277869e52a80SHarald Welte 277917314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 278017314379SLydia Wang struct hda_codec *codec, 278117314379SLydia Wang struct snd_pcm_substream *substream) 278217314379SLydia Wang { 278317314379SLydia Wang int idle = substream->pstr->substream_opened == 1 278417314379SLydia Wang && substream->ref_count == 0; 278517314379SLydia Wang 278617314379SLydia Wang analog_low_current_mode(codec, idle); 278717314379SLydia Wang return 0; 278817314379SLydia Wang } 278917314379SLydia Wang 279090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 27910aa62aefSHarald Welte .substreams = 2, 2792f7278fd0SJosepch Chan .channels_min = 2, 2793f7278fd0SJosepch Chan .channels_max = 8, 2794f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2795f7278fd0SJosepch Chan .ops = { 2796f7278fd0SJosepch Chan .open = via_playback_pcm_open, 27970aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 279817314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 279917314379SLydia Wang .close = via_pcm_open_close 2800f7278fd0SJosepch Chan }, 2801f7278fd0SJosepch Chan }; 2802f7278fd0SJosepch Chan 280390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 28040aa62aefSHarald Welte .substreams = 2, 2805f7278fd0SJosepch Chan .channels_min = 2, 2806f7278fd0SJosepch Chan .channels_max = 4, 2807f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2808f7278fd0SJosepch Chan .ops = { 2809f7278fd0SJosepch Chan .open = via_playback_pcm_open, 28100aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 28110aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 2812f7278fd0SJosepch Chan }, 2813f7278fd0SJosepch Chan }; 2814f7278fd0SJosepch Chan 281590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_analog_capture = { 2816f7278fd0SJosepch Chan .substreams = 2, 2817f7278fd0SJosepch Chan .channels_min = 2, 2818f7278fd0SJosepch Chan .channels_max = 2, 2819f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 2820f7278fd0SJosepch Chan .ops = { 282117314379SLydia Wang .open = via_pcm_open_close, 2822f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 282317314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 282417314379SLydia Wang .close = via_pcm_open_close 2825f7278fd0SJosepch Chan }, 2826f7278fd0SJosepch Chan }; 2827f7278fd0SJosepch Chan 282890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_playback = { 2829f7278fd0SJosepch Chan .substreams = 1, 2830f7278fd0SJosepch Chan .channels_min = 2, 2831f7278fd0SJosepch Chan .channels_max = 2, 2832f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 2833f7278fd0SJosepch Chan .ops = { 2834f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 2835f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 28369da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 28379da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 2838f7278fd0SJosepch Chan }, 2839f7278fd0SJosepch Chan }; 2840f7278fd0SJosepch Chan 284190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { 2842f7278fd0SJosepch Chan .substreams = 1, 2843f7278fd0SJosepch Chan .channels_min = 2, 2844f7278fd0SJosepch Chan .channels_max = 2, 2845f7278fd0SJosepch Chan }; 2846f7278fd0SJosepch Chan 2847f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 2848f7278fd0SJosepch Chan { 2849f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 2850f7278fd0SJosepch Chan int err; 2851f7278fd0SJosepch Chan 2852f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2853f7278fd0SJosepch Chan if (err < 0) 2854f7278fd0SJosepch Chan return err; 2855*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2856f7278fd0SJosepch Chan if (err < 0) 2857f7278fd0SJosepch Chan return err; 2858f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2859f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 2860f7278fd0SJosepch Chan 2861*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2862f7278fd0SJosepch Chan if (err < 0) 2863f7278fd0SJosepch Chan return err; 2864*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2865f7278fd0SJosepch Chan if (err < 0) 2866f7278fd0SJosepch Chan return err; 2867620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2868f7278fd0SJosepch Chan if (err < 0) 2869f7278fd0SJosepch Chan return err; 2870f7278fd0SJosepch Chan 2871f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2872f7278fd0SJosepch Chan 28730852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2874f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 287555d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 2876f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 2877f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 2878f7278fd0SJosepch Chan 2879603c4019STakashi Iwai if (spec->kctls.list) 2880603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2881f7278fd0SJosepch Chan 28820aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 28830aa62aefSHarald Welte 2884f8fdd495SHarald Welte if (spec->hp_mux) 28853d83e577STakashi Iwai via_hp_build(codec); 2886f7278fd0SJosepch Chan 28875b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2888f7278fd0SJosepch Chan return 1; 2889f7278fd0SJosepch Chan } 2890f7278fd0SJosepch Chan 2891f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 289290dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2893f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2894f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2895f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2896f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2897f7278fd0SJosepch Chan { } /* end */ 2898f7278fd0SJosepch Chan }; 2899f7278fd0SJosepch Chan #endif 29003e95b9abSLydia Wang 29013e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 29023e95b9abSLydia Wang { 29033e95b9abSLydia Wang struct via_spec *spec = codec->spec; 29043e95b9abSLydia Wang int imux_is_smixer; 29053e95b9abSLydia Wang unsigned int parm; 29063e95b9abSLydia Wang int is_8ch = 0; 2907bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2908bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 29093e95b9abSLydia Wang is_8ch = 1; 29103e95b9abSLydia Wang 29113e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 29123e95b9abSLydia Wang imux_is_smixer = 29133e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 29143e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 29153e95b9abSLydia Wang /* inputs */ 29163e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 29173e95b9abSLydia Wang parm = AC_PWRST_D3; 29183e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29193e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29203e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29213e95b9abSLydia Wang if (imux_is_smixer) 29223e95b9abSLydia Wang parm = AC_PWRST_D0; 29233e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 29243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 29253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 29263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 29273e95b9abSLydia Wang 29283e95b9abSLydia Wang /* outputs */ 29293e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 29303e95b9abSLydia Wang parm = AC_PWRST_D3; 29313e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 29323e95b9abSLydia Wang if (spec->smart51_enabled) 29333e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 29353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 29363e95b9abSLydia Wang 29373e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 29383e95b9abSLydia Wang if (is_8ch) { 29393e95b9abSLydia Wang parm = AC_PWRST_D3; 29403e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 29413e95b9abSLydia Wang if (spec->smart51_enabled) 29423e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 29443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 29463e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2947bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2948bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2949bc92df7fSLydia Wang parm = AC_PWRST_D3; 2950bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2951bc92df7fSLydia Wang if (spec->smart51_enabled) 2952bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2953bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2954bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2955bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2956bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29573e95b9abSLydia Wang } 29583e95b9abSLydia Wang 29593e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 29603e95b9abSLydia Wang parm = AC_PWRST_D3; 29613e95b9abSLydia Wang /* force to D0 for internal Speaker */ 29623e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 29633e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 29643e95b9abSLydia Wang if (is_8ch) 29653e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 29663e95b9abSLydia Wang 29673e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 29683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 29693e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 29703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 29713e95b9abSLydia Wang if (is_8ch) { 29723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 29733e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 29753e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2976bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2977bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2978bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29793e95b9abSLydia Wang } 29803e95b9abSLydia Wang 2981518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2982f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2983f7278fd0SJosepch Chan { 2984f7278fd0SJosepch Chan struct via_spec *spec; 2985f7278fd0SJosepch Chan int err; 2986f7278fd0SJosepch Chan 2987518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2988518bf3baSLydia Wang return patch_vt1708S(codec); 2989f7278fd0SJosepch Chan /* create a codec specific record */ 29905b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2991f7278fd0SJosepch Chan if (spec == NULL) 2992f7278fd0SJosepch Chan return -ENOMEM; 2993f7278fd0SJosepch Chan 2994620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2995620e2b28STakashi Iwai 2996f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 2997f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 2998f7278fd0SJosepch Chan if (err < 0) { 2999f7278fd0SJosepch Chan via_free(codec); 3000f7278fd0SJosepch Chan return err; 3001f7278fd0SJosepch Chan } else if (!err) { 3002f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3003f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3004f7278fd0SJosepch Chan } 3005f7278fd0SJosepch Chan 300669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 300769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3008f7278fd0SJosepch Chan 3009f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3010f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3011f7278fd0SJosepch Chan 3012f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3013f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3014f7278fd0SJosepch Chan 3015a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3016f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3017f7278fd0SJosepch Chan spec->num_mixers++; 3018f7278fd0SJosepch Chan } 3019f7278fd0SJosepch Chan 3020f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3021f7278fd0SJosepch Chan 3022f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 302369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3024f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3025f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3026f7278fd0SJosepch Chan #endif 3027f7278fd0SJosepch Chan 30283e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 30293e95b9abSLydia Wang 3030f7278fd0SJosepch Chan return 0; 3031f7278fd0SJosepch Chan } 3032f7278fd0SJosepch Chan 3033f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3034f7278fd0SJosepch Chan { 3035f7278fd0SJosepch Chan struct via_spec *spec; 3036f7278fd0SJosepch Chan int err; 3037f7278fd0SJosepch Chan 3038f7278fd0SJosepch Chan /* create a codec specific record */ 30395b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3040f7278fd0SJosepch Chan if (spec == NULL) 3041f7278fd0SJosepch Chan return -ENOMEM; 3042f7278fd0SJosepch Chan 3043f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3044f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3045f7278fd0SJosepch Chan if (err < 0) { 3046f7278fd0SJosepch Chan via_free(codec); 3047f7278fd0SJosepch Chan return err; 3048f7278fd0SJosepch Chan } else if (!err) { 3049f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3050f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3051f7278fd0SJosepch Chan } 3052f7278fd0SJosepch Chan 305369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 305469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3055f7278fd0SJosepch Chan 3056f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3057f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3058f7278fd0SJosepch Chan 3059f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3060f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3061f7278fd0SJosepch Chan 3062a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3063f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3064f7278fd0SJosepch Chan spec->num_mixers++; 3065f7278fd0SJosepch Chan } 3066f7278fd0SJosepch Chan 3067f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3068f7278fd0SJosepch Chan 3069f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 307069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3071f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3072f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3073f7278fd0SJosepch Chan #endif 3074c577b8a1SJoseph Chan 30753e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 30763e95b9abSLydia Wang 3077c577b8a1SJoseph Chan return 0; 3078c577b8a1SJoseph Chan } 3079c577b8a1SJoseph Chan 3080d949cac1SHarald Welte /* Patch for VT1708S */ 3081d949cac1SHarald Welte 3082d949cac1SHarald Welte /* capture mixer elements */ 308390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3084d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3085d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3086d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3087d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 30886369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 30896369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 30906369bcfcSLydia Wang HDA_INPUT), 3091d949cac1SHarald Welte { 3092d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3093d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3094d949cac1SHarald Welte * So call somewhat different.. 3095d949cac1SHarald Welte */ 3096d949cac1SHarald Welte /* .name = "Capture Source", */ 3097d949cac1SHarald Welte .name = "Input Source", 3098d949cac1SHarald Welte .count = 1, 3099d949cac1SHarald Welte .info = via_mux_enum_info, 3100d949cac1SHarald Welte .get = via_mux_enum_get, 3101d949cac1SHarald Welte .put = via_mux_enum_put, 3102d949cac1SHarald Welte }, 3103d949cac1SHarald Welte { } /* end */ 3104d949cac1SHarald Welte }; 3105d949cac1SHarald Welte 310690dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3107d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3108d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3109d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3110d949cac1SHarald Welte 3111d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3112d949cac1SHarald Welte * analog-loopback mixer widget */ 3113d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3114d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3115d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3116d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3117d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3118d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3119d949cac1SHarald Welte 3120d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3121d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 31225691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3123d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 31245691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3125d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3126d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3127bc7e7e5cSLydia Wang /* don't bybass mixer */ 3128bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3129d949cac1SHarald Welte { } 3130d949cac1SHarald Welte }; 3131d949cac1SHarald Welte 313290dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3133a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3134a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3135a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3136a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3137a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3138a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3139a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3140a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3141a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 314269e52a80SHarald Welte { } 314369e52a80SHarald Welte }; 314469e52a80SHarald Welte 314590dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3146bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3147bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3148bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3149bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3150bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3151bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3152bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3153bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3154bc92df7fSLydia Wang { } 3155bc92df7fSLydia Wang }; 3156bc92df7fSLydia Wang 315790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3158d949cac1SHarald Welte .substreams = 2, 3159d949cac1SHarald Welte .channels_min = 2, 3160d949cac1SHarald Welte .channels_max = 8, 3161d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3162d949cac1SHarald Welte .ops = { 3163d949cac1SHarald Welte .open = via_playback_pcm_open, 3164c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3165c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 316617314379SLydia Wang .close = via_pcm_open_close 3167d949cac1SHarald Welte }, 3168d949cac1SHarald Welte }; 3169d949cac1SHarald Welte 317090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1705_pcm_analog_playback = { 3171bc92df7fSLydia Wang .substreams = 2, 3172bc92df7fSLydia Wang .channels_min = 2, 3173bc92df7fSLydia Wang .channels_max = 6, 3174bc92df7fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3175bc92df7fSLydia Wang .ops = { 3176bc92df7fSLydia Wang .open = via_playback_pcm_open, 3177bc92df7fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3178bc92df7fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3179bc92df7fSLydia Wang .close = via_pcm_open_close 3180bc92df7fSLydia Wang }, 3181bc92df7fSLydia Wang }; 3182bc92df7fSLydia Wang 318390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3184d949cac1SHarald Welte .substreams = 2, 3185d949cac1SHarald Welte .channels_min = 2, 3186d949cac1SHarald Welte .channels_max = 2, 3187d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3188d949cac1SHarald Welte .ops = { 318917314379SLydia Wang .open = via_pcm_open_close, 3190d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 319117314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 319217314379SLydia Wang .close = via_pcm_open_close 3193d949cac1SHarald Welte }, 3194d949cac1SHarald Welte }; 3195d949cac1SHarald Welte 319690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { 31979da29271STakashi Iwai .substreams = 1, 3198d949cac1SHarald Welte .channels_min = 2, 3199d949cac1SHarald Welte .channels_max = 2, 3200d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3201d949cac1SHarald Welte .ops = { 3202d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3203d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 32049da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 32059da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3206d949cac1SHarald Welte }, 3207d949cac1SHarald Welte }; 3208d949cac1SHarald Welte 32099da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 32109da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 32119da29271STakashi Iwai { 32129da29271STakashi Iwai struct via_spec *spec = codec->spec; 32139da29271STakashi Iwai int i; 32149da29271STakashi Iwai 32159da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 32169da29271STakashi Iwai hda_nid_t nid; 32179da29271STakashi Iwai int conn; 32189da29271STakashi Iwai 32199da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 32209da29271STakashi Iwai if (!nid) 32219da29271STakashi Iwai continue; 32229da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 32239da29271STakashi Iwai if (conn < 1) 32249da29271STakashi Iwai continue; 32259da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 32269da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 32279da29271STakashi Iwai else { 32289da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 32299da29271STakashi Iwai break; /* at most two dig outs */ 32309da29271STakashi Iwai } 32319da29271STakashi Iwai } 32329da29271STakashi Iwai } 32339da29271STakashi Iwai 3234d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3235d949cac1SHarald Welte { 3236d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3237d949cac1SHarald Welte int err; 3238d949cac1SHarald Welte 32399da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3240d949cac1SHarald Welte if (err < 0) 3241d949cac1SHarald Welte return err; 3242*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3243d949cac1SHarald Welte if (err < 0) 3244d949cac1SHarald Welte return err; 3245d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3246d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3247d949cac1SHarald Welte 3248*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3249d949cac1SHarald Welte if (err < 0) 3250d949cac1SHarald Welte return err; 3251*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3252d949cac1SHarald Welte if (err < 0) 3253d949cac1SHarald Welte return err; 3254620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3255d949cac1SHarald Welte if (err < 0) 3256d949cac1SHarald Welte return err; 3257d949cac1SHarald Welte 3258d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3259d949cac1SHarald Welte 32609da29271STakashi Iwai fill_dig_outs(codec); 326198aa34c0SHarald Welte 3262603c4019STakashi Iwai if (spec->kctls.list) 3263603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3264d949cac1SHarald Welte 32650aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 32660aa62aefSHarald Welte 3267f8fdd495SHarald Welte if (spec->hp_mux) 32683d83e577STakashi Iwai via_hp_build(codec); 3269d949cac1SHarald Welte 32705b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3271d949cac1SHarald Welte return 1; 3272d949cac1SHarald Welte } 3273d949cac1SHarald Welte 3274d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 327590dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3276d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3277d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3278d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3279d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3280d949cac1SHarald Welte { } /* end */ 3281d949cac1SHarald Welte }; 3282d949cac1SHarald Welte #endif 3283d949cac1SHarald Welte 32846369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 32856369bcfcSLydia Wang int offset, int num_steps, int step_size) 32866369bcfcSLydia Wang { 32876369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 32886369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 32896369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 32906369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 32916369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 32926369bcfcSLydia Wang } 32936369bcfcSLydia Wang 3294d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3295d949cac1SHarald Welte { 3296d949cac1SHarald Welte struct via_spec *spec; 3297d949cac1SHarald Welte int err; 3298d949cac1SHarald Welte 3299d949cac1SHarald Welte /* create a codec specific record */ 33005b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3301d949cac1SHarald Welte if (spec == NULL) 3302d949cac1SHarald Welte return -ENOMEM; 3303d949cac1SHarald Welte 3304620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3305620e2b28STakashi Iwai 3306d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3307d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3308d949cac1SHarald Welte if (err < 0) { 3309d949cac1SHarald Welte via_free(codec); 3310d949cac1SHarald Welte return err; 3311d949cac1SHarald Welte } else if (!err) { 3312d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3313d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3314d949cac1SHarald Welte } 3315d949cac1SHarald Welte 331669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3317bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3318bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3319bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3320bc92df7fSLydia Wang else 3321bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3322bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3323d949cac1SHarald Welte 3324bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3325bc92df7fSLydia Wang spec->stream_analog_playback = &vt1705_pcm_analog_playback; 3326bc92df7fSLydia Wang else 3327d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3328d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3329d949cac1SHarald Welte 3330d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3331d949cac1SHarald Welte 3332a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 33336369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 33346369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3335d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3336d949cac1SHarald Welte spec->num_mixers++; 3337d949cac1SHarald Welte } 3338d949cac1SHarald Welte 3339d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3340d949cac1SHarald Welte 3341d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 334269e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3343d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3344d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3345d949cac1SHarald Welte #endif 3346d949cac1SHarald Welte 3347518bf3baSLydia Wang /* correct names for VT1708BCE */ 3348518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3349518bf3baSLydia Wang kfree(codec->chip_name); 3350518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3351518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3352518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3353518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3354970f630fSLydia Wang } 3355bc92df7fSLydia Wang /* correct names for VT1705 */ 3356bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3357bc92df7fSLydia Wang kfree(codec->chip_name); 3358bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3359bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3360bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3361bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3362bc92df7fSLydia Wang } 33633e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3364d949cac1SHarald Welte return 0; 3365d949cac1SHarald Welte } 3366d949cac1SHarald Welte 3367d949cac1SHarald Welte /* Patch for VT1702 */ 3368d949cac1SHarald Welte 3369d949cac1SHarald Welte /* capture mixer elements */ 337090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = { 3371d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3372d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3373d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3374d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3375d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3376d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3377d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3378d949cac1SHarald Welte HDA_INPUT), 3379d949cac1SHarald Welte { 3380d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3381d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3382d949cac1SHarald Welte * So call somewhat different.. 3383d949cac1SHarald Welte */ 3384d949cac1SHarald Welte /* .name = "Capture Source", */ 3385d949cac1SHarald Welte .name = "Input Source", 3386d949cac1SHarald Welte .count = 1, 3387d949cac1SHarald Welte .info = via_mux_enum_info, 3388d949cac1SHarald Welte .get = via_mux_enum_get, 3389d949cac1SHarald Welte .put = via_mux_enum_put, 3390d949cac1SHarald Welte }, 3391d949cac1SHarald Welte { } /* end */ 3392d949cac1SHarald Welte }; 3393d949cac1SHarald Welte 339490dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3395d949cac1SHarald Welte /* 3396d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3397d949cac1SHarald Welte */ 3398d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3399d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3400d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3401d949cac1SHarald Welte 3402d949cac1SHarald Welte 3403d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3404d949cac1SHarald Welte * mixer widget 3405d949cac1SHarald Welte */ 3406d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3407d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3408d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3409d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3410d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3411d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3412d949cac1SHarald Welte 3413d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3414d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3415d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3416d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3417d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3418bc7e7e5cSLydia Wang /* mixer enable */ 3419bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3420bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3421bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3422d949cac1SHarald Welte { } 3423d949cac1SHarald Welte }; 3424d949cac1SHarald Welte 342590dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 3426a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3427a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3428a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3429a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3430a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3431a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 343269e52a80SHarald Welte { } 343369e52a80SHarald Welte }; 343469e52a80SHarald Welte 343590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_playback = { 34360aa62aefSHarald Welte .substreams = 2, 3437d949cac1SHarald Welte .channels_min = 2, 3438d949cac1SHarald Welte .channels_max = 2, 3439d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3440d949cac1SHarald Welte .ops = { 3441d949cac1SHarald Welte .open = via_playback_pcm_open, 34420aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 344317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 344417314379SLydia Wang .close = via_pcm_open_close 3445d949cac1SHarald Welte }, 3446d949cac1SHarald Welte }; 3447d949cac1SHarald Welte 344890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_capture = { 3449d949cac1SHarald Welte .substreams = 3, 3450d949cac1SHarald Welte .channels_min = 2, 3451d949cac1SHarald Welte .channels_max = 2, 3452d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 3453d949cac1SHarald Welte .ops = { 345417314379SLydia Wang .open = via_pcm_open_close, 3455d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 345617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 345717314379SLydia Wang .close = via_pcm_open_close 3458d949cac1SHarald Welte }, 3459d949cac1SHarald Welte }; 3460d949cac1SHarald Welte 346190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_digital_playback = { 34625691ec7fSHarald Welte .substreams = 2, 3463d949cac1SHarald Welte .channels_min = 2, 3464d949cac1SHarald Welte .channels_max = 2, 3465d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3466d949cac1SHarald Welte .ops = { 3467d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3468d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 34699da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 34709da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3471d949cac1SHarald Welte }, 3472d949cac1SHarald Welte }; 3473d949cac1SHarald Welte 3474d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 3475d949cac1SHarald Welte { 3476d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3477d949cac1SHarald Welte int err; 3478d949cac1SHarald Welte 34799da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3480d949cac1SHarald Welte if (err < 0) 3481d949cac1SHarald Welte return err; 3482*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3483d949cac1SHarald Welte if (err < 0) 3484d949cac1SHarald Welte return err; 3485d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3486d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3487d949cac1SHarald Welte 3488*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3489d949cac1SHarald Welte if (err < 0) 3490d949cac1SHarald Welte return err; 3491*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3492d949cac1SHarald Welte if (err < 0) 3493d949cac1SHarald Welte return err; 3494c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 3495c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 3496c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 3497c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 3498c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 3499c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 3500620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3501d949cac1SHarald Welte if (err < 0) 3502d949cac1SHarald Welte return err; 3503d949cac1SHarald Welte 3504d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3505d949cac1SHarald Welte 35069da29271STakashi Iwai fill_dig_outs(codec); 350798aa34c0SHarald Welte 3508603c4019STakashi Iwai if (spec->kctls.list) 3509603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3510d949cac1SHarald Welte 35110aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 35120aa62aefSHarald Welte 3513f8fdd495SHarald Welte if (spec->hp_mux) 35143d83e577STakashi Iwai via_hp_build(codec); 3515d949cac1SHarald Welte 3516d949cac1SHarald Welte return 1; 3517d949cac1SHarald Welte } 3518d949cac1SHarald Welte 3519d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 352090dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 3521d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 3522d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 3523d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 3524d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 3525d949cac1SHarald Welte { } /* end */ 3526d949cac1SHarald Welte }; 3527d949cac1SHarald Welte #endif 3528d949cac1SHarald Welte 35293e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 35303e95b9abSLydia Wang { 35313e95b9abSLydia Wang int imux_is_smixer = 35323e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 35333e95b9abSLydia Wang unsigned int parm; 35343e95b9abSLydia Wang /* inputs */ 35353e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 35363e95b9abSLydia Wang parm = AC_PWRST_D3; 35373e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 35383e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 35393e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 35403e95b9abSLydia Wang if (imux_is_smixer) 35413e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 35423e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 35433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 35443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 35453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 35463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 35473e95b9abSLydia Wang 35483e95b9abSLydia Wang /* outputs */ 35493e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 35503e95b9abSLydia Wang parm = AC_PWRST_D3; 35513e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 35523e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 35533e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 35543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 35553e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 35563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 35573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 35583e95b9abSLydia Wang } 35593e95b9abSLydia Wang 3560d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3561d949cac1SHarald Welte { 3562d949cac1SHarald Welte struct via_spec *spec; 3563d949cac1SHarald Welte int err; 3564d949cac1SHarald Welte 3565d949cac1SHarald Welte /* create a codec specific record */ 35665b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3567d949cac1SHarald Welte if (spec == NULL) 3568d949cac1SHarald Welte return -ENOMEM; 3569d949cac1SHarald Welte 3570620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 3571620e2b28STakashi Iwai 3572d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3573d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 3574d949cac1SHarald Welte if (err < 0) { 3575d949cac1SHarald Welte via_free(codec); 3576d949cac1SHarald Welte return err; 3577d949cac1SHarald Welte } else if (!err) { 3578d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3579d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3580d949cac1SHarald Welte } 3581d949cac1SHarald Welte 358269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 358369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 3584d949cac1SHarald Welte 3585d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 3586d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 3587d949cac1SHarald Welte 3588d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 3589d949cac1SHarald Welte 3590a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3591d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 3592d949cac1SHarald Welte spec->num_mixers++; 3593d949cac1SHarald Welte } 3594d949cac1SHarald Welte 3595d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3596d949cac1SHarald Welte 3597d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 359869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3599d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3600d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 3601d949cac1SHarald Welte #endif 3602d949cac1SHarald Welte 36033e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 3604d949cac1SHarald Welte return 0; 3605d949cac1SHarald Welte } 3606d949cac1SHarald Welte 3607eb7188caSLydia Wang /* Patch for VT1718S */ 3608eb7188caSLydia Wang 3609eb7188caSLydia Wang /* capture mixer elements */ 361090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { 3611eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 3612eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 3613eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 3614eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 3615eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 3616eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 3617eb7188caSLydia Wang HDA_INPUT), 3618eb7188caSLydia Wang { 3619eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3620eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 3621eb7188caSLydia Wang * So call somewhat different.. 3622eb7188caSLydia Wang */ 3623eb7188caSLydia Wang .name = "Input Source", 3624eb7188caSLydia Wang .count = 2, 3625eb7188caSLydia Wang .info = via_mux_enum_info, 3626eb7188caSLydia Wang .get = via_mux_enum_get, 3627eb7188caSLydia Wang .put = via_mux_enum_put, 3628eb7188caSLydia Wang }, 3629eb7188caSLydia Wang { } /* end */ 3630eb7188caSLydia Wang }; 3631eb7188caSLydia Wang 363290dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 3633eb7188caSLydia Wang /* 3634eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3635eb7188caSLydia Wang */ 3636eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3637eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3638eb7188caSLydia Wang 36394ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 36404ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 3641eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3642eb7188caSLydia Wang * mixer widget 3643eb7188caSLydia Wang */ 3644eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3645eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 3646eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3647eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 3648eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 36494ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 3650eb7188caSLydia Wang 3651eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 3652eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 3653eb7188caSLydia Wang /* PW9 PW10 Output enable */ 3654eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3655eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 3656eb7188caSLydia Wang /* PW11 Input enable */ 3657eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 3658eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 3659eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 3660eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 3661eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3662eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3663eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3664eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3665eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3666eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3667eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3668eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3669eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3670eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 3671eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 3672eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 3673eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 3674eb7188caSLydia Wang /* Unmute MW4's index 0 */ 3675eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3676eb7188caSLydia Wang { } 3677eb7188caSLydia Wang }; 3678eb7188caSLydia Wang 3679eb7188caSLydia Wang 368090dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 3681eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 3682eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3683eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3684eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3685eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3686eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3687eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3688eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3689eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3690eb7188caSLydia Wang { } 3691eb7188caSLydia Wang }; 3692eb7188caSLydia Wang 369390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { 3694eb7188caSLydia Wang .substreams = 2, 3695eb7188caSLydia Wang .channels_min = 2, 3696eb7188caSLydia Wang .channels_max = 10, 3697eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 3698eb7188caSLydia Wang .ops = { 3699eb7188caSLydia Wang .open = via_playback_pcm_open, 3700eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3701eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3702eb7188caSLydia Wang .close = via_pcm_open_close, 3703eb7188caSLydia Wang }, 3704eb7188caSLydia Wang }; 3705eb7188caSLydia Wang 370690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_capture = { 3707eb7188caSLydia Wang .substreams = 2, 3708eb7188caSLydia Wang .channels_min = 2, 3709eb7188caSLydia Wang .channels_max = 2, 3710eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3711eb7188caSLydia Wang .ops = { 3712eb7188caSLydia Wang .open = via_pcm_open_close, 3713eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 3714eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 3715eb7188caSLydia Wang .close = via_pcm_open_close, 3716eb7188caSLydia Wang }, 3717eb7188caSLydia Wang }; 3718eb7188caSLydia Wang 371990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_playback = { 3720eb7188caSLydia Wang .substreams = 2, 3721eb7188caSLydia Wang .channels_min = 2, 3722eb7188caSLydia Wang .channels_max = 2, 3723eb7188caSLydia Wang /* NID is set in via_build_pcms */ 3724eb7188caSLydia Wang .ops = { 3725eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 3726eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 3727eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 3728eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 3729eb7188caSLydia Wang }, 3730eb7188caSLydia Wang }; 3731eb7188caSLydia Wang 373290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { 3733eb7188caSLydia Wang .substreams = 1, 3734eb7188caSLydia Wang .channels_min = 2, 3735eb7188caSLydia Wang .channels_max = 2, 3736eb7188caSLydia Wang }; 3737eb7188caSLydia Wang 3738eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 3739eb7188caSLydia Wang { 3740eb7188caSLydia Wang struct via_spec *spec = codec->spec; 3741eb7188caSLydia Wang int err; 3742eb7188caSLydia Wang 3743eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3744eb7188caSLydia Wang 3745eb7188caSLydia Wang if (err < 0) 3746eb7188caSLydia Wang return err; 3747*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 3748eb7188caSLydia Wang if (err < 0) 3749eb7188caSLydia Wang return err; 3750eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3751eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 3752eb7188caSLydia Wang 3753*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 3754eb7188caSLydia Wang if (err < 0) 3755eb7188caSLydia Wang return err; 3756*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 3757eb7188caSLydia Wang if (err < 0) 3758eb7188caSLydia Wang return err; 3759620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 3760eb7188caSLydia Wang if (err < 0) 3761eb7188caSLydia Wang return err; 3762eb7188caSLydia Wang 3763eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3764eb7188caSLydia Wang 3765eb7188caSLydia Wang fill_dig_outs(codec); 3766eb7188caSLydia Wang 3767eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 3768eb7188caSLydia Wang spec->dig_in_nid = 0x13; 3769eb7188caSLydia Wang 3770eb7188caSLydia Wang if (spec->kctls.list) 3771eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 3772eb7188caSLydia Wang 3773eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 3774eb7188caSLydia Wang 3775eb7188caSLydia Wang if (spec->hp_mux) 37763d83e577STakashi Iwai via_hp_build(codec); 3777eb7188caSLydia Wang 37785b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3779eb7188caSLydia Wang 3780eb7188caSLydia Wang return 1; 3781eb7188caSLydia Wang } 3782eb7188caSLydia Wang 3783eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 378490dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 3785eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 3786eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 3787eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 3788eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 3789eb7188caSLydia Wang { } /* end */ 3790eb7188caSLydia Wang }; 3791eb7188caSLydia Wang #endif 3792eb7188caSLydia Wang 37933e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 37943e95b9abSLydia Wang { 37953e95b9abSLydia Wang struct via_spec *spec = codec->spec; 37963e95b9abSLydia Wang int imux_is_smixer; 37973e95b9abSLydia Wang unsigned int parm; 37983e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 37993e95b9abSLydia Wang imux_is_smixer = 38003e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 38013e95b9abSLydia Wang /* inputs */ 38023e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 38033e95b9abSLydia Wang parm = AC_PWRST_D3; 38043e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 38053e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 38063e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 38073e95b9abSLydia Wang if (imux_is_smixer) 38083e95b9abSLydia Wang parm = AC_PWRST_D0; 38093e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 38103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 38113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 38123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 38133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 38143e95b9abSLydia Wang 38153e95b9abSLydia Wang /* outputs */ 38163e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 38173e95b9abSLydia Wang parm = AC_PWRST_D3; 38183e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 38193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 38203e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 38213e95b9abSLydia Wang 38223e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 38233e95b9abSLydia Wang parm = AC_PWRST_D3; 38243e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 38253e95b9abSLydia Wang if (spec->smart51_enabled) 38263e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 38273e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 38283e95b9abSLydia Wang 38293e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 38303e95b9abSLydia Wang parm = AC_PWRST_D3; 38313e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 38323e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 38333e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 38343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 38353e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 38363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 38373e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 38383e95b9abSLydia Wang 38393e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 38403e95b9abSLydia Wang parm = AC_PWRST_D3; 38413e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 38423e95b9abSLydia Wang if (spec->smart51_enabled) 38433e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 38443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 38453e95b9abSLydia Wang 38463e95b9abSLydia Wang if (spec->hp_independent_mode) { 38473e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 38483e95b9abSLydia Wang parm = AC_PWRST_D3; 38493e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 38503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 38513e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 38523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 38533e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 38543e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 38553e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 38563e95b9abSLydia Wang } 38573e95b9abSLydia Wang } 38583e95b9abSLydia Wang 3859eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 3860eb7188caSLydia Wang { 3861eb7188caSLydia Wang struct via_spec *spec; 3862eb7188caSLydia Wang int err; 3863eb7188caSLydia Wang 3864eb7188caSLydia Wang /* create a codec specific record */ 38655b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3866eb7188caSLydia Wang if (spec == NULL) 3867eb7188caSLydia Wang return -ENOMEM; 3868eb7188caSLydia Wang 3869620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3870620e2b28STakashi Iwai 3871eb7188caSLydia Wang /* automatic parse from the BIOS config */ 3872eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 3873eb7188caSLydia Wang if (err < 0) { 3874eb7188caSLydia Wang via_free(codec); 3875eb7188caSLydia Wang return err; 3876eb7188caSLydia Wang } else if (!err) { 3877eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 3878eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 3879eb7188caSLydia Wang } 3880eb7188caSLydia Wang 3881eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 3882eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 3883eb7188caSLydia Wang 3884eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 3885eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 3886eb7188caSLydia Wang 3887eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 3888bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 3889eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 3890eb7188caSLydia Wang 3891a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3892bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 3893bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 3894eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 3895eb7188caSLydia Wang spec->num_mixers++; 3896eb7188caSLydia Wang } 3897eb7188caSLydia Wang 3898eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 3899eb7188caSLydia Wang 3900eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 39010f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 3902eb7188caSLydia Wang 3903eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3904eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 3905eb7188caSLydia Wang #endif 3906eb7188caSLydia Wang 39073e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 39083e95b9abSLydia Wang 3909eb7188caSLydia Wang return 0; 3910eb7188caSLydia Wang } 3911f3db423dSLydia Wang 3912f3db423dSLydia Wang /* Patch for VT1716S */ 3913f3db423dSLydia Wang 3914f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 3915f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 3916f3db423dSLydia Wang { 3917f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 3918f3db423dSLydia Wang uinfo->count = 1; 3919f3db423dSLydia Wang uinfo->value.integer.min = 0; 3920f3db423dSLydia Wang uinfo->value.integer.max = 1; 3921f3db423dSLydia Wang return 0; 3922f3db423dSLydia Wang } 3923f3db423dSLydia Wang 3924f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 3925f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3926f3db423dSLydia Wang { 3927f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3928f3db423dSLydia Wang int index = 0; 3929f3db423dSLydia Wang 3930f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 3931f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 3932f3db423dSLydia Wang if (index != -1) 3933f3db423dSLydia Wang *ucontrol->value.integer.value = index; 3934f3db423dSLydia Wang 3935f3db423dSLydia Wang return 0; 3936f3db423dSLydia Wang } 3937f3db423dSLydia Wang 3938f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 3939f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3940f3db423dSLydia Wang { 3941f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3942f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3943f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 3944f3db423dSLydia Wang 3945f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 3946f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 3947f3db423dSLydia Wang spec->dmic_enabled = index; 39483e95b9abSLydia Wang set_widgets_power_state(codec); 3949f3db423dSLydia Wang return 1; 3950f3db423dSLydia Wang } 3951f3db423dSLydia Wang 3952f3db423dSLydia Wang /* capture mixer elements */ 395390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { 3954f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3955f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3956f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3957f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3958f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 3959f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 3960f3db423dSLydia Wang HDA_INPUT), 3961f3db423dSLydia Wang { 3962f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3963f3db423dSLydia Wang .name = "Input Source", 3964f3db423dSLydia Wang .count = 1, 3965f3db423dSLydia Wang .info = via_mux_enum_info, 3966f3db423dSLydia Wang .get = via_mux_enum_get, 3967f3db423dSLydia Wang .put = via_mux_enum_put, 3968f3db423dSLydia Wang }, 3969f3db423dSLydia Wang { } /* end */ 3970f3db423dSLydia Wang }; 3971f3db423dSLydia Wang 397290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 3973f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 3974f3db423dSLydia Wang { 3975f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3976f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 39775b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 3978f3db423dSLydia Wang .count = 1, 3979f3db423dSLydia Wang .info = vt1716s_dmic_info, 3980f3db423dSLydia Wang .get = vt1716s_dmic_get, 3981f3db423dSLydia Wang .put = vt1716s_dmic_put, 3982f3db423dSLydia Wang }, 3983f3db423dSLydia Wang {} /* end */ 3984f3db423dSLydia Wang }; 3985f3db423dSLydia Wang 3986f3db423dSLydia Wang 3987f3db423dSLydia Wang /* mono-out mixer elements */ 398890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3989f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3990f3db423dSLydia Wang { } /* end */ 3991f3db423dSLydia Wang }; 3992f3db423dSLydia Wang 399390dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 3994f3db423dSLydia Wang /* 3995f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 3996f3db423dSLydia Wang */ 3997f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3998f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3999f3db423dSLydia Wang 4000f3db423dSLydia Wang 4001f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4002f3db423dSLydia Wang * mixer widget 4003f3db423dSLydia Wang */ 4004f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4005f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4006f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4007f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4008f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4009f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4010f3db423dSLydia Wang 4011f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4012f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4013f3db423dSLydia Wang 4014f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4015f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4016f3db423dSLydia Wang 4017f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4018f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4019f3db423dSLydia Wang 4020f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4021f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4022f3db423dSLydia Wang 4023f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4024f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4025f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4026f3db423dSLydia Wang 4027f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4028f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4029f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4030f3db423dSLydia Wang /* PW12 Output enable */ 4031f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4032f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4033f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4034f3db423dSLydia Wang /* don't bybass mixer */ 4035f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4036f3db423dSLydia Wang /* Enable mono output */ 4037f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4038f3db423dSLydia Wang { } 4039f3db423dSLydia Wang }; 4040f3db423dSLydia Wang 4041f3db423dSLydia Wang 404290dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 4043f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4044f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4045f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4046f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4047f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4048f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4049f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4050f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4051f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4052f3db423dSLydia Wang { } 4053f3db423dSLydia Wang }; 4054f3db423dSLydia Wang 405590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4056f3db423dSLydia Wang .substreams = 2, 4057f3db423dSLydia Wang .channels_min = 2, 4058f3db423dSLydia Wang .channels_max = 6, 4059f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4060f3db423dSLydia Wang .ops = { 4061f3db423dSLydia Wang .open = via_playback_pcm_open, 4062f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4063f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4064f3db423dSLydia Wang .close = via_pcm_open_close, 4065f3db423dSLydia Wang }, 4066f3db423dSLydia Wang }; 4067f3db423dSLydia Wang 406890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4069f3db423dSLydia Wang .substreams = 2, 4070f3db423dSLydia Wang .channels_min = 2, 4071f3db423dSLydia Wang .channels_max = 2, 4072f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4073f3db423dSLydia Wang .ops = { 4074f3db423dSLydia Wang .open = via_pcm_open_close, 4075f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4076f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4077f3db423dSLydia Wang .close = via_pcm_open_close, 4078f3db423dSLydia Wang }, 4079f3db423dSLydia Wang }; 4080f3db423dSLydia Wang 408190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4082f3db423dSLydia Wang .substreams = 2, 4083f3db423dSLydia Wang .channels_min = 2, 4084f3db423dSLydia Wang .channels_max = 2, 4085f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4086f3db423dSLydia Wang .ops = { 4087f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4088f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4089f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4090f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4091f3db423dSLydia Wang }, 4092f3db423dSLydia Wang }; 4093f3db423dSLydia Wang 4094f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 4095f3db423dSLydia Wang { 4096f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4097f3db423dSLydia Wang int err; 4098f3db423dSLydia Wang 4099f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4100f3db423dSLydia Wang if (err < 0) 4101f3db423dSLydia Wang return err; 4102*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 4103f3db423dSLydia Wang if (err < 0) 4104f3db423dSLydia Wang return err; 4105f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4106f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 4107f3db423dSLydia Wang 4108*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 4109f3db423dSLydia Wang if (err < 0) 4110f3db423dSLydia Wang return err; 4111*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 4112f3db423dSLydia Wang if (err < 0) 4113f3db423dSLydia Wang return err; 4114620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 4115f3db423dSLydia Wang if (err < 0) 4116f3db423dSLydia Wang return err; 4117f3db423dSLydia Wang 4118f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4119f3db423dSLydia Wang 4120f3db423dSLydia Wang fill_dig_outs(codec); 4121f3db423dSLydia Wang 4122f3db423dSLydia Wang if (spec->kctls.list) 4123f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4124f3db423dSLydia Wang 4125f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 4126f3db423dSLydia Wang 4127f3db423dSLydia Wang if (spec->hp_mux) 41283d83e577STakashi Iwai via_hp_build(codec); 4129f3db423dSLydia Wang 41305b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4131f3db423dSLydia Wang 4132f3db423dSLydia Wang return 1; 4133f3db423dSLydia Wang } 4134f3db423dSLydia Wang 4135f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 413690dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 4137f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 4138f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 4139f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 4140f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 4141f3db423dSLydia Wang { } /* end */ 4142f3db423dSLydia Wang }; 4143f3db423dSLydia Wang #endif 4144f3db423dSLydia Wang 41453e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 41463e95b9abSLydia Wang { 41473e95b9abSLydia Wang struct via_spec *spec = codec->spec; 41483e95b9abSLydia Wang int imux_is_smixer; 41493e95b9abSLydia Wang unsigned int parm; 41503e95b9abSLydia Wang unsigned int mono_out, present; 41513e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 41523e95b9abSLydia Wang imux_is_smixer = 41533e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 41543e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 41553e95b9abSLydia Wang /* inputs */ 41563e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 41573e95b9abSLydia Wang parm = AC_PWRST_D3; 41583e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 41593e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 41603e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 41613e95b9abSLydia Wang if (imux_is_smixer) 41623e95b9abSLydia Wang parm = AC_PWRST_D0; 41633e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 41643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 41653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 41663e95b9abSLydia Wang 41673e95b9abSLydia Wang parm = AC_PWRST_D3; 41683e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 41693e95b9abSLydia Wang /* PW11 (22h) */ 41703e95b9abSLydia Wang if (spec->dmic_enabled) 41713e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 41723e95b9abSLydia Wang else 41733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 41743e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 41753e95b9abSLydia Wang 41763e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 41773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 41783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 41793e95b9abSLydia Wang 41803e95b9abSLydia Wang /* outputs */ 41813e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 41823e95b9abSLydia Wang parm = AC_PWRST_D3; 41833e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 41843e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 41853e95b9abSLydia Wang if (spec->smart51_enabled) 41863e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 41873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 41883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 41893e95b9abSLydia Wang 41903e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 41913e95b9abSLydia Wang parm = AC_PWRST_D3; 41923e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 41933e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 41943e95b9abSLydia Wang if (spec->smart51_enabled) 41953e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 41963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 41973e95b9abSLydia Wang 41983e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 41993e95b9abSLydia Wang if (spec->smart51_enabled) 42003e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 42013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 42023e95b9abSLydia Wang 42033e95b9abSLydia Wang /* Mono out */ 42043e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 42053e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 42063e95b9abSLydia Wang 42073e95b9abSLydia Wang if (present) 42083e95b9abSLydia Wang mono_out = 0; 42093e95b9abSLydia Wang else { 42103e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 42113e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 42123e95b9abSLydia Wang mono_out = 0; 42133e95b9abSLydia Wang else 42143e95b9abSLydia Wang mono_out = 1; 42153e95b9abSLydia Wang } 42163e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 42173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 42183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 42193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 42203e95b9abSLydia Wang 42213e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 42223e95b9abSLydia Wang parm = AC_PWRST_D3; 42233e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 42243e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 42253e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 42263e95b9abSLydia Wang if (spec->hp_independent_mode) 42273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 42283e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 42293e95b9abSLydia Wang 42303e95b9abSLydia Wang /* force to D0 for internal Speaker */ 42313e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 42323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 42333e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 42343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 42353e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 42363e95b9abSLydia Wang } 42373e95b9abSLydia Wang 4238f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 4239f3db423dSLydia Wang { 4240f3db423dSLydia Wang struct via_spec *spec; 4241f3db423dSLydia Wang int err; 4242f3db423dSLydia Wang 4243f3db423dSLydia Wang /* create a codec specific record */ 42445b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4245f3db423dSLydia Wang if (spec == NULL) 4246f3db423dSLydia Wang return -ENOMEM; 4247f3db423dSLydia Wang 4248620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 4249620e2b28STakashi Iwai 4250f3db423dSLydia Wang /* automatic parse from the BIOS config */ 4251f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 4252f3db423dSLydia Wang if (err < 0) { 4253f3db423dSLydia Wang via_free(codec); 4254f3db423dSLydia Wang return err; 4255f3db423dSLydia Wang } else if (!err) { 4256f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4257f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 4258f3db423dSLydia Wang } 4259f3db423dSLydia Wang 4260f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 4261f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 4262f3db423dSLydia Wang 4263f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 4264f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 4265f3db423dSLydia Wang 4266f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 4267f3db423dSLydia Wang 4268a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4269f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 4270f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4271f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 4272f3db423dSLydia Wang spec->num_mixers++; 4273f3db423dSLydia Wang } 4274f3db423dSLydia Wang 4275f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 4276f3db423dSLydia Wang spec->num_mixers++; 4277f3db423dSLydia Wang 4278f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 4279f3db423dSLydia Wang 4280f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 4281f3db423dSLydia Wang 4282f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 42830f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4284f3db423dSLydia Wang 4285f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4286f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 4287f3db423dSLydia Wang #endif 4288f3db423dSLydia Wang 42893e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 4290f3db423dSLydia Wang return 0; 4291f3db423dSLydia Wang } 429225eaba2fSLydia Wang 429325eaba2fSLydia Wang /* for vt2002P */ 429425eaba2fSLydia Wang 429525eaba2fSLydia Wang /* capture mixer elements */ 429690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { 429725eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 429825eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 429925eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 430025eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 430125eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 430225eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 430325eaba2fSLydia Wang HDA_INPUT), 430425eaba2fSLydia Wang { 430525eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 430625eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 430725eaba2fSLydia Wang * So call somewhat different.. 430825eaba2fSLydia Wang */ 430925eaba2fSLydia Wang /* .name = "Capture Source", */ 431025eaba2fSLydia Wang .name = "Input Source", 431125eaba2fSLydia Wang .count = 2, 431225eaba2fSLydia Wang .info = via_mux_enum_info, 431325eaba2fSLydia Wang .get = via_mux_enum_get, 431425eaba2fSLydia Wang .put = via_mux_enum_put, 431525eaba2fSLydia Wang }, 431625eaba2fSLydia Wang { } /* end */ 431725eaba2fSLydia Wang }; 431825eaba2fSLydia Wang 431990dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 4320eadb9a80SLydia Wang /* Class-D speaker related verbs */ 4321eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 4322eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 4323eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 432425eaba2fSLydia Wang /* 432525eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 432625eaba2fSLydia Wang */ 432725eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 432825eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 432925eaba2fSLydia Wang 433025eaba2fSLydia Wang 433125eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 433225eaba2fSLydia Wang * mixer widget 433325eaba2fSLydia Wang */ 433425eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 433525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 433625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 433725eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 433825eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 433925eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 434025eaba2fSLydia Wang 434125eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 434225eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 434325eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 434425eaba2fSLydia Wang 434525eaba2fSLydia Wang /* PW9 Output enable */ 434625eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 434725eaba2fSLydia Wang 434825eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 434925eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 435025eaba2fSLydia Wang 435125eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 435225eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 435325eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 435425eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 435525eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 435625eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 435725eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 435825eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 435925eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 436025eaba2fSLydia Wang 436125eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 436225eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 436325eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 436425eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 436525eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 436625eaba2fSLydia Wang 436725eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 436825eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 436925eaba2fSLydia Wang 437025eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 437125eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 437225eaba2fSLydia Wang { } 437325eaba2fSLydia Wang }; 437490dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 437511890956SLydia Wang /* 437611890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 437711890956SLydia Wang */ 437811890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 437911890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 438011890956SLydia Wang 438111890956SLydia Wang 438211890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 438311890956SLydia Wang * mixer widget 438411890956SLydia Wang */ 438511890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 438611890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 438711890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 438811890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 438911890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 439011890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 439111890956SLydia Wang 439211890956SLydia Wang /* MUX Indices: Mic = 0 */ 439311890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 439411890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 439511890956SLydia Wang 439611890956SLydia Wang /* PW9 Output enable */ 439711890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 439811890956SLydia Wang 439911890956SLydia Wang /* Enable Boost Volume backdoor */ 440011890956SLydia Wang {0x1, 0xfb9, 0x24}, 440111890956SLydia Wang 440211890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 440311890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 440411890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 440511890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 440611890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 440711890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 440811890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 440911890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 441011890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 441111890956SLydia Wang 441211890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 441311890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 441411890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 441511890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 441611890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 441711890956SLydia Wang 441811890956SLydia Wang /* set PW0 index=0 (MW0) */ 441911890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 442011890956SLydia Wang 442111890956SLydia Wang /* Enable AOW0 to MW9 */ 442211890956SLydia Wang {0x1, 0xfb8, 0x88}, 442311890956SLydia Wang { } 442411890956SLydia Wang }; 442525eaba2fSLydia Wang 442625eaba2fSLydia Wang 442790dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 442825eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 442925eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 443025eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 443125eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 443225eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 443325eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 443425eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 443525eaba2fSLydia Wang { } 443625eaba2fSLydia Wang }; 443790dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 443811890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 443911890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 444011890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 444111890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 444211890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 444311890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 444411890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 444511890956SLydia Wang { } 444611890956SLydia Wang }; 444725eaba2fSLydia Wang 444890dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_playback = { 444925eaba2fSLydia Wang .substreams = 2, 445025eaba2fSLydia Wang .channels_min = 2, 445125eaba2fSLydia Wang .channels_max = 2, 445225eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 445325eaba2fSLydia Wang .ops = { 445425eaba2fSLydia Wang .open = via_playback_pcm_open, 445525eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 445625eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 445725eaba2fSLydia Wang .close = via_pcm_open_close, 445825eaba2fSLydia Wang }, 445925eaba2fSLydia Wang }; 446025eaba2fSLydia Wang 446190dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_capture = { 446225eaba2fSLydia Wang .substreams = 2, 446325eaba2fSLydia Wang .channels_min = 2, 446425eaba2fSLydia Wang .channels_max = 2, 446525eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 446625eaba2fSLydia Wang .ops = { 446725eaba2fSLydia Wang .open = via_pcm_open_close, 446825eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 446925eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 447025eaba2fSLydia Wang .close = via_pcm_open_close, 447125eaba2fSLydia Wang }, 447225eaba2fSLydia Wang }; 447325eaba2fSLydia Wang 447490dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { 447525eaba2fSLydia Wang .substreams = 1, 447625eaba2fSLydia Wang .channels_min = 2, 447725eaba2fSLydia Wang .channels_max = 2, 447825eaba2fSLydia Wang /* NID is set in via_build_pcms */ 447925eaba2fSLydia Wang .ops = { 448025eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 448125eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 448225eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 448325eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 448425eaba2fSLydia Wang }, 448525eaba2fSLydia Wang }; 448625eaba2fSLydia Wang 448725eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 448825eaba2fSLydia Wang { 448925eaba2fSLydia Wang struct via_spec *spec = codec->spec; 449025eaba2fSLydia Wang int err; 449125eaba2fSLydia Wang 449225eaba2fSLydia Wang 449325eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 449425eaba2fSLydia Wang if (err < 0) 449525eaba2fSLydia Wang return err; 449625eaba2fSLydia Wang 4497*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 449825eaba2fSLydia Wang if (err < 0) 449925eaba2fSLydia Wang return err; 450025eaba2fSLydia Wang 450125eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 450225eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 450325eaba2fSLydia Wang 4504*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 450525eaba2fSLydia Wang if (err < 0) 450625eaba2fSLydia Wang return err; 4507*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 450825eaba2fSLydia Wang if (err < 0) 450925eaba2fSLydia Wang return err; 4510620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 451125eaba2fSLydia Wang if (err < 0) 451225eaba2fSLydia Wang return err; 451325eaba2fSLydia Wang 451425eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 451525eaba2fSLydia Wang 451625eaba2fSLydia Wang fill_dig_outs(codec); 451725eaba2fSLydia Wang 451825eaba2fSLydia Wang if (spec->kctls.list) 451925eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 452025eaba2fSLydia Wang 452125eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 452225eaba2fSLydia Wang 452325eaba2fSLydia Wang if (spec->hp_mux) 45243d83e577STakashi Iwai via_hp_build(codec); 452525eaba2fSLydia Wang 452625eaba2fSLydia Wang return 1; 452725eaba2fSLydia Wang } 452825eaba2fSLydia Wang 452925eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 453090dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 453125eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 453225eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 453325eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 453425eaba2fSLydia Wang { } /* end */ 453525eaba2fSLydia Wang }; 453625eaba2fSLydia Wang #endif 453725eaba2fSLydia Wang 45383e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 45393e95b9abSLydia Wang { 45403e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45413e95b9abSLydia Wang int imux_is_smixer; 45423e95b9abSLydia Wang unsigned int parm; 45433e95b9abSLydia Wang unsigned int present; 45443e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 45453e95b9abSLydia Wang imux_is_smixer = 45463e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 45473e95b9abSLydia Wang /* inputs */ 45483e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45493e95b9abSLydia Wang parm = AC_PWRST_D3; 45503e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45513e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45523e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45533e95b9abSLydia Wang parm = AC_PWRST_D0; 45543e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 45553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45593e95b9abSLydia Wang 45603e95b9abSLydia Wang /* outputs */ 45613e95b9abSLydia Wang /* AOW0 (8h)*/ 45623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 45633e95b9abSLydia Wang 456411890956SLydia Wang if (spec->codec_type == VT1802) { 456511890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 456611890956SLydia Wang parm = AC_PWRST_D3; 456711890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 456811890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 456911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 457011890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 457111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 457211890956SLydia Wang } else { 45733e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 45743e95b9abSLydia Wang parm = AC_PWRST_D3; 45753e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 45763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 45773e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 45783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 45793e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 458011890956SLydia Wang } 45813e95b9abSLydia Wang 458211890956SLydia Wang if (spec->codec_type == VT1802) { 458311890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 458411890956SLydia Wang parm = AC_PWRST_D3; 458511890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 458611890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 458711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 458811890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 458911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 459011890956SLydia Wang } else { 45913e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 45923e95b9abSLydia Wang parm = AC_PWRST_D3; 45933e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 45943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 45953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 45963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 45973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 459811890956SLydia Wang } 45993e95b9abSLydia Wang 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 /* Class-D */ 46053e95b9abSLydia Wang /* PW0 (24h), MW0(18h/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 parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 461111890956SLydia Wang if (spec->codec_type == VT1802) 461211890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 461311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 461411890956SLydia Wang else 46153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 46163e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 46183e95b9abSLydia Wang 46193e95b9abSLydia Wang /* Mono Out */ 46203e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 46213e95b9abSLydia Wang 46223e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 462311890956SLydia Wang if (spec->codec_type == VT1802) { 462411890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 462511890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 462611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 462711890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 462811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 462911890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 463011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 463111890956SLydia Wang } else { 46323e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 46333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 46343e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 46363e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 46383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 463911890956SLydia Wang } 46403e95b9abSLydia Wang /* MW9 (21h) */ 46413e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 46423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 46433e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 46443e95b9abSLydia Wang else 46453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 46463e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 46473e95b9abSLydia Wang } 464825eaba2fSLydia Wang 464925eaba2fSLydia Wang /* patch for vt2002P */ 465025eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 465125eaba2fSLydia Wang { 465225eaba2fSLydia Wang struct via_spec *spec; 465325eaba2fSLydia Wang int err; 465425eaba2fSLydia Wang 465525eaba2fSLydia Wang /* create a codec specific record */ 46565b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 465725eaba2fSLydia Wang if (spec == NULL) 465825eaba2fSLydia Wang return -ENOMEM; 465925eaba2fSLydia Wang 4660620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4661620e2b28STakashi Iwai 466225eaba2fSLydia Wang /* automatic parse from the BIOS config */ 466325eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 466425eaba2fSLydia Wang if (err < 0) { 466525eaba2fSLydia Wang via_free(codec); 466625eaba2fSLydia Wang return err; 466725eaba2fSLydia Wang } else if (!err) { 466825eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 466925eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 467025eaba2fSLydia Wang } 467125eaba2fSLydia Wang 467211890956SLydia Wang if (spec->codec_type == VT1802) 467311890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 467411890956SLydia Wang vt1802_volume_init_verbs; 467511890956SLydia Wang else 467611890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 467711890956SLydia Wang vt2002P_volume_init_verbs; 467825eaba2fSLydia Wang 467911890956SLydia Wang if (spec->codec_type == VT1802) 468011890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 468111890956SLydia Wang vt1802_uniwill_init_verbs; 468211890956SLydia Wang else 468311890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 468411890956SLydia Wang vt2002P_uniwill_init_verbs; 468511890956SLydia Wang 468625eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 468725eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 468825eaba2fSLydia Wang 468925eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 469025eaba2fSLydia Wang 4691a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 469225eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 469325eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 469425eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 469525eaba2fSLydia Wang spec->num_mixers++; 469625eaba2fSLydia Wang } 469725eaba2fSLydia Wang 469825eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 469925eaba2fSLydia Wang 470025eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 47010f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 470225eaba2fSLydia Wang 470325eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 470425eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 470525eaba2fSLydia Wang #endif 470625eaba2fSLydia Wang 47073e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 470825eaba2fSLydia Wang return 0; 470925eaba2fSLydia Wang } 4710ab6734e7SLydia Wang 4711ab6734e7SLydia Wang /* for vt1812 */ 4712ab6734e7SLydia Wang 4713ab6734e7SLydia Wang /* capture mixer elements */ 471490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = { 4715ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4716ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4717ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4718ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4719ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4720ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 4721ab6734e7SLydia Wang HDA_INPUT), 4722ab6734e7SLydia Wang { 4723ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4724ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4725ab6734e7SLydia Wang * So call somewhat different.. 4726ab6734e7SLydia Wang */ 4727ab6734e7SLydia Wang .name = "Input Source", 4728ab6734e7SLydia Wang .count = 2, 4729ab6734e7SLydia Wang .info = via_mux_enum_info, 4730ab6734e7SLydia Wang .get = via_mux_enum_get, 4731ab6734e7SLydia Wang .put = via_mux_enum_put, 4732ab6734e7SLydia Wang }, 4733ab6734e7SLydia Wang { } /* end */ 4734ab6734e7SLydia Wang }; 4735ab6734e7SLydia Wang 473690dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 4737ab6734e7SLydia Wang /* 4738ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4739ab6734e7SLydia Wang */ 4740ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4741ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4742ab6734e7SLydia Wang 4743ab6734e7SLydia Wang 4744ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4745ab6734e7SLydia Wang * mixer widget 4746ab6734e7SLydia Wang */ 4747ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4748ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4749ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4750ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4751ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4752ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4753ab6734e7SLydia Wang 4754ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 4755ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 4756ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 4757ab6734e7SLydia Wang 4758ab6734e7SLydia Wang /* PW9 Output enable */ 4759ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4760ab6734e7SLydia Wang 4761ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 4762ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 4763ab6734e7SLydia Wang 4764ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 4765ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4766ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4767ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4768ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4769ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4770ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4771ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4772ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4773ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4774ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4775ab6734e7SLydia Wang 4776ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 4777ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 4778ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 4779ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 4780ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 4781ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 4782ab6734e7SLydia Wang 4783ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 4784ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 4785ab6734e7SLydia Wang { } 4786ab6734e7SLydia Wang }; 4787ab6734e7SLydia Wang 4788ab6734e7SLydia Wang 478990dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 4790ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 4791ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4792ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 4793ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4794ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 4795ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4796ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4797ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4798ab6734e7SLydia Wang { } 4799ab6734e7SLydia Wang }; 4800ab6734e7SLydia Wang 480190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_playback = { 4802ab6734e7SLydia Wang .substreams = 2, 4803ab6734e7SLydia Wang .channels_min = 2, 4804ab6734e7SLydia Wang .channels_max = 2, 4805ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4806ab6734e7SLydia Wang .ops = { 4807ab6734e7SLydia Wang .open = via_playback_pcm_open, 4808ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 4809ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4810ab6734e7SLydia Wang .close = via_pcm_open_close, 4811ab6734e7SLydia Wang }, 4812ab6734e7SLydia Wang }; 4813ab6734e7SLydia Wang 481490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_capture = { 4815ab6734e7SLydia Wang .substreams = 2, 4816ab6734e7SLydia Wang .channels_min = 2, 4817ab6734e7SLydia Wang .channels_max = 2, 4818ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4819ab6734e7SLydia Wang .ops = { 4820ab6734e7SLydia Wang .open = via_pcm_open_close, 4821ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 4822ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 4823ab6734e7SLydia Wang .close = via_pcm_open_close, 4824ab6734e7SLydia Wang }, 4825ab6734e7SLydia Wang }; 4826ab6734e7SLydia Wang 482790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_digital_playback = { 4828ab6734e7SLydia Wang .substreams = 1, 4829ab6734e7SLydia Wang .channels_min = 2, 4830ab6734e7SLydia Wang .channels_max = 2, 4831ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 4832ab6734e7SLydia Wang .ops = { 4833ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 4834ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 4835ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 4836ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4837ab6734e7SLydia Wang }, 4838ab6734e7SLydia Wang }; 4839ab6734e7SLydia Wang 4840ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 4841ab6734e7SLydia Wang { 4842ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 4843ab6734e7SLydia Wang int err; 4844ab6734e7SLydia Wang 4845ab6734e7SLydia Wang 4846ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4847ab6734e7SLydia Wang if (err < 0) 4848ab6734e7SLydia Wang return err; 4849ab6734e7SLydia Wang fill_dig_outs(codec); 4850*4a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 4851ab6734e7SLydia Wang if (err < 0) 4852ab6734e7SLydia Wang return err; 4853ab6734e7SLydia Wang 4854ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 4855ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 4856ab6734e7SLydia Wang 4857*4a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 4858ab6734e7SLydia Wang if (err < 0) 4859ab6734e7SLydia Wang return err; 4860*4a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 4861ab6734e7SLydia Wang if (err < 0) 4862ab6734e7SLydia Wang return err; 4863620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 4864ab6734e7SLydia Wang if (err < 0) 4865ab6734e7SLydia Wang return err; 4866ab6734e7SLydia Wang 4867ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4868ab6734e7SLydia Wang 4869ab6734e7SLydia Wang fill_dig_outs(codec); 4870ab6734e7SLydia Wang 4871ab6734e7SLydia Wang if (spec->kctls.list) 4872ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4873ab6734e7SLydia Wang 4874ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 4875ab6734e7SLydia Wang 4876ab6734e7SLydia Wang if (spec->hp_mux) 48773d83e577STakashi Iwai via_hp_build(codec); 4878ab6734e7SLydia Wang 4879ab6734e7SLydia Wang return 1; 4880ab6734e7SLydia Wang } 4881ab6734e7SLydia Wang 4882ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 488390dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 4884ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 4885ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 4886ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 4887ab6734e7SLydia Wang { } /* end */ 4888ab6734e7SLydia Wang }; 4889ab6734e7SLydia Wang #endif 4890ab6734e7SLydia Wang 48913e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 48923e95b9abSLydia Wang { 48933e95b9abSLydia Wang struct via_spec *spec = codec->spec; 48943e95b9abSLydia Wang int imux_is_smixer = 48953e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 48963e95b9abSLydia Wang unsigned int parm; 48973e95b9abSLydia Wang unsigned int present; 48983e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 48993e95b9abSLydia Wang imux_is_smixer = 49003e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 49013e95b9abSLydia Wang /* inputs */ 49023e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 49033e95b9abSLydia Wang parm = AC_PWRST_D3; 49043e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 49053e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 49063e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 49073e95b9abSLydia Wang parm = AC_PWRST_D0; 49083e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 49093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 49103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 49113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 49123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 49133e95b9abSLydia Wang 49143e95b9abSLydia Wang /* outputs */ 49153e95b9abSLydia Wang /* AOW0 (8h)*/ 49163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 49173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49183e95b9abSLydia Wang 49193e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 49203e95b9abSLydia Wang parm = AC_PWRST_D3; 49213e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 49223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 49233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 49243e95b9abSLydia Wang 49253e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 49263e95b9abSLydia Wang parm = AC_PWRST_D3; 49273e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 49283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 49293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 49303e95b9abSLydia Wang if (spec->hp_independent_mode) 49313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 49323e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49333e95b9abSLydia Wang 49343e95b9abSLydia Wang /* Internal Speaker */ 49353e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 49363e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 49373e95b9abSLydia Wang 49383e95b9abSLydia Wang parm = AC_PWRST_D3; 49393e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 49403e95b9abSLydia Wang if (present) { 49413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 49423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 49433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 49443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 49453e95b9abSLydia Wang } else { 49463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 49473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 49493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49503e95b9abSLydia Wang } 49513e95b9abSLydia Wang 49523e95b9abSLydia Wang 49533e95b9abSLydia Wang /* Mono Out */ 49543e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 49553e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 49563e95b9abSLydia Wang 49573e95b9abSLydia Wang parm = AC_PWRST_D3; 49583e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 49593e95b9abSLydia Wang if (present) { 49603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 49613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 49623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 49633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 49643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 49653e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 49663e95b9abSLydia Wang } else { 49673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 49683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 49703e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 49723e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 49733e95b9abSLydia Wang } 49743e95b9abSLydia Wang 49753e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 49763e95b9abSLydia Wang parm = AC_PWRST_D3; 49773e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 49783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 49793e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 49803e95b9abSLydia Wang 49813e95b9abSLydia Wang } 4982ab6734e7SLydia Wang 4983ab6734e7SLydia Wang /* patch for vt1812 */ 4984ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 4985ab6734e7SLydia Wang { 4986ab6734e7SLydia Wang struct via_spec *spec; 4987ab6734e7SLydia Wang int err; 4988ab6734e7SLydia Wang 4989ab6734e7SLydia Wang /* create a codec specific record */ 49905b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4991ab6734e7SLydia Wang if (spec == NULL) 4992ab6734e7SLydia Wang return -ENOMEM; 4993ab6734e7SLydia Wang 4994620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 4995620e2b28STakashi Iwai 4996ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 4997ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 4998ab6734e7SLydia Wang if (err < 0) { 4999ab6734e7SLydia Wang via_free(codec); 5000ab6734e7SLydia Wang return err; 5001ab6734e7SLydia Wang } else if (!err) { 5002ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5003ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 5004ab6734e7SLydia Wang } 5005ab6734e7SLydia Wang 5006ab6734e7SLydia Wang 5007ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 5008ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 5009ab6734e7SLydia Wang 5010ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 5011ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 5012ab6734e7SLydia Wang 5013ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 5014ab6734e7SLydia Wang 5015a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 5016ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 5017ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 5018ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 5019ab6734e7SLydia Wang spec->num_mixers++; 5020ab6734e7SLydia Wang } 5021ab6734e7SLydia Wang 5022ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 5023ab6734e7SLydia Wang 5024ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 50250f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5026ab6734e7SLydia Wang 5027ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5028ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 5029ab6734e7SLydia Wang #endif 5030ab6734e7SLydia Wang 50313e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 5032ab6734e7SLydia Wang return 0; 5033ab6734e7SLydia Wang } 5034ab6734e7SLydia Wang 5035c577b8a1SJoseph Chan /* 5036c577b8a1SJoseph Chan * patch entries 5037c577b8a1SJoseph Chan */ 503890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 50393218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 50403218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 50413218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 50423218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 50433218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 5044f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 50453218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 5046f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 50473218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 5048f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 50493218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 5050f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 50513218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 5052f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 50533218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 5054f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 50553218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 5056f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 50573218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 5058f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 50593218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 5060f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 50613218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 5062f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 50633218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 5064f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 50653218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 5066f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 50673218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 5068f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 50693218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 5070f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 50713218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 5072f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 50733218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 5074f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 50753218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 5076d949cac1SHarald Welte .patch = patch_vt1708S}, 50773218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 5078d949cac1SHarald Welte .patch = patch_vt1708S}, 50793218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 5080d949cac1SHarald Welte .patch = patch_vt1708S}, 50813218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 5082d949cac1SHarald Welte .patch = patch_vt1708S}, 5083bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 5084d949cac1SHarald Welte .patch = patch_vt1708S}, 50853218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 5086d949cac1SHarald Welte .patch = patch_vt1708S}, 50873218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 5088d949cac1SHarald Welte .patch = patch_vt1708S}, 50893218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 5090d949cac1SHarald Welte .patch = patch_vt1708S}, 50913218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 5092d949cac1SHarald Welte .patch = patch_vt1702}, 50933218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 5094d949cac1SHarald Welte .patch = patch_vt1702}, 50953218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 5096d949cac1SHarald Welte .patch = patch_vt1702}, 50973218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 5098d949cac1SHarald Welte .patch = patch_vt1702}, 50993218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 5100d949cac1SHarald Welte .patch = patch_vt1702}, 51013218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 5102d949cac1SHarald Welte .patch = patch_vt1702}, 51033218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 5104d949cac1SHarald Welte .patch = patch_vt1702}, 51053218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 5106d949cac1SHarald Welte .patch = patch_vt1702}, 5107eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 5108eb7188caSLydia Wang .patch = patch_vt1718S}, 5109eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 5110eb7188caSLydia Wang .patch = patch_vt1718S}, 5111bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 5112bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 5113bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 5114bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 5115f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 5116f3db423dSLydia Wang .patch = patch_vt1716S}, 5117f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 5118f3db423dSLydia Wang .patch = patch_vt1716S}, 511925eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 512025eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 5121ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 512236dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 512336dd5c4aSLydia Wang .patch = patch_vt1708S}, 512411890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 512511890956SLydia Wang .patch = patch_vt2002P}, 512611890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 512711890956SLydia Wang .patch = patch_vt2002P}, 5128c577b8a1SJoseph Chan {} /* terminator */ 5129c577b8a1SJoseph Chan }; 51301289e9e8STakashi Iwai 51311289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 51321289e9e8STakashi Iwai 51331289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 51341289e9e8STakashi Iwai .preset = snd_hda_preset_via, 51351289e9e8STakashi Iwai .owner = THIS_MODULE, 51361289e9e8STakashi Iwai }; 51371289e9e8STakashi Iwai 51381289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 51391289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 51401289e9e8STakashi Iwai 51411289e9e8STakashi Iwai static int __init patch_via_init(void) 51421289e9e8STakashi Iwai { 51431289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 51441289e9e8STakashi Iwai } 51451289e9e8STakashi Iwai 51461289e9e8STakashi Iwai static void __exit patch_via_exit(void) 51471289e9e8STakashi Iwai { 51481289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 51491289e9e8STakashi Iwai } 51501289e9e8STakashi Iwai 51511289e9e8STakashi Iwai module_init(patch_via_init) 51521289e9e8STakashi Iwai module_exit(patch_via_exit) 5153