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, 101*11890956SLydia Wang VT1802, 102d7426329SHarald Welte CODEC_TYPES, 103d7426329SHarald Welte }; 104d7426329SHarald Welte 105*11890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 106*11890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 107*11890956SLydia Wang (spec)->codec_type == VT1812 ||\ 108*11890956SLydia Wang (spec)->codec_type == VT1802) 109*11890956SLydia Wang 1101f2e99feSLydia Wang struct via_spec { 1111f2e99feSLydia Wang /* codec parameterization */ 112f3db423dSLydia Wang struct snd_kcontrol_new *mixers[6]; 1131f2e99feSLydia Wang unsigned int num_mixers; 1141f2e99feSLydia Wang 1151f2e99feSLydia Wang struct hda_verb *init_verbs[5]; 1161f2e99feSLydia Wang unsigned int num_iverbs; 1171f2e99feSLydia Wang 1181f2e99feSLydia Wang char *stream_name_analog; 1191f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_playback; 1201f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_capture; 1211f2e99feSLydia Wang 1221f2e99feSLydia Wang char *stream_name_digital; 1231f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_playback; 1241f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_capture; 1251f2e99feSLydia Wang 1261f2e99feSLydia Wang /* playback */ 1271f2e99feSLydia Wang struct hda_multi_out multiout; 1281f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1291f2e99feSLydia Wang 1301f2e99feSLydia Wang /* capture */ 1311f2e99feSLydia Wang unsigned int num_adc_nids; 1321f2e99feSLydia Wang hda_nid_t *adc_nids; 1331f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1341f2e99feSLydia Wang hda_nid_t dig_in_nid; 1351f2e99feSLydia Wang hda_nid_t dig_in_pin; 1361f2e99feSLydia Wang 1371f2e99feSLydia Wang /* capture source */ 1381f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1391f2e99feSLydia Wang unsigned int cur_mux[3]; 1401f2e99feSLydia Wang 1411f2e99feSLydia Wang /* PCM information */ 1421f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1431f2e99feSLydia Wang 1441f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1451f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1461f2e99feSLydia Wang struct snd_array kctls; 1471f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1481f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1491f2e99feSLydia Wang 1501f2e99feSLydia Wang /* HP mode source */ 1511f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1521f2e99feSLydia Wang unsigned int hp_independent_mode; 1531f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1541f2e99feSLydia Wang unsigned int smart51_enabled; 155f3db423dSLydia Wang unsigned int dmic_enabled; 1561f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1571f2e99feSLydia Wang 1581f2e99feSLydia Wang /* work to check hp jack state */ 1591f2e99feSLydia Wang struct hda_codec *codec; 1601f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1611f2e99feSLydia Wang int vt1708_jack_detectect; 1621f2e99feSLydia Wang int vt1708_hp_present; 1633e95b9abSLydia Wang 1643e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1653e95b9abSLydia Wang 1661f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1671f2e99feSLydia Wang struct hda_loopback_check loopback; 1681f2e99feSLydia Wang #endif 1691f2e99feSLydia Wang }; 1701f2e99feSLydia Wang 1710341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1725b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1735b0cb1d8SJaroslav Kysela { 1745b0cb1d8SJaroslav Kysela struct via_spec *spec; 1755b0cb1d8SJaroslav Kysela 1765b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1775b0cb1d8SJaroslav Kysela if (spec == NULL) 1785b0cb1d8SJaroslav Kysela return NULL; 1795b0cb1d8SJaroslav Kysela 1805b0cb1d8SJaroslav Kysela codec->spec = spec; 1815b0cb1d8SJaroslav Kysela spec->codec = codec; 1820341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1830341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1840341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1850341ccd7SLydia Wang spec->codec_type = VT1708S; 1865b0cb1d8SJaroslav Kysela return spec; 1875b0cb1d8SJaroslav Kysela } 1885b0cb1d8SJaroslav Kysela 189744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 190d7426329SHarald Welte { 191744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 192d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 193d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 194d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 195d7426329SHarald Welte 196d7426329SHarald Welte /* get codec type */ 197d7426329SHarald Welte if (ven_id != 0x1106) 198d7426329SHarald Welte codec_type = UNKNOWN; 199d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 200d7426329SHarald Welte codec_type = VT1708; 201d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 202d7426329SHarald Welte codec_type = VT1709_10CH; 203d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 204d7426329SHarald Welte codec_type = VT1709_6CH; 205518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 206d7426329SHarald Welte codec_type = VT1708B_8CH; 207518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 208518bf3baSLydia Wang codec_type = VT1708BCE; 209518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 210d7426329SHarald Welte codec_type = VT1708B_4CH; 211d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 212d7426329SHarald Welte && (dev_id >> 12) < 8) 213d7426329SHarald Welte codec_type = VT1708S; 214d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 215d7426329SHarald Welte && (dev_id >> 12) < 8) 216d7426329SHarald Welte codec_type = VT1702; 217eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 218eb7188caSLydia Wang && (dev_id >> 12) < 8) 219eb7188caSLydia Wang codec_type = VT1718S; 220f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 221f3db423dSLydia Wang codec_type = VT1716S; 222bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 223bb3c6bfcSLydia Wang codec_type = VT1718S; 22425eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 22525eaba2fSLydia Wang codec_type = VT2002P; 226ab6734e7SLydia Wang else if (dev_id == 0x0448) 227ab6734e7SLydia Wang codec_type = VT1812; 22836dd5c4aSLydia Wang else if (dev_id == 0x0440) 22936dd5c4aSLydia Wang codec_type = VT1708S; 230*11890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 231*11890956SLydia Wang codec_type = VT1802; 232d7426329SHarald Welte else 233d7426329SHarald Welte codec_type = UNKNOWN; 234d7426329SHarald Welte return codec_type; 235d7426329SHarald Welte }; 236d7426329SHarald Welte 23769e52a80SHarald Welte #define VIA_HP_EVENT 0x01 23869e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 239a34df19aSLydia Wang #define VIA_JACK_EVENT 0x04 240f3db423dSLydia Wang #define VIA_MONO_EVENT 0x08 24125eaba2fSLydia Wang #define VIA_SPEAKER_EVENT 0x10 24225eaba2fSLydia Wang #define VIA_BIND_HP_EVENT 0x20 24369e52a80SHarald Welte 244c577b8a1SJoseph Chan enum { 245c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 246c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 247f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 24825eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 249c577b8a1SJoseph Chan }; 250c577b8a1SJoseph Chan 251c577b8a1SJoseph Chan enum { 252eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 253c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 254c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 255c577b8a1SJoseph Chan AUTO_SEQ_SIDE 256c577b8a1SJoseph Chan }; 257c577b8a1SJoseph Chan 258f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2591f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2601f2e99feSLydia Wang 2611f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2621f2e99feSLydia Wang { 2631f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2641f2e99feSLydia Wang return; 2651f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2661f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2671f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2681f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2691f2e99feSLydia Wang msecs_to_jiffies(100)); 2701f2e99feSLydia Wang } 2711f2e99feSLydia Wang 2721f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2731f2e99feSLydia Wang { 2741f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2751f2e99feSLydia Wang return; 2761f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2771f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2781f2e99feSLydia Wang return; 2791f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2801f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2815b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2821f2e99feSLydia Wang } 283f5271101SLydia Wang 2843e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2853e95b9abSLydia Wang { 2863e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2873e95b9abSLydia Wang if (spec->set_widgets_power_state) 2883e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2893e95b9abSLydia Wang } 29025eaba2fSLydia Wang 291f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 292f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 293f5271101SLydia Wang { 294f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 295f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 296f5271101SLydia Wang 2973e95b9abSLydia Wang set_widgets_power_state(codec); 298f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2991f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3001f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3011f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3021f2e99feSLydia Wang else 3031f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3041f2e99feSLydia Wang } 305f5271101SLydia Wang return change; 306f5271101SLydia Wang } 307f5271101SLydia Wang 308f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 309f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 310f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 311f5271101SLydia Wang .name = NULL, \ 312f5271101SLydia Wang .index = 0, \ 313f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 314f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 315f5271101SLydia Wang .put = analog_input_switch_put, \ 316f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 317f5271101SLydia Wang 31825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 31925eaba2fSLydia Wang 32025eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 32125eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 32225eaba2fSLydia Wang { 32325eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 32425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 32525eaba2fSLydia Wang int i; 32625eaba2fSLydia Wang int change = 0; 32725eaba2fSLydia Wang 32825eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 32925eaba2fSLydia Wang int lmute, rmute; 33025eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 33125eaba2fSLydia Wang snd_printd("Invalid control!\n"); 33225eaba2fSLydia Wang return change; 33325eaba2fSLydia Wang } 33425eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 33525eaba2fSLydia Wang ucontrol); 33625eaba2fSLydia Wang /* Get mute value */ 33725eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 33825eaba2fSLydia Wang valp++; 33925eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 34025eaba2fSLydia Wang 34125eaba2fSLydia Wang /* Set hp pins */ 34225eaba2fSLydia Wang if (!spec->hp_independent_mode) { 34325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 34425eaba2fSLydia Wang snd_hda_codec_amp_update( 34525eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 34625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 34725eaba2fSLydia Wang lmute); 34825eaba2fSLydia Wang snd_hda_codec_amp_update( 34925eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 35025eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35125eaba2fSLydia Wang rmute); 35225eaba2fSLydia Wang } 35325eaba2fSLydia Wang } 35425eaba2fSLydia Wang 35525eaba2fSLydia Wang if (!lmute && !rmute) { 35625eaba2fSLydia Wang /* Line Outs */ 35725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 35825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 35925eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 36025eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 36125eaba2fSLydia Wang /* Speakers */ 36225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 36325eaba2fSLydia Wang snd_hda_codec_amp_stereo( 36425eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 36525eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 36625eaba2fSLydia Wang /* unmute */ 36725eaba2fSLydia Wang via_hp_bind_automute(codec); 36825eaba2fSLydia Wang 36925eaba2fSLydia Wang } else { 37025eaba2fSLydia Wang if (lmute) { 37125eaba2fSLydia Wang /* Mute all left channels */ 37225eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 37325eaba2fSLydia Wang snd_hda_codec_amp_update( 37425eaba2fSLydia Wang codec, 37525eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 37625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37725eaba2fSLydia Wang lmute); 37825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37925eaba2fSLydia Wang snd_hda_codec_amp_update( 38025eaba2fSLydia Wang codec, 38125eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 38225eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 38325eaba2fSLydia Wang lmute); 38425eaba2fSLydia Wang } 38525eaba2fSLydia Wang if (rmute) { 38625eaba2fSLydia Wang /* mute all right channels */ 38725eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38825eaba2fSLydia Wang snd_hda_codec_amp_update( 38925eaba2fSLydia Wang codec, 39025eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 39125eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39225eaba2fSLydia Wang rmute); 39325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39425eaba2fSLydia Wang snd_hda_codec_amp_update( 39525eaba2fSLydia Wang codec, 39625eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39725eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39825eaba2fSLydia Wang rmute); 39925eaba2fSLydia Wang } 40025eaba2fSLydia Wang } 40125eaba2fSLydia Wang return change; 40225eaba2fSLydia Wang } 40325eaba2fSLydia Wang 40425eaba2fSLydia Wang #define BIND_PIN_MUTE \ 40525eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 40625eaba2fSLydia Wang .name = NULL, \ 40725eaba2fSLydia Wang .index = 0, \ 40825eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 40925eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 41025eaba2fSLydia Wang .put = bind_pin_switch_put, \ 41125eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 41225eaba2fSLydia Wang 41371eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = { 414c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 415c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 416f5271101SLydia Wang ANALOG_INPUT_MUTE, 41725eaba2fSLydia Wang BIND_PIN_MUTE, 418c577b8a1SJoseph Chan }; 419c577b8a1SJoseph Chan 420c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 421c577b8a1SJoseph Chan /* ADC1-2 */ 422c577b8a1SJoseph Chan 0x15, 0x27 423c577b8a1SJoseph Chan }; 424c577b8a1SJoseph Chan 425c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 426c577b8a1SJoseph Chan /* ADC1-2 */ 427c577b8a1SJoseph Chan 0x14, 0x15, 0x16 428c577b8a1SJoseph Chan }; 429c577b8a1SJoseph Chan 430f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 431f7278fd0SJosepch Chan /* ADC1-2 */ 432f7278fd0SJosepch Chan 0x13, 0x14 433f7278fd0SJosepch Chan }; 434f7278fd0SJosepch Chan 435d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 436d949cac1SHarald Welte /* ADC1-2 */ 437d949cac1SHarald Welte 0x13, 0x14 438d949cac1SHarald Welte }; 439d949cac1SHarald Welte 440d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 441d949cac1SHarald Welte /* ADC1-2 */ 442d949cac1SHarald Welte 0x12, 0x20, 0x1F 443d949cac1SHarald Welte }; 444d949cac1SHarald Welte 445eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 446eb7188caSLydia Wang /* ADC1-2 */ 447eb7188caSLydia Wang 0x10, 0x11 448eb7188caSLydia Wang }; 449eb7188caSLydia Wang 450f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 451f3db423dSLydia Wang /* ADC1-2 */ 452f3db423dSLydia Wang 0x13, 0x14 453f3db423dSLydia Wang }; 454f3db423dSLydia Wang 45525eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 45625eaba2fSLydia Wang /* ADC1-2 */ 45725eaba2fSLydia Wang 0x10, 0x11 45825eaba2fSLydia Wang }; 45925eaba2fSLydia Wang 460ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = { 461ab6734e7SLydia Wang /* ADC1-2 */ 462ab6734e7SLydia Wang 0x10, 0x11 463ab6734e7SLydia Wang }; 464ab6734e7SLydia Wang 465ab6734e7SLydia Wang 466c577b8a1SJoseph Chan /* add dynamic controls */ 4677b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 4687b315bb4STakashi Iwai int idx, unsigned long val) 469c577b8a1SJoseph Chan { 470c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 471c577b8a1SJoseph Chan 472603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 473603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 474c577b8a1SJoseph Chan if (!knew) 475c577b8a1SJoseph Chan return -ENOMEM; 47671eb7dccSLydia Wang *knew = via_control_templates[type]; 477c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 478c577b8a1SJoseph Chan if (!knew->name) 479c577b8a1SJoseph Chan return -ENOMEM; 4804d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4815e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 482c577b8a1SJoseph Chan knew->private_value = val; 483c577b8a1SJoseph Chan return 0; 484c577b8a1SJoseph Chan } 485c577b8a1SJoseph Chan 4867b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4877b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4887b315bb4STakashi Iwai 4895b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, 4905b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *tmpl) 4915b0cb1d8SJaroslav Kysela { 4925b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 4935b0cb1d8SJaroslav Kysela 4945b0cb1d8SJaroslav Kysela snd_array_init(&spec->kctls, sizeof(*knew), 32); 4955b0cb1d8SJaroslav Kysela knew = snd_array_new(&spec->kctls); 4965b0cb1d8SJaroslav Kysela if (!knew) 4975b0cb1d8SJaroslav Kysela return NULL; 4985b0cb1d8SJaroslav Kysela *knew = *tmpl; 4995b0cb1d8SJaroslav Kysela knew->name = kstrdup(tmpl->name, GFP_KERNEL); 5005b0cb1d8SJaroslav Kysela if (!knew->name) 5015b0cb1d8SJaroslav Kysela return NULL; 502b331439dSTakashi Iwai return knew; 5035b0cb1d8SJaroslav Kysela } 5045b0cb1d8SJaroslav Kysela 505603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 506603c4019STakashi Iwai { 507603c4019STakashi Iwai struct via_spec *spec = codec->spec; 508603c4019STakashi Iwai 509603c4019STakashi Iwai if (spec->kctls.list) { 510603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 511603c4019STakashi Iwai int i; 512603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 513603c4019STakashi Iwai kfree(kctl[i].name); 514603c4019STakashi Iwai } 515603c4019STakashi Iwai snd_array_free(&spec->kctls); 516603c4019STakashi Iwai } 517603c4019STakashi Iwai 518c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 5199510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 5207b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 521c577b8a1SJoseph Chan { 522c577b8a1SJoseph Chan char name[32]; 523c577b8a1SJoseph Chan int err; 524c577b8a1SJoseph Chan 525c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 5267b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 527c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 528c577b8a1SJoseph Chan if (err < 0) 529c577b8a1SJoseph Chan return err; 530c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5317b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 532c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 533c577b8a1SJoseph Chan if (err < 0) 534c577b8a1SJoseph Chan return err; 535c577b8a1SJoseph Chan return 0; 536c577b8a1SJoseph Chan } 537c577b8a1SJoseph Chan 538c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 539c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 540c577b8a1SJoseph Chan int dac_idx) 541c577b8a1SJoseph Chan { 542c577b8a1SJoseph Chan /* set as output */ 543c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 544c577b8a1SJoseph Chan pin_type); 545c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 546c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 547d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 548d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 549d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 550c577b8a1SJoseph Chan } 551c577b8a1SJoseph Chan 552c577b8a1SJoseph Chan 553c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 554c577b8a1SJoseph Chan { 555c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 556c577b8a1SJoseph Chan int i; 557c577b8a1SJoseph Chan 558c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 559c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 560c577b8a1SJoseph Chan if (nid) 561c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 562c577b8a1SJoseph Chan } 563c577b8a1SJoseph Chan } 564c577b8a1SJoseph Chan 565c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 566c577b8a1SJoseph Chan { 567c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 568c577b8a1SJoseph Chan hda_nid_t pin; 56925eaba2fSLydia Wang int i; 570c577b8a1SJoseph Chan 57125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 57225eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 573c577b8a1SJoseph Chan if (pin) /* connect to front */ 574c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 575c577b8a1SJoseph Chan } 57625eaba2fSLydia Wang } 577c577b8a1SJoseph Chan 57832e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 57932e0191dSClemens Ladisch 580c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 581c577b8a1SJoseph Chan { 582c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5837b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 58432e0191dSClemens Ladisch unsigned int ctl; 585c577b8a1SJoseph Chan int i; 586c577b8a1SJoseph Chan 5877b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5887b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 58932e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 59032e0191dSClemens Ladisch ctl = PIN_OUT; 59130649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 59232e0191dSClemens Ladisch ctl = PIN_VREF50; 59332e0191dSClemens Ladisch else 59432e0191dSClemens Ladisch ctl = PIN_IN; 595c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 59632e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 597c577b8a1SJoseph Chan } 598c577b8a1SJoseph Chan } 599f5271101SLydia Wang 600f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 601f5271101SLydia Wang unsigned int *affected_parm) 602f5271101SLydia Wang { 603f5271101SLydia Wang unsigned parm; 604f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 605f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 606f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 607f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 608d56757abSTakashi Iwai unsigned present = snd_hda_jack_detect(codec, nid); 6091564b287SLydia Wang struct via_spec *spec = codec->spec; 6101564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 6111564b287SLydia Wang || ((no_presence || present) 6121564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 613f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 614f5271101SLydia Wang parm = AC_PWRST_D0; 615f5271101SLydia Wang } else 616f5271101SLydia Wang parm = AC_PWRST_D3; 617f5271101SLydia Wang 618f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 619f5271101SLydia Wang } 620f5271101SLydia Wang 621c577b8a1SJoseph Chan /* 622c577b8a1SJoseph Chan * input MUX handling 623c577b8a1SJoseph Chan */ 624c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 625c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 626c577b8a1SJoseph Chan { 627c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 628c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 629c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 630c577b8a1SJoseph Chan } 631c577b8a1SJoseph Chan 632c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 633c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 634c577b8a1SJoseph Chan { 635c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 636c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 637c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 638c577b8a1SJoseph Chan 639c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 640c577b8a1SJoseph Chan return 0; 641c577b8a1SJoseph Chan } 642c577b8a1SJoseph Chan 643c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 644c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 645c577b8a1SJoseph Chan { 646c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 647c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 648c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 649bff5fbf5SLydia Wang int ret; 650c577b8a1SJoseph Chan 651337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 652337b9d02STakashi Iwai return -EINVAL; 653a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 654a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 655a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 656a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 657a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 658bff5fbf5SLydia Wang 659bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 660bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 661bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 662a80e6e3cSLydia Wang /* update jack power state */ 6633e95b9abSLydia Wang set_widgets_power_state(codec); 664a80e6e3cSLydia Wang 665bff5fbf5SLydia Wang return ret; 666c577b8a1SJoseph Chan } 667c577b8a1SJoseph Chan 6680aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6690aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6700aa62aefSHarald Welte { 6710aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6720aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6730aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6740aa62aefSHarald Welte } 6750aa62aefSHarald Welte 6760aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 6770aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 6780aa62aefSHarald Welte { 6790aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6805b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 681eb7188caSLydia Wang unsigned int pinsel; 682eb7188caSLydia Wang 683eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 684eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 6850aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 6860aa62aefSHarald Welte 0x00); 6870aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 6880aa62aefSHarald Welte 6890aa62aefSHarald Welte return 0; 6900aa62aefSHarald Welte } 6910aa62aefSHarald Welte 6920713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 6930713efebSLydia Wang { 6940713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 6950713efebSLydia Wang if (ctl) { 6960713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 6970713efebSLydia Wang ctl->vd[0].access |= active 6980713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 6990713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7000713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7010713efebSLydia Wang } 7020713efebSLydia Wang } 7030713efebSLydia Wang 7045b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7055b0cb1d8SJaroslav Kysela { 7065b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7075b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7085b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7095b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7105b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 7115b0cb1d8SJaroslav Kysela default: return 0; 7125b0cb1d8SJaroslav Kysela } 7135b0cb1d8SJaroslav Kysela } 7145b0cb1d8SJaroslav Kysela 715cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 716cdc1784dSLydia Wang { 717cdc1784dSLydia Wang /* mute side channel */ 718cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 719cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 720cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 7215b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 722cdc1784dSLydia Wang 723cdc1784dSLydia Wang if (sw3) 724cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 725cdc1784dSLydia Wang parm); 726cdc1784dSLydia Wang return 0; 727cdc1784dSLydia Wang } 728cdc1784dSLydia Wang 7290aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7300aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7310aa62aefSHarald Welte { 7320aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7330aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7345b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7350aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 736cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 737cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 738cdc1784dSLydia Wang ? 1 : 0; 739ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 740ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 741ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 742ce0e5a9eSLydia Wang else 743ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 744ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7450aa62aefSHarald Welte 746ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 747ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 748ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 749cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 750cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 751cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7520aa62aefSHarald Welte 0, 0, 0); 7530aa62aefSHarald Welte 754cdc1784dSLydia Wang update_side_mute_status(codec); 7550713efebSLydia Wang /* update HP volume/swtich active state */ 7560713efebSLydia Wang if (spec->codec_type == VT1708S 757eb7188caSLydia Wang || spec->codec_type == VT1702 758f3db423dSLydia Wang || spec->codec_type == VT1718S 75925eaba2fSLydia Wang || spec->codec_type == VT1716S 760*11890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7610713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7620713efebSLydia Wang spec->hp_independent_mode); 7630713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 7640713efebSLydia Wang spec->hp_independent_mode); 7650713efebSLydia Wang } 766ce0e5a9eSLydia Wang /* update jack power state */ 7673e95b9abSLydia Wang set_widgets_power_state(codec); 7680aa62aefSHarald Welte return 0; 7690aa62aefSHarald Welte } 7700aa62aefSHarald Welte 7715b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = { 7720aa62aefSHarald Welte { 7730aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7740aa62aefSHarald Welte .name = "Independent HP", 7750aa62aefSHarald Welte .info = via_independent_hp_info, 7760aa62aefSHarald Welte .get = via_independent_hp_get, 7770aa62aefSHarald Welte .put = via_independent_hp_put, 7780aa62aefSHarald Welte }, 7795b0cb1d8SJaroslav Kysela { 7805b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 7815b0cb1d8SJaroslav Kysela .name = "Independent HP", 7825b0cb1d8SJaroslav Kysela }, 7830aa62aefSHarald Welte }; 7840aa62aefSHarald Welte 7853d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7865b0cb1d8SJaroslav Kysela { 7873d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7885b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7895b0cb1d8SJaroslav Kysela hda_nid_t nid; 7903d83e577STakashi Iwai int nums; 7913d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 7925b0cb1d8SJaroslav Kysela 7935b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7945b0cb1d8SJaroslav Kysela case VT1718S: 7955b0cb1d8SJaroslav Kysela nid = 0x34; 7965b0cb1d8SJaroslav Kysela break; 7975b0cb1d8SJaroslav Kysela case VT2002P: 798*11890956SLydia Wang case VT1802: 7995b0cb1d8SJaroslav Kysela nid = 0x35; 8005b0cb1d8SJaroslav Kysela break; 8015b0cb1d8SJaroslav Kysela case VT1812: 8025b0cb1d8SJaroslav Kysela nid = 0x3d; 8035b0cb1d8SJaroslav Kysela break; 8045b0cb1d8SJaroslav Kysela default: 8055b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8065b0cb1d8SJaroslav Kysela break; 8075b0cb1d8SJaroslav Kysela } 8085b0cb1d8SJaroslav Kysela 809ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 810ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 811ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8123d83e577STakashi Iwai if (nums <= 1) 8133d83e577STakashi Iwai return 0; 814ee3c35c0SLydia Wang } 8153d83e577STakashi Iwai 8163d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8173d83e577STakashi Iwai if (knew == NULL) 8183d83e577STakashi Iwai return -ENOMEM; 8193d83e577STakashi Iwai 8205b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8215b0cb1d8SJaroslav Kysela knew->private_value = nid; 8225b0cb1d8SJaroslav Kysela 8235b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8245b0cb1d8SJaroslav Kysela if (knew == NULL) 8255b0cb1d8SJaroslav Kysela return -ENOMEM; 8265b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8275b0cb1d8SJaroslav Kysela 8285b0cb1d8SJaroslav Kysela return 0; 8295b0cb1d8SJaroslav Kysela } 8305b0cb1d8SJaroslav Kysela 8311564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8321564b287SLydia Wang { 8331564b287SLydia Wang int i; 8341564b287SLydia Wang struct snd_ctl_elem_id id; 8351564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 8361564b287SLydia Wang 8371564b287SLydia Wang memset(&id, 0, sizeof(id)); 8381564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8391564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8401564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 8411564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 8421564b287SLydia Wang &id); 8431564b287SLydia Wang } 8441564b287SLydia Wang } 8451564b287SLydia Wang 8461564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8471564b287SLydia Wang { 8481564b287SLydia Wang struct via_spec *spec = codec->spec; 8491564b287SLydia Wang hda_nid_t nid_mixer; 8501564b287SLydia Wang int start_idx; 8511564b287SLydia Wang int end_idx; 8521564b287SLydia Wang int i; 8531564b287SLydia Wang /* get nid of MW0 and start & end index */ 8541564b287SLydia Wang switch (spec->codec_type) { 8551564b287SLydia Wang case VT1708: 8561564b287SLydia Wang nid_mixer = 0x17; 8571564b287SLydia Wang start_idx = 2; 8581564b287SLydia Wang end_idx = 4; 8591564b287SLydia Wang break; 8601564b287SLydia Wang case VT1709_10CH: 8611564b287SLydia Wang case VT1709_6CH: 8621564b287SLydia Wang nid_mixer = 0x18; 8631564b287SLydia Wang start_idx = 2; 8641564b287SLydia Wang end_idx = 4; 8651564b287SLydia Wang break; 8661564b287SLydia Wang case VT1708B_8CH: 8671564b287SLydia Wang case VT1708B_4CH: 8681564b287SLydia Wang case VT1708S: 869f3db423dSLydia Wang case VT1716S: 8701564b287SLydia Wang nid_mixer = 0x16; 8711564b287SLydia Wang start_idx = 2; 8721564b287SLydia Wang end_idx = 4; 8731564b287SLydia Wang break; 874ab657e0cSLydia Wang case VT1718S: 875ab657e0cSLydia Wang nid_mixer = 0x21; 876ab657e0cSLydia Wang start_idx = 1; 877ab657e0cSLydia Wang end_idx = 3; 878ab657e0cSLydia Wang break; 8791564b287SLydia Wang default: 8801564b287SLydia Wang return; 8811564b287SLydia Wang } 8821564b287SLydia Wang /* check AA path's mute status */ 8831564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 8841564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 8851564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 8861564b287SLydia Wang HDA_AMP_MUTE, val); 8871564b287SLydia Wang } 8881564b287SLydia Wang } 8891564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 8901564b287SLydia Wang { 8917b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8927b315bb4STakashi Iwai int i; 8937b315bb4STakashi Iwai 8947b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 8957b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 89686e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 8971564b287SLydia Wang } 8987b315bb4STakashi Iwai return 0; 8991564b287SLydia Wang } 9001564b287SLydia Wang 9011564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9021564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9031564b287SLydia Wang { 9041564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9051564b287SLydia Wang uinfo->count = 1; 9061564b287SLydia Wang uinfo->value.integer.min = 0; 9071564b287SLydia Wang uinfo->value.integer.max = 1; 9081564b287SLydia Wang return 0; 9091564b287SLydia Wang } 9101564b287SLydia Wang 9111564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9121564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9131564b287SLydia Wang { 9141564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9151564b287SLydia Wang struct via_spec *spec = codec->spec; 9167b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9171564b287SLydia Wang int on = 1; 9181564b287SLydia Wang int i; 9191564b287SLydia Wang 9207b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9217b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9227b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 9237b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 92486e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9257b315bb4STakashi Iwai continue; 92686e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9277b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9281564b287SLydia Wang continue; /* ignore FMic for independent HP */ 9297b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9301564b287SLydia Wang on = 0; 9311564b287SLydia Wang } 9321564b287SLydia Wang *ucontrol->value.integer.value = on; 9331564b287SLydia Wang return 0; 9341564b287SLydia Wang } 9351564b287SLydia Wang 9361564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9371564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9381564b287SLydia Wang { 9391564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9401564b287SLydia Wang struct via_spec *spec = codec->spec; 9417b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9421564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9431564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9441564b287SLydia Wang int i; 9451564b287SLydia Wang 9467b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9477b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9487b315bb4STakashi Iwai unsigned int parm; 9497b315bb4STakashi Iwai 95086e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9517b315bb4STakashi Iwai continue; 95286e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9537b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9541564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 9557b315bb4STakashi Iwai 9567b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 9571564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9581564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 9591564b287SLydia Wang parm |= out_in; 9601564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 9611564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 9621564b287SLydia Wang parm); 9631564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 9641564b287SLydia Wang mute_aa_path(codec, 1); 9651564b287SLydia Wang notify_aa_path_ctls(codec); 9661564b287SLydia Wang } 9677b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 968eb7188caSLydia Wang snd_hda_codec_amp_stereo( 969eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 970eb7188caSLydia Wang HDA_AMP_UNMUTE); 9711564b287SLydia Wang } 97286e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 973f3db423dSLydia Wang if (spec->codec_type == VT1708S 974f3db423dSLydia Wang || spec->codec_type == VT1716S) { 9751564b287SLydia Wang /* input = index 1 (AOW3) */ 9761564b287SLydia Wang snd_hda_codec_write( 9771564b287SLydia Wang codec, nid, 0, 9781564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 9791564b287SLydia Wang snd_hda_codec_amp_stereo( 9801564b287SLydia Wang codec, nid, HDA_OUTPUT, 9811564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 9821564b287SLydia Wang } 9831564b287SLydia Wang } 9841564b287SLydia Wang } 9851564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 9863e95b9abSLydia Wang set_widgets_power_state(codec); 9871564b287SLydia Wang return 1; 9881564b287SLydia Wang } 9891564b287SLydia Wang 9905b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = { 9911564b287SLydia Wang { 9921564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9931564b287SLydia Wang .name = "Smart 5.1", 9941564b287SLydia Wang .count = 1, 9951564b287SLydia Wang .info = via_smart51_info, 9961564b287SLydia Wang .get = via_smart51_get, 9971564b287SLydia Wang .put = via_smart51_put, 9981564b287SLydia Wang }, 9995b0cb1d8SJaroslav Kysela { 10005b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 10015b0cb1d8SJaroslav Kysela .name = "Smart 5.1", 10025b0cb1d8SJaroslav Kysela } 10031564b287SLydia Wang }; 10041564b287SLydia Wang 10055b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 10065b0cb1d8SJaroslav Kysela { 10075b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10087b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10095b0cb1d8SJaroslav Kysela hda_nid_t nid; 10105b0cb1d8SJaroslav Kysela int i; 10115b0cb1d8SJaroslav Kysela 10125b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[0]); 10135b0cb1d8SJaroslav Kysela if (knew == NULL) 10145b0cb1d8SJaroslav Kysela return -ENOMEM; 10155b0cb1d8SJaroslav Kysela 10167b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10177b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 101886e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 10195b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[1]); 10205b0cb1d8SJaroslav Kysela if (knew == NULL) 10215b0cb1d8SJaroslav Kysela return -ENOMEM; 10225b0cb1d8SJaroslav Kysela knew->subdevice = nid; 10237b315bb4STakashi Iwai break; 10245b0cb1d8SJaroslav Kysela } 10255b0cb1d8SJaroslav Kysela } 10265b0cb1d8SJaroslav Kysela 10275b0cb1d8SJaroslav Kysela return 0; 10285b0cb1d8SJaroslav Kysela } 10295b0cb1d8SJaroslav Kysela 1030c577b8a1SJoseph Chan /* capture mixer elements */ 1031c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1032c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1033c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1034c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1035c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1036c577b8a1SJoseph Chan { 1037c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1038c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1039c577b8a1SJoseph Chan * So call somewhat different.. 1040c577b8a1SJoseph Chan */ 1041c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1042c577b8a1SJoseph Chan .name = "Input Source", 1043c577b8a1SJoseph Chan .count = 1, 1044c577b8a1SJoseph Chan .info = via_mux_enum_info, 1045c577b8a1SJoseph Chan .get = via_mux_enum_get, 1046c577b8a1SJoseph Chan .put = via_mux_enum_put, 1047c577b8a1SJoseph Chan }, 1048c577b8a1SJoseph Chan { } /* end */ 1049c577b8a1SJoseph Chan }; 1050f5271101SLydia Wang 1051f5271101SLydia Wang /* check AA path's mute statue */ 1052f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1053f5271101SLydia Wang { 1054f5271101SLydia Wang int mute = 1; 1055f5271101SLydia Wang hda_nid_t nid_mixer; 1056f5271101SLydia Wang int start_idx; 1057f5271101SLydia Wang int end_idx; 1058f5271101SLydia Wang int i; 1059f5271101SLydia Wang struct via_spec *spec = codec->spec; 1060f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1061f5271101SLydia Wang switch (spec->codec_type) { 1062f5271101SLydia Wang case VT1708B_8CH: 1063f5271101SLydia Wang case VT1708B_4CH: 1064f5271101SLydia Wang case VT1708S: 1065f3db423dSLydia Wang case VT1716S: 1066f5271101SLydia Wang nid_mixer = 0x16; 1067f5271101SLydia Wang start_idx = 2; 1068f5271101SLydia Wang end_idx = 4; 1069f5271101SLydia Wang break; 1070f5271101SLydia Wang case VT1702: 1071f5271101SLydia Wang nid_mixer = 0x1a; 1072f5271101SLydia Wang start_idx = 1; 1073f5271101SLydia Wang end_idx = 3; 1074f5271101SLydia Wang break; 1075eb7188caSLydia Wang case VT1718S: 1076eb7188caSLydia Wang nid_mixer = 0x21; 1077eb7188caSLydia Wang start_idx = 1; 1078eb7188caSLydia Wang end_idx = 3; 1079eb7188caSLydia Wang break; 108025eaba2fSLydia Wang case VT2002P: 1081ab6734e7SLydia Wang case VT1812: 1082*11890956SLydia Wang case VT1802: 108325eaba2fSLydia Wang nid_mixer = 0x21; 108425eaba2fSLydia Wang start_idx = 0; 108525eaba2fSLydia Wang end_idx = 2; 108625eaba2fSLydia Wang break; 1087f5271101SLydia Wang default: 1088f5271101SLydia Wang return 0; 1089f5271101SLydia Wang } 1090f5271101SLydia Wang /* check AA path's mute status */ 1091f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1092f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1093f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1094f5271101SLydia Wang int shift = 8 * (i % 4); 1095f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1096f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1097f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1098f5271101SLydia Wang /* check mute status while the pin is connected */ 1099f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1100f5271101SLydia Wang HDA_INPUT, i) >> 7; 1101f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1102f5271101SLydia Wang HDA_INPUT, i) >> 7; 1103f5271101SLydia Wang if (!mute_l || !mute_r) { 1104f5271101SLydia Wang mute = 0; 1105f5271101SLydia Wang break; 1106f5271101SLydia Wang } 1107f5271101SLydia Wang } 1108f5271101SLydia Wang } 1109f5271101SLydia Wang return mute; 1110f5271101SLydia Wang } 1111f5271101SLydia Wang 1112f5271101SLydia Wang /* enter/exit analog low-current mode */ 1113f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1114f5271101SLydia Wang { 1115f5271101SLydia Wang struct via_spec *spec = codec->spec; 1116f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1117f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1118f5271101SLydia Wang unsigned int verb = 0; 1119f5271101SLydia Wang unsigned int parm = 0; 1120f5271101SLydia Wang 1121f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1122f5271101SLydia Wang enable = enable && saved_stream_idle; 1123f5271101SLydia Wang else { 1124f5271101SLydia Wang enable = enable && stream_idle; 1125f5271101SLydia Wang saved_stream_idle = stream_idle; 1126f5271101SLydia Wang } 1127f5271101SLydia Wang 1128f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1129f5271101SLydia Wang switch (spec->codec_type) { 1130f5271101SLydia Wang case VT1708B_8CH: 1131f5271101SLydia Wang case VT1708B_4CH: 1132f5271101SLydia Wang verb = 0xf70; 1133f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1134f5271101SLydia Wang break; 1135f5271101SLydia Wang case VT1708S: 1136eb7188caSLydia Wang case VT1718S: 1137f3db423dSLydia Wang case VT1716S: 1138f5271101SLydia Wang verb = 0xf73; 1139f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1140f5271101SLydia Wang break; 1141f5271101SLydia Wang case VT1702: 1142f5271101SLydia Wang verb = 0xf73; 1143f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1144f5271101SLydia Wang break; 114525eaba2fSLydia Wang case VT2002P: 1146ab6734e7SLydia Wang case VT1812: 1147*11890956SLydia Wang case VT1802: 114825eaba2fSLydia Wang verb = 0xf93; 114925eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 115025eaba2fSLydia Wang break; 1151f5271101SLydia Wang default: 1152f5271101SLydia Wang return; /* other codecs are not supported */ 1153f5271101SLydia Wang } 1154f5271101SLydia Wang /* send verb */ 1155f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1156f5271101SLydia Wang } 1157f5271101SLydia Wang 1158c577b8a1SJoseph Chan /* 1159c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1160c577b8a1SJoseph Chan */ 1161c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1162c577b8a1SJoseph Chan /* 1163c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1164c577b8a1SJoseph Chan */ 1165c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1166c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1167c577b8a1SJoseph Chan 1168c577b8a1SJoseph Chan 1169f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1170c577b8a1SJoseph Chan * mixer widget 1171c577b8a1SJoseph Chan */ 1172c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1173f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1174f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1175f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1176f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1177f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1178c577b8a1SJoseph Chan 1179c577b8a1SJoseph Chan /* 1180c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1181c577b8a1SJoseph Chan */ 1182c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1183c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1184c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1185c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1186c577b8a1SJoseph Chan 1187bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1188bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1189c577b8a1SJoseph Chan /* PW9 Output enable */ 1190c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1191f7278fd0SJosepch Chan { } 1192c577b8a1SJoseph Chan }; 1193c577b8a1SJoseph Chan 1194c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1195c577b8a1SJoseph Chan struct hda_codec *codec, 1196c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1197c577b8a1SJoseph Chan { 1198c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 119917314379SLydia Wang int idle = substream->pstr->substream_opened == 1 120017314379SLydia Wang && substream->ref_count == 0; 120117314379SLydia Wang analog_low_current_mode(codec, idle); 12029a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12039a08160bSTakashi Iwai hinfo); 1204c577b8a1SJoseph Chan } 1205c577b8a1SJoseph Chan 12060aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 12070aa62aefSHarald Welte unsigned int stream_tag, 12080aa62aefSHarald Welte unsigned int format, 12090aa62aefSHarald Welte struct snd_pcm_substream *substream) 12100aa62aefSHarald Welte { 12110aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12120aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 12130aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 12140aa62aefSHarald Welte int chs = substream->runtime->channels; 12150aa62aefSHarald Welte int i; 12160aa62aefSHarald Welte 12170aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12180aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12190aa62aefSHarald Welte if (chs == 2 && 12200aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12210aa62aefSHarald Welte format) && 12220aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 12230aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12240aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12250aa62aefSHarald Welte * be updated */ 12260aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 12270aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12280aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12290aa62aefSHarald Welte codec->spdif_ctls & 12300aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12310aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12320aa62aefSHarald Welte stream_tag, 0, format); 12330aa62aefSHarald Welte /* turn on again (if needed) */ 12340aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 12350aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12360aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12370aa62aefSHarald Welte codec->spdif_ctls & 0xff); 12380aa62aefSHarald Welte } else { 12390aa62aefSHarald Welte mout->dig_out_used = 0; 12400aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12410aa62aefSHarald Welte 0, 0, 0); 12420aa62aefSHarald Welte } 12430aa62aefSHarald Welte } 12440aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12450aa62aefSHarald Welte 12460aa62aefSHarald Welte /* front */ 12470aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12480aa62aefSHarald Welte 0, format); 12490aa62aefSHarald Welte 1250eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1251eb7188caSLydia Wang && !spec->hp_independent_mode) 12520aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12530aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12540aa62aefSHarald Welte 0, format); 12550aa62aefSHarald Welte 12560aa62aefSHarald Welte /* extra outputs copied from front */ 12570aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12580aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12590aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12600aa62aefSHarald Welte mout->extra_out_nid[i], 12610aa62aefSHarald Welte stream_tag, 0, format); 12620aa62aefSHarald Welte 12630aa62aefSHarald Welte /* surrounds */ 12640aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12650aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12660aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12670aa62aefSHarald Welte i * 2, format); 12680aa62aefSHarald Welte else /* copy front */ 12690aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12700aa62aefSHarald Welte 0, format); 12710aa62aefSHarald Welte } 12720aa62aefSHarald Welte } 12730aa62aefSHarald Welte 12740aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 12750aa62aefSHarald Welte struct hda_codec *codec, 12760aa62aefSHarald Welte unsigned int stream_tag, 12770aa62aefSHarald Welte unsigned int format, 12780aa62aefSHarald Welte struct snd_pcm_substream *substream) 12790aa62aefSHarald Welte { 12800aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12810aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 12820aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 12830aa62aefSHarald Welte 12840aa62aefSHarald Welte if (substream->number == 0) 12850aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 12860aa62aefSHarald Welte substream); 12870aa62aefSHarald Welte else { 12880aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 12890aa62aefSHarald Welte spec->hp_independent_mode) 12900aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 12910aa62aefSHarald Welte stream_tag, 0, format); 12920aa62aefSHarald Welte } 12931f2e99feSLydia Wang vt1708_start_hp_work(spec); 12940aa62aefSHarald Welte return 0; 12950aa62aefSHarald Welte } 12960aa62aefSHarald Welte 12970aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 12980aa62aefSHarald Welte struct hda_codec *codec, 12990aa62aefSHarald Welte struct snd_pcm_substream *substream) 13000aa62aefSHarald Welte { 13010aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13020aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 13030aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 13040aa62aefSHarald Welte int i; 13050aa62aefSHarald Welte 13060aa62aefSHarald Welte if (substream->number == 0) { 13070aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13080aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13090aa62aefSHarald Welte 13100aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13110aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13120aa62aefSHarald Welte 0, 0, 0); 13130aa62aefSHarald Welte 13140aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13150aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13160aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13170aa62aefSHarald Welte mout->extra_out_nid[i], 13180aa62aefSHarald Welte 0, 0, 0); 13190aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13200aa62aefSHarald Welte if (mout->dig_out_nid && 13210aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13220aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13230aa62aefSHarald Welte 0, 0, 0); 13240aa62aefSHarald Welte mout->dig_out_used = 0; 13250aa62aefSHarald Welte } 13260aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13270aa62aefSHarald Welte } else { 13280aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13290aa62aefSHarald Welte spec->hp_independent_mode) 13300aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13310aa62aefSHarald Welte 0, 0, 0); 13320aa62aefSHarald Welte } 13331f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13340aa62aefSHarald Welte return 0; 13350aa62aefSHarald Welte } 13360aa62aefSHarald Welte 1337c577b8a1SJoseph Chan /* 1338c577b8a1SJoseph Chan * Digital out 1339c577b8a1SJoseph Chan */ 1340c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1341c577b8a1SJoseph Chan struct hda_codec *codec, 1342c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1343c577b8a1SJoseph Chan { 1344c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1345c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1346c577b8a1SJoseph Chan } 1347c577b8a1SJoseph Chan 1348c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1349c577b8a1SJoseph Chan struct hda_codec *codec, 1350c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1351c577b8a1SJoseph Chan { 1352c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1353c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1354c577b8a1SJoseph Chan } 1355c577b8a1SJoseph Chan 13565691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 135798aa34c0SHarald Welte struct hda_codec *codec, 135898aa34c0SHarald Welte unsigned int stream_tag, 135998aa34c0SHarald Welte unsigned int format, 136098aa34c0SHarald Welte struct snd_pcm_substream *substream) 136198aa34c0SHarald Welte { 136298aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13639da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13649da29271STakashi Iwai stream_tag, format, substream); 13659da29271STakashi Iwai } 13665691ec7fSHarald Welte 13679da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 13689da29271STakashi Iwai struct hda_codec *codec, 13699da29271STakashi Iwai struct snd_pcm_substream *substream) 13709da29271STakashi Iwai { 13719da29271STakashi Iwai struct via_spec *spec = codec->spec; 13729da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 137398aa34c0SHarald Welte return 0; 137498aa34c0SHarald Welte } 137598aa34c0SHarald Welte 1376c577b8a1SJoseph Chan /* 1377c577b8a1SJoseph Chan * Analog capture 1378c577b8a1SJoseph Chan */ 1379c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1380c577b8a1SJoseph Chan struct hda_codec *codec, 1381c577b8a1SJoseph Chan unsigned int stream_tag, 1382c577b8a1SJoseph Chan unsigned int format, 1383c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1384c577b8a1SJoseph Chan { 1385c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1386c577b8a1SJoseph Chan 1387c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1388c577b8a1SJoseph Chan stream_tag, 0, format); 1389c577b8a1SJoseph Chan return 0; 1390c577b8a1SJoseph Chan } 1391c577b8a1SJoseph Chan 1392c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1393c577b8a1SJoseph Chan struct hda_codec *codec, 1394c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1395c577b8a1SJoseph Chan { 1396c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1397888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1398c577b8a1SJoseph Chan return 0; 1399c577b8a1SJoseph Chan } 1400c577b8a1SJoseph Chan 1401c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 14020aa62aefSHarald Welte .substreams = 2, 1403c577b8a1SJoseph Chan .channels_min = 2, 1404c577b8a1SJoseph Chan .channels_max = 8, 1405c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1406c577b8a1SJoseph Chan .ops = { 1407c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14080aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14090aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1410c577b8a1SJoseph Chan }, 1411c577b8a1SJoseph Chan }; 1412c577b8a1SJoseph Chan 1413bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1414c873cc25SLydia Wang .substreams = 2, 1415bc9b5623STakashi Iwai .channels_min = 2, 1416bc9b5623STakashi Iwai .channels_max = 8, 1417bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1418bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1419bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1420bc9b5623STakashi Iwai * disable the 24bit format, so far. 1421bc9b5623STakashi Iwai */ 1422bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1423bc9b5623STakashi Iwai .ops = { 1424bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1425c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1426c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1427bc9b5623STakashi Iwai }, 1428bc9b5623STakashi Iwai }; 1429bc9b5623STakashi Iwai 1430c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1431c577b8a1SJoseph Chan .substreams = 2, 1432c577b8a1SJoseph Chan .channels_min = 2, 1433c577b8a1SJoseph Chan .channels_max = 2, 1434c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1435c577b8a1SJoseph Chan .ops = { 1436c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1437c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1438c577b8a1SJoseph Chan }, 1439c577b8a1SJoseph Chan }; 1440c577b8a1SJoseph Chan 1441c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1442c577b8a1SJoseph Chan .substreams = 1, 1443c577b8a1SJoseph Chan .channels_min = 2, 1444c577b8a1SJoseph Chan .channels_max = 2, 1445c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1446c577b8a1SJoseph Chan .ops = { 1447c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14486b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14499da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14509da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1451c577b8a1SJoseph Chan }, 1452c577b8a1SJoseph Chan }; 1453c577b8a1SJoseph Chan 1454c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1455c577b8a1SJoseph Chan .substreams = 1, 1456c577b8a1SJoseph Chan .channels_min = 2, 1457c577b8a1SJoseph Chan .channels_max = 2, 1458c577b8a1SJoseph Chan }; 1459c577b8a1SJoseph Chan 1460c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1461c577b8a1SJoseph Chan { 1462c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 14635b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 14645b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 14655b0cb1d8SJaroslav Kysela int err, i; 1466c577b8a1SJoseph Chan 1467c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1468c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1469c577b8a1SJoseph Chan if (err < 0) 1470c577b8a1SJoseph Chan return err; 1471c577b8a1SJoseph Chan } 1472c577b8a1SJoseph Chan 1473c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1474c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1475c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1476c577b8a1SJoseph Chan if (err < 0) 1477c577b8a1SJoseph Chan return err; 14789a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 14799a08160bSTakashi Iwai &spec->multiout); 14809a08160bSTakashi Iwai if (err < 0) 14819a08160bSTakashi Iwai return err; 14829a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1483c577b8a1SJoseph Chan } 1484c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1485c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1486c577b8a1SJoseph Chan if (err < 0) 1487c577b8a1SJoseph Chan return err; 1488c577b8a1SJoseph Chan } 148917314379SLydia Wang 14905b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 14915b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 14925b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 149321949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 14945b0cb1d8SJaroslav Kysela if (err < 0) 14955b0cb1d8SJaroslav Kysela return err; 14965b0cb1d8SJaroslav Kysela } 14975b0cb1d8SJaroslav Kysela 14985b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 14995b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15005b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15015b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15025b0cb1d8SJaroslav Kysela continue; 15035b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15045b0cb1d8SJaroslav Kysela if (kctl == NULL) 15055b0cb1d8SJaroslav Kysela continue; 15065b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15075b0cb1d8SJaroslav Kysela knew->subdevice); 15085b0cb1d8SJaroslav Kysela } 15095b0cb1d8SJaroslav Kysela } 15105b0cb1d8SJaroslav Kysela 151117314379SLydia Wang /* init power states */ 15123e95b9abSLydia Wang set_widgets_power_state(codec); 151317314379SLydia Wang analog_low_current_mode(codec, 1); 151417314379SLydia Wang 1515603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1516c577b8a1SJoseph Chan return 0; 1517c577b8a1SJoseph Chan } 1518c577b8a1SJoseph Chan 1519c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1520c577b8a1SJoseph Chan { 1521c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1522c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1523c577b8a1SJoseph Chan 1524c577b8a1SJoseph Chan codec->num_pcms = 1; 1525c577b8a1SJoseph Chan codec->pcm_info = info; 1526c577b8a1SJoseph Chan 1527c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1528377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1529377ff31aSLydia Wang *(spec->stream_analog_playback); 1530377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1531377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1532c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1533c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1534c577b8a1SJoseph Chan 1535c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1536c577b8a1SJoseph Chan spec->multiout.max_channels; 1537c577b8a1SJoseph Chan 1538c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1539c577b8a1SJoseph Chan codec->num_pcms++; 1540c577b8a1SJoseph Chan info++; 1541c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 15427ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1543c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1544c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1545c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1546c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1547c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1548c577b8a1SJoseph Chan } 1549c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1550c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1551c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1552c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1553c577b8a1SJoseph Chan spec->dig_in_nid; 1554c577b8a1SJoseph Chan } 1555c577b8a1SJoseph Chan } 1556c577b8a1SJoseph Chan 1557c577b8a1SJoseph Chan return 0; 1558c577b8a1SJoseph Chan } 1559c577b8a1SJoseph Chan 1560c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1561c577b8a1SJoseph Chan { 1562c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1563c577b8a1SJoseph Chan 1564c577b8a1SJoseph Chan if (!spec) 1565c577b8a1SJoseph Chan return; 1566c577b8a1SJoseph Chan 1567603c4019STakashi Iwai via_free_kctls(codec); 15681f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1569c577b8a1SJoseph Chan kfree(codec->spec); 1570c577b8a1SJoseph Chan } 1571c577b8a1SJoseph Chan 157269e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 157369e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 157469e52a80SHarald Welte { 1575dcf34c8cSLydia Wang unsigned int present = 0; 157669e52a80SHarald Welte struct via_spec *spec = codec->spec; 157769e52a80SHarald Welte 1578d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1579dcf34c8cSLydia Wang 1580dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1581dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 1582dcf34c8cSLydia Wang /* auto mute */ 1583dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 1584dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 1585dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1586dcf34c8cSLydia Wang /* notify change */ 1587dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 1588dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1589dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 1590dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1591dcf34c8cSLydia Wang &id); 1592dcf34c8cSLydia Wang } 159369e52a80SHarald Welte } 159469e52a80SHarald Welte 1595f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1596f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1597f3db423dSLydia Wang { 1598f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1599f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1600f3db423dSLydia Wang 1601f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1602f3db423dSLydia Wang return; 1603f3db423dSLydia Wang 1604d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1605d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1606f3db423dSLydia Wang 1607f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1608f3db423dSLydia Wang if (lineout_present) { 1609f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1610f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 1611f3db423dSLydia Wang return; 1612f3db423dSLydia Wang } 1613f3db423dSLydia Wang 1614d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1615f3db423dSLydia Wang 1616f3db423dSLydia Wang if (!spec->hp_independent_mode) 1617f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1618f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1619f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 1620f3db423dSLydia Wang } 1621f3db423dSLydia Wang 162269e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 162369e52a80SHarald Welte { 162469e52a80SHarald Welte unsigned int gpio_data; 162569e52a80SHarald Welte unsigned int vol_counter; 162669e52a80SHarald Welte unsigned int vol; 162769e52a80SHarald Welte unsigned int master_vol; 162869e52a80SHarald Welte 162969e52a80SHarald Welte struct via_spec *spec = codec->spec; 163069e52a80SHarald Welte 163169e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 163269e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 163369e52a80SHarald Welte 163469e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 163569e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 163669e52a80SHarald Welte 163769e52a80SHarald Welte vol = vol_counter & 0x1F; 163869e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 163969e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 164069e52a80SHarald Welte AC_AMP_GET_INPUT); 164169e52a80SHarald Welte 164269e52a80SHarald Welte if (gpio_data == 0x02) { 164369e52a80SHarald Welte /* unmute line out */ 164469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 164569e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 164669e52a80SHarald Welte 164769e52a80SHarald Welte if (vol_counter & 0x20) { 164869e52a80SHarald Welte /* decrease volume */ 164969e52a80SHarald Welte if (vol > master_vol) 165069e52a80SHarald Welte vol = master_vol; 165169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 165269e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 165369e52a80SHarald Welte master_vol-vol); 165469e52a80SHarald Welte } else { 165569e52a80SHarald Welte /* increase volume */ 165669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 165769e52a80SHarald Welte HDA_AMP_VOLMASK, 165869e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 165969e52a80SHarald Welte (master_vol+vol)); 166069e52a80SHarald Welte } 166169e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 166269e52a80SHarald Welte /* mute line out */ 166369e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 166469e52a80SHarald Welte spec->autocfg.line_out_pins[0], 166569e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 166669e52a80SHarald Welte HDA_AMP_MUTE); 166769e52a80SHarald Welte } 166869e52a80SHarald Welte } 166969e52a80SHarald Welte 167025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 167125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 167225eaba2fSLydia Wang { 167325eaba2fSLydia Wang unsigned int hp_present; 167425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 167525eaba2fSLydia Wang 1676ab6734e7SLydia Wang if (spec->codec_type != VT2002P && spec->codec_type != VT1812) 167725eaba2fSLydia Wang return; 167825eaba2fSLydia Wang 1679d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 168025eaba2fSLydia Wang 168125eaba2fSLydia Wang if (!spec->hp_independent_mode) { 168225eaba2fSLydia Wang struct snd_ctl_elem_id id; 168325eaba2fSLydia Wang snd_hda_codec_amp_stereo( 168425eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 168525eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 168625eaba2fSLydia Wang /* notify change */ 168725eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 168825eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 168925eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 169025eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 169125eaba2fSLydia Wang &id); 169225eaba2fSLydia Wang } 169325eaba2fSLydia Wang } 169425eaba2fSLydia Wang 169525eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 169625eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 169725eaba2fSLydia Wang { 169801a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 169901a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 170001a1796bSakpm@linux-foundation.org */ 170101a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 170225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 170325eaba2fSLydia Wang int i; 170425eaba2fSLydia Wang 170525eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 170625eaba2fSLydia Wang return; 170725eaba2fSLydia Wang 1708d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 170925eaba2fSLydia Wang 1710d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 171125eaba2fSLydia Wang 171225eaba2fSLydia Wang if (!spec->hp_independent_mode) { 171325eaba2fSLydia Wang /* Mute Line-Outs */ 171425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 171525eaba2fSLydia Wang snd_hda_codec_amp_stereo( 171625eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 171725eaba2fSLydia Wang HDA_OUTPUT, 0, 171825eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 171925eaba2fSLydia Wang if (hp_present) 172025eaba2fSLydia Wang present = hp_present; 172125eaba2fSLydia Wang } 172225eaba2fSLydia Wang /* Speakers */ 172325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 172425eaba2fSLydia Wang snd_hda_codec_amp_stereo( 172525eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 172625eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 172725eaba2fSLydia Wang } 172825eaba2fSLydia Wang 172925eaba2fSLydia Wang 173069e52a80SHarald Welte /* unsolicited event for jack sensing */ 173169e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 173269e52a80SHarald Welte unsigned int res) 173369e52a80SHarald Welte { 173469e52a80SHarald Welte res >>= 26; 1735a34df19aSLydia Wang if (res & VIA_HP_EVENT) 173669e52a80SHarald Welte via_hp_automute(codec); 1737a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 173869e52a80SHarald Welte via_gpio_control(codec); 1739a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 17403e95b9abSLydia Wang set_widgets_power_state(codec); 1741f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 1742f3db423dSLydia Wang via_mono_automute(codec); 174325eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 174425eaba2fSLydia Wang via_speaker_automute(codec); 174525eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 174625eaba2fSLydia Wang via_hp_bind_automute(codec); 174769e52a80SHarald Welte } 174869e52a80SHarald Welte 1749c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1750c577b8a1SJoseph Chan { 1751c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 175269e52a80SHarald Welte int i; 175369e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 175469e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 175569e52a80SHarald Welte 1756f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1757f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 175855d1d6c1STakashi Iwai if (spec->dig_in_pin) { 175955d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1760f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 176112b74c80STakashi Iwai PIN_OUT); 176255d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1763f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1764f7278fd0SJosepch Chan } 176512b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 176612b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 176712b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1768f7278fd0SJosepch Chan 17699da29271STakashi Iwai /* assign slave outs */ 17709da29271STakashi Iwai if (spec->slave_dig_outs[0]) 17719da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 17725691ec7fSHarald Welte 1773c577b8a1SJoseph Chan return 0; 1774c577b8a1SJoseph Chan } 1775c577b8a1SJoseph Chan 17761f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 17771f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 17781f2e99feSLydia Wang { 17791f2e99feSLydia Wang struct via_spec *spec = codec->spec; 17801f2e99feSLydia Wang vt1708_stop_hp_work(spec); 17811f2e99feSLydia Wang return 0; 17821f2e99feSLydia Wang } 17831f2e99feSLydia Wang #endif 17841f2e99feSLydia Wang 1785cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1786cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1787cb53c626STakashi Iwai { 1788cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1789cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1790cb53c626STakashi Iwai } 1791cb53c626STakashi Iwai #endif 1792cb53c626STakashi Iwai 1793c577b8a1SJoseph Chan /* 1794c577b8a1SJoseph Chan */ 1795c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 1796c577b8a1SJoseph Chan .build_controls = via_build_controls, 1797c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1798c577b8a1SJoseph Chan .init = via_init, 1799c577b8a1SJoseph Chan .free = via_free, 18001f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18011f2e99feSLydia Wang .suspend = via_suspend, 18021f2e99feSLydia Wang #endif 1803cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1804cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1805cb53c626STakashi Iwai #endif 1806c577b8a1SJoseph Chan }; 1807c577b8a1SJoseph Chan 1808c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 1809c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 1810c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1811c577b8a1SJoseph Chan { 1812c577b8a1SJoseph Chan int i; 1813c577b8a1SJoseph Chan hda_nid_t nid; 1814c577b8a1SJoseph Chan 1815c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 1816c577b8a1SJoseph Chan 1817c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1818c577b8a1SJoseph Chan 1819c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 1820c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1821c577b8a1SJoseph Chan if (nid) { 1822c577b8a1SJoseph Chan /* config dac list */ 1823c577b8a1SJoseph Chan switch (i) { 1824c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1825c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1826c577b8a1SJoseph Chan break; 1827c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1828c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1829c577b8a1SJoseph Chan break; 1830c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1831fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1832c577b8a1SJoseph Chan break; 1833c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1834fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 1835c577b8a1SJoseph Chan break; 1836c577b8a1SJoseph Chan } 1837c577b8a1SJoseph Chan } 1838c577b8a1SJoseph Chan } 1839c577b8a1SJoseph Chan 1840c577b8a1SJoseph Chan return 0; 1841c577b8a1SJoseph Chan } 1842c577b8a1SJoseph Chan 1843c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1844c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 1845c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1846c577b8a1SJoseph Chan { 1847c577b8a1SJoseph Chan char name[32]; 1848ea734963STakashi Iwai static const char * const chname[4] = { 1849ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 1850ea734963STakashi Iwai }; 18519645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 1852c577b8a1SJoseph Chan int i, err; 1853c577b8a1SJoseph Chan 1854c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1855c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1856c577b8a1SJoseph Chan 1857c577b8a1SJoseph Chan if (!nid) 1858c577b8a1SJoseph Chan continue; 1859c577b8a1SJoseph Chan 18609645c203SLydia Wang nid_vol = nid_vols[i]; 1861c577b8a1SJoseph Chan 1862c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1863c577b8a1SJoseph Chan /* Center/LFE */ 1864c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1865c577b8a1SJoseph Chan "Center Playback Volume", 1866f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1867f7278fd0SJosepch Chan HDA_OUTPUT)); 1868c577b8a1SJoseph Chan if (err < 0) 1869c577b8a1SJoseph Chan return err; 1870c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1871c577b8a1SJoseph Chan "LFE Playback Volume", 1872f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1873f7278fd0SJosepch Chan HDA_OUTPUT)); 1874c577b8a1SJoseph Chan if (err < 0) 1875c577b8a1SJoseph Chan return err; 1876c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1877c577b8a1SJoseph Chan "Center Playback Switch", 1878f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1879f7278fd0SJosepch Chan HDA_OUTPUT)); 1880c577b8a1SJoseph Chan if (err < 0) 1881c577b8a1SJoseph Chan return err; 1882c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1883c577b8a1SJoseph Chan "LFE Playback Switch", 1884f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1885f7278fd0SJosepch Chan HDA_OUTPUT)); 1886c577b8a1SJoseph Chan if (err < 0) 1887c577b8a1SJoseph Chan return err; 1888c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 1889c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1890c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1891c577b8a1SJoseph Chan "Master Front Playback Volume", 18929645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1893f7278fd0SJosepch Chan HDA_INPUT)); 1894c577b8a1SJoseph Chan if (err < 0) 1895c577b8a1SJoseph Chan return err; 1896c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1897c577b8a1SJoseph Chan "Master Front Playback Switch", 18989645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1899f7278fd0SJosepch Chan HDA_INPUT)); 1900c577b8a1SJoseph Chan if (err < 0) 1901c577b8a1SJoseph Chan return err; 1902c577b8a1SJoseph Chan 1903c577b8a1SJoseph Chan /* add control to PW3 */ 1904c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1905c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1906f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1907f7278fd0SJosepch Chan HDA_OUTPUT)); 1908c577b8a1SJoseph Chan if (err < 0) 1909c577b8a1SJoseph Chan return err; 1910c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1911c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1912f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1913f7278fd0SJosepch Chan HDA_OUTPUT)); 1914c577b8a1SJoseph Chan if (err < 0) 1915c577b8a1SJoseph Chan return err; 1916c577b8a1SJoseph Chan } else { 1917c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1918c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1919f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1920f7278fd0SJosepch Chan HDA_OUTPUT)); 1921c577b8a1SJoseph Chan if (err < 0) 1922c577b8a1SJoseph Chan return err; 1923c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1924c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1925f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1926f7278fd0SJosepch Chan HDA_OUTPUT)); 1927c577b8a1SJoseph Chan if (err < 0) 1928c577b8a1SJoseph Chan return err; 1929c577b8a1SJoseph Chan } 1930c577b8a1SJoseph Chan } 1931c577b8a1SJoseph Chan 1932c577b8a1SJoseph Chan return 0; 1933c577b8a1SJoseph Chan } 1934c577b8a1SJoseph Chan 19350aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 19360aa62aefSHarald Welte { 19370aa62aefSHarald Welte int i; 19380aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1939ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 19400aa62aefSHarald Welte 19410aa62aefSHarald Welte /* for hp mode select */ 194210a20af7STakashi Iwai for (i = 0; texts[i]; i++) 194310a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 19440aa62aefSHarald Welte 19450aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 19460aa62aefSHarald Welte } 19470aa62aefSHarald Welte 1948c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1949c577b8a1SJoseph Chan { 1950c577b8a1SJoseph Chan int err; 1951c577b8a1SJoseph Chan 1952c577b8a1SJoseph Chan if (!pin) 1953c577b8a1SJoseph Chan return 0; 1954c577b8a1SJoseph Chan 1955c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 1956cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 1957c577b8a1SJoseph Chan 1958c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1959c577b8a1SJoseph Chan "Headphone Playback Volume", 1960c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1961c577b8a1SJoseph Chan if (err < 0) 1962c577b8a1SJoseph Chan return err; 1963c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1964c577b8a1SJoseph Chan "Headphone Playback Switch", 1965c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1966c577b8a1SJoseph Chan if (err < 0) 1967c577b8a1SJoseph Chan return err; 1968c577b8a1SJoseph Chan 19690aa62aefSHarald Welte create_hp_imux(spec); 19700aa62aefSHarald Welte 1971c577b8a1SJoseph Chan return 0; 1972c577b8a1SJoseph Chan } 1973c577b8a1SJoseph Chan 1974c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 197510a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, 1976f3268512STakashi Iwai const struct auto_pin_cfg *cfg, 1977f3268512STakashi Iwai hda_nid_t cap_nid, 1978f3268512STakashi Iwai hda_nid_t pin_idxs[], int num_idxs) 1979c577b8a1SJoseph Chan { 198010a20af7STakashi Iwai struct via_spec *spec = codec->spec; 19810aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 19827b315bb4STakashi Iwai int i, err, idx, type, type_idx = 0; 1983c577b8a1SJoseph Chan 1984c577b8a1SJoseph Chan /* for internal loopback recording select */ 1985f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 1986f3268512STakashi Iwai if (pin_idxs[idx] == 0xff) { 198710a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 1988f3268512STakashi Iwai break; 1989f3268512STakashi Iwai } 1990f3268512STakashi Iwai } 1991c577b8a1SJoseph Chan 19927b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 199310a20af7STakashi Iwai const char *label; 19947b315bb4STakashi Iwai type = cfg->inputs[i].type; 1995f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 19967b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 1997c577b8a1SJoseph Chan break; 1998f3268512STakashi Iwai if (idx >= num_idxs) 1999f3268512STakashi Iwai continue; 20007b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 20017b315bb4STakashi Iwai type_idx++; 20027b315bb4STakashi Iwai else 20037b315bb4STakashi Iwai type_idx = 0; 200410a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 200516922281SLydia Wang if (spec->codec_type == VT1708S || 200616922281SLydia Wang spec->codec_type == VT1702 || 200716922281SLydia Wang spec->codec_type == VT1716S) 200816922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 200916922281SLydia Wang idx+1, cap_nid); 201016922281SLydia Wang else 201116922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 201216922281SLydia Wang idx, cap_nid); 2013c577b8a1SJoseph Chan if (err < 0) 2014c577b8a1SJoseph Chan return err; 201510a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2016c577b8a1SJoseph Chan } 2017c577b8a1SJoseph Chan return 0; 2018c577b8a1SJoseph Chan } 2019c577b8a1SJoseph Chan 2020f3268512STakashi Iwai /* create playback/capture controls for input pins */ 202110a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, 2022f3268512STakashi Iwai const struct auto_pin_cfg *cfg) 2023f3268512STakashi Iwai { 2024f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; 202510a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, 2026f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2027f3268512STakashi Iwai } 2028f3268512STakashi Iwai 2029cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2030cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2031cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2032cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2033cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2034cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2035cb53c626STakashi Iwai { } /* end */ 2036cb53c626STakashi Iwai }; 2037cb53c626STakashi Iwai #endif 2038cb53c626STakashi Iwai 203976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 204076d9b0ddSHarald Welte { 204176d9b0ddSHarald Welte unsigned int def_conf; 204276d9b0ddSHarald Welte unsigned char seqassoc; 204376d9b0ddSHarald Welte 20442f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 204576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 204676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 204782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 204882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 204976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 20502f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 205176d9b0ddSHarald Welte } 205276d9b0ddSHarald Welte 205376d9b0ddSHarald Welte return; 205476d9b0ddSHarald Welte } 205576d9b0ddSHarald Welte 20561f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 20571f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20581f2e99feSLydia Wang { 20591f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20601f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20611f2e99feSLydia Wang 20621f2e99feSLydia Wang if (spec->codec_type != VT1708) 20631f2e99feSLydia Wang return 0; 20641f2e99feSLydia Wang spec->vt1708_jack_detectect = 20651f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 20661f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 20671f2e99feSLydia Wang return 0; 20681f2e99feSLydia Wang } 20691f2e99feSLydia Wang 20701f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 20711f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20721f2e99feSLydia Wang { 20731f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20741f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20751f2e99feSLydia Wang int change; 20761f2e99feSLydia Wang 20771f2e99feSLydia Wang if (spec->codec_type != VT1708) 20781f2e99feSLydia Wang return 0; 20791f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 20801f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 20811f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 20821f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 20831f2e99feSLydia Wang mute_aa_path(codec, 1); 20841f2e99feSLydia Wang notify_aa_path_ctls(codec); 20851f2e99feSLydia Wang } 20861f2e99feSLydia Wang return change; 20871f2e99feSLydia Wang } 20881f2e99feSLydia Wang 20891f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 20901f2e99feSLydia Wang { 20911f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20921f2e99feSLydia Wang .name = "Jack Detect", 20931f2e99feSLydia Wang .count = 1, 20941f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 20951f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 20961f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 20971f2e99feSLydia Wang }, 20981f2e99feSLydia Wang {} /* end */ 20991f2e99feSLydia Wang }; 21001f2e99feSLydia Wang 2101c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2102c577b8a1SJoseph Chan { 2103c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2104c577b8a1SJoseph Chan int err; 2105c577b8a1SJoseph Chan 210676d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 210776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 210876d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 210976d9b0ddSHarald Welte 2110c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2111c577b8a1SJoseph Chan if (err < 0) 2112c577b8a1SJoseph Chan return err; 2113c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2114c577b8a1SJoseph Chan if (err < 0) 2115c577b8a1SJoseph Chan return err; 2116c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2117c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2118c577b8a1SJoseph Chan 2119c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2120c577b8a1SJoseph Chan if (err < 0) 2121c577b8a1SJoseph Chan return err; 2122c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2123c577b8a1SJoseph Chan if (err < 0) 2124c577b8a1SJoseph Chan return err; 212510a20af7STakashi Iwai err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); 2126c577b8a1SJoseph Chan if (err < 0) 2127c577b8a1SJoseph Chan return err; 21281f2e99feSLydia Wang /* add jack detect on/off control */ 21291f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 21301f2e99feSLydia Wang if (err < 0) 21311f2e99feSLydia Wang return err; 2132c577b8a1SJoseph Chan 2133c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2134c577b8a1SJoseph Chan 21350852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2136c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 213755d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2138c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2139c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2140c577b8a1SJoseph Chan 2141603c4019STakashi Iwai if (spec->kctls.list) 2142603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2143c577b8a1SJoseph Chan 214469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2145c577b8a1SJoseph Chan 21460aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 21470aa62aefSHarald Welte 2148f8fdd495SHarald Welte if (spec->hp_mux) 21493d83e577STakashi Iwai via_hp_build(codec); 2150c577b8a1SJoseph Chan 21515b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2152c577b8a1SJoseph Chan return 1; 2153c577b8a1SJoseph Chan } 2154c577b8a1SJoseph Chan 2155c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2156c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2157c577b8a1SJoseph Chan { 215825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 215925eaba2fSLydia Wang 2160c577b8a1SJoseph Chan via_init(codec); 2161c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2162c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2163c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2164*11890956SLydia Wang 2165*11890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 216625eaba2fSLydia Wang via_hp_bind_automute(codec); 216725eaba2fSLydia Wang } else { 216825eaba2fSLydia Wang via_hp_automute(codec); 216925eaba2fSLydia Wang via_speaker_automute(codec); 217025eaba2fSLydia Wang } 217125eaba2fSLydia Wang 2172c577b8a1SJoseph Chan return 0; 2173c577b8a1SJoseph Chan } 2174c577b8a1SJoseph Chan 21751f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 21761f2e99feSLydia Wang { 21771f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 21781f2e99feSLydia Wang vt1708_hp_work.work); 21791f2e99feSLydia Wang if (spec->codec_type != VT1708) 21801f2e99feSLydia Wang return; 21811f2e99feSLydia Wang /* if jack state toggled */ 21821f2e99feSLydia Wang if (spec->vt1708_hp_present 2183d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 21841f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 21851f2e99feSLydia Wang via_hp_automute(spec->codec); 21861f2e99feSLydia Wang } 21871f2e99feSLydia Wang vt1708_start_hp_work(spec); 21881f2e99feSLydia Wang } 21891f2e99feSLydia Wang 2190337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2191337b9d02STakashi Iwai { 2192337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2193337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2194337b9d02STakashi Iwai unsigned int type; 2195337b9d02STakashi Iwai int i, n; 2196337b9d02STakashi Iwai 2197337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2198337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2199337b9d02STakashi Iwai while (nid) { 2200a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22011c55d521STakashi Iwai if (type == AC_WID_PIN) 22021c55d521STakashi Iwai break; 2203337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2204337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2205337b9d02STakashi Iwai if (n <= 0) 2206337b9d02STakashi Iwai break; 2207337b9d02STakashi Iwai if (n > 1) { 2208337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2209337b9d02STakashi Iwai break; 2210337b9d02STakashi Iwai } 2211337b9d02STakashi Iwai nid = conn[0]; 2212337b9d02STakashi Iwai } 2213337b9d02STakashi Iwai } 22141c55d521STakashi Iwai return 0; 2215337b9d02STakashi Iwai } 2216337b9d02STakashi Iwai 2217c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2218c577b8a1SJoseph Chan { 2219c577b8a1SJoseph Chan struct via_spec *spec; 2220c577b8a1SJoseph Chan int err; 2221c577b8a1SJoseph Chan 2222c577b8a1SJoseph Chan /* create a codec specific record */ 22235b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2224c577b8a1SJoseph Chan if (spec == NULL) 2225c577b8a1SJoseph Chan return -ENOMEM; 2226c577b8a1SJoseph Chan 2227c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2228c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2229c577b8a1SJoseph Chan if (err < 0) { 2230c577b8a1SJoseph Chan via_free(codec); 2231c577b8a1SJoseph Chan return err; 2232c577b8a1SJoseph Chan } else if (!err) { 2233c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2234c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2235c577b8a1SJoseph Chan } 2236c577b8a1SJoseph Chan 2237c577b8a1SJoseph Chan 2238c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2239c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2240bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2241bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2242bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2243c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2244c577b8a1SJoseph Chan 2245c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2246c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2247c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2248c577b8a1SJoseph Chan 2249c577b8a1SJoseph Chan 2250c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2251c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2252c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 22530f67a611STakashi Iwai get_mux_nids(codec); 2254c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2255c577b8a1SJoseph Chan spec->num_mixers++; 2256c577b8a1SJoseph Chan } 2257c577b8a1SJoseph Chan 2258c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2259c577b8a1SJoseph Chan 2260c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2261cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2262cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2263cb53c626STakashi Iwai #endif 22641f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2265c577b8a1SJoseph Chan return 0; 2266c577b8a1SJoseph Chan } 2267c577b8a1SJoseph Chan 2268c577b8a1SJoseph Chan /* capture mixer elements */ 2269c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2270c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2271c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2272c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2273c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2274c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2275c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2276c577b8a1SJoseph Chan { 2277c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2278c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2279c577b8a1SJoseph Chan * So call somewhat different.. 2280c577b8a1SJoseph Chan */ 2281c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2282c577b8a1SJoseph Chan .name = "Input Source", 2283c577b8a1SJoseph Chan .count = 1, 2284c577b8a1SJoseph Chan .info = via_mux_enum_info, 2285c577b8a1SJoseph Chan .get = via_mux_enum_get, 2286c577b8a1SJoseph Chan .put = via_mux_enum_put, 2287c577b8a1SJoseph Chan }, 2288c577b8a1SJoseph Chan { } /* end */ 2289c577b8a1SJoseph Chan }; 2290c577b8a1SJoseph Chan 229169e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2292a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2293a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 229469e52a80SHarald Welte { } 229569e52a80SHarald Welte }; 229669e52a80SHarald Welte 2297c577b8a1SJoseph Chan /* 2298c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2299c577b8a1SJoseph Chan */ 2300c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2301c577b8a1SJoseph Chan /* 2302c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2303c577b8a1SJoseph Chan */ 2304c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2305c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2306c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2307c577b8a1SJoseph Chan 2308c577b8a1SJoseph Chan 2309f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2310c577b8a1SJoseph Chan * mixer widget 2311c577b8a1SJoseph Chan */ 2312c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2313f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2314f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2315f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2316f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2317f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2318c577b8a1SJoseph Chan 2319c577b8a1SJoseph Chan /* 2320c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2321c577b8a1SJoseph Chan */ 2322c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2323c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2324c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2325c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2326c577b8a1SJoseph Chan 2327c577b8a1SJoseph Chan /* 2328c577b8a1SJoseph Chan * Unmute PW3 and PW4 2329c577b8a1SJoseph Chan */ 2330c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2331c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2332c577b8a1SJoseph Chan 2333bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2334bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2335c577b8a1SJoseph Chan /* PW9 Output enable */ 2336c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2337c577b8a1SJoseph Chan { } 2338c577b8a1SJoseph Chan }; 2339c577b8a1SJoseph Chan 2340c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2341c577b8a1SJoseph Chan .substreams = 1, 2342c577b8a1SJoseph Chan .channels_min = 2, 2343c577b8a1SJoseph Chan .channels_max = 10, 2344c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2345c577b8a1SJoseph Chan .ops = { 2346c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2347c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2348c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2349c577b8a1SJoseph Chan }, 2350c577b8a1SJoseph Chan }; 2351c577b8a1SJoseph Chan 2352c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2353c577b8a1SJoseph Chan .substreams = 1, 2354c577b8a1SJoseph Chan .channels_min = 2, 2355c577b8a1SJoseph Chan .channels_max = 6, 2356c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2357c577b8a1SJoseph Chan .ops = { 2358c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2359c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2360c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2361c577b8a1SJoseph Chan }, 2362c577b8a1SJoseph Chan }; 2363c577b8a1SJoseph Chan 2364c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2365c577b8a1SJoseph Chan .substreams = 2, 2366c577b8a1SJoseph Chan .channels_min = 2, 2367c577b8a1SJoseph Chan .channels_max = 2, 2368c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2369c577b8a1SJoseph Chan .ops = { 2370c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2371c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2372c577b8a1SJoseph Chan }, 2373c577b8a1SJoseph Chan }; 2374c577b8a1SJoseph Chan 2375c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2376c577b8a1SJoseph Chan .substreams = 1, 2377c577b8a1SJoseph Chan .channels_min = 2, 2378c577b8a1SJoseph Chan .channels_max = 2, 2379c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2380c577b8a1SJoseph Chan .ops = { 2381c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2382c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2383c577b8a1SJoseph Chan }, 2384c577b8a1SJoseph Chan }; 2385c577b8a1SJoseph Chan 2386c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2387c577b8a1SJoseph Chan .substreams = 1, 2388c577b8a1SJoseph Chan .channels_min = 2, 2389c577b8a1SJoseph Chan .channels_max = 2, 2390c577b8a1SJoseph Chan }; 2391c577b8a1SJoseph Chan 2392c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2393c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2394c577b8a1SJoseph Chan { 2395c577b8a1SJoseph Chan int i; 2396c577b8a1SJoseph Chan hda_nid_t nid; 2397c577b8a1SJoseph Chan 2398c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2399c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2400c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2401c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2402c577b8a1SJoseph Chan 2403c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2404c577b8a1SJoseph Chan 2405c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2406c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2407c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2408c577b8a1SJoseph Chan if (nid) { 2409c577b8a1SJoseph Chan /* config dac list */ 2410c577b8a1SJoseph Chan switch (i) { 2411c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2412c577b8a1SJoseph Chan /* AOW0 */ 2413c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2414c577b8a1SJoseph Chan break; 2415c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2416c577b8a1SJoseph Chan /* AOW2 */ 2417c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2418c577b8a1SJoseph Chan break; 2419c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2420c577b8a1SJoseph Chan /* AOW3 */ 2421fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2422c577b8a1SJoseph Chan break; 2423c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2424c577b8a1SJoseph Chan /* AOW1 */ 2425fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2426c577b8a1SJoseph Chan break; 2427c577b8a1SJoseph Chan default: 2428c577b8a1SJoseph Chan break; 2429c577b8a1SJoseph Chan } 2430c577b8a1SJoseph Chan } 2431c577b8a1SJoseph Chan } 2432c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2433c577b8a1SJoseph Chan 2434c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2435c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2436c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2437c577b8a1SJoseph Chan if (nid) { 2438c577b8a1SJoseph Chan /* config dac list */ 2439c577b8a1SJoseph Chan switch (i) { 2440c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2441c577b8a1SJoseph Chan /* AOW0 */ 2442c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2443c577b8a1SJoseph Chan break; 2444c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2445c577b8a1SJoseph Chan /* AOW2 */ 2446c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2447c577b8a1SJoseph Chan break; 2448c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2449c577b8a1SJoseph Chan /* AOW1 */ 2450c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2451c577b8a1SJoseph Chan break; 2452c577b8a1SJoseph Chan default: 2453c577b8a1SJoseph Chan break; 2454c577b8a1SJoseph Chan } 2455c577b8a1SJoseph Chan } 2456c577b8a1SJoseph Chan } 2457c577b8a1SJoseph Chan } 2458c577b8a1SJoseph Chan 2459c577b8a1SJoseph Chan return 0; 2460c577b8a1SJoseph Chan } 2461c577b8a1SJoseph Chan 2462c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2463c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2464c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2465c577b8a1SJoseph Chan { 2466c577b8a1SJoseph Chan char name[32]; 2467ea734963STakashi Iwai static const char * const chname[4] = { 2468ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2469ea734963STakashi Iwai }; 24704483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2471c577b8a1SJoseph Chan int i, err; 2472c577b8a1SJoseph Chan 2473c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2474c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2475c577b8a1SJoseph Chan 2476c577b8a1SJoseph Chan if (!nid) 2477c577b8a1SJoseph Chan continue; 2478c577b8a1SJoseph Chan 24794483a2f5SLydia Wang nid_vol = nid_vols[i]; 24804483a2f5SLydia Wang 2481c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2482c577b8a1SJoseph Chan /* Center/LFE */ 2483c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2484c577b8a1SJoseph Chan "Center Playback Volume", 24854483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2486f7278fd0SJosepch Chan HDA_OUTPUT)); 2487c577b8a1SJoseph Chan if (err < 0) 2488c577b8a1SJoseph Chan return err; 2489c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2490c577b8a1SJoseph Chan "LFE Playback Volume", 24914483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2492f7278fd0SJosepch Chan HDA_OUTPUT)); 2493c577b8a1SJoseph Chan if (err < 0) 2494c577b8a1SJoseph Chan return err; 2495c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2496c577b8a1SJoseph Chan "Center Playback Switch", 24974483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2498f7278fd0SJosepch Chan HDA_OUTPUT)); 2499c577b8a1SJoseph Chan if (err < 0) 2500c577b8a1SJoseph Chan return err; 2501c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2502c577b8a1SJoseph Chan "LFE Playback Switch", 25034483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2504f7278fd0SJosepch Chan HDA_OUTPUT)); 2505c577b8a1SJoseph Chan if (err < 0) 2506c577b8a1SJoseph Chan return err; 2507c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 25084483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2509c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2510c577b8a1SJoseph Chan "Master Front Playback Volume", 25114483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2512f7278fd0SJosepch Chan HDA_INPUT)); 2513c577b8a1SJoseph Chan if (err < 0) 2514c577b8a1SJoseph Chan return err; 2515c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2516c577b8a1SJoseph Chan "Master Front Playback Switch", 25174483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2518f7278fd0SJosepch Chan HDA_INPUT)); 2519c577b8a1SJoseph Chan if (err < 0) 2520c577b8a1SJoseph Chan return err; 2521c577b8a1SJoseph Chan 2522c577b8a1SJoseph Chan /* add control to PW3 */ 2523c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2524c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2525f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2526f7278fd0SJosepch Chan HDA_OUTPUT)); 2527c577b8a1SJoseph Chan if (err < 0) 2528c577b8a1SJoseph Chan return err; 2529c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2530c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2531f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2532f7278fd0SJosepch Chan HDA_OUTPUT)); 2533c577b8a1SJoseph Chan if (err < 0) 2534c577b8a1SJoseph Chan return err; 2535c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2536c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2537c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 25384483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2539f7278fd0SJosepch Chan HDA_OUTPUT)); 2540c577b8a1SJoseph Chan if (err < 0) 2541c577b8a1SJoseph Chan return err; 2542c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2543c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 25444483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2545f7278fd0SJosepch Chan HDA_OUTPUT)); 2546c577b8a1SJoseph Chan if (err < 0) 2547c577b8a1SJoseph Chan return err; 2548c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2549c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2550c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 25514483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2552f7278fd0SJosepch Chan HDA_OUTPUT)); 2553c577b8a1SJoseph Chan if (err < 0) 2554c577b8a1SJoseph Chan return err; 2555c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2556c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 25574483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2558f7278fd0SJosepch Chan HDA_OUTPUT)); 2559c577b8a1SJoseph Chan if (err < 0) 2560c577b8a1SJoseph Chan return err; 2561c577b8a1SJoseph Chan } 2562c577b8a1SJoseph Chan } 2563c577b8a1SJoseph Chan 2564c577b8a1SJoseph Chan return 0; 2565c577b8a1SJoseph Chan } 2566c577b8a1SJoseph Chan 2567c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2568c577b8a1SJoseph Chan { 2569c577b8a1SJoseph Chan int err; 2570c577b8a1SJoseph Chan 2571c577b8a1SJoseph Chan if (!pin) 2572c577b8a1SJoseph Chan return 0; 2573c577b8a1SJoseph Chan 2574c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2575c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2576c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2577c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2578cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2579c577b8a1SJoseph Chan 2580c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2581c577b8a1SJoseph Chan "Headphone Playback Volume", 2582c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2583c577b8a1SJoseph Chan if (err < 0) 2584c577b8a1SJoseph Chan return err; 2585c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2586c577b8a1SJoseph Chan "Headphone Playback Switch", 2587c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2588c577b8a1SJoseph Chan if (err < 0) 2589c577b8a1SJoseph Chan return err; 2590c577b8a1SJoseph Chan 2591c577b8a1SJoseph Chan return 0; 2592c577b8a1SJoseph Chan } 2593c577b8a1SJoseph Chan 2594c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 259510a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, 2596c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2597c577b8a1SJoseph Chan { 2598f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; 259910a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, 2600f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2601c577b8a1SJoseph Chan } 2602c577b8a1SJoseph Chan 2603c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2604c577b8a1SJoseph Chan { 2605c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2606c577b8a1SJoseph Chan int err; 2607c577b8a1SJoseph Chan 2608c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2609c577b8a1SJoseph Chan if (err < 0) 2610c577b8a1SJoseph Chan return err; 2611c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2612c577b8a1SJoseph Chan if (err < 0) 2613c577b8a1SJoseph Chan return err; 2614c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2615c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2616c577b8a1SJoseph Chan 2617c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2618c577b8a1SJoseph Chan if (err < 0) 2619c577b8a1SJoseph Chan return err; 2620c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2621c577b8a1SJoseph Chan if (err < 0) 2622c577b8a1SJoseph Chan return err; 262310a20af7STakashi Iwai err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); 2624c577b8a1SJoseph Chan if (err < 0) 2625c577b8a1SJoseph Chan return err; 2626c577b8a1SJoseph Chan 2627c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2628c577b8a1SJoseph Chan 26290852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2630c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 263155d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2632c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2633c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2634c577b8a1SJoseph Chan 2635603c4019STakashi Iwai if (spec->kctls.list) 2636603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2637c577b8a1SJoseph Chan 26380aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2639c577b8a1SJoseph Chan 2640f8fdd495SHarald Welte if (spec->hp_mux) 26413d83e577STakashi Iwai via_hp_build(codec); 2642f8fdd495SHarald Welte 26435b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2644c577b8a1SJoseph Chan return 1; 2645c577b8a1SJoseph Chan } 2646c577b8a1SJoseph Chan 2647cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2648cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 2649cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2650cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2651cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2652cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2653cb53c626STakashi Iwai { } /* end */ 2654cb53c626STakashi Iwai }; 2655cb53c626STakashi Iwai #endif 2656cb53c626STakashi Iwai 2657c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2658c577b8a1SJoseph Chan { 2659c577b8a1SJoseph Chan struct via_spec *spec; 2660c577b8a1SJoseph Chan int err; 2661c577b8a1SJoseph Chan 2662c577b8a1SJoseph Chan /* create a codec specific record */ 26635b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2664c577b8a1SJoseph Chan if (spec == NULL) 2665c577b8a1SJoseph Chan return -ENOMEM; 2666c577b8a1SJoseph Chan 2667c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2668c577b8a1SJoseph Chan if (err < 0) { 2669c577b8a1SJoseph Chan via_free(codec); 2670c577b8a1SJoseph Chan return err; 2671c577b8a1SJoseph Chan } else if (!err) { 2672c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2673c577b8a1SJoseph Chan "Using genenic mode...\n"); 2674c577b8a1SJoseph Chan } 2675c577b8a1SJoseph Chan 267669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 267769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2678c577b8a1SJoseph Chan 2679c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2680c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2681c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2682c577b8a1SJoseph Chan 2683c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2684c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2685c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2686c577b8a1SJoseph Chan 2687c577b8a1SJoseph Chan 2688c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2689c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2690c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2691337b9d02STakashi Iwai get_mux_nids(codec); 2692c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2693c577b8a1SJoseph Chan spec->num_mixers++; 2694c577b8a1SJoseph Chan } 2695c577b8a1SJoseph Chan 2696c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2697c577b8a1SJoseph Chan 2698c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 269969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2700cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2701cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2702cb53c626STakashi Iwai #endif 2703c577b8a1SJoseph Chan 2704c577b8a1SJoseph Chan return 0; 2705c577b8a1SJoseph Chan } 2706c577b8a1SJoseph Chan /* 2707c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2708c577b8a1SJoseph Chan */ 2709c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2710c577b8a1SJoseph Chan /* 2711c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2712c577b8a1SJoseph Chan */ 2713c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2714c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2715c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2716c577b8a1SJoseph Chan 2717c577b8a1SJoseph Chan 2718c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2719c577b8a1SJoseph Chan * mixer widget 2720c577b8a1SJoseph Chan */ 2721c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2722c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2723c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2724c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2725c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2726c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2727c577b8a1SJoseph Chan 2728c577b8a1SJoseph Chan /* 2729c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2730c577b8a1SJoseph Chan */ 2731c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2732c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2733c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2734c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2735c577b8a1SJoseph Chan 2736c577b8a1SJoseph Chan /* 2737c577b8a1SJoseph Chan * Unmute PW3 and PW4 2738c577b8a1SJoseph Chan */ 2739c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2740c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2741c577b8a1SJoseph Chan 2742c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2743c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2744c577b8a1SJoseph Chan /* PW9 Output enable */ 2745c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2746c577b8a1SJoseph Chan { } 2747c577b8a1SJoseph Chan }; 2748c577b8a1SJoseph Chan 2749c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2750c577b8a1SJoseph Chan { 2751c577b8a1SJoseph Chan struct via_spec *spec; 2752c577b8a1SJoseph Chan int err; 2753c577b8a1SJoseph Chan 2754c577b8a1SJoseph Chan /* create a codec specific record */ 27555b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2756c577b8a1SJoseph Chan if (spec == NULL) 2757c577b8a1SJoseph Chan return -ENOMEM; 2758c577b8a1SJoseph Chan 2759c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2760c577b8a1SJoseph Chan if (err < 0) { 2761c577b8a1SJoseph Chan via_free(codec); 2762c577b8a1SJoseph Chan return err; 2763c577b8a1SJoseph Chan } else if (!err) { 2764c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2765c577b8a1SJoseph Chan "Using genenic mode...\n"); 2766c577b8a1SJoseph Chan } 2767c577b8a1SJoseph Chan 276869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 276969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2770c577b8a1SJoseph Chan 2771c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2772c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2773c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2774c577b8a1SJoseph Chan 2775c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2776c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2777c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2778c577b8a1SJoseph Chan 2779c577b8a1SJoseph Chan 2780c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2781c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2782c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2783337b9d02STakashi Iwai get_mux_nids(codec); 2784c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2785c577b8a1SJoseph Chan spec->num_mixers++; 2786c577b8a1SJoseph Chan } 2787c577b8a1SJoseph Chan 2788c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2789c577b8a1SJoseph Chan 2790c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 279169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2792cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2793cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2794cb53c626STakashi Iwai #endif 2795f7278fd0SJosepch Chan return 0; 2796f7278fd0SJosepch Chan } 2797f7278fd0SJosepch Chan 2798f7278fd0SJosepch Chan /* capture mixer elements */ 2799f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2800f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2801f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2802f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2803f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2804f7278fd0SJosepch Chan { 2805f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2806f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2807f7278fd0SJosepch Chan * So call somewhat different.. 2808f7278fd0SJosepch Chan */ 2809f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2810f7278fd0SJosepch Chan .name = "Input Source", 2811f7278fd0SJosepch Chan .count = 1, 2812f7278fd0SJosepch Chan .info = via_mux_enum_info, 2813f7278fd0SJosepch Chan .get = via_mux_enum_get, 2814f7278fd0SJosepch Chan .put = via_mux_enum_put, 2815f7278fd0SJosepch Chan }, 2816f7278fd0SJosepch Chan { } /* end */ 2817f7278fd0SJosepch Chan }; 2818f7278fd0SJosepch Chan /* 2819f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2820f7278fd0SJosepch Chan */ 2821f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2822f7278fd0SJosepch Chan /* 2823f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2824f7278fd0SJosepch Chan */ 2825f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2826f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2827f7278fd0SJosepch Chan 2828f7278fd0SJosepch Chan 2829f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2830f7278fd0SJosepch Chan * mixer widget 2831f7278fd0SJosepch Chan */ 2832f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2833f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2834f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2835f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2836f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2837f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2838f7278fd0SJosepch Chan 2839f7278fd0SJosepch Chan /* 2840f7278fd0SJosepch Chan * Set up output mixers 2841f7278fd0SJosepch Chan */ 2842f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2843f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2844f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2845f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2846f7278fd0SJosepch Chan 2847f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2848bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2849f7278fd0SJosepch Chan /* PW9 Output enable */ 2850f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2851f7278fd0SJosepch Chan /* PW10 Input enable */ 2852f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2853f7278fd0SJosepch Chan { } 2854f7278fd0SJosepch Chan }; 2855f7278fd0SJosepch Chan 2856f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2857f7278fd0SJosepch Chan /* 2858f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2859f7278fd0SJosepch Chan */ 2860f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2861f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2862f7278fd0SJosepch Chan 2863f7278fd0SJosepch Chan 2864f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2865f7278fd0SJosepch Chan * mixer widget 2866f7278fd0SJosepch Chan */ 2867f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2868f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2869f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2870f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2871f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2872f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2873f7278fd0SJosepch Chan 2874f7278fd0SJosepch Chan /* 2875f7278fd0SJosepch Chan * Set up output mixers 2876f7278fd0SJosepch Chan */ 2877f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2878f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2879f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2880f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2881f7278fd0SJosepch Chan 2882f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2883f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2884f7278fd0SJosepch Chan /* PW9 Output enable */ 2885f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2886f7278fd0SJosepch Chan /* PW10 Input enable */ 2887f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2888f7278fd0SJosepch Chan { } 2889f7278fd0SJosepch Chan }; 2890f7278fd0SJosepch Chan 289169e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 2892a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2893a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2894a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2895a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2896a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2897a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2898a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2899a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2900a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 290169e52a80SHarald Welte { } 290269e52a80SHarald Welte }; 290369e52a80SHarald Welte 290417314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 290517314379SLydia Wang struct hda_codec *codec, 290617314379SLydia Wang struct snd_pcm_substream *substream) 290717314379SLydia Wang { 290817314379SLydia Wang int idle = substream->pstr->substream_opened == 1 290917314379SLydia Wang && substream->ref_count == 0; 291017314379SLydia Wang 291117314379SLydia Wang analog_low_current_mode(codec, idle); 291217314379SLydia Wang return 0; 291317314379SLydia Wang } 291417314379SLydia Wang 2915f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 29160aa62aefSHarald Welte .substreams = 2, 2917f7278fd0SJosepch Chan .channels_min = 2, 2918f7278fd0SJosepch Chan .channels_max = 8, 2919f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2920f7278fd0SJosepch Chan .ops = { 2921f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29220aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 292317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 292417314379SLydia Wang .close = via_pcm_open_close 2925f7278fd0SJosepch Chan }, 2926f7278fd0SJosepch Chan }; 2927f7278fd0SJosepch Chan 2928f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 29290aa62aefSHarald Welte .substreams = 2, 2930f7278fd0SJosepch Chan .channels_min = 2, 2931f7278fd0SJosepch Chan .channels_max = 4, 2932f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2933f7278fd0SJosepch Chan .ops = { 2934f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29350aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 29360aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 2937f7278fd0SJosepch Chan }, 2938f7278fd0SJosepch Chan }; 2939f7278fd0SJosepch Chan 2940f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 2941f7278fd0SJosepch Chan .substreams = 2, 2942f7278fd0SJosepch Chan .channels_min = 2, 2943f7278fd0SJosepch Chan .channels_max = 2, 2944f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 2945f7278fd0SJosepch Chan .ops = { 294617314379SLydia Wang .open = via_pcm_open_close, 2947f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 294817314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 294917314379SLydia Wang .close = via_pcm_open_close 2950f7278fd0SJosepch Chan }, 2951f7278fd0SJosepch Chan }; 2952f7278fd0SJosepch Chan 2953f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 2954f7278fd0SJosepch Chan .substreams = 1, 2955f7278fd0SJosepch Chan .channels_min = 2, 2956f7278fd0SJosepch Chan .channels_max = 2, 2957f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 2958f7278fd0SJosepch Chan .ops = { 2959f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 2960f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 29619da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 29629da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 2963f7278fd0SJosepch Chan }, 2964f7278fd0SJosepch Chan }; 2965f7278fd0SJosepch Chan 2966f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 2967f7278fd0SJosepch Chan .substreams = 1, 2968f7278fd0SJosepch Chan .channels_min = 2, 2969f7278fd0SJosepch Chan .channels_max = 2, 2970f7278fd0SJosepch Chan }; 2971f7278fd0SJosepch Chan 2972f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 2973f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 2974f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2975f7278fd0SJosepch Chan { 2976f7278fd0SJosepch Chan int i; 2977f7278fd0SJosepch Chan hda_nid_t nid; 2978f7278fd0SJosepch Chan 2979f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 2980f7278fd0SJosepch Chan 2981f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 2982f7278fd0SJosepch Chan 2983f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 2984f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 2985f7278fd0SJosepch Chan if (nid) { 2986f7278fd0SJosepch Chan /* config dac list */ 2987f7278fd0SJosepch Chan switch (i) { 2988f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 2989f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 2990f7278fd0SJosepch Chan break; 2991f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 2992f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 2993f7278fd0SJosepch Chan break; 2994f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 2995fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2996f7278fd0SJosepch Chan break; 2997f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 2998fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 2999f7278fd0SJosepch Chan break; 3000f7278fd0SJosepch Chan } 3001f7278fd0SJosepch Chan } 3002f7278fd0SJosepch Chan } 3003f7278fd0SJosepch Chan 3004f7278fd0SJosepch Chan return 0; 3005f7278fd0SJosepch Chan } 3006f7278fd0SJosepch Chan 3007f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3008f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3009f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3010f7278fd0SJosepch Chan { 3011f7278fd0SJosepch Chan char name[32]; 3012ea734963STakashi Iwai static const char * const chname[4] = { 3013ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3014ea734963STakashi Iwai }; 3015fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3016f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3017f7278fd0SJosepch Chan int i, err; 3018f7278fd0SJosepch Chan 3019f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3020f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3021f7278fd0SJosepch Chan 3022f7278fd0SJosepch Chan if (!nid) 3023f7278fd0SJosepch Chan continue; 3024f7278fd0SJosepch Chan 3025f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3026f7278fd0SJosepch Chan 3027f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3028f7278fd0SJosepch Chan /* Center/LFE */ 3029f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3030f7278fd0SJosepch Chan "Center Playback Volume", 3031f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3032f7278fd0SJosepch Chan HDA_OUTPUT)); 3033f7278fd0SJosepch Chan if (err < 0) 3034f7278fd0SJosepch Chan return err; 3035f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3036f7278fd0SJosepch Chan "LFE Playback Volume", 3037f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3038f7278fd0SJosepch Chan HDA_OUTPUT)); 3039f7278fd0SJosepch Chan if (err < 0) 3040f7278fd0SJosepch Chan return err; 3041f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3042f7278fd0SJosepch Chan "Center Playback Switch", 3043f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3044f7278fd0SJosepch Chan HDA_OUTPUT)); 3045f7278fd0SJosepch Chan if (err < 0) 3046f7278fd0SJosepch Chan return err; 3047f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3048f7278fd0SJosepch Chan "LFE Playback Switch", 3049f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3050f7278fd0SJosepch Chan HDA_OUTPUT)); 3051f7278fd0SJosepch Chan if (err < 0) 3052f7278fd0SJosepch Chan return err; 3053f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3054f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3055f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3056f7278fd0SJosepch Chan "Master Front Playback Volume", 3057f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3058f7278fd0SJosepch Chan HDA_INPUT)); 3059f7278fd0SJosepch Chan if (err < 0) 3060f7278fd0SJosepch Chan return err; 3061f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3062f7278fd0SJosepch Chan "Master Front Playback Switch", 3063f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3064f7278fd0SJosepch Chan HDA_INPUT)); 3065f7278fd0SJosepch Chan if (err < 0) 3066f7278fd0SJosepch Chan return err; 3067f7278fd0SJosepch Chan 3068f7278fd0SJosepch Chan /* add control to PW3 */ 3069f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3070f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3071f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3072f7278fd0SJosepch Chan HDA_OUTPUT)); 3073f7278fd0SJosepch Chan if (err < 0) 3074f7278fd0SJosepch Chan return err; 3075f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3076f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3077f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3078f7278fd0SJosepch Chan HDA_OUTPUT)); 3079f7278fd0SJosepch Chan if (err < 0) 3080f7278fd0SJosepch Chan return err; 3081f7278fd0SJosepch Chan } else { 3082f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3083f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3084f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3085f7278fd0SJosepch Chan HDA_OUTPUT)); 3086f7278fd0SJosepch Chan if (err < 0) 3087f7278fd0SJosepch Chan return err; 3088f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3089f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3090f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3091f7278fd0SJosepch Chan HDA_OUTPUT)); 3092f7278fd0SJosepch Chan if (err < 0) 3093f7278fd0SJosepch Chan return err; 3094f7278fd0SJosepch Chan } 3095f7278fd0SJosepch Chan } 3096f7278fd0SJosepch Chan 3097f7278fd0SJosepch Chan return 0; 3098f7278fd0SJosepch Chan } 3099f7278fd0SJosepch Chan 3100f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3101f7278fd0SJosepch Chan { 3102f7278fd0SJosepch Chan int err; 3103f7278fd0SJosepch Chan 3104f7278fd0SJosepch Chan if (!pin) 3105f7278fd0SJosepch Chan return 0; 3106f7278fd0SJosepch Chan 3107f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3108cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3109f7278fd0SJosepch Chan 3110f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3111f7278fd0SJosepch Chan "Headphone Playback Volume", 3112f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3113f7278fd0SJosepch Chan if (err < 0) 3114f7278fd0SJosepch Chan return err; 3115f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3116f7278fd0SJosepch Chan "Headphone Playback Switch", 3117f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3118f7278fd0SJosepch Chan if (err < 0) 3119f7278fd0SJosepch Chan return err; 3120f7278fd0SJosepch Chan 31210aa62aefSHarald Welte create_hp_imux(spec); 31220aa62aefSHarald Welte 3123f7278fd0SJosepch Chan return 0; 3124f7278fd0SJosepch Chan } 3125f7278fd0SJosepch Chan 3126f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 312710a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, 3128f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3129f7278fd0SJosepch Chan { 3130f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; 313110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3132f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3133f7278fd0SJosepch Chan } 3134f7278fd0SJosepch Chan 3135f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3136f7278fd0SJosepch Chan { 3137f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3138f7278fd0SJosepch Chan int err; 3139f7278fd0SJosepch Chan 3140f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3141f7278fd0SJosepch Chan if (err < 0) 3142f7278fd0SJosepch Chan return err; 3143f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3144f7278fd0SJosepch Chan if (err < 0) 3145f7278fd0SJosepch Chan return err; 3146f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3147f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3148f7278fd0SJosepch Chan 3149f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3150f7278fd0SJosepch Chan if (err < 0) 3151f7278fd0SJosepch Chan return err; 3152f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3153f7278fd0SJosepch Chan if (err < 0) 3154f7278fd0SJosepch Chan return err; 315510a20af7STakashi Iwai err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); 3156f7278fd0SJosepch Chan if (err < 0) 3157f7278fd0SJosepch Chan return err; 3158f7278fd0SJosepch Chan 3159f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3160f7278fd0SJosepch Chan 31610852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3162f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 316355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3164f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3165f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3166f7278fd0SJosepch Chan 3167603c4019STakashi Iwai if (spec->kctls.list) 3168603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3169f7278fd0SJosepch Chan 31700aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31710aa62aefSHarald Welte 3172f8fdd495SHarald Welte if (spec->hp_mux) 31733d83e577STakashi Iwai via_hp_build(codec); 3174f7278fd0SJosepch Chan 31755b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3176f7278fd0SJosepch Chan return 1; 3177f7278fd0SJosepch Chan } 3178f7278fd0SJosepch Chan 3179f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3180f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3181f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3182f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3183f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3184f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3185f7278fd0SJosepch Chan { } /* end */ 3186f7278fd0SJosepch Chan }; 3187f7278fd0SJosepch Chan #endif 31883e95b9abSLydia Wang 31893e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 31903e95b9abSLydia Wang { 31913e95b9abSLydia Wang struct via_spec *spec = codec->spec; 31923e95b9abSLydia Wang int imux_is_smixer; 31933e95b9abSLydia Wang unsigned int parm; 31943e95b9abSLydia Wang int is_8ch = 0; 3195bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 3196bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 31973e95b9abSLydia Wang is_8ch = 1; 31983e95b9abSLydia Wang 31993e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 32003e95b9abSLydia Wang imux_is_smixer = 32013e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 32023e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 32033e95b9abSLydia Wang /* inputs */ 32043e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 32053e95b9abSLydia Wang parm = AC_PWRST_D3; 32063e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32073e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32083e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 32093e95b9abSLydia Wang if (imux_is_smixer) 32103e95b9abSLydia Wang parm = AC_PWRST_D0; 32113e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 32123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 32133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 32143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 32153e95b9abSLydia Wang 32163e95b9abSLydia Wang /* outputs */ 32173e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 32183e95b9abSLydia Wang parm = AC_PWRST_D3; 32193e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 32203e95b9abSLydia Wang if (spec->smart51_enabled) 32213e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 32233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 32243e95b9abSLydia Wang 32253e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 32263e95b9abSLydia Wang if (is_8ch) { 32273e95b9abSLydia Wang parm = AC_PWRST_D3; 32283e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 32293e95b9abSLydia Wang if (spec->smart51_enabled) 32303e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 32323e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 32343e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3235bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 3236bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 3237bc92df7fSLydia Wang parm = AC_PWRST_D3; 3238bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 3239bc92df7fSLydia Wang if (spec->smart51_enabled) 3240bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 3241bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 3242bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3243bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3244bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32453e95b9abSLydia Wang } 32463e95b9abSLydia Wang 32473e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 32483e95b9abSLydia Wang parm = AC_PWRST_D3; 32493e95b9abSLydia Wang /* force to D0 for internal Speaker */ 32503e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 32513e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 32523e95b9abSLydia Wang if (is_8ch) 32533e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 32543e95b9abSLydia Wang 32553e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 32563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 32573e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 32583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 32593e95b9abSLydia Wang if (is_8ch) { 32603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 32613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 32633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3264bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 3265bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3266bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32673e95b9abSLydia Wang } 32683e95b9abSLydia Wang 3269518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3270f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3271f7278fd0SJosepch Chan { 3272f7278fd0SJosepch Chan struct via_spec *spec; 3273f7278fd0SJosepch Chan int err; 3274f7278fd0SJosepch Chan 3275518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3276518bf3baSLydia Wang return patch_vt1708S(codec); 3277f7278fd0SJosepch Chan /* create a codec specific record */ 32785b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3279f7278fd0SJosepch Chan if (spec == NULL) 3280f7278fd0SJosepch Chan return -ENOMEM; 3281f7278fd0SJosepch Chan 3282f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3283f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3284f7278fd0SJosepch Chan if (err < 0) { 3285f7278fd0SJosepch Chan via_free(codec); 3286f7278fd0SJosepch Chan return err; 3287f7278fd0SJosepch Chan } else if (!err) { 3288f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3289f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3290f7278fd0SJosepch Chan } 3291f7278fd0SJosepch Chan 329269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 329369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3294f7278fd0SJosepch Chan 3295f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3296f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3297f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3298f7278fd0SJosepch Chan 3299f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3300f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3301f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3302f7278fd0SJosepch Chan 3303f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3304f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3305f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3306337b9d02STakashi Iwai get_mux_nids(codec); 3307f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3308f7278fd0SJosepch Chan spec->num_mixers++; 3309f7278fd0SJosepch Chan } 3310f7278fd0SJosepch Chan 3311f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3312f7278fd0SJosepch Chan 3313f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 331469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3315f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3316f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3317f7278fd0SJosepch Chan #endif 3318f7278fd0SJosepch Chan 33193e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 33203e95b9abSLydia Wang 3321f7278fd0SJosepch Chan return 0; 3322f7278fd0SJosepch Chan } 3323f7278fd0SJosepch Chan 3324f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3325f7278fd0SJosepch Chan { 3326f7278fd0SJosepch Chan struct via_spec *spec; 3327f7278fd0SJosepch Chan int err; 3328f7278fd0SJosepch Chan 3329f7278fd0SJosepch Chan /* create a codec specific record */ 33305b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3331f7278fd0SJosepch Chan if (spec == NULL) 3332f7278fd0SJosepch Chan return -ENOMEM; 3333f7278fd0SJosepch Chan 3334f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3335f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3336f7278fd0SJosepch Chan if (err < 0) { 3337f7278fd0SJosepch Chan via_free(codec); 3338f7278fd0SJosepch Chan return err; 3339f7278fd0SJosepch Chan } else if (!err) { 3340f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3341f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3342f7278fd0SJosepch Chan } 3343f7278fd0SJosepch Chan 334469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 334569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3346f7278fd0SJosepch Chan 3347f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3348f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3349f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3350f7278fd0SJosepch Chan 3351f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3352f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3353f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3354f7278fd0SJosepch Chan 3355f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3356f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3357f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3358337b9d02STakashi Iwai get_mux_nids(codec); 3359f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3360f7278fd0SJosepch Chan spec->num_mixers++; 3361f7278fd0SJosepch Chan } 3362f7278fd0SJosepch Chan 3363f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3364f7278fd0SJosepch Chan 3365f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 336669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3367f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3368f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3369f7278fd0SJosepch Chan #endif 3370c577b8a1SJoseph Chan 33713e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 33723e95b9abSLydia Wang 3373c577b8a1SJoseph Chan return 0; 3374c577b8a1SJoseph Chan } 3375c577b8a1SJoseph Chan 3376d949cac1SHarald Welte /* Patch for VT1708S */ 3377d949cac1SHarald Welte 3378d949cac1SHarald Welte /* capture mixer elements */ 3379d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3380d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3381d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3382d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3383d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 33846369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 33856369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 33866369bcfcSLydia Wang HDA_INPUT), 3387d949cac1SHarald Welte { 3388d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3389d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3390d949cac1SHarald Welte * So call somewhat different.. 3391d949cac1SHarald Welte */ 3392d949cac1SHarald Welte /* .name = "Capture Source", */ 3393d949cac1SHarald Welte .name = "Input Source", 3394d949cac1SHarald Welte .count = 1, 3395d949cac1SHarald Welte .info = via_mux_enum_info, 3396d949cac1SHarald Welte .get = via_mux_enum_get, 3397d949cac1SHarald Welte .put = via_mux_enum_put, 3398d949cac1SHarald Welte }, 3399d949cac1SHarald Welte { } /* end */ 3400d949cac1SHarald Welte }; 3401d949cac1SHarald Welte 3402d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3403d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3404d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3405d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3406d949cac1SHarald Welte 3407d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3408d949cac1SHarald Welte * analog-loopback mixer widget */ 3409d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3410d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3411d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3412d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3413d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3414d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3415d949cac1SHarald Welte 3416d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3417d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 34185691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3419d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 34205691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3421d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3422d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3423bc7e7e5cSLydia Wang /* don't bybass mixer */ 3424bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3425d949cac1SHarald Welte { } 3426d949cac1SHarald Welte }; 3427d949cac1SHarald Welte 342869e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3429a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3430a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3431a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3432a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3433a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3434a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3435a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3436a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3437a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 343869e52a80SHarald Welte { } 343969e52a80SHarald Welte }; 344069e52a80SHarald Welte 3441bc92df7fSLydia Wang static struct hda_verb vt1705_uniwill_init_verbs[] = { 3442bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3443bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3444bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3445bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3446bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3447bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3448bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3449bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3450bc92df7fSLydia Wang { } 3451bc92df7fSLydia Wang }; 3452bc92df7fSLydia Wang 3453d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3454d949cac1SHarald Welte .substreams = 2, 3455d949cac1SHarald Welte .channels_min = 2, 3456d949cac1SHarald Welte .channels_max = 8, 3457d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3458d949cac1SHarald Welte .ops = { 3459d949cac1SHarald Welte .open = via_playback_pcm_open, 3460c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3461c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 346217314379SLydia Wang .close = via_pcm_open_close 3463d949cac1SHarald Welte }, 3464d949cac1SHarald Welte }; 3465d949cac1SHarald Welte 3466bc92df7fSLydia Wang static struct hda_pcm_stream vt1705_pcm_analog_playback = { 3467bc92df7fSLydia Wang .substreams = 2, 3468bc92df7fSLydia Wang .channels_min = 2, 3469bc92df7fSLydia Wang .channels_max = 6, 3470bc92df7fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3471bc92df7fSLydia Wang .ops = { 3472bc92df7fSLydia Wang .open = via_playback_pcm_open, 3473bc92df7fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3474bc92df7fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3475bc92df7fSLydia Wang .close = via_pcm_open_close 3476bc92df7fSLydia Wang }, 3477bc92df7fSLydia Wang }; 3478bc92df7fSLydia Wang 3479d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3480d949cac1SHarald Welte .substreams = 2, 3481d949cac1SHarald Welte .channels_min = 2, 3482d949cac1SHarald Welte .channels_max = 2, 3483d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3484d949cac1SHarald Welte .ops = { 348517314379SLydia Wang .open = via_pcm_open_close, 3486d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 348717314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 348817314379SLydia Wang .close = via_pcm_open_close 3489d949cac1SHarald Welte }, 3490d949cac1SHarald Welte }; 3491d949cac1SHarald Welte 3492d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 34939da29271STakashi Iwai .substreams = 1, 3494d949cac1SHarald Welte .channels_min = 2, 3495d949cac1SHarald Welte .channels_max = 2, 3496d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3497d949cac1SHarald Welte .ops = { 3498d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3499d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 35009da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 35019da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3502d949cac1SHarald Welte }, 3503d949cac1SHarald Welte }; 3504d949cac1SHarald Welte 3505d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3506d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3507d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3508d949cac1SHarald Welte { 3509d949cac1SHarald Welte int i; 3510d949cac1SHarald Welte hda_nid_t nid; 3511d949cac1SHarald Welte 3512d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3513d949cac1SHarald Welte 3514d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3515d949cac1SHarald Welte 3516d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3517d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3518d949cac1SHarald Welte if (nid) { 3519d949cac1SHarald Welte /* config dac list */ 3520d949cac1SHarald Welte switch (i) { 3521d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3522d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3523d949cac1SHarald Welte break; 3524d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3525bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3526bc92df7fSLydia Wang spec->multiout.dac_nids[i] = 0x25; 3527bc92df7fSLydia Wang else 3528d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3529d949cac1SHarald Welte break; 3530d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3531d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3532d949cac1SHarald Welte break; 3533d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3534d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3535d949cac1SHarald Welte break; 3536d949cac1SHarald Welte } 3537d949cac1SHarald Welte } 3538d949cac1SHarald Welte } 3539d949cac1SHarald Welte 354032e0191dSClemens Ladisch /* for Smart 5.1, line/mic inputs double as output pins */ 354132e0191dSClemens Ladisch if (cfg->line_outs == 1) { 354232e0191dSClemens Ladisch spec->multiout.num_dacs = 3; 354332e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11; 3544bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3545bc92df7fSLydia Wang spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x25; 3546bc92df7fSLydia Wang else 354732e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24; 354832e0191dSClemens Ladisch } 354932e0191dSClemens Ladisch 3550d949cac1SHarald Welte return 0; 3551d949cac1SHarald Welte } 3552d949cac1SHarald Welte 3553d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3554bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, 3555d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3556d949cac1SHarald Welte { 3557bc92df7fSLydia Wang struct via_spec *spec = codec->spec; 3558d949cac1SHarald Welte char name[32]; 3559ea734963STakashi Iwai static const char * const chname[4] = { 3560ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3561ea734963STakashi Iwai }; 3562bc92df7fSLydia Wang hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, 3563bc92df7fSLydia Wang {0x10, 0x11, 0x25, 0} }; 3564bc92df7fSLydia Wang hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, 3565bc92df7fSLydia Wang {0x1C, 0x18, 0x27, 0} }; 3566d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3567d949cac1SHarald Welte int i, err; 3568d949cac1SHarald Welte 3569d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3570d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3571d949cac1SHarald Welte 357232e0191dSClemens Ladisch /* for Smart 5.1, there are always at least six channels */ 357332e0191dSClemens Ladisch if (!nid && i > AUTO_SEQ_CENLFE) 3574d949cac1SHarald Welte continue; 3575d949cac1SHarald Welte 3576bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3577bc92df7fSLydia Wang nid_vol = nid_vols[1][i]; 3578bc92df7fSLydia Wang nid_mute = nid_mutes[1][i]; 3579bc92df7fSLydia Wang } else { 3580bc92df7fSLydia Wang nid_vol = nid_vols[0][i]; 3581bc92df7fSLydia Wang nid_mute = nid_mutes[0][i]; 3582bc92df7fSLydia Wang } 3583bc92df7fSLydia Wang if (!nid_vol && !nid_mute) 3584bc92df7fSLydia Wang continue; 3585d949cac1SHarald Welte 3586d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3587d949cac1SHarald Welte /* Center/LFE */ 3588d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3589d949cac1SHarald Welte "Center Playback Volume", 3590d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3591d949cac1SHarald Welte HDA_OUTPUT)); 3592d949cac1SHarald Welte if (err < 0) 3593d949cac1SHarald Welte return err; 3594d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3595d949cac1SHarald Welte "LFE Playback Volume", 3596d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3597d949cac1SHarald Welte HDA_OUTPUT)); 3598d949cac1SHarald Welte if (err < 0) 3599d949cac1SHarald Welte return err; 3600d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3601d949cac1SHarald Welte "Center Playback Switch", 3602d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3603d949cac1SHarald Welte 1, 0, 3604d949cac1SHarald Welte HDA_OUTPUT)); 3605d949cac1SHarald Welte if (err < 0) 3606d949cac1SHarald Welte return err; 3607d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3608d949cac1SHarald Welte "LFE Playback Switch", 3609d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3610d949cac1SHarald Welte 2, 0, 3611d949cac1SHarald Welte HDA_OUTPUT)); 3612d949cac1SHarald Welte if (err < 0) 3613d949cac1SHarald Welte return err; 3614d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3615d949cac1SHarald Welte /* add control to mixer index 0 */ 3616d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3617d949cac1SHarald Welte "Master Front Playback Volume", 3618d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3619d949cac1SHarald Welte HDA_INPUT)); 3620d949cac1SHarald Welte if (err < 0) 3621d949cac1SHarald Welte return err; 3622d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3623d949cac1SHarald Welte "Master Front Playback Switch", 3624d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3625d949cac1SHarald Welte HDA_INPUT)); 3626d949cac1SHarald Welte if (err < 0) 3627d949cac1SHarald Welte return err; 3628d949cac1SHarald Welte 3629d949cac1SHarald Welte /* Front */ 3630d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3631d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3632d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3633d949cac1SHarald Welte HDA_OUTPUT)); 3634d949cac1SHarald Welte if (err < 0) 3635d949cac1SHarald Welte return err; 3636d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3637d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3638d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3639d949cac1SHarald Welte 3, 0, 3640d949cac1SHarald Welte HDA_OUTPUT)); 3641d949cac1SHarald Welte if (err < 0) 3642d949cac1SHarald Welte return err; 3643d949cac1SHarald Welte } else { 3644d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3645d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3646d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3647d949cac1SHarald Welte HDA_OUTPUT)); 3648d949cac1SHarald Welte if (err < 0) 3649d949cac1SHarald Welte return err; 3650d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3651d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3652d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3653d949cac1SHarald Welte 3, 0, 3654d949cac1SHarald Welte HDA_OUTPUT)); 3655d949cac1SHarald Welte if (err < 0) 3656d949cac1SHarald Welte return err; 3657d949cac1SHarald Welte } 3658d949cac1SHarald Welte } 3659d949cac1SHarald Welte 3660d949cac1SHarald Welte return 0; 3661d949cac1SHarald Welte } 3662d949cac1SHarald Welte 3663d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3664d949cac1SHarald Welte { 3665d949cac1SHarald Welte int err; 3666d949cac1SHarald Welte 3667d949cac1SHarald Welte if (!pin) 3668d949cac1SHarald Welte return 0; 3669d949cac1SHarald Welte 3670d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3671cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3672d949cac1SHarald Welte 3673d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3674d949cac1SHarald Welte "Headphone Playback Volume", 3675d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3676d949cac1SHarald Welte if (err < 0) 3677d949cac1SHarald Welte return err; 3678d949cac1SHarald Welte 3679d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3680d949cac1SHarald Welte "Headphone Playback Switch", 3681d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3682d949cac1SHarald Welte if (err < 0) 3683d949cac1SHarald Welte return err; 3684d949cac1SHarald Welte 36850aa62aefSHarald Welte create_hp_imux(spec); 36860aa62aefSHarald Welte 3687d949cac1SHarald Welte return 0; 3688d949cac1SHarald Welte } 3689d949cac1SHarald Welte 3690d949cac1SHarald Welte /* create playback/capture controls for input pins */ 369110a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, 3692d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3693d949cac1SHarald Welte { 3694f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 369510a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3696f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3697d949cac1SHarald Welte } 3698d949cac1SHarald Welte 36999da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 37009da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 37019da29271STakashi Iwai { 37029da29271STakashi Iwai struct via_spec *spec = codec->spec; 37039da29271STakashi Iwai int i; 37049da29271STakashi Iwai 37059da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 37069da29271STakashi Iwai hda_nid_t nid; 37079da29271STakashi Iwai int conn; 37089da29271STakashi Iwai 37099da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 37109da29271STakashi Iwai if (!nid) 37119da29271STakashi Iwai continue; 37129da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 37139da29271STakashi Iwai if (conn < 1) 37149da29271STakashi Iwai continue; 37159da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 37169da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 37179da29271STakashi Iwai else { 37189da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 37199da29271STakashi Iwai break; /* at most two dig outs */ 37209da29271STakashi Iwai } 37219da29271STakashi Iwai } 37229da29271STakashi Iwai } 37239da29271STakashi Iwai 3724d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3725d949cac1SHarald Welte { 3726d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3727d949cac1SHarald Welte int err; 3728d949cac1SHarald Welte 37299da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3730d949cac1SHarald Welte if (err < 0) 3731d949cac1SHarald Welte return err; 3732d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3733d949cac1SHarald Welte if (err < 0) 3734d949cac1SHarald Welte return err; 3735d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3736d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3737d949cac1SHarald Welte 3738bc92df7fSLydia Wang err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); 3739d949cac1SHarald Welte if (err < 0) 3740d949cac1SHarald Welte return err; 3741d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3742d949cac1SHarald Welte if (err < 0) 3743d949cac1SHarald Welte return err; 374410a20af7STakashi Iwai err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); 3745d949cac1SHarald Welte if (err < 0) 3746d949cac1SHarald Welte return err; 3747d949cac1SHarald Welte 3748d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3749d949cac1SHarald Welte 37509da29271STakashi Iwai fill_dig_outs(codec); 375198aa34c0SHarald Welte 3752603c4019STakashi Iwai if (spec->kctls.list) 3753603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3754d949cac1SHarald Welte 37550aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 37560aa62aefSHarald Welte 3757f8fdd495SHarald Welte if (spec->hp_mux) 37583d83e577STakashi Iwai via_hp_build(codec); 3759d949cac1SHarald Welte 37605b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3761d949cac1SHarald Welte return 1; 3762d949cac1SHarald Welte } 3763d949cac1SHarald Welte 3764d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3765d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 3766d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3767d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3768d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3769d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3770d949cac1SHarald Welte { } /* end */ 3771d949cac1SHarald Welte }; 3772d949cac1SHarald Welte #endif 3773d949cac1SHarald Welte 37746369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 37756369bcfcSLydia Wang int offset, int num_steps, int step_size) 37766369bcfcSLydia Wang { 37776369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 37786369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 37796369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 37806369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 37816369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 37826369bcfcSLydia Wang } 37836369bcfcSLydia Wang 3784d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3785d949cac1SHarald Welte { 3786d949cac1SHarald Welte struct via_spec *spec; 3787d949cac1SHarald Welte int err; 3788d949cac1SHarald Welte 3789d949cac1SHarald Welte /* create a codec specific record */ 37905b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3791d949cac1SHarald Welte if (spec == NULL) 3792d949cac1SHarald Welte return -ENOMEM; 3793d949cac1SHarald Welte 3794d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3795d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3796d949cac1SHarald Welte if (err < 0) { 3797d949cac1SHarald Welte via_free(codec); 3798d949cac1SHarald Welte return err; 3799d949cac1SHarald Welte } else if (!err) { 3800d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3801d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3802d949cac1SHarald Welte } 3803d949cac1SHarald Welte 380469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3805bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3806bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3807bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3808bc92df7fSLydia Wang else 3809bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3810bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3811d949cac1SHarald Welte 381236dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 381336dd5c4aSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3814bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3815bc92df7fSLydia Wang spec->stream_name_analog = "VT1705 Analog"; 381636dd5c4aSLydia Wang else 3817d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 3818bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3819bc92df7fSLydia Wang spec->stream_analog_playback = &vt1705_pcm_analog_playback; 3820bc92df7fSLydia Wang else 3821d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3822d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3823d949cac1SHarald Welte 382436dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 382536dd5c4aSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3826bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3827bc92df7fSLydia Wang spec->stream_name_digital = "VT1705 Digital"; 382836dd5c4aSLydia Wang else 3829d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 3830d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3831d949cac1SHarald Welte 3832d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 3833d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 3834d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 3835337b9d02STakashi Iwai get_mux_nids(codec); 38366369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 38376369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3838d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3839d949cac1SHarald Welte spec->num_mixers++; 3840d949cac1SHarald Welte } 3841d949cac1SHarald Welte 3842d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3843d949cac1SHarald Welte 3844d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 384569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3846d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3847d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3848d949cac1SHarald Welte #endif 3849d949cac1SHarald Welte 3850518bf3baSLydia Wang /* correct names for VT1708BCE */ 3851518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3852518bf3baSLydia Wang kfree(codec->chip_name); 3853518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3854518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3855518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3856518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3857518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 3858518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 3859518bf3baSLydia Wang } 3860970f630fSLydia Wang /* correct names for VT1818S */ 3861970f630fSLydia Wang if (codec->vendor_id == 0x11060440) { 3862970f630fSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3863970f630fSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3864970f630fSLydia Wang } 3865bc92df7fSLydia Wang /* correct names for VT1705 */ 3866bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3867bc92df7fSLydia Wang kfree(codec->chip_name); 3868bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3869bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3870bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3871bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3872bc92df7fSLydia Wang } 38733e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3874d949cac1SHarald Welte return 0; 3875d949cac1SHarald Welte } 3876d949cac1SHarald Welte 3877d949cac1SHarald Welte /* Patch for VT1702 */ 3878d949cac1SHarald Welte 3879d949cac1SHarald Welte /* capture mixer elements */ 3880d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 3881d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3882d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3883d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3884d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3885d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3886d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3887d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3888d949cac1SHarald Welte HDA_INPUT), 3889d949cac1SHarald Welte { 3890d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3891d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3892d949cac1SHarald Welte * So call somewhat different.. 3893d949cac1SHarald Welte */ 3894d949cac1SHarald Welte /* .name = "Capture Source", */ 3895d949cac1SHarald Welte .name = "Input Source", 3896d949cac1SHarald Welte .count = 1, 3897d949cac1SHarald Welte .info = via_mux_enum_info, 3898d949cac1SHarald Welte .get = via_mux_enum_get, 3899d949cac1SHarald Welte .put = via_mux_enum_put, 3900d949cac1SHarald Welte }, 3901d949cac1SHarald Welte { } /* end */ 3902d949cac1SHarald Welte }; 3903d949cac1SHarald Welte 3904d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 3905d949cac1SHarald Welte /* 3906d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3907d949cac1SHarald Welte */ 3908d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3909d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3910d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3911d949cac1SHarald Welte 3912d949cac1SHarald Welte 3913d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3914d949cac1SHarald Welte * mixer widget 3915d949cac1SHarald Welte */ 3916d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3917d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3918d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3919d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3920d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3921d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3922d949cac1SHarald Welte 3923d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3924d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3925d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3926d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3927d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3928bc7e7e5cSLydia Wang /* mixer enable */ 3929bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3930bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3931bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3932d949cac1SHarald Welte { } 3933d949cac1SHarald Welte }; 3934d949cac1SHarald Welte 393569e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 3936a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3937a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3938a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3939a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3940a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3941a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 394269e52a80SHarald Welte { } 394369e52a80SHarald Welte }; 394469e52a80SHarald Welte 3945d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 39460aa62aefSHarald Welte .substreams = 2, 3947d949cac1SHarald Welte .channels_min = 2, 3948d949cac1SHarald Welte .channels_max = 2, 3949d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3950d949cac1SHarald Welte .ops = { 3951d949cac1SHarald Welte .open = via_playback_pcm_open, 39520aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 395317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 395417314379SLydia Wang .close = via_pcm_open_close 3955d949cac1SHarald Welte }, 3956d949cac1SHarald Welte }; 3957d949cac1SHarald Welte 3958d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 3959d949cac1SHarald Welte .substreams = 3, 3960d949cac1SHarald Welte .channels_min = 2, 3961d949cac1SHarald Welte .channels_max = 2, 3962d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 3963d949cac1SHarald Welte .ops = { 396417314379SLydia Wang .open = via_pcm_open_close, 3965d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 396617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 396717314379SLydia Wang .close = via_pcm_open_close 3968d949cac1SHarald Welte }, 3969d949cac1SHarald Welte }; 3970d949cac1SHarald Welte 3971d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 39725691ec7fSHarald Welte .substreams = 2, 3973d949cac1SHarald Welte .channels_min = 2, 3974d949cac1SHarald Welte .channels_max = 2, 3975d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3976d949cac1SHarald Welte .ops = { 3977d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3978d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 39799da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 39809da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3981d949cac1SHarald Welte }, 3982d949cac1SHarald Welte }; 3983d949cac1SHarald Welte 3984d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3985d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 3986d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3987d949cac1SHarald Welte { 3988d949cac1SHarald Welte spec->multiout.num_dacs = 1; 3989d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3990d949cac1SHarald Welte 3991d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 3992d949cac1SHarald Welte /* config dac list */ 3993d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 3994d949cac1SHarald Welte } 3995d949cac1SHarald Welte 3996d949cac1SHarald Welte return 0; 3997d949cac1SHarald Welte } 3998d949cac1SHarald Welte 3999d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4000d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4001d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4002d949cac1SHarald Welte { 4003d949cac1SHarald Welte int err; 4004d949cac1SHarald Welte 4005d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4006d949cac1SHarald Welte return -1; 4007d949cac1SHarald Welte 4008d949cac1SHarald Welte /* add control to mixer index 0 */ 4009d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4010d949cac1SHarald Welte "Master Front Playback Volume", 4011d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4012d949cac1SHarald Welte if (err < 0) 4013d949cac1SHarald Welte return err; 4014d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4015d949cac1SHarald Welte "Master Front Playback Switch", 4016d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4017d949cac1SHarald Welte if (err < 0) 4018d949cac1SHarald Welte return err; 4019d949cac1SHarald Welte 4020d949cac1SHarald Welte /* Front */ 4021d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4022d949cac1SHarald Welte "Front Playback Volume", 4023d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4024d949cac1SHarald Welte if (err < 0) 4025d949cac1SHarald Welte return err; 4026d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4027d949cac1SHarald Welte "Front Playback Switch", 4028d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4029d949cac1SHarald Welte if (err < 0) 4030d949cac1SHarald Welte return err; 4031d949cac1SHarald Welte 4032d949cac1SHarald Welte return 0; 4033d949cac1SHarald Welte } 4034d949cac1SHarald Welte 4035d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4036d949cac1SHarald Welte { 40370713efebSLydia Wang int err, i; 40380713efebSLydia Wang struct hda_input_mux *imux; 4039ea734963STakashi Iwai static const char * const texts[] = { "ON", "OFF", NULL}; 4040d949cac1SHarald Welte if (!pin) 4041d949cac1SHarald Welte return 0; 4042d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4043cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4044d949cac1SHarald Welte 4045d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4046d949cac1SHarald Welte "Headphone Playback Volume", 4047d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4048d949cac1SHarald Welte if (err < 0) 4049d949cac1SHarald Welte return err; 4050d949cac1SHarald Welte 4051d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4052d949cac1SHarald Welte "Headphone Playback Switch", 4053d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4054d949cac1SHarald Welte if (err < 0) 4055d949cac1SHarald Welte return err; 4056d949cac1SHarald Welte 40570713efebSLydia Wang imux = &spec->private_imux[1]; 40580aa62aefSHarald Welte 40590713efebSLydia Wang /* for hp mode select */ 406010a20af7STakashi Iwai for (i = 0; texts[i]; i++) 406110a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 40620713efebSLydia Wang 40630713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4064d949cac1SHarald Welte return 0; 4065d949cac1SHarald Welte } 4066d949cac1SHarald Welte 4067d949cac1SHarald Welte /* create playback/capture controls for input pins */ 406810a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, 4069d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4070d949cac1SHarald Welte { 4071f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; 407210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, 4073f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4074d949cac1SHarald Welte } 4075d949cac1SHarald Welte 4076d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4077d949cac1SHarald Welte { 4078d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4079d949cac1SHarald Welte int err; 4080d949cac1SHarald Welte 40819da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4082d949cac1SHarald Welte if (err < 0) 4083d949cac1SHarald Welte return err; 4084d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4085d949cac1SHarald Welte if (err < 0) 4086d949cac1SHarald Welte return err; 4087d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4088d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4089d949cac1SHarald Welte 4090d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4091d949cac1SHarald Welte if (err < 0) 4092d949cac1SHarald Welte return err; 4093d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4094d949cac1SHarald Welte if (err < 0) 4095d949cac1SHarald Welte return err; 4096c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4097c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4098c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4099c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4100c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4101c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 410210a20af7STakashi Iwai err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); 4103d949cac1SHarald Welte if (err < 0) 4104d949cac1SHarald Welte return err; 4105d949cac1SHarald Welte 4106d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4107d949cac1SHarald Welte 41089da29271STakashi Iwai fill_dig_outs(codec); 410998aa34c0SHarald Welte 4110603c4019STakashi Iwai if (spec->kctls.list) 4111603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4112d949cac1SHarald Welte 41130aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 41140aa62aefSHarald Welte 4115f8fdd495SHarald Welte if (spec->hp_mux) 41163d83e577STakashi Iwai via_hp_build(codec); 4117d949cac1SHarald Welte 4118d949cac1SHarald Welte return 1; 4119d949cac1SHarald Welte } 4120d949cac1SHarald Welte 4121d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4122d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4123d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4124d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4125d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4126d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4127d949cac1SHarald Welte { } /* end */ 4128d949cac1SHarald Welte }; 4129d949cac1SHarald Welte #endif 4130d949cac1SHarald Welte 41313e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 41323e95b9abSLydia Wang { 41333e95b9abSLydia Wang int imux_is_smixer = 41343e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 41353e95b9abSLydia Wang unsigned int parm; 41363e95b9abSLydia Wang /* inputs */ 41373e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 41383e95b9abSLydia Wang parm = AC_PWRST_D3; 41393e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 41403e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 41413e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 41423e95b9abSLydia Wang if (imux_is_smixer) 41433e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 41443e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 41453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 41463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 41473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 41483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 41493e95b9abSLydia Wang 41503e95b9abSLydia Wang /* outputs */ 41513e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 41523e95b9abSLydia Wang parm = AC_PWRST_D3; 41533e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 41543e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 41553e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 41563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 41573e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 41583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 41593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 41603e95b9abSLydia Wang } 41613e95b9abSLydia Wang 4162d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4163d949cac1SHarald Welte { 4164d949cac1SHarald Welte struct via_spec *spec; 4165d949cac1SHarald Welte int err; 4166d949cac1SHarald Welte 4167d949cac1SHarald Welte /* create a codec specific record */ 41685b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4169d949cac1SHarald Welte if (spec == NULL) 4170d949cac1SHarald Welte return -ENOMEM; 4171d949cac1SHarald Welte 4172d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4173d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4174d949cac1SHarald Welte if (err < 0) { 4175d949cac1SHarald Welte via_free(codec); 4176d949cac1SHarald Welte return err; 4177d949cac1SHarald Welte } else if (!err) { 4178d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4179d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4180d949cac1SHarald Welte } 4181d949cac1SHarald Welte 418269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 418369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4184d949cac1SHarald Welte 4185d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4186d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4187d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4188d949cac1SHarald Welte 4189d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4190d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4191d949cac1SHarald Welte 4192d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4193d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4194d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4195337b9d02STakashi Iwai get_mux_nids(codec); 4196d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4197d949cac1SHarald Welte spec->num_mixers++; 4198d949cac1SHarald Welte } 4199d949cac1SHarald Welte 4200d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4201d949cac1SHarald Welte 4202d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 420369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4204d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4205d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4206d949cac1SHarald Welte #endif 4207d949cac1SHarald Welte 42083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 4209d949cac1SHarald Welte return 0; 4210d949cac1SHarald Welte } 4211d949cac1SHarald Welte 4212eb7188caSLydia Wang /* Patch for VT1718S */ 4213eb7188caSLydia Wang 4214eb7188caSLydia Wang /* capture mixer elements */ 4215eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4216eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4217eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4218eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4219eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4220eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4221eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4222eb7188caSLydia Wang HDA_INPUT), 4223eb7188caSLydia Wang { 4224eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4225eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4226eb7188caSLydia Wang * So call somewhat different.. 4227eb7188caSLydia Wang */ 4228eb7188caSLydia Wang .name = "Input Source", 4229eb7188caSLydia Wang .count = 2, 4230eb7188caSLydia Wang .info = via_mux_enum_info, 4231eb7188caSLydia Wang .get = via_mux_enum_get, 4232eb7188caSLydia Wang .put = via_mux_enum_put, 4233eb7188caSLydia Wang }, 4234eb7188caSLydia Wang { } /* end */ 4235eb7188caSLydia Wang }; 4236eb7188caSLydia Wang 4237eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4238eb7188caSLydia Wang /* 4239eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4240eb7188caSLydia Wang */ 4241eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4242eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4243eb7188caSLydia Wang 4244eb7188caSLydia Wang 4245eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4246eb7188caSLydia Wang * mixer widget 4247eb7188caSLydia Wang */ 4248eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4249eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4250eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4251eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4252eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4253eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4254eb7188caSLydia Wang 4255eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4256eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4257eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4258eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4259eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4260eb7188caSLydia Wang /* PW11 Input enable */ 4261eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4262eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4263eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4264eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4265eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4266eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4267eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4268eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4269eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4270eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4271eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4272eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4273eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4274eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4275eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4276eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4277eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4278eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4279eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4280eb7188caSLydia Wang { } 4281eb7188caSLydia Wang }; 4282eb7188caSLydia Wang 4283eb7188caSLydia Wang 4284eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4285eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4286eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4287eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4288eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4289eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4290eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4291eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4292eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4293eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4294eb7188caSLydia Wang { } 4295eb7188caSLydia Wang }; 4296eb7188caSLydia Wang 4297eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4298eb7188caSLydia Wang .substreams = 2, 4299eb7188caSLydia Wang .channels_min = 2, 4300eb7188caSLydia Wang .channels_max = 10, 4301eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4302eb7188caSLydia Wang .ops = { 4303eb7188caSLydia Wang .open = via_playback_pcm_open, 4304eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4305eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4306eb7188caSLydia Wang .close = via_pcm_open_close, 4307eb7188caSLydia Wang }, 4308eb7188caSLydia Wang }; 4309eb7188caSLydia Wang 4310eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4311eb7188caSLydia Wang .substreams = 2, 4312eb7188caSLydia Wang .channels_min = 2, 4313eb7188caSLydia Wang .channels_max = 2, 4314eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4315eb7188caSLydia Wang .ops = { 4316eb7188caSLydia Wang .open = via_pcm_open_close, 4317eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4318eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4319eb7188caSLydia Wang .close = via_pcm_open_close, 4320eb7188caSLydia Wang }, 4321eb7188caSLydia Wang }; 4322eb7188caSLydia Wang 4323eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4324eb7188caSLydia Wang .substreams = 2, 4325eb7188caSLydia Wang .channels_min = 2, 4326eb7188caSLydia Wang .channels_max = 2, 4327eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4328eb7188caSLydia Wang .ops = { 4329eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4330eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4331eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4332eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4333eb7188caSLydia Wang }, 4334eb7188caSLydia Wang }; 4335eb7188caSLydia Wang 4336eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4337eb7188caSLydia Wang .substreams = 1, 4338eb7188caSLydia Wang .channels_min = 2, 4339eb7188caSLydia Wang .channels_max = 2, 4340eb7188caSLydia Wang }; 4341eb7188caSLydia Wang 4342eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4343eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4344eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4345eb7188caSLydia Wang { 4346eb7188caSLydia Wang int i; 4347eb7188caSLydia Wang hda_nid_t nid; 4348eb7188caSLydia Wang 4349eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4350eb7188caSLydia Wang 4351eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4352eb7188caSLydia Wang 4353eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4354eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4355eb7188caSLydia Wang if (nid) { 4356eb7188caSLydia Wang /* config dac list */ 4357eb7188caSLydia Wang switch (i) { 4358eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4359eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4360eb7188caSLydia Wang break; 4361eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4362eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4363eb7188caSLydia Wang break; 4364eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4365eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4366eb7188caSLydia Wang break; 4367eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4368eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4369eb7188caSLydia Wang break; 4370eb7188caSLydia Wang } 4371eb7188caSLydia Wang } 4372eb7188caSLydia Wang } 4373eb7188caSLydia Wang 4374eb7188caSLydia Wang return 0; 4375eb7188caSLydia Wang } 4376eb7188caSLydia Wang 4377eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4378eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4379eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4380eb7188caSLydia Wang { 4381eb7188caSLydia Wang char name[32]; 4382ea734963STakashi Iwai static const char * const chname[4] = { 4383ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 4384ea734963STakashi Iwai }; 4385eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4386eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4387eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4388eb7188caSLydia Wang int i, err; 4389eb7188caSLydia Wang 4390eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4391eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4392eb7188caSLydia Wang 4393eb7188caSLydia Wang if (!nid) 4394eb7188caSLydia Wang continue; 4395eb7188caSLydia Wang nid_vol = nid_vols[i]; 4396eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4397eb7188caSLydia Wang 4398eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4399eb7188caSLydia Wang /* Center/LFE */ 4400eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4401eb7188caSLydia Wang "Center Playback Volume", 4402eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4403eb7188caSLydia Wang HDA_OUTPUT)); 4404eb7188caSLydia Wang if (err < 0) 4405eb7188caSLydia Wang return err; 4406eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4407eb7188caSLydia Wang "LFE Playback Volume", 4408eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4409eb7188caSLydia Wang HDA_OUTPUT)); 4410eb7188caSLydia Wang if (err < 0) 4411eb7188caSLydia Wang return err; 4412eb7188caSLydia Wang err = via_add_control( 4413eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4414eb7188caSLydia Wang "Center Playback Switch", 4415eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4416eb7188caSLydia Wang HDA_OUTPUT)); 4417eb7188caSLydia Wang if (err < 0) 4418eb7188caSLydia Wang return err; 4419eb7188caSLydia Wang err = via_add_control( 4420eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4421eb7188caSLydia Wang "LFE Playback Switch", 4422eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4423eb7188caSLydia Wang HDA_OUTPUT)); 4424eb7188caSLydia Wang if (err < 0) 4425eb7188caSLydia Wang return err; 4426eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4427eb7188caSLydia Wang /* Front */ 4428eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4429eb7188caSLydia Wang err = via_add_control( 4430eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4431eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4432eb7188caSLydia Wang if (err < 0) 4433eb7188caSLydia Wang return err; 4434eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4435eb7188caSLydia Wang err = via_add_control( 4436eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4437eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4438eb7188caSLydia Wang HDA_OUTPUT)); 4439eb7188caSLydia Wang if (err < 0) 4440eb7188caSLydia Wang return err; 4441eb7188caSLydia Wang } else { 4442eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4443eb7188caSLydia Wang err = via_add_control( 4444eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4445eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4446eb7188caSLydia Wang if (err < 0) 4447eb7188caSLydia Wang return err; 4448eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4449eb7188caSLydia Wang err = via_add_control( 4450eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4451eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4452eb7188caSLydia Wang HDA_OUTPUT)); 4453eb7188caSLydia Wang if (err < 0) 4454eb7188caSLydia Wang return err; 4455eb7188caSLydia Wang } 4456eb7188caSLydia Wang } 4457eb7188caSLydia Wang return 0; 4458eb7188caSLydia Wang } 4459eb7188caSLydia Wang 4460eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4461eb7188caSLydia Wang { 4462eb7188caSLydia Wang int err; 4463eb7188caSLydia Wang 4464eb7188caSLydia Wang if (!pin) 4465eb7188caSLydia Wang return 0; 4466eb7188caSLydia Wang 4467eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4468eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4469eb7188caSLydia Wang 4470eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4471eb7188caSLydia Wang "Headphone Playback Volume", 4472eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4473eb7188caSLydia Wang if (err < 0) 4474eb7188caSLydia Wang return err; 4475eb7188caSLydia Wang 4476eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4477eb7188caSLydia Wang "Headphone Playback Switch", 4478eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4479eb7188caSLydia Wang if (err < 0) 4480eb7188caSLydia Wang return err; 4481eb7188caSLydia Wang 4482eb7188caSLydia Wang create_hp_imux(spec); 4483eb7188caSLydia Wang return 0; 4484eb7188caSLydia Wang } 4485eb7188caSLydia Wang 4486eb7188caSLydia Wang /* create playback/capture controls for input pins */ 448710a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, 4488eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4489eb7188caSLydia Wang { 4490f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; 449110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 4492f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4493eb7188caSLydia Wang } 4494eb7188caSLydia Wang 4495eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4496eb7188caSLydia Wang { 4497eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4498eb7188caSLydia Wang int err; 4499eb7188caSLydia Wang 4500eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4501eb7188caSLydia Wang 4502eb7188caSLydia Wang if (err < 0) 4503eb7188caSLydia Wang return err; 4504eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4505eb7188caSLydia Wang if (err < 0) 4506eb7188caSLydia Wang return err; 4507eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4508eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4509eb7188caSLydia Wang 4510eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4511eb7188caSLydia Wang if (err < 0) 4512eb7188caSLydia Wang return err; 4513eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4514eb7188caSLydia Wang if (err < 0) 4515eb7188caSLydia Wang return err; 451610a20af7STakashi Iwai err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4517eb7188caSLydia Wang if (err < 0) 4518eb7188caSLydia Wang return err; 4519eb7188caSLydia Wang 4520eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4521eb7188caSLydia Wang 4522eb7188caSLydia Wang fill_dig_outs(codec); 4523eb7188caSLydia Wang 4524eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4525eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4526eb7188caSLydia Wang 4527eb7188caSLydia Wang if (spec->kctls.list) 4528eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4529eb7188caSLydia Wang 4530eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4531eb7188caSLydia Wang 4532eb7188caSLydia Wang if (spec->hp_mux) 45333d83e577STakashi Iwai via_hp_build(codec); 4534eb7188caSLydia Wang 45355b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4536eb7188caSLydia Wang 4537eb7188caSLydia Wang return 1; 4538eb7188caSLydia Wang } 4539eb7188caSLydia Wang 4540eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4541eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4542eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4543eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4544eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4545eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4546eb7188caSLydia Wang { } /* end */ 4547eb7188caSLydia Wang }; 4548eb7188caSLydia Wang #endif 4549eb7188caSLydia Wang 45503e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 45513e95b9abSLydia Wang { 45523e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45533e95b9abSLydia Wang int imux_is_smixer; 45543e95b9abSLydia Wang unsigned int parm; 45553e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 45563e95b9abSLydia Wang imux_is_smixer = 45573e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 45583e95b9abSLydia Wang /* inputs */ 45593e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45603e95b9abSLydia Wang parm = AC_PWRST_D3; 45613e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45623e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45633e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45643e95b9abSLydia Wang if (imux_is_smixer) 45653e95b9abSLydia Wang parm = AC_PWRST_D0; 45663e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 45673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45713e95b9abSLydia Wang 45723e95b9abSLydia Wang /* outputs */ 45733e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 45743e95b9abSLydia Wang parm = AC_PWRST_D3; 45753e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 45763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 45773e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 45783e95b9abSLydia Wang 45793e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 45803e95b9abSLydia Wang parm = AC_PWRST_D3; 45813e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 45823e95b9abSLydia Wang if (spec->smart51_enabled) 45833e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45843e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 45853e95b9abSLydia Wang 45863e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 45873e95b9abSLydia Wang parm = AC_PWRST_D3; 45883e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 45893e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 45903e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 45923e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 45933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 45943e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 45953e95b9abSLydia Wang 45963e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 45973e95b9abSLydia Wang parm = AC_PWRST_D3; 45983e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 45993e95b9abSLydia Wang if (spec->smart51_enabled) 46003e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 46013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 46023e95b9abSLydia Wang 46033e95b9abSLydia Wang if (spec->hp_independent_mode) { 46043e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 46053e95b9abSLydia Wang parm = AC_PWRST_D3; 46063e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 46073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 46083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 46103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46113e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 46123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46133e95b9abSLydia Wang } 46143e95b9abSLydia Wang } 46153e95b9abSLydia Wang 4616eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4617eb7188caSLydia Wang { 4618eb7188caSLydia Wang struct via_spec *spec; 4619eb7188caSLydia Wang int err; 4620eb7188caSLydia Wang 4621eb7188caSLydia Wang /* create a codec specific record */ 46225b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4623eb7188caSLydia Wang if (spec == NULL) 4624eb7188caSLydia Wang return -ENOMEM; 4625eb7188caSLydia Wang 4626eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4627eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4628eb7188caSLydia Wang if (err < 0) { 4629eb7188caSLydia Wang via_free(codec); 4630eb7188caSLydia Wang return err; 4631eb7188caSLydia Wang } else if (!err) { 4632eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4633eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4634eb7188caSLydia Wang } 4635eb7188caSLydia Wang 4636eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4637eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4638eb7188caSLydia Wang 4639bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4640bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4641bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4642bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4643bb3c6bfcSLydia Wang else 4644eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4645eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4646eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4647eb7188caSLydia Wang 4648bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4649bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4650bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4651bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4652bb3c6bfcSLydia Wang else 4653eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4654eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4655bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4656eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4657eb7188caSLydia Wang 4658eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4659eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4660eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4661eb7188caSLydia Wang get_mux_nids(codec); 4662bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4663bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4664eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4665eb7188caSLydia Wang spec->num_mixers++; 4666eb7188caSLydia Wang } 4667eb7188caSLydia Wang 4668eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4669eb7188caSLydia Wang 4670eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 46710f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4672eb7188caSLydia Wang 4673eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4674eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4675eb7188caSLydia Wang #endif 4676eb7188caSLydia Wang 46773e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 46783e95b9abSLydia Wang 4679eb7188caSLydia Wang return 0; 4680eb7188caSLydia Wang } 4681f3db423dSLydia Wang 4682f3db423dSLydia Wang /* Patch for VT1716S */ 4683f3db423dSLydia Wang 4684f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4685f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4686f3db423dSLydia Wang { 4687f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4688f3db423dSLydia Wang uinfo->count = 1; 4689f3db423dSLydia Wang uinfo->value.integer.min = 0; 4690f3db423dSLydia Wang uinfo->value.integer.max = 1; 4691f3db423dSLydia Wang return 0; 4692f3db423dSLydia Wang } 4693f3db423dSLydia Wang 4694f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4695f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4696f3db423dSLydia Wang { 4697f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4698f3db423dSLydia Wang int index = 0; 4699f3db423dSLydia Wang 4700f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4701f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4702f3db423dSLydia Wang if (index != -1) 4703f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4704f3db423dSLydia Wang 4705f3db423dSLydia Wang return 0; 4706f3db423dSLydia Wang } 4707f3db423dSLydia Wang 4708f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4709f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4710f3db423dSLydia Wang { 4711f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4712f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4713f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4714f3db423dSLydia Wang 4715f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4716f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4717f3db423dSLydia Wang spec->dmic_enabled = index; 47183e95b9abSLydia Wang set_widgets_power_state(codec); 4719f3db423dSLydia Wang return 1; 4720f3db423dSLydia Wang } 4721f3db423dSLydia Wang 4722f3db423dSLydia Wang /* capture mixer elements */ 4723f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4724f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4725f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4726f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4727f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4728f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4729f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4730f3db423dSLydia Wang HDA_INPUT), 4731f3db423dSLydia Wang { 4732f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4733f3db423dSLydia Wang .name = "Input Source", 4734f3db423dSLydia Wang .count = 1, 4735f3db423dSLydia Wang .info = via_mux_enum_info, 4736f3db423dSLydia Wang .get = via_mux_enum_get, 4737f3db423dSLydia Wang .put = via_mux_enum_put, 4738f3db423dSLydia Wang }, 4739f3db423dSLydia Wang { } /* end */ 4740f3db423dSLydia Wang }; 4741f3db423dSLydia Wang 4742f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4743f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4744f3db423dSLydia Wang { 4745f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4746f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 47475b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 4748f3db423dSLydia Wang .count = 1, 4749f3db423dSLydia Wang .info = vt1716s_dmic_info, 4750f3db423dSLydia Wang .get = vt1716s_dmic_get, 4751f3db423dSLydia Wang .put = vt1716s_dmic_put, 4752f3db423dSLydia Wang }, 4753f3db423dSLydia Wang {} /* end */ 4754f3db423dSLydia Wang }; 4755f3db423dSLydia Wang 4756f3db423dSLydia Wang 4757f3db423dSLydia Wang /* mono-out mixer elements */ 4758f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4759f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4760f3db423dSLydia Wang { } /* end */ 4761f3db423dSLydia Wang }; 4762f3db423dSLydia Wang 4763f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 4764f3db423dSLydia Wang /* 4765f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4766f3db423dSLydia Wang */ 4767f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4768f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4769f3db423dSLydia Wang 4770f3db423dSLydia Wang 4771f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4772f3db423dSLydia Wang * mixer widget 4773f3db423dSLydia Wang */ 4774f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4775f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4776f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4777f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4778f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4779f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4780f3db423dSLydia Wang 4781f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4782f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4783f3db423dSLydia Wang 4784f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4785f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4786f3db423dSLydia Wang 4787f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4788f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4789f3db423dSLydia Wang 4790f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4791f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4792f3db423dSLydia Wang 4793f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4794f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4795f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4796f3db423dSLydia Wang 4797f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4798f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4799f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4800f3db423dSLydia Wang /* PW12 Output enable */ 4801f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4802f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4803f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4804f3db423dSLydia Wang /* don't bybass mixer */ 4805f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4806f3db423dSLydia Wang /* Enable mono output */ 4807f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4808f3db423dSLydia Wang { } 4809f3db423dSLydia Wang }; 4810f3db423dSLydia Wang 4811f3db423dSLydia Wang 4812f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 4813f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4814f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4815f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4816f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4817f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4818f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4819f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4820f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4821f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4822f3db423dSLydia Wang { } 4823f3db423dSLydia Wang }; 4824f3db423dSLydia Wang 4825f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4826f3db423dSLydia Wang .substreams = 2, 4827f3db423dSLydia Wang .channels_min = 2, 4828f3db423dSLydia Wang .channels_max = 6, 4829f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4830f3db423dSLydia Wang .ops = { 4831f3db423dSLydia Wang .open = via_playback_pcm_open, 4832f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4833f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4834f3db423dSLydia Wang .close = via_pcm_open_close, 4835f3db423dSLydia Wang }, 4836f3db423dSLydia Wang }; 4837f3db423dSLydia Wang 4838f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4839f3db423dSLydia Wang .substreams = 2, 4840f3db423dSLydia Wang .channels_min = 2, 4841f3db423dSLydia Wang .channels_max = 2, 4842f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4843f3db423dSLydia Wang .ops = { 4844f3db423dSLydia Wang .open = via_pcm_open_close, 4845f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4846f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4847f3db423dSLydia Wang .close = via_pcm_open_close, 4848f3db423dSLydia Wang }, 4849f3db423dSLydia Wang }; 4850f3db423dSLydia Wang 4851f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4852f3db423dSLydia Wang .substreams = 2, 4853f3db423dSLydia Wang .channels_min = 2, 4854f3db423dSLydia Wang .channels_max = 2, 4855f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4856f3db423dSLydia Wang .ops = { 4857f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4858f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4859f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4860f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4861f3db423dSLydia Wang }, 4862f3db423dSLydia Wang }; 4863f3db423dSLydia Wang 4864f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4865f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 4866f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4867f3db423dSLydia Wang { int i; 4868f3db423dSLydia Wang hda_nid_t nid; 4869f3db423dSLydia Wang 4870f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4871f3db423dSLydia Wang 4872f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4873f3db423dSLydia Wang 4874f3db423dSLydia Wang for (i = 0; i < 3; i++) { 4875f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4876f3db423dSLydia Wang if (nid) { 4877f3db423dSLydia Wang /* config dac list */ 4878f3db423dSLydia Wang switch (i) { 4879f3db423dSLydia Wang case AUTO_SEQ_FRONT: 4880f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 4881f3db423dSLydia Wang break; 4882f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 4883f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 4884f3db423dSLydia Wang break; 4885f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 4886f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 4887f3db423dSLydia Wang break; 4888f3db423dSLydia Wang } 4889f3db423dSLydia Wang } 4890f3db423dSLydia Wang } 4891f3db423dSLydia Wang 4892f3db423dSLydia Wang return 0; 4893f3db423dSLydia Wang } 4894f3db423dSLydia Wang 4895f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 4896f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 4897f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4898f3db423dSLydia Wang { 4899f3db423dSLydia Wang char name[32]; 4900ea734963STakashi Iwai static const char * const chname[3] = { 4901ea734963STakashi Iwai "Front", "Surround", "C/LFE" 4902ea734963STakashi Iwai }; 4903f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 4904f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 4905f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 4906f3db423dSLydia Wang int i, err; 4907f3db423dSLydia Wang 4908f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 4909f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4910f3db423dSLydia Wang 4911f3db423dSLydia Wang if (!nid) 4912f3db423dSLydia Wang continue; 4913f3db423dSLydia Wang 4914f3db423dSLydia Wang nid_vol = nid_vols[i]; 4915f3db423dSLydia Wang nid_mute = nid_mutes[i]; 4916f3db423dSLydia Wang 4917f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4918f3db423dSLydia Wang err = via_add_control( 4919f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4920f3db423dSLydia Wang "Center Playback Volume", 4921f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 4922f3db423dSLydia Wang if (err < 0) 4923f3db423dSLydia Wang return err; 4924f3db423dSLydia Wang err = via_add_control( 4925f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4926f3db423dSLydia Wang "LFE Playback Volume", 4927f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 4928f3db423dSLydia Wang if (err < 0) 4929f3db423dSLydia Wang return err; 4930f3db423dSLydia Wang err = via_add_control( 4931f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4932f3db423dSLydia Wang "Center Playback Switch", 4933f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4934f3db423dSLydia Wang HDA_OUTPUT)); 4935f3db423dSLydia Wang if (err < 0) 4936f3db423dSLydia Wang return err; 4937f3db423dSLydia Wang err = via_add_control( 4938f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4939f3db423dSLydia Wang "LFE Playback Switch", 4940f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4941f3db423dSLydia Wang HDA_OUTPUT)); 4942f3db423dSLydia Wang if (err < 0) 4943f3db423dSLydia Wang return err; 4944f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4945f3db423dSLydia Wang 4946f3db423dSLydia Wang err = via_add_control( 4947f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4948f3db423dSLydia Wang "Master Front Playback Volume", 4949f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4950f3db423dSLydia Wang if (err < 0) 4951f3db423dSLydia Wang return err; 4952f3db423dSLydia Wang err = via_add_control( 4953f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4954f3db423dSLydia Wang "Master Front Playback Switch", 4955f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4956f3db423dSLydia Wang if (err < 0) 4957f3db423dSLydia Wang return err; 4958f3db423dSLydia Wang 4959f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4960f3db423dSLydia Wang err = via_add_control( 4961f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4962f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4963f3db423dSLydia Wang if (err < 0) 4964f3db423dSLydia Wang return err; 4965f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4966f3db423dSLydia Wang err = via_add_control( 4967f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4968f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4969f3db423dSLydia Wang HDA_OUTPUT)); 4970f3db423dSLydia Wang if (err < 0) 4971f3db423dSLydia Wang return err; 4972f3db423dSLydia Wang } else { 4973f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4974f3db423dSLydia Wang err = via_add_control( 4975f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4976f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4977f3db423dSLydia Wang if (err < 0) 4978f3db423dSLydia Wang return err; 4979f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4980f3db423dSLydia Wang err = via_add_control( 4981f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4982f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4983f3db423dSLydia Wang HDA_OUTPUT)); 4984f3db423dSLydia Wang if (err < 0) 4985f3db423dSLydia Wang return err; 4986f3db423dSLydia Wang } 4987f3db423dSLydia Wang } 4988f3db423dSLydia Wang return 0; 4989f3db423dSLydia Wang } 4990f3db423dSLydia Wang 4991f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4992f3db423dSLydia Wang { 4993f3db423dSLydia Wang int err; 4994f3db423dSLydia Wang 4995f3db423dSLydia Wang if (!pin) 4996f3db423dSLydia Wang return 0; 4997f3db423dSLydia Wang 4998f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 4999f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5000f3db423dSLydia Wang 5001f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5002f3db423dSLydia Wang "Headphone Playback Volume", 5003f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5004f3db423dSLydia Wang if (err < 0) 5005f3db423dSLydia Wang return err; 5006f3db423dSLydia Wang 5007f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5008f3db423dSLydia Wang "Headphone Playback Switch", 5009f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5010f3db423dSLydia Wang if (err < 0) 5011f3db423dSLydia Wang return err; 5012f3db423dSLydia Wang 5013f3db423dSLydia Wang create_hp_imux(spec); 5014f3db423dSLydia Wang return 0; 5015f3db423dSLydia Wang } 5016f3db423dSLydia Wang 5017f3db423dSLydia Wang /* create playback/capture controls for input pins */ 501810a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, 5019f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5020f3db423dSLydia Wang { 5021f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 502210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 5023f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5024f3db423dSLydia Wang } 5025f3db423dSLydia Wang 5026f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5027f3db423dSLydia Wang { 5028f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5029f3db423dSLydia Wang int err; 5030f3db423dSLydia Wang 5031f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5032f3db423dSLydia Wang if (err < 0) 5033f3db423dSLydia Wang return err; 5034f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5035f3db423dSLydia Wang if (err < 0) 5036f3db423dSLydia Wang return err; 5037f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5038f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5039f3db423dSLydia Wang 5040f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5041f3db423dSLydia Wang if (err < 0) 5042f3db423dSLydia Wang return err; 5043f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5044f3db423dSLydia Wang if (err < 0) 5045f3db423dSLydia Wang return err; 504610a20af7STakashi Iwai err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); 5047f3db423dSLydia Wang if (err < 0) 5048f3db423dSLydia Wang return err; 5049f3db423dSLydia Wang 5050f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5051f3db423dSLydia Wang 5052f3db423dSLydia Wang fill_dig_outs(codec); 5053f3db423dSLydia Wang 5054f3db423dSLydia Wang if (spec->kctls.list) 5055f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5056f3db423dSLydia Wang 5057f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5058f3db423dSLydia Wang 5059f3db423dSLydia Wang if (spec->hp_mux) 50603d83e577STakashi Iwai via_hp_build(codec); 5061f3db423dSLydia Wang 50625b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5063f3db423dSLydia Wang 5064f3db423dSLydia Wang return 1; 5065f3db423dSLydia Wang } 5066f3db423dSLydia Wang 5067f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5068f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5069f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5070f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5071f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5072f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5073f3db423dSLydia Wang { } /* end */ 5074f3db423dSLydia Wang }; 5075f3db423dSLydia Wang #endif 5076f3db423dSLydia Wang 50773e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 50783e95b9abSLydia Wang { 50793e95b9abSLydia Wang struct via_spec *spec = codec->spec; 50803e95b9abSLydia Wang int imux_is_smixer; 50813e95b9abSLydia Wang unsigned int parm; 50823e95b9abSLydia Wang unsigned int mono_out, present; 50833e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 50843e95b9abSLydia Wang imux_is_smixer = 50853e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 50863e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 50873e95b9abSLydia Wang /* inputs */ 50883e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 50893e95b9abSLydia Wang parm = AC_PWRST_D3; 50903e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 50913e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 50923e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 50933e95b9abSLydia Wang if (imux_is_smixer) 50943e95b9abSLydia Wang parm = AC_PWRST_D0; 50953e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 50963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 50973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 50983e95b9abSLydia Wang 50993e95b9abSLydia Wang parm = AC_PWRST_D3; 51003e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51013e95b9abSLydia Wang /* PW11 (22h) */ 51023e95b9abSLydia Wang if (spec->dmic_enabled) 51033e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 51043e95b9abSLydia Wang else 51053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 51063e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 51073e95b9abSLydia Wang 51083e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 51093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 51103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 51113e95b9abSLydia Wang 51123e95b9abSLydia Wang /* outputs */ 51133e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 51143e95b9abSLydia Wang parm = AC_PWRST_D3; 51153e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 51163e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 51173e95b9abSLydia Wang if (spec->smart51_enabled) 51183e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 51193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 51203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 51213e95b9abSLydia Wang 51223e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 51233e95b9abSLydia Wang parm = AC_PWRST_D3; 51243e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 51253e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 51263e95b9abSLydia Wang if (spec->smart51_enabled) 51273e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 51283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 51293e95b9abSLydia Wang 51303e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 51313e95b9abSLydia Wang if (spec->smart51_enabled) 51323e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 51343e95b9abSLydia Wang 51353e95b9abSLydia Wang /* Mono out */ 51363e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 51373e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 51383e95b9abSLydia Wang 51393e95b9abSLydia Wang if (present) 51403e95b9abSLydia Wang mono_out = 0; 51413e95b9abSLydia Wang else { 51423e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 51433e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 51443e95b9abSLydia Wang mono_out = 0; 51453e95b9abSLydia Wang else 51463e95b9abSLydia Wang mono_out = 1; 51473e95b9abSLydia Wang } 51483e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 51493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 51503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 51513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 51523e95b9abSLydia Wang 51533e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 51543e95b9abSLydia Wang parm = AC_PWRST_D3; 51553e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 51563e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 51573e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 51583e95b9abSLydia Wang if (spec->hp_independent_mode) 51593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 51603e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 51613e95b9abSLydia Wang 51623e95b9abSLydia Wang /* force to D0 for internal Speaker */ 51633e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 51643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 51653e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 51663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 51673e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 51683e95b9abSLydia Wang } 51693e95b9abSLydia Wang 5170f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5171f3db423dSLydia Wang { 5172f3db423dSLydia Wang struct via_spec *spec; 5173f3db423dSLydia Wang int err; 5174f3db423dSLydia Wang 5175f3db423dSLydia Wang /* create a codec specific record */ 51765b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5177f3db423dSLydia Wang if (spec == NULL) 5178f3db423dSLydia Wang return -ENOMEM; 5179f3db423dSLydia Wang 5180f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5181f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5182f3db423dSLydia Wang if (err < 0) { 5183f3db423dSLydia Wang via_free(codec); 5184f3db423dSLydia Wang return err; 5185f3db423dSLydia Wang } else if (!err) { 5186f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5187f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5188f3db423dSLydia Wang } 5189f3db423dSLydia Wang 5190f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5191f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5192f3db423dSLydia Wang 5193f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5194f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5195f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5196f3db423dSLydia Wang 5197f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5198f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5199f3db423dSLydia Wang 5200f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5201f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5202f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5203f3db423dSLydia Wang get_mux_nids(codec); 5204f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5205f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5206f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5207f3db423dSLydia Wang spec->num_mixers++; 5208f3db423dSLydia Wang } 5209f3db423dSLydia Wang 5210f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5211f3db423dSLydia Wang spec->num_mixers++; 5212f3db423dSLydia Wang 5213f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5214f3db423dSLydia Wang 5215f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5216f3db423dSLydia Wang 5217f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 52180f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5219f3db423dSLydia Wang 5220f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5221f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5222f3db423dSLydia Wang #endif 5223f3db423dSLydia Wang 52243e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 5225f3db423dSLydia Wang return 0; 5226f3db423dSLydia Wang } 522725eaba2fSLydia Wang 522825eaba2fSLydia Wang /* for vt2002P */ 522925eaba2fSLydia Wang 523025eaba2fSLydia Wang /* capture mixer elements */ 523125eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 523225eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 523325eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 523425eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 523525eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 523625eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 523725eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 523825eaba2fSLydia Wang HDA_INPUT), 523925eaba2fSLydia Wang { 524025eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 524125eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 524225eaba2fSLydia Wang * So call somewhat different.. 524325eaba2fSLydia Wang */ 524425eaba2fSLydia Wang /* .name = "Capture Source", */ 524525eaba2fSLydia Wang .name = "Input Source", 524625eaba2fSLydia Wang .count = 2, 524725eaba2fSLydia Wang .info = via_mux_enum_info, 524825eaba2fSLydia Wang .get = via_mux_enum_get, 524925eaba2fSLydia Wang .put = via_mux_enum_put, 525025eaba2fSLydia Wang }, 525125eaba2fSLydia Wang { } /* end */ 525225eaba2fSLydia Wang }; 525325eaba2fSLydia Wang 525425eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 525525eaba2fSLydia Wang /* 525625eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 525725eaba2fSLydia Wang */ 525825eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 525925eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 526025eaba2fSLydia Wang 526125eaba2fSLydia Wang 526225eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 526325eaba2fSLydia Wang * mixer widget 526425eaba2fSLydia Wang */ 526525eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 526625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 526725eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 526825eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 526925eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 527025eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 527125eaba2fSLydia Wang 527225eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 527325eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 527425eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 527525eaba2fSLydia Wang 527625eaba2fSLydia Wang /* PW9 Output enable */ 527725eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 527825eaba2fSLydia Wang 527925eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 528025eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 528125eaba2fSLydia Wang 528225eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 528325eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 528425eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 528525eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 528625eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 528725eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 528825eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 528925eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 529025eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 529125eaba2fSLydia Wang 529225eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 529325eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 529425eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 529525eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 529625eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 529725eaba2fSLydia Wang 529825eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 529925eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 530025eaba2fSLydia Wang 530125eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 530225eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 530325eaba2fSLydia Wang { } 530425eaba2fSLydia Wang }; 5305*11890956SLydia Wang static struct hda_verb vt1802_volume_init_verbs[] = { 5306*11890956SLydia Wang /* 5307*11890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5308*11890956SLydia Wang */ 5309*11890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5310*11890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5311*11890956SLydia Wang 5312*11890956SLydia Wang 5313*11890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5314*11890956SLydia Wang * mixer widget 5315*11890956SLydia Wang */ 5316*11890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5317*11890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5318*11890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5319*11890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5320*11890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5321*11890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5322*11890956SLydia Wang 5323*11890956SLydia Wang /* MUX Indices: Mic = 0 */ 5324*11890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5325*11890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5326*11890956SLydia Wang 5327*11890956SLydia Wang /* PW9 Output enable */ 5328*11890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5329*11890956SLydia Wang 5330*11890956SLydia Wang /* Enable Boost Volume backdoor */ 5331*11890956SLydia Wang {0x1, 0xfb9, 0x24}, 5332*11890956SLydia Wang 5333*11890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5334*11890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5335*11890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5336*11890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5337*11890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5338*11890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5339*11890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5340*11890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5341*11890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5342*11890956SLydia Wang 5343*11890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 5344*11890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5345*11890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5346*11890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5347*11890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5348*11890956SLydia Wang 5349*11890956SLydia Wang /* set PW0 index=0 (MW0) */ 5350*11890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 5351*11890956SLydia Wang 5352*11890956SLydia Wang /* Enable AOW0 to MW9 */ 5353*11890956SLydia Wang {0x1, 0xfb8, 0x88}, 5354*11890956SLydia Wang { } 5355*11890956SLydia Wang }; 535625eaba2fSLydia Wang 535725eaba2fSLydia Wang 535825eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 535925eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 536025eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 536125eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 536225eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 536325eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536425eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536525eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536625eaba2fSLydia Wang { } 536725eaba2fSLydia Wang }; 5368*11890956SLydia Wang static struct hda_verb vt1802_uniwill_init_verbs[] = { 5369*11890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 5370*11890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5371*11890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5372*11890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5373*11890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5374*11890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5375*11890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5376*11890956SLydia Wang { } 5377*11890956SLydia Wang }; 537825eaba2fSLydia Wang 537925eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 538025eaba2fSLydia Wang .substreams = 2, 538125eaba2fSLydia Wang .channels_min = 2, 538225eaba2fSLydia Wang .channels_max = 2, 538325eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 538425eaba2fSLydia Wang .ops = { 538525eaba2fSLydia Wang .open = via_playback_pcm_open, 538625eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 538725eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 538825eaba2fSLydia Wang .close = via_pcm_open_close, 538925eaba2fSLydia Wang }, 539025eaba2fSLydia Wang }; 539125eaba2fSLydia Wang 539225eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 539325eaba2fSLydia Wang .substreams = 2, 539425eaba2fSLydia Wang .channels_min = 2, 539525eaba2fSLydia Wang .channels_max = 2, 539625eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 539725eaba2fSLydia Wang .ops = { 539825eaba2fSLydia Wang .open = via_pcm_open_close, 539925eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 540025eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 540125eaba2fSLydia Wang .close = via_pcm_open_close, 540225eaba2fSLydia Wang }, 540325eaba2fSLydia Wang }; 540425eaba2fSLydia Wang 540525eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 540625eaba2fSLydia Wang .substreams = 1, 540725eaba2fSLydia Wang .channels_min = 2, 540825eaba2fSLydia Wang .channels_max = 2, 540925eaba2fSLydia Wang /* NID is set in via_build_pcms */ 541025eaba2fSLydia Wang .ops = { 541125eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 541225eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 541325eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 541425eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 541525eaba2fSLydia Wang }, 541625eaba2fSLydia Wang }; 541725eaba2fSLydia Wang 541825eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 541925eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 542025eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 542125eaba2fSLydia Wang { 542225eaba2fSLydia Wang spec->multiout.num_dacs = 1; 542325eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 542425eaba2fSLydia Wang if (cfg->line_out_pins[0]) 542525eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 542625eaba2fSLydia Wang return 0; 542725eaba2fSLydia Wang } 542825eaba2fSLydia Wang 542925eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 543025eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 543125eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 543225eaba2fSLydia Wang { 543325eaba2fSLydia Wang int err; 5434*11890956SLydia Wang hda_nid_t sw_nid; 543525eaba2fSLydia Wang 543625eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 543725eaba2fSLydia Wang return -1; 543825eaba2fSLydia Wang 5439*11890956SLydia Wang if (spec->codec_type == VT1802) 5440*11890956SLydia Wang sw_nid = 0x28; 5441*11890956SLydia Wang else 5442*11890956SLydia Wang sw_nid = 0x26; 544325eaba2fSLydia Wang 544425eaba2fSLydia Wang /* Line-Out: PortE */ 544525eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 544625eaba2fSLydia Wang "Master Front Playback Volume", 544725eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 544825eaba2fSLydia Wang if (err < 0) 544925eaba2fSLydia Wang return err; 545025eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 545125eaba2fSLydia Wang "Master Front Playback Switch", 5452*11890956SLydia Wang HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT)); 545325eaba2fSLydia Wang if (err < 0) 545425eaba2fSLydia Wang return err; 545525eaba2fSLydia Wang 545625eaba2fSLydia Wang return 0; 545725eaba2fSLydia Wang } 545825eaba2fSLydia Wang 545925eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 546025eaba2fSLydia Wang { 546125eaba2fSLydia Wang int err; 546225eaba2fSLydia Wang 546325eaba2fSLydia Wang if (!pin) 546425eaba2fSLydia Wang return 0; 546525eaba2fSLydia Wang 546625eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 546725eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 546825eaba2fSLydia Wang 546925eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 547025eaba2fSLydia Wang "Headphone Playback Volume", 547125eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 547225eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 547325eaba2fSLydia Wang if (err < 0) 547425eaba2fSLydia Wang return err; 547525eaba2fSLydia Wang 547625eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 547725eaba2fSLydia Wang "Headphone Playback Switch", 547825eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 547925eaba2fSLydia Wang if (err < 0) 548025eaba2fSLydia Wang return err; 548125eaba2fSLydia Wang 548225eaba2fSLydia Wang create_hp_imux(spec); 548325eaba2fSLydia Wang return 0; 548425eaba2fSLydia Wang } 548525eaba2fSLydia Wang 548625eaba2fSLydia Wang /* create playback/capture controls for input pins */ 548710a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, 548825eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 548925eaba2fSLydia Wang { 549010a20af7STakashi Iwai struct via_spec *spec = codec->spec; 549125eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5492f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; 5493f3268512STakashi Iwai int err; 549425eaba2fSLydia Wang 549510a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5496f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 549725eaba2fSLydia Wang if (err < 0) 549825eaba2fSLydia Wang return err; 549925eaba2fSLydia Wang /* build volume/mute control of loopback */ 55007b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); 550125eaba2fSLydia Wang if (err < 0) 550225eaba2fSLydia Wang return err; 550325eaba2fSLydia Wang 550425eaba2fSLydia Wang /* for digital mic select */ 550510a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); 550625eaba2fSLydia Wang 550725eaba2fSLydia Wang return 0; 550825eaba2fSLydia Wang } 550925eaba2fSLydia Wang 551025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 551125eaba2fSLydia Wang { 551225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 551325eaba2fSLydia Wang int err; 551425eaba2fSLydia Wang 551525eaba2fSLydia Wang 551625eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 551725eaba2fSLydia Wang if (err < 0) 551825eaba2fSLydia Wang return err; 551925eaba2fSLydia Wang 552025eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 552125eaba2fSLydia Wang if (err < 0) 552225eaba2fSLydia Wang return err; 552325eaba2fSLydia Wang 552425eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 552525eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 552625eaba2fSLydia Wang 552725eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 552825eaba2fSLydia Wang if (err < 0) 552925eaba2fSLydia Wang return err; 553025eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 553125eaba2fSLydia Wang if (err < 0) 553225eaba2fSLydia Wang return err; 553310a20af7STakashi Iwai err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); 553425eaba2fSLydia Wang if (err < 0) 553525eaba2fSLydia Wang return err; 553625eaba2fSLydia Wang 553725eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 553825eaba2fSLydia Wang 553925eaba2fSLydia Wang fill_dig_outs(codec); 554025eaba2fSLydia Wang 554125eaba2fSLydia Wang if (spec->kctls.list) 554225eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 554325eaba2fSLydia Wang 554425eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 554525eaba2fSLydia Wang 554625eaba2fSLydia Wang if (spec->hp_mux) 55473d83e577STakashi Iwai via_hp_build(codec); 554825eaba2fSLydia Wang 554925eaba2fSLydia Wang return 1; 555025eaba2fSLydia Wang } 555125eaba2fSLydia Wang 555225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 555325eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 555425eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 555525eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 555625eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 555725eaba2fSLydia Wang { } /* end */ 555825eaba2fSLydia Wang }; 555925eaba2fSLydia Wang #endif 556025eaba2fSLydia Wang 55613e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 55623e95b9abSLydia Wang { 55633e95b9abSLydia Wang struct via_spec *spec = codec->spec; 55643e95b9abSLydia Wang int imux_is_smixer; 55653e95b9abSLydia Wang unsigned int parm; 55663e95b9abSLydia Wang unsigned int present; 55673e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 55683e95b9abSLydia Wang imux_is_smixer = 55693e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 55703e95b9abSLydia Wang /* inputs */ 55713e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 55723e95b9abSLydia Wang parm = AC_PWRST_D3; 55733e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 55743e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 55753e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 55763e95b9abSLydia Wang parm = AC_PWRST_D0; 55773e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 55783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 55793e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 55803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 55813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 55823e95b9abSLydia Wang 55833e95b9abSLydia Wang /* outputs */ 55843e95b9abSLydia Wang /* AOW0 (8h)*/ 55853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 55863e95b9abSLydia Wang 5587*11890956SLydia Wang if (spec->codec_type == VT1802) { 5588*11890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 5589*11890956SLydia Wang parm = AC_PWRST_D3; 5590*11890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 5591*11890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 5592*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5593*11890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 5594*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5595*11890956SLydia Wang } else { 55963e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 55973e95b9abSLydia Wang parm = AC_PWRST_D3; 55983e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 55993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 56003e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 56023e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 5603*11890956SLydia Wang } 56043e95b9abSLydia Wang 5605*11890956SLydia Wang if (spec->codec_type == VT1802) { 5606*11890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 5607*11890956SLydia Wang parm = AC_PWRST_D3; 5608*11890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 5609*11890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 5610*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5611*11890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 5612*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5613*11890956SLydia Wang } else { 56143e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 56153e95b9abSLydia Wang parm = AC_PWRST_D3; 56163e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 56173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 56183e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 56203e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 5621*11890956SLydia Wang } 56223e95b9abSLydia Wang 56233e95b9abSLydia Wang if (spec->hp_independent_mode) 56243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 56253e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 56263e95b9abSLydia Wang 56273e95b9abSLydia Wang /* Class-D */ 56283e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 56293e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 56303e95b9abSLydia Wang 56313e95b9abSLydia Wang parm = AC_PWRST_D3; 56323e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 56333e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 5634*11890956SLydia Wang if (spec->codec_type == VT1802) 5635*11890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 5636*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5637*11890956SLydia Wang else 56383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 56393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 56413e95b9abSLydia Wang 56423e95b9abSLydia Wang /* Mono Out */ 56433e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 56443e95b9abSLydia Wang 56453e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 5646*11890956SLydia Wang if (spec->codec_type == VT1802) { 5647*11890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 5648*11890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 5649*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5650*11890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 5651*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5652*11890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 5653*11890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 5654*11890956SLydia Wang } else { 56553e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 56563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 56573e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 56593e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 56613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 5662*11890956SLydia Wang } 56633e95b9abSLydia Wang /* MW9 (21h) */ 56643e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 56653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 56663e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 56673e95b9abSLydia Wang else 56683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 56693e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 56703e95b9abSLydia Wang } 567125eaba2fSLydia Wang 567225eaba2fSLydia Wang /* patch for vt2002P */ 567325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 567425eaba2fSLydia Wang { 567525eaba2fSLydia Wang struct via_spec *spec; 567625eaba2fSLydia Wang int err; 567725eaba2fSLydia Wang 567825eaba2fSLydia Wang /* create a codec specific record */ 56795b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 568025eaba2fSLydia Wang if (spec == NULL) 568125eaba2fSLydia Wang return -ENOMEM; 568225eaba2fSLydia Wang 568325eaba2fSLydia Wang /* automatic parse from the BIOS config */ 568425eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 568525eaba2fSLydia Wang if (err < 0) { 568625eaba2fSLydia Wang via_free(codec); 568725eaba2fSLydia Wang return err; 568825eaba2fSLydia Wang } else if (!err) { 568925eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 569025eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 569125eaba2fSLydia Wang } 569225eaba2fSLydia Wang 5693*11890956SLydia Wang if (spec->codec_type == VT1802) 5694*11890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 5695*11890956SLydia Wang vt1802_volume_init_verbs; 5696*11890956SLydia Wang else 5697*11890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 5698*11890956SLydia Wang vt2002P_volume_init_verbs; 569925eaba2fSLydia Wang 5700*11890956SLydia Wang if (spec->codec_type == VT1802) 5701*11890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 5702*11890956SLydia Wang vt1802_uniwill_init_verbs; 5703*11890956SLydia Wang else 5704*11890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 5705*11890956SLydia Wang vt2002P_uniwill_init_verbs; 5706*11890956SLydia Wang 5707*11890956SLydia Wang if (spec->codec_type == VT1802) 5708*11890956SLydia Wang spec->stream_name_analog = "VT1802 Analog"; 5709*11890956SLydia Wang else 571025eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 571125eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 571225eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 571325eaba2fSLydia Wang 5714*11890956SLydia Wang if (spec->codec_type == VT1802) 5715*11890956SLydia Wang spec->stream_name_digital = "VT1802 Digital"; 5716*11890956SLydia Wang else 571725eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 571825eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 571925eaba2fSLydia Wang 572025eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 572125eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 572225eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 572325eaba2fSLydia Wang get_mux_nids(codec); 572425eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 572525eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 572625eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 572725eaba2fSLydia Wang spec->num_mixers++; 572825eaba2fSLydia Wang } 572925eaba2fSLydia Wang 573025eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 573125eaba2fSLydia Wang 573225eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 57330f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 573425eaba2fSLydia Wang 573525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 573625eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 573725eaba2fSLydia Wang #endif 573825eaba2fSLydia Wang 57393e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 574025eaba2fSLydia Wang return 0; 574125eaba2fSLydia Wang } 5742ab6734e7SLydia Wang 5743ab6734e7SLydia Wang /* for vt1812 */ 5744ab6734e7SLydia Wang 5745ab6734e7SLydia Wang /* capture mixer elements */ 5746ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = { 5747ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5748ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5749ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5750ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5751ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5752ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5753ab6734e7SLydia Wang HDA_INPUT), 5754ab6734e7SLydia Wang { 5755ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5756ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5757ab6734e7SLydia Wang * So call somewhat different.. 5758ab6734e7SLydia Wang */ 5759ab6734e7SLydia Wang .name = "Input Source", 5760ab6734e7SLydia Wang .count = 2, 5761ab6734e7SLydia Wang .info = via_mux_enum_info, 5762ab6734e7SLydia Wang .get = via_mux_enum_get, 5763ab6734e7SLydia Wang .put = via_mux_enum_put, 5764ab6734e7SLydia Wang }, 5765ab6734e7SLydia Wang { } /* end */ 5766ab6734e7SLydia Wang }; 5767ab6734e7SLydia Wang 5768ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = { 5769ab6734e7SLydia Wang /* 5770ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5771ab6734e7SLydia Wang */ 5772ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5773ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5774ab6734e7SLydia Wang 5775ab6734e7SLydia Wang 5776ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5777ab6734e7SLydia Wang * mixer widget 5778ab6734e7SLydia Wang */ 5779ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5780ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5781ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5782ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5783ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5784ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5785ab6734e7SLydia Wang 5786ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5787ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5788ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5789ab6734e7SLydia Wang 5790ab6734e7SLydia Wang /* PW9 Output enable */ 5791ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5792ab6734e7SLydia Wang 5793ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5794ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5795ab6734e7SLydia Wang 5796ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5797ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5798ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5799ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5800ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5801ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5802ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5803ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5804ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5805ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5806ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5807ab6734e7SLydia Wang 5808ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5809ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5810ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5811ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5812ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5813ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5814ab6734e7SLydia Wang 5815ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5816ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5817ab6734e7SLydia Wang { } 5818ab6734e7SLydia Wang }; 5819ab6734e7SLydia Wang 5820ab6734e7SLydia Wang 5821ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = { 5822ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5823ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5824ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5825ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5826ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5827ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5828ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5829ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5830ab6734e7SLydia Wang { } 5831ab6734e7SLydia Wang }; 5832ab6734e7SLydia Wang 5833ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = { 5834ab6734e7SLydia Wang .substreams = 2, 5835ab6734e7SLydia Wang .channels_min = 2, 5836ab6734e7SLydia Wang .channels_max = 2, 5837ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5838ab6734e7SLydia Wang .ops = { 5839ab6734e7SLydia Wang .open = via_playback_pcm_open, 5840ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5841ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5842ab6734e7SLydia Wang .close = via_pcm_open_close, 5843ab6734e7SLydia Wang }, 5844ab6734e7SLydia Wang }; 5845ab6734e7SLydia Wang 5846ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = { 5847ab6734e7SLydia Wang .substreams = 2, 5848ab6734e7SLydia Wang .channels_min = 2, 5849ab6734e7SLydia Wang .channels_max = 2, 5850ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5851ab6734e7SLydia Wang .ops = { 5852ab6734e7SLydia Wang .open = via_pcm_open_close, 5853ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5854ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5855ab6734e7SLydia Wang .close = via_pcm_open_close, 5856ab6734e7SLydia Wang }, 5857ab6734e7SLydia Wang }; 5858ab6734e7SLydia Wang 5859ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = { 5860ab6734e7SLydia Wang .substreams = 1, 5861ab6734e7SLydia Wang .channels_min = 2, 5862ab6734e7SLydia Wang .channels_max = 2, 5863ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5864ab6734e7SLydia Wang .ops = { 5865ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5866ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5867ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5868ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5869ab6734e7SLydia Wang }, 5870ab6734e7SLydia Wang }; 5871ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5872ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5873ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5874ab6734e7SLydia Wang { 5875ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5876ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5877ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5878ab6734e7SLydia Wang spec->multiout.dac_nids[0] = 0x8; 5879ab6734e7SLydia Wang return 0; 5880ab6734e7SLydia Wang } 5881ab6734e7SLydia Wang 5882ab6734e7SLydia Wang 5883ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5884ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5885ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5886ab6734e7SLydia Wang { 5887ab6734e7SLydia Wang int err; 5888ab6734e7SLydia Wang 5889ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5890ab6734e7SLydia Wang return -1; 5891ab6734e7SLydia Wang 5892ab6734e7SLydia Wang /* Line-Out: PortE */ 5893ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 58943d83e577STakashi Iwai "Front Playback Volume", 5895ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5896ab6734e7SLydia Wang if (err < 0) 5897ab6734e7SLydia Wang return err; 5898ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 58993d83e577STakashi Iwai "Front Playback Switch", 5900ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5901ab6734e7SLydia Wang if (err < 0) 5902ab6734e7SLydia Wang return err; 5903ab6734e7SLydia Wang 5904ab6734e7SLydia Wang return 0; 5905ab6734e7SLydia Wang } 5906ab6734e7SLydia Wang 5907ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5908ab6734e7SLydia Wang { 5909ab6734e7SLydia Wang int err; 5910ab6734e7SLydia Wang 5911ab6734e7SLydia Wang if (!pin) 5912ab6734e7SLydia Wang return 0; 5913ab6734e7SLydia Wang 5914ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 5915ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 5916ab6734e7SLydia Wang 5917ab6734e7SLydia Wang 5918ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5919ab6734e7SLydia Wang "Headphone Playback Volume", 5920ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 5921ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5922ab6734e7SLydia Wang if (err < 0) 5923ab6734e7SLydia Wang return err; 5924ab6734e7SLydia Wang 5925ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5926ab6734e7SLydia Wang "Headphone Playback Switch", 5927ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5928ab6734e7SLydia Wang if (err < 0) 5929ab6734e7SLydia Wang return err; 5930ab6734e7SLydia Wang 5931ab6734e7SLydia Wang create_hp_imux(spec); 5932ab6734e7SLydia Wang return 0; 5933ab6734e7SLydia Wang } 5934ab6734e7SLydia Wang 5935ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 593610a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, 5937ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5938ab6734e7SLydia Wang { 593910a20af7STakashi Iwai struct via_spec *spec = codec->spec; 5940ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5941f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; 5942f3268512STakashi Iwai int err; 5943ab6734e7SLydia Wang 594410a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5945f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5946ab6734e7SLydia Wang if (err < 0) 5947ab6734e7SLydia Wang return err; 5948f3268512STakashi Iwai 5949ab6734e7SLydia Wang /* build volume/mute control of loopback */ 59507b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); 5951ab6734e7SLydia Wang if (err < 0) 5952ab6734e7SLydia Wang return err; 5953ab6734e7SLydia Wang 5954ab6734e7SLydia Wang /* for digital mic select */ 595510a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); 5956ab6734e7SLydia Wang 5957ab6734e7SLydia Wang return 0; 5958ab6734e7SLydia Wang } 5959ab6734e7SLydia Wang 5960ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 5961ab6734e7SLydia Wang { 5962ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 5963ab6734e7SLydia Wang int err; 5964ab6734e7SLydia Wang 5965ab6734e7SLydia Wang 5966ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5967ab6734e7SLydia Wang if (err < 0) 5968ab6734e7SLydia Wang return err; 5969ab6734e7SLydia Wang fill_dig_outs(codec); 5970ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 5971ab6734e7SLydia Wang if (err < 0) 5972ab6734e7SLydia Wang return err; 5973ab6734e7SLydia Wang 5974ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 5975ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 5976ab6734e7SLydia Wang 5977ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 5978ab6734e7SLydia Wang if (err < 0) 5979ab6734e7SLydia Wang return err; 5980ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5981ab6734e7SLydia Wang if (err < 0) 5982ab6734e7SLydia Wang return err; 598310a20af7STakashi Iwai err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); 5984ab6734e7SLydia Wang if (err < 0) 5985ab6734e7SLydia Wang return err; 5986ab6734e7SLydia Wang 5987ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5988ab6734e7SLydia Wang 5989ab6734e7SLydia Wang fill_dig_outs(codec); 5990ab6734e7SLydia Wang 5991ab6734e7SLydia Wang if (spec->kctls.list) 5992ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5993ab6734e7SLydia Wang 5994ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 5995ab6734e7SLydia Wang 5996ab6734e7SLydia Wang if (spec->hp_mux) 59973d83e577STakashi Iwai via_hp_build(codec); 5998ab6734e7SLydia Wang 5999ab6734e7SLydia Wang return 1; 6000ab6734e7SLydia Wang } 6001ab6734e7SLydia Wang 6002ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6003ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = { 6004ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 6005ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 6006ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 6007ab6734e7SLydia Wang { } /* end */ 6008ab6734e7SLydia Wang }; 6009ab6734e7SLydia Wang #endif 6010ab6734e7SLydia Wang 60113e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 60123e95b9abSLydia Wang { 60133e95b9abSLydia Wang struct via_spec *spec = codec->spec; 60143e95b9abSLydia Wang int imux_is_smixer = 60153e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 60163e95b9abSLydia Wang unsigned int parm; 60173e95b9abSLydia Wang unsigned int present; 60183e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 60193e95b9abSLydia Wang imux_is_smixer = 60203e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 60213e95b9abSLydia Wang /* inputs */ 60223e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 60233e95b9abSLydia Wang parm = AC_PWRST_D3; 60243e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 60253e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 60263e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 60273e95b9abSLydia Wang parm = AC_PWRST_D0; 60283e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 60293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 60303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 60313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 60323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 60333e95b9abSLydia Wang 60343e95b9abSLydia Wang /* outputs */ 60353e95b9abSLydia Wang /* AOW0 (8h)*/ 60363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 60373e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60383e95b9abSLydia Wang 60393e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 60403e95b9abSLydia Wang parm = AC_PWRST_D3; 60413e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 60423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 60433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 60443e95b9abSLydia Wang 60453e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 60463e95b9abSLydia Wang parm = AC_PWRST_D3; 60473e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 60483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 60493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 60503e95b9abSLydia Wang if (spec->hp_independent_mode) 60513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 60523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60533e95b9abSLydia Wang 60543e95b9abSLydia Wang /* Internal Speaker */ 60553e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 60563e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 60573e95b9abSLydia Wang 60583e95b9abSLydia Wang parm = AC_PWRST_D3; 60593e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 60603e95b9abSLydia Wang if (present) { 60613e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 60623e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 60643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60653e95b9abSLydia Wang } else { 60663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 60673e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 60693e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60703e95b9abSLydia Wang } 60713e95b9abSLydia Wang 60723e95b9abSLydia Wang 60733e95b9abSLydia Wang /* Mono Out */ 60743e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 60753e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 60763e95b9abSLydia Wang 60773e95b9abSLydia Wang parm = AC_PWRST_D3; 60783e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 60793e95b9abSLydia Wang if (present) { 60803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 60813e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 60833e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 60853e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60863e95b9abSLydia Wang } else { 60873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 60883e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 60903e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 60923e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60933e95b9abSLydia Wang } 60943e95b9abSLydia Wang 60953e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 60963e95b9abSLydia Wang parm = AC_PWRST_D3; 60973e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 60983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 60993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 61003e95b9abSLydia Wang 61013e95b9abSLydia Wang } 6102ab6734e7SLydia Wang 6103ab6734e7SLydia Wang /* patch for vt1812 */ 6104ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 6105ab6734e7SLydia Wang { 6106ab6734e7SLydia Wang struct via_spec *spec; 6107ab6734e7SLydia Wang int err; 6108ab6734e7SLydia Wang 6109ab6734e7SLydia Wang /* create a codec specific record */ 61105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 6111ab6734e7SLydia Wang if (spec == NULL) 6112ab6734e7SLydia Wang return -ENOMEM; 6113ab6734e7SLydia Wang 6114ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 6115ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 6116ab6734e7SLydia Wang if (err < 0) { 6117ab6734e7SLydia Wang via_free(codec); 6118ab6734e7SLydia Wang return err; 6119ab6734e7SLydia Wang } else if (!err) { 6120ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 6121ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 6122ab6734e7SLydia Wang } 6123ab6734e7SLydia Wang 6124ab6734e7SLydia Wang 6125ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 6126ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6127ab6734e7SLydia Wang 6128ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 6129ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6130ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6131ab6734e7SLydia Wang 6132ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 6133ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6134ab6734e7SLydia Wang 6135ab6734e7SLydia Wang 6136ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 6137ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 6138ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 6139ab6734e7SLydia Wang get_mux_nids(codec); 6140ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6141ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6142ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6143ab6734e7SLydia Wang spec->num_mixers++; 6144ab6734e7SLydia Wang } 6145ab6734e7SLydia Wang 6146ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6147ab6734e7SLydia Wang 6148ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 61490f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 6150ab6734e7SLydia Wang 6151ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6152ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6153ab6734e7SLydia Wang #endif 6154ab6734e7SLydia Wang 61553e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 6156ab6734e7SLydia Wang return 0; 6157ab6734e7SLydia Wang } 6158ab6734e7SLydia Wang 6159c577b8a1SJoseph Chan /* 6160c577b8a1SJoseph Chan * patch entries 6161c577b8a1SJoseph Chan */ 61621289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 61633218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 61643218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 61653218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 61663218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 61673218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6168f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61693218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6170f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61713218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6172f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61733218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6174f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61753218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6176f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61773218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6178f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61793218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6180f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61813218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6182f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61833218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6184f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61853218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6186f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61873218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6188f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61893218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6190f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61913218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6192f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61933218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6194f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61953218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6196f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61973218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6198f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61993218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6200d949cac1SHarald Welte .patch = patch_vt1708S}, 62013218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6202d949cac1SHarald Welte .patch = patch_vt1708S}, 62033218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6204d949cac1SHarald Welte .patch = patch_vt1708S}, 62053218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6206d949cac1SHarald Welte .patch = patch_vt1708S}, 6207bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 6208d949cac1SHarald Welte .patch = patch_vt1708S}, 62093218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6210d949cac1SHarald Welte .patch = patch_vt1708S}, 62113218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6212d949cac1SHarald Welte .patch = patch_vt1708S}, 62133218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6214d949cac1SHarald Welte .patch = patch_vt1708S}, 62153218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6216d949cac1SHarald Welte .patch = patch_vt1702}, 62173218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6218d949cac1SHarald Welte .patch = patch_vt1702}, 62193218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6220d949cac1SHarald Welte .patch = patch_vt1702}, 62213218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6222d949cac1SHarald Welte .patch = patch_vt1702}, 62233218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6224d949cac1SHarald Welte .patch = patch_vt1702}, 62253218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6226d949cac1SHarald Welte .patch = patch_vt1702}, 62273218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6228d949cac1SHarald Welte .patch = patch_vt1702}, 62293218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6230d949cac1SHarald Welte .patch = patch_vt1702}, 6231eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6232eb7188caSLydia Wang .patch = patch_vt1718S}, 6233eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6234eb7188caSLydia Wang .patch = patch_vt1718S}, 6235bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6236bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6237bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6238bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6239f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6240f3db423dSLydia Wang .patch = patch_vt1716S}, 6241f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6242f3db423dSLydia Wang .patch = patch_vt1716S}, 624325eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 624425eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6245ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 624636dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 624736dd5c4aSLydia Wang .patch = patch_vt1708S}, 6248*11890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 6249*11890956SLydia Wang .patch = patch_vt2002P}, 6250*11890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 6251*11890956SLydia Wang .patch = patch_vt2002P}, 6252c577b8a1SJoseph Chan {} /* terminator */ 6253c577b8a1SJoseph Chan }; 62541289e9e8STakashi Iwai 62551289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 62561289e9e8STakashi Iwai 62571289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 62581289e9e8STakashi Iwai .preset = snd_hda_preset_via, 62591289e9e8STakashi Iwai .owner = THIS_MODULE, 62601289e9e8STakashi Iwai }; 62611289e9e8STakashi Iwai 62621289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 62631289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 62641289e9e8STakashi Iwai 62651289e9e8STakashi Iwai static int __init patch_via_init(void) 62661289e9e8STakashi Iwai { 62671289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 62681289e9e8STakashi Iwai } 62691289e9e8STakashi Iwai 62701289e9e8STakashi Iwai static void __exit patch_via_exit(void) 62711289e9e8STakashi Iwai { 62721289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 62731289e9e8STakashi Iwai } 62741289e9e8STakashi Iwai 62751289e9e8STakashi Iwai module_init(patch_via_init) 62761289e9e8STakashi Iwai module_exit(patch_via_exit) 6277