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, 101d7426329SHarald Welte CODEC_TYPES, 102d7426329SHarald Welte }; 103d7426329SHarald Welte 1041f2e99feSLydia Wang struct via_spec { 1051f2e99feSLydia Wang /* codec parameterization */ 106f3db423dSLydia Wang struct snd_kcontrol_new *mixers[6]; 1071f2e99feSLydia Wang unsigned int num_mixers; 1081f2e99feSLydia Wang 1091f2e99feSLydia Wang struct hda_verb *init_verbs[5]; 1101f2e99feSLydia Wang unsigned int num_iverbs; 1111f2e99feSLydia Wang 1121f2e99feSLydia Wang char *stream_name_analog; 1131f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_playback; 1141f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_capture; 1151f2e99feSLydia Wang 1161f2e99feSLydia Wang char *stream_name_digital; 1171f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_playback; 1181f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_capture; 1191f2e99feSLydia Wang 1201f2e99feSLydia Wang /* playback */ 1211f2e99feSLydia Wang struct hda_multi_out multiout; 1221f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1231f2e99feSLydia Wang 1241f2e99feSLydia Wang /* capture */ 1251f2e99feSLydia Wang unsigned int num_adc_nids; 1261f2e99feSLydia Wang hda_nid_t *adc_nids; 1271f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1281f2e99feSLydia Wang hda_nid_t dig_in_nid; 1291f2e99feSLydia Wang hda_nid_t dig_in_pin; 1301f2e99feSLydia Wang 1311f2e99feSLydia Wang /* capture source */ 1321f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1331f2e99feSLydia Wang unsigned int cur_mux[3]; 1341f2e99feSLydia Wang 1351f2e99feSLydia Wang /* PCM information */ 1361f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1371f2e99feSLydia Wang 1381f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1391f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1401f2e99feSLydia Wang struct snd_array kctls; 1411f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1421f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1431f2e99feSLydia Wang 1441f2e99feSLydia Wang /* HP mode source */ 1451f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1461f2e99feSLydia Wang unsigned int hp_independent_mode; 1471f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1481f2e99feSLydia Wang unsigned int smart51_enabled; 149f3db423dSLydia Wang unsigned int dmic_enabled; 1501f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1511f2e99feSLydia Wang 1521f2e99feSLydia Wang /* work to check hp jack state */ 1531f2e99feSLydia Wang struct hda_codec *codec; 1541f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1551f2e99feSLydia Wang int vt1708_jack_detectect; 1561f2e99feSLydia Wang int vt1708_hp_present; 1573e95b9abSLydia Wang 1583e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1593e95b9abSLydia Wang 1601f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1611f2e99feSLydia Wang struct hda_loopback_check loopback; 1621f2e99feSLydia Wang #endif 1631f2e99feSLydia Wang }; 1641f2e99feSLydia Wang 1650341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1665b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1675b0cb1d8SJaroslav Kysela { 1685b0cb1d8SJaroslav Kysela struct via_spec *spec; 1695b0cb1d8SJaroslav Kysela 1705b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1715b0cb1d8SJaroslav Kysela if (spec == NULL) 1725b0cb1d8SJaroslav Kysela return NULL; 1735b0cb1d8SJaroslav Kysela 1745b0cb1d8SJaroslav Kysela codec->spec = spec; 1755b0cb1d8SJaroslav Kysela spec->codec = codec; 1760341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1770341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1780341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1790341ccd7SLydia Wang spec->codec_type = VT1708S; 1805b0cb1d8SJaroslav Kysela return spec; 1815b0cb1d8SJaroslav Kysela } 1825b0cb1d8SJaroslav Kysela 183744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 184d7426329SHarald Welte { 185744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 186d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 187d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 188d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 189d7426329SHarald Welte 190d7426329SHarald Welte /* get codec type */ 191d7426329SHarald Welte if (ven_id != 0x1106) 192d7426329SHarald Welte codec_type = UNKNOWN; 193d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 194d7426329SHarald Welte codec_type = VT1708; 195d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 196d7426329SHarald Welte codec_type = VT1709_10CH; 197d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 198d7426329SHarald Welte codec_type = VT1709_6CH; 199518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 200d7426329SHarald Welte codec_type = VT1708B_8CH; 201518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 202518bf3baSLydia Wang codec_type = VT1708BCE; 203518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 204d7426329SHarald Welte codec_type = VT1708B_4CH; 205d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 206d7426329SHarald Welte && (dev_id >> 12) < 8) 207d7426329SHarald Welte codec_type = VT1708S; 208d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 209d7426329SHarald Welte && (dev_id >> 12) < 8) 210d7426329SHarald Welte codec_type = VT1702; 211eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 212eb7188caSLydia Wang && (dev_id >> 12) < 8) 213eb7188caSLydia Wang codec_type = VT1718S; 214f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 215f3db423dSLydia Wang codec_type = VT1716S; 216bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 217bb3c6bfcSLydia Wang codec_type = VT1718S; 21825eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 21925eaba2fSLydia Wang codec_type = VT2002P; 220ab6734e7SLydia Wang else if (dev_id == 0x0448) 221ab6734e7SLydia Wang codec_type = VT1812; 22236dd5c4aSLydia Wang else if (dev_id == 0x0440) 22336dd5c4aSLydia Wang codec_type = VT1708S; 224d7426329SHarald Welte else 225d7426329SHarald Welte codec_type = UNKNOWN; 226d7426329SHarald Welte return codec_type; 227d7426329SHarald Welte }; 228d7426329SHarald Welte 22969e52a80SHarald Welte #define VIA_HP_EVENT 0x01 23069e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 231a34df19aSLydia Wang #define VIA_JACK_EVENT 0x04 232f3db423dSLydia Wang #define VIA_MONO_EVENT 0x08 23325eaba2fSLydia Wang #define VIA_SPEAKER_EVENT 0x10 23425eaba2fSLydia Wang #define VIA_BIND_HP_EVENT 0x20 23569e52a80SHarald Welte 236c577b8a1SJoseph Chan enum { 237c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 238c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 239f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 24025eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 241c577b8a1SJoseph Chan }; 242c577b8a1SJoseph Chan 243c577b8a1SJoseph Chan enum { 244eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 245c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 246c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 247c577b8a1SJoseph Chan AUTO_SEQ_SIDE 248c577b8a1SJoseph Chan }; 249c577b8a1SJoseph Chan 250f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2511f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2521f2e99feSLydia Wang 2531f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2541f2e99feSLydia Wang { 2551f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2561f2e99feSLydia Wang return; 2571f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2581f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2591f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2601f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2611f2e99feSLydia Wang msecs_to_jiffies(100)); 2621f2e99feSLydia Wang } 2631f2e99feSLydia Wang 2641f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2651f2e99feSLydia Wang { 2661f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2671f2e99feSLydia Wang return; 2681f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2691f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2701f2e99feSLydia Wang return; 2711f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2721f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2735b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2741f2e99feSLydia Wang } 275f5271101SLydia Wang 2763e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2773e95b9abSLydia Wang { 2783e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2793e95b9abSLydia Wang if (spec->set_widgets_power_state) 2803e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2813e95b9abSLydia Wang } 28225eaba2fSLydia Wang 283f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 284f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 285f5271101SLydia Wang { 286f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 287f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 288f5271101SLydia Wang 2893e95b9abSLydia Wang set_widgets_power_state(codec); 290f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2911f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2921f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2931f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2941f2e99feSLydia Wang else 2951f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2961f2e99feSLydia Wang } 297f5271101SLydia Wang return change; 298f5271101SLydia Wang } 299f5271101SLydia Wang 300f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 301f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 302f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 303f5271101SLydia Wang .name = NULL, \ 304f5271101SLydia Wang .index = 0, \ 305f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 306f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 307f5271101SLydia Wang .put = analog_input_switch_put, \ 308f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 309f5271101SLydia Wang 31025eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 31125eaba2fSLydia Wang 31225eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 31325eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 31425eaba2fSLydia Wang { 31525eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 31625eaba2fSLydia Wang struct via_spec *spec = codec->spec; 31725eaba2fSLydia Wang int i; 31825eaba2fSLydia Wang int change = 0; 31925eaba2fSLydia Wang 32025eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 32125eaba2fSLydia Wang int lmute, rmute; 32225eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 32325eaba2fSLydia Wang snd_printd("Invalid control!\n"); 32425eaba2fSLydia Wang return change; 32525eaba2fSLydia Wang } 32625eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 32725eaba2fSLydia Wang ucontrol); 32825eaba2fSLydia Wang /* Get mute value */ 32925eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 33025eaba2fSLydia Wang valp++; 33125eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 33225eaba2fSLydia Wang 33325eaba2fSLydia Wang /* Set hp pins */ 33425eaba2fSLydia Wang if (!spec->hp_independent_mode) { 33525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 33625eaba2fSLydia Wang snd_hda_codec_amp_update( 33725eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 33825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 33925eaba2fSLydia Wang lmute); 34025eaba2fSLydia Wang snd_hda_codec_amp_update( 34125eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 34225eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 34325eaba2fSLydia Wang rmute); 34425eaba2fSLydia Wang } 34525eaba2fSLydia Wang } 34625eaba2fSLydia Wang 34725eaba2fSLydia Wang if (!lmute && !rmute) { 34825eaba2fSLydia Wang /* Line Outs */ 34925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 35025eaba2fSLydia Wang snd_hda_codec_amp_stereo( 35125eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 35225eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 35325eaba2fSLydia Wang /* Speakers */ 35425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 35525eaba2fSLydia Wang snd_hda_codec_amp_stereo( 35625eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 35725eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 35825eaba2fSLydia Wang /* unmute */ 35925eaba2fSLydia Wang via_hp_bind_automute(codec); 36025eaba2fSLydia Wang 36125eaba2fSLydia Wang } else { 36225eaba2fSLydia Wang if (lmute) { 36325eaba2fSLydia Wang /* Mute all left channels */ 36425eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 36525eaba2fSLydia Wang snd_hda_codec_amp_update( 36625eaba2fSLydia Wang codec, 36725eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 36825eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36925eaba2fSLydia Wang lmute); 37025eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37125eaba2fSLydia Wang snd_hda_codec_amp_update( 37225eaba2fSLydia Wang codec, 37325eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 37425eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37525eaba2fSLydia Wang lmute); 37625eaba2fSLydia Wang } 37725eaba2fSLydia Wang if (rmute) { 37825eaba2fSLydia Wang /* mute all right channels */ 37925eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38025eaba2fSLydia Wang snd_hda_codec_amp_update( 38125eaba2fSLydia Wang codec, 38225eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 38325eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 38425eaba2fSLydia Wang rmute); 38525eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 38625eaba2fSLydia Wang snd_hda_codec_amp_update( 38725eaba2fSLydia Wang codec, 38825eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 38925eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39025eaba2fSLydia Wang rmute); 39125eaba2fSLydia Wang } 39225eaba2fSLydia Wang } 39325eaba2fSLydia Wang return change; 39425eaba2fSLydia Wang } 39525eaba2fSLydia Wang 39625eaba2fSLydia Wang #define BIND_PIN_MUTE \ 39725eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 39825eaba2fSLydia Wang .name = NULL, \ 39925eaba2fSLydia Wang .index = 0, \ 40025eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 40125eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 40225eaba2fSLydia Wang .put = bind_pin_switch_put, \ 40325eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 40425eaba2fSLydia Wang 40571eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = { 406c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 407c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 408f5271101SLydia Wang ANALOG_INPUT_MUTE, 40925eaba2fSLydia Wang BIND_PIN_MUTE, 410c577b8a1SJoseph Chan }; 411c577b8a1SJoseph Chan 412c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 413c577b8a1SJoseph Chan /* ADC1-2 */ 414c577b8a1SJoseph Chan 0x15, 0x27 415c577b8a1SJoseph Chan }; 416c577b8a1SJoseph Chan 417c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 418c577b8a1SJoseph Chan /* ADC1-2 */ 419c577b8a1SJoseph Chan 0x14, 0x15, 0x16 420c577b8a1SJoseph Chan }; 421c577b8a1SJoseph Chan 422f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 423f7278fd0SJosepch Chan /* ADC1-2 */ 424f7278fd0SJosepch Chan 0x13, 0x14 425f7278fd0SJosepch Chan }; 426f7278fd0SJosepch Chan 427d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 428d949cac1SHarald Welte /* ADC1-2 */ 429d949cac1SHarald Welte 0x13, 0x14 430d949cac1SHarald Welte }; 431d949cac1SHarald Welte 432d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 433d949cac1SHarald Welte /* ADC1-2 */ 434d949cac1SHarald Welte 0x12, 0x20, 0x1F 435d949cac1SHarald Welte }; 436d949cac1SHarald Welte 437eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 438eb7188caSLydia Wang /* ADC1-2 */ 439eb7188caSLydia Wang 0x10, 0x11 440eb7188caSLydia Wang }; 441eb7188caSLydia Wang 442f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 443f3db423dSLydia Wang /* ADC1-2 */ 444f3db423dSLydia Wang 0x13, 0x14 445f3db423dSLydia Wang }; 446f3db423dSLydia Wang 44725eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 44825eaba2fSLydia Wang /* ADC1-2 */ 44925eaba2fSLydia Wang 0x10, 0x11 45025eaba2fSLydia Wang }; 45125eaba2fSLydia Wang 452ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = { 453ab6734e7SLydia Wang /* ADC1-2 */ 454ab6734e7SLydia Wang 0x10, 0x11 455ab6734e7SLydia Wang }; 456ab6734e7SLydia Wang 457ab6734e7SLydia Wang 458c577b8a1SJoseph Chan /* add dynamic controls */ 4597b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 4607b315bb4STakashi Iwai int idx, unsigned long val) 461c577b8a1SJoseph Chan { 462c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 463c577b8a1SJoseph Chan 464603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 465603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 466c577b8a1SJoseph Chan if (!knew) 467c577b8a1SJoseph Chan return -ENOMEM; 46871eb7dccSLydia Wang *knew = via_control_templates[type]; 469c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 470c577b8a1SJoseph Chan if (!knew->name) 471c577b8a1SJoseph Chan return -ENOMEM; 4724d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4735e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 474c577b8a1SJoseph Chan knew->private_value = val; 475c577b8a1SJoseph Chan return 0; 476c577b8a1SJoseph Chan } 477c577b8a1SJoseph Chan 4787b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4797b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4807b315bb4STakashi Iwai 4815b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, 4825b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *tmpl) 4835b0cb1d8SJaroslav Kysela { 4845b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 4855b0cb1d8SJaroslav Kysela 4865b0cb1d8SJaroslav Kysela snd_array_init(&spec->kctls, sizeof(*knew), 32); 4875b0cb1d8SJaroslav Kysela knew = snd_array_new(&spec->kctls); 4885b0cb1d8SJaroslav Kysela if (!knew) 4895b0cb1d8SJaroslav Kysela return NULL; 4905b0cb1d8SJaroslav Kysela *knew = *tmpl; 4915b0cb1d8SJaroslav Kysela knew->name = kstrdup(tmpl->name, GFP_KERNEL); 4925b0cb1d8SJaroslav Kysela if (!knew->name) 4935b0cb1d8SJaroslav Kysela return NULL; 494b331439dSTakashi Iwai return knew; 4955b0cb1d8SJaroslav Kysela } 4965b0cb1d8SJaroslav Kysela 497603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 498603c4019STakashi Iwai { 499603c4019STakashi Iwai struct via_spec *spec = codec->spec; 500603c4019STakashi Iwai 501603c4019STakashi Iwai if (spec->kctls.list) { 502603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 503603c4019STakashi Iwai int i; 504603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 505603c4019STakashi Iwai kfree(kctl[i].name); 506603c4019STakashi Iwai } 507603c4019STakashi Iwai snd_array_free(&spec->kctls); 508603c4019STakashi Iwai } 509603c4019STakashi Iwai 510c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 5119510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 5127b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 513c577b8a1SJoseph Chan { 514c577b8a1SJoseph Chan char name[32]; 515c577b8a1SJoseph Chan int err; 516c577b8a1SJoseph Chan 517c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 5187b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 519c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 520c577b8a1SJoseph Chan if (err < 0) 521c577b8a1SJoseph Chan return err; 522c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5237b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 524c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 525c577b8a1SJoseph Chan if (err < 0) 526c577b8a1SJoseph Chan return err; 527c577b8a1SJoseph Chan return 0; 528c577b8a1SJoseph Chan } 529c577b8a1SJoseph Chan 530c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 531c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 532c577b8a1SJoseph Chan int dac_idx) 533c577b8a1SJoseph Chan { 534c577b8a1SJoseph Chan /* set as output */ 535c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 536c577b8a1SJoseph Chan pin_type); 537c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 538c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 539d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 540d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 541d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 542c577b8a1SJoseph Chan } 543c577b8a1SJoseph Chan 544c577b8a1SJoseph Chan 545c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 546c577b8a1SJoseph Chan { 547c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 548c577b8a1SJoseph Chan int i; 549c577b8a1SJoseph Chan 550c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 551c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 552c577b8a1SJoseph Chan if (nid) 553c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 554c577b8a1SJoseph Chan } 555c577b8a1SJoseph Chan } 556c577b8a1SJoseph Chan 557c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 558c577b8a1SJoseph Chan { 559c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 560c577b8a1SJoseph Chan hda_nid_t pin; 56125eaba2fSLydia Wang int i; 562c577b8a1SJoseph Chan 56325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 56425eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 565c577b8a1SJoseph Chan if (pin) /* connect to front */ 566c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 567c577b8a1SJoseph Chan } 56825eaba2fSLydia Wang } 569c577b8a1SJoseph Chan 57032e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 57132e0191dSClemens Ladisch 572c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 573c577b8a1SJoseph Chan { 574c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5757b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 57632e0191dSClemens Ladisch unsigned int ctl; 577c577b8a1SJoseph Chan int i; 578c577b8a1SJoseph Chan 5797b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5807b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 58132e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 58232e0191dSClemens Ladisch ctl = PIN_OUT; 58330649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 58432e0191dSClemens Ladisch ctl = PIN_VREF50; 58532e0191dSClemens Ladisch else 58632e0191dSClemens Ladisch ctl = PIN_IN; 587c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 58832e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 589c577b8a1SJoseph Chan } 590c577b8a1SJoseph Chan } 591f5271101SLydia Wang 592f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 593f5271101SLydia Wang unsigned int *affected_parm) 594f5271101SLydia Wang { 595f5271101SLydia Wang unsigned parm; 596f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 597f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 598f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 599f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 600d56757abSTakashi Iwai unsigned present = snd_hda_jack_detect(codec, nid); 6011564b287SLydia Wang struct via_spec *spec = codec->spec; 6021564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 6031564b287SLydia Wang || ((no_presence || present) 6041564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 605f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 606f5271101SLydia Wang parm = AC_PWRST_D0; 607f5271101SLydia Wang } else 608f5271101SLydia Wang parm = AC_PWRST_D3; 609f5271101SLydia Wang 610f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 611f5271101SLydia Wang } 612f5271101SLydia Wang 613c577b8a1SJoseph Chan /* 614c577b8a1SJoseph Chan * input MUX handling 615c577b8a1SJoseph Chan */ 616c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 617c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 618c577b8a1SJoseph Chan { 619c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 620c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 621c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 622c577b8a1SJoseph Chan } 623c577b8a1SJoseph Chan 624c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 625c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 626c577b8a1SJoseph Chan { 627c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 628c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 629c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 630c577b8a1SJoseph Chan 631c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 632c577b8a1SJoseph Chan return 0; 633c577b8a1SJoseph Chan } 634c577b8a1SJoseph Chan 635c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 636c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 637c577b8a1SJoseph Chan { 638c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 639c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 640c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 641bff5fbf5SLydia Wang int ret; 642c577b8a1SJoseph Chan 643337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 644337b9d02STakashi Iwai return -EINVAL; 645a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 646a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 647a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 648a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 649a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 650bff5fbf5SLydia Wang 651bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 652bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 653bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 654a80e6e3cSLydia Wang /* update jack power state */ 6553e95b9abSLydia Wang set_widgets_power_state(codec); 656a80e6e3cSLydia Wang 657bff5fbf5SLydia Wang return ret; 658c577b8a1SJoseph Chan } 659c577b8a1SJoseph Chan 6600aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6610aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6620aa62aefSHarald Welte { 6630aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6640aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6650aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6660aa62aefSHarald Welte } 6670aa62aefSHarald Welte 6680aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 6690aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 6700aa62aefSHarald Welte { 6710aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6725b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 673eb7188caSLydia Wang unsigned int pinsel; 674eb7188caSLydia Wang 675eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 676eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 6770aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 6780aa62aefSHarald Welte 0x00); 6790aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 6800aa62aefSHarald Welte 6810aa62aefSHarald Welte return 0; 6820aa62aefSHarald Welte } 6830aa62aefSHarald Welte 6840713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 6850713efebSLydia Wang { 6860713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 6870713efebSLydia Wang if (ctl) { 6880713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 6890713efebSLydia Wang ctl->vd[0].access |= active 6900713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 6910713efebSLydia Wang snd_ctl_notify(codec->bus->card, 6920713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 6930713efebSLydia Wang } 6940713efebSLydia Wang } 6950713efebSLydia Wang 6965b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 6975b0cb1d8SJaroslav Kysela { 6985b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 6995b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7005b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7015b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7025b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 7035b0cb1d8SJaroslav Kysela default: return 0; 7045b0cb1d8SJaroslav Kysela } 7055b0cb1d8SJaroslav Kysela } 7065b0cb1d8SJaroslav Kysela 707cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 708cdc1784dSLydia Wang { 709cdc1784dSLydia Wang /* mute side channel */ 710cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 711cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 712cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 7135b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 714cdc1784dSLydia Wang 715cdc1784dSLydia Wang if (sw3) 716cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 717cdc1784dSLydia Wang parm); 718cdc1784dSLydia Wang return 0; 719cdc1784dSLydia Wang } 720cdc1784dSLydia Wang 7210aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7220aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7230aa62aefSHarald Welte { 7240aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7250aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7265b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7270aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 728cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 729cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 730cdc1784dSLydia Wang ? 1 : 0; 731ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 732ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 733ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 734ce0e5a9eSLydia Wang else 735ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 736ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7370aa62aefSHarald Welte 738ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 739ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 740ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 741cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 742cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 743cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7440aa62aefSHarald Welte 0, 0, 0); 7450aa62aefSHarald Welte 746cdc1784dSLydia Wang update_side_mute_status(codec); 7470713efebSLydia Wang /* update HP volume/swtich active state */ 7480713efebSLydia Wang if (spec->codec_type == VT1708S 749eb7188caSLydia Wang || spec->codec_type == VT1702 750f3db423dSLydia Wang || spec->codec_type == VT1718S 75125eaba2fSLydia Wang || spec->codec_type == VT1716S 752ab6734e7SLydia Wang || spec->codec_type == VT2002P 753ab6734e7SLydia Wang || spec->codec_type == VT1812) { 7540713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7550713efebSLydia Wang spec->hp_independent_mode); 7560713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 7570713efebSLydia Wang spec->hp_independent_mode); 7580713efebSLydia Wang } 759ce0e5a9eSLydia Wang /* update jack power state */ 7603e95b9abSLydia Wang set_widgets_power_state(codec); 7610aa62aefSHarald Welte return 0; 7620aa62aefSHarald Welte } 7630aa62aefSHarald Welte 7645b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = { 7650aa62aefSHarald Welte { 7660aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7670aa62aefSHarald Welte .name = "Independent HP", 7680aa62aefSHarald Welte .info = via_independent_hp_info, 7690aa62aefSHarald Welte .get = via_independent_hp_get, 7700aa62aefSHarald Welte .put = via_independent_hp_put, 7710aa62aefSHarald Welte }, 7725b0cb1d8SJaroslav Kysela { 7735b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 7745b0cb1d8SJaroslav Kysela .name = "Independent HP", 7755b0cb1d8SJaroslav Kysela }, 7760aa62aefSHarald Welte }; 7770aa62aefSHarald Welte 7783d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7795b0cb1d8SJaroslav Kysela { 7803d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7815b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7825b0cb1d8SJaroslav Kysela hda_nid_t nid; 7833d83e577STakashi Iwai int nums; 7843d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 7855b0cb1d8SJaroslav Kysela 7865b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7875b0cb1d8SJaroslav Kysela case VT1718S: 7885b0cb1d8SJaroslav Kysela nid = 0x34; 7895b0cb1d8SJaroslav Kysela break; 7905b0cb1d8SJaroslav Kysela case VT2002P: 7915b0cb1d8SJaroslav Kysela nid = 0x35; 7925b0cb1d8SJaroslav Kysela break; 7935b0cb1d8SJaroslav Kysela case VT1812: 7945b0cb1d8SJaroslav Kysela nid = 0x3d; 7955b0cb1d8SJaroslav Kysela break; 7965b0cb1d8SJaroslav Kysela default: 7975b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 7985b0cb1d8SJaroslav Kysela break; 7995b0cb1d8SJaroslav Kysela } 8005b0cb1d8SJaroslav Kysela 801ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 802ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 803ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8043d83e577STakashi Iwai if (nums <= 1) 8053d83e577STakashi Iwai return 0; 806ee3c35c0SLydia Wang } 8073d83e577STakashi Iwai 8083d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8093d83e577STakashi Iwai if (knew == NULL) 8103d83e577STakashi Iwai return -ENOMEM; 8113d83e577STakashi Iwai 8125b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8135b0cb1d8SJaroslav Kysela knew->private_value = nid; 8145b0cb1d8SJaroslav Kysela 8155b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8165b0cb1d8SJaroslav Kysela if (knew == NULL) 8175b0cb1d8SJaroslav Kysela return -ENOMEM; 8185b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8195b0cb1d8SJaroslav Kysela 8205b0cb1d8SJaroslav Kysela return 0; 8215b0cb1d8SJaroslav Kysela } 8225b0cb1d8SJaroslav Kysela 8231564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8241564b287SLydia Wang { 8251564b287SLydia Wang int i; 8261564b287SLydia Wang struct snd_ctl_elem_id id; 8271564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 8281564b287SLydia Wang 8291564b287SLydia Wang memset(&id, 0, sizeof(id)); 8301564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8311564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8321564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 8331564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 8341564b287SLydia Wang &id); 8351564b287SLydia Wang } 8361564b287SLydia Wang } 8371564b287SLydia Wang 8381564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8391564b287SLydia Wang { 8401564b287SLydia Wang struct via_spec *spec = codec->spec; 8411564b287SLydia Wang hda_nid_t nid_mixer; 8421564b287SLydia Wang int start_idx; 8431564b287SLydia Wang int end_idx; 8441564b287SLydia Wang int i; 8451564b287SLydia Wang /* get nid of MW0 and start & end index */ 8461564b287SLydia Wang switch (spec->codec_type) { 8471564b287SLydia Wang case VT1708: 8481564b287SLydia Wang nid_mixer = 0x17; 8491564b287SLydia Wang start_idx = 2; 8501564b287SLydia Wang end_idx = 4; 8511564b287SLydia Wang break; 8521564b287SLydia Wang case VT1709_10CH: 8531564b287SLydia Wang case VT1709_6CH: 8541564b287SLydia Wang nid_mixer = 0x18; 8551564b287SLydia Wang start_idx = 2; 8561564b287SLydia Wang end_idx = 4; 8571564b287SLydia Wang break; 8581564b287SLydia Wang case VT1708B_8CH: 8591564b287SLydia Wang case VT1708B_4CH: 8601564b287SLydia Wang case VT1708S: 861f3db423dSLydia Wang case VT1716S: 8621564b287SLydia Wang nid_mixer = 0x16; 8631564b287SLydia Wang start_idx = 2; 8641564b287SLydia Wang end_idx = 4; 8651564b287SLydia Wang break; 866ab657e0cSLydia Wang case VT1718S: 867ab657e0cSLydia Wang nid_mixer = 0x21; 868ab657e0cSLydia Wang start_idx = 1; 869ab657e0cSLydia Wang end_idx = 3; 870ab657e0cSLydia Wang break; 8711564b287SLydia Wang default: 8721564b287SLydia Wang return; 8731564b287SLydia Wang } 8741564b287SLydia Wang /* check AA path's mute status */ 8751564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 8761564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 8771564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 8781564b287SLydia Wang HDA_AMP_MUTE, val); 8791564b287SLydia Wang } 8801564b287SLydia Wang } 8811564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 8821564b287SLydia Wang { 8837b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8847b315bb4STakashi Iwai int i; 8857b315bb4STakashi Iwai 8867b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 8877b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 88886e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 8891564b287SLydia Wang } 8907b315bb4STakashi Iwai return 0; 8911564b287SLydia Wang } 8921564b287SLydia Wang 8931564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 8941564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 8951564b287SLydia Wang { 8961564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 8971564b287SLydia Wang uinfo->count = 1; 8981564b287SLydia Wang uinfo->value.integer.min = 0; 8991564b287SLydia Wang uinfo->value.integer.max = 1; 9001564b287SLydia Wang return 0; 9011564b287SLydia Wang } 9021564b287SLydia Wang 9031564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9041564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9051564b287SLydia Wang { 9061564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9071564b287SLydia Wang struct via_spec *spec = codec->spec; 9087b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9091564b287SLydia Wang int on = 1; 9101564b287SLydia Wang int i; 9111564b287SLydia Wang 9127b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9137b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9147b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 9157b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 91686e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9177b315bb4STakashi Iwai continue; 91886e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9197b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9201564b287SLydia Wang continue; /* ignore FMic for independent HP */ 9217b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9221564b287SLydia Wang on = 0; 9231564b287SLydia Wang } 9241564b287SLydia Wang *ucontrol->value.integer.value = on; 9251564b287SLydia Wang return 0; 9261564b287SLydia Wang } 9271564b287SLydia Wang 9281564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9291564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9301564b287SLydia Wang { 9311564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9321564b287SLydia Wang struct via_spec *spec = codec->spec; 9337b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9341564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9351564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9361564b287SLydia Wang int i; 9371564b287SLydia Wang 9387b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9397b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9407b315bb4STakashi Iwai unsigned int parm; 9417b315bb4STakashi Iwai 94286e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9437b315bb4STakashi Iwai continue; 94486e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9457b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9461564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 9477b315bb4STakashi Iwai 9487b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 9491564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9501564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 9511564b287SLydia Wang parm |= out_in; 9521564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 9531564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 9541564b287SLydia Wang parm); 9551564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 9561564b287SLydia Wang mute_aa_path(codec, 1); 9571564b287SLydia Wang notify_aa_path_ctls(codec); 9581564b287SLydia Wang } 9597b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 960eb7188caSLydia Wang snd_hda_codec_amp_stereo( 961eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 962eb7188caSLydia Wang HDA_AMP_UNMUTE); 9631564b287SLydia Wang } 96486e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 965f3db423dSLydia Wang if (spec->codec_type == VT1708S 966f3db423dSLydia Wang || spec->codec_type == VT1716S) { 9671564b287SLydia Wang /* input = index 1 (AOW3) */ 9681564b287SLydia Wang snd_hda_codec_write( 9691564b287SLydia Wang codec, nid, 0, 9701564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 9711564b287SLydia Wang snd_hda_codec_amp_stereo( 9721564b287SLydia Wang codec, nid, HDA_OUTPUT, 9731564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 9741564b287SLydia Wang } 9751564b287SLydia Wang } 9761564b287SLydia Wang } 9771564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 9783e95b9abSLydia Wang set_widgets_power_state(codec); 9791564b287SLydia Wang return 1; 9801564b287SLydia Wang } 9811564b287SLydia Wang 9825b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = { 9831564b287SLydia Wang { 9841564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9851564b287SLydia Wang .name = "Smart 5.1", 9861564b287SLydia Wang .count = 1, 9871564b287SLydia Wang .info = via_smart51_info, 9881564b287SLydia Wang .get = via_smart51_get, 9891564b287SLydia Wang .put = via_smart51_put, 9901564b287SLydia Wang }, 9915b0cb1d8SJaroslav Kysela { 9925b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 9935b0cb1d8SJaroslav Kysela .name = "Smart 5.1", 9945b0cb1d8SJaroslav Kysela } 9951564b287SLydia Wang }; 9961564b287SLydia Wang 9975b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 9985b0cb1d8SJaroslav Kysela { 9995b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10007b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10015b0cb1d8SJaroslav Kysela hda_nid_t nid; 10025b0cb1d8SJaroslav Kysela int i; 10035b0cb1d8SJaroslav Kysela 10045b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[0]); 10055b0cb1d8SJaroslav Kysela if (knew == NULL) 10065b0cb1d8SJaroslav Kysela return -ENOMEM; 10075b0cb1d8SJaroslav Kysela 10087b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10097b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 101086e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 10115b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[1]); 10125b0cb1d8SJaroslav Kysela if (knew == NULL) 10135b0cb1d8SJaroslav Kysela return -ENOMEM; 10145b0cb1d8SJaroslav Kysela knew->subdevice = nid; 10157b315bb4STakashi Iwai break; 10165b0cb1d8SJaroslav Kysela } 10175b0cb1d8SJaroslav Kysela } 10185b0cb1d8SJaroslav Kysela 10195b0cb1d8SJaroslav Kysela return 0; 10205b0cb1d8SJaroslav Kysela } 10215b0cb1d8SJaroslav Kysela 1022c577b8a1SJoseph Chan /* capture mixer elements */ 1023c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1024c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1025c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1026c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1027c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1028c577b8a1SJoseph Chan { 1029c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1030c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1031c577b8a1SJoseph Chan * So call somewhat different.. 1032c577b8a1SJoseph Chan */ 1033c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1034c577b8a1SJoseph Chan .name = "Input Source", 1035c577b8a1SJoseph Chan .count = 1, 1036c577b8a1SJoseph Chan .info = via_mux_enum_info, 1037c577b8a1SJoseph Chan .get = via_mux_enum_get, 1038c577b8a1SJoseph Chan .put = via_mux_enum_put, 1039c577b8a1SJoseph Chan }, 1040c577b8a1SJoseph Chan { } /* end */ 1041c577b8a1SJoseph Chan }; 1042f5271101SLydia Wang 1043f5271101SLydia Wang /* check AA path's mute statue */ 1044f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1045f5271101SLydia Wang { 1046f5271101SLydia Wang int mute = 1; 1047f5271101SLydia Wang hda_nid_t nid_mixer; 1048f5271101SLydia Wang int start_idx; 1049f5271101SLydia Wang int end_idx; 1050f5271101SLydia Wang int i; 1051f5271101SLydia Wang struct via_spec *spec = codec->spec; 1052f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1053f5271101SLydia Wang switch (spec->codec_type) { 1054f5271101SLydia Wang case VT1708B_8CH: 1055f5271101SLydia Wang case VT1708B_4CH: 1056f5271101SLydia Wang case VT1708S: 1057f3db423dSLydia Wang case VT1716S: 1058f5271101SLydia Wang nid_mixer = 0x16; 1059f5271101SLydia Wang start_idx = 2; 1060f5271101SLydia Wang end_idx = 4; 1061f5271101SLydia Wang break; 1062f5271101SLydia Wang case VT1702: 1063f5271101SLydia Wang nid_mixer = 0x1a; 1064f5271101SLydia Wang start_idx = 1; 1065f5271101SLydia Wang end_idx = 3; 1066f5271101SLydia Wang break; 1067eb7188caSLydia Wang case VT1718S: 1068eb7188caSLydia Wang nid_mixer = 0x21; 1069eb7188caSLydia Wang start_idx = 1; 1070eb7188caSLydia Wang end_idx = 3; 1071eb7188caSLydia Wang break; 107225eaba2fSLydia Wang case VT2002P: 1073ab6734e7SLydia Wang case VT1812: 107425eaba2fSLydia Wang nid_mixer = 0x21; 107525eaba2fSLydia Wang start_idx = 0; 107625eaba2fSLydia Wang end_idx = 2; 107725eaba2fSLydia Wang break; 1078f5271101SLydia Wang default: 1079f5271101SLydia Wang return 0; 1080f5271101SLydia Wang } 1081f5271101SLydia Wang /* check AA path's mute status */ 1082f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1083f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1084f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1085f5271101SLydia Wang int shift = 8 * (i % 4); 1086f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1087f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1088f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1089f5271101SLydia Wang /* check mute status while the pin is connected */ 1090f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1091f5271101SLydia Wang HDA_INPUT, i) >> 7; 1092f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1093f5271101SLydia Wang HDA_INPUT, i) >> 7; 1094f5271101SLydia Wang if (!mute_l || !mute_r) { 1095f5271101SLydia Wang mute = 0; 1096f5271101SLydia Wang break; 1097f5271101SLydia Wang } 1098f5271101SLydia Wang } 1099f5271101SLydia Wang } 1100f5271101SLydia Wang return mute; 1101f5271101SLydia Wang } 1102f5271101SLydia Wang 1103f5271101SLydia Wang /* enter/exit analog low-current mode */ 1104f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1105f5271101SLydia Wang { 1106f5271101SLydia Wang struct via_spec *spec = codec->spec; 1107f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1108f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1109f5271101SLydia Wang unsigned int verb = 0; 1110f5271101SLydia Wang unsigned int parm = 0; 1111f5271101SLydia Wang 1112f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1113f5271101SLydia Wang enable = enable && saved_stream_idle; 1114f5271101SLydia Wang else { 1115f5271101SLydia Wang enable = enable && stream_idle; 1116f5271101SLydia Wang saved_stream_idle = stream_idle; 1117f5271101SLydia Wang } 1118f5271101SLydia Wang 1119f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1120f5271101SLydia Wang switch (spec->codec_type) { 1121f5271101SLydia Wang case VT1708B_8CH: 1122f5271101SLydia Wang case VT1708B_4CH: 1123f5271101SLydia Wang verb = 0xf70; 1124f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1125f5271101SLydia Wang break; 1126f5271101SLydia Wang case VT1708S: 1127eb7188caSLydia Wang case VT1718S: 1128f3db423dSLydia Wang case VT1716S: 1129f5271101SLydia Wang verb = 0xf73; 1130f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1131f5271101SLydia Wang break; 1132f5271101SLydia Wang case VT1702: 1133f5271101SLydia Wang verb = 0xf73; 1134f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1135f5271101SLydia Wang break; 113625eaba2fSLydia Wang case VT2002P: 1137ab6734e7SLydia Wang case VT1812: 113825eaba2fSLydia Wang verb = 0xf93; 113925eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 114025eaba2fSLydia Wang break; 1141f5271101SLydia Wang default: 1142f5271101SLydia Wang return; /* other codecs are not supported */ 1143f5271101SLydia Wang } 1144f5271101SLydia Wang /* send verb */ 1145f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1146f5271101SLydia Wang } 1147f5271101SLydia Wang 1148c577b8a1SJoseph Chan /* 1149c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1150c577b8a1SJoseph Chan */ 1151c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1152c577b8a1SJoseph Chan /* 1153c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1154c577b8a1SJoseph Chan */ 1155c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1156c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1157c577b8a1SJoseph Chan 1158c577b8a1SJoseph Chan 1159f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1160c577b8a1SJoseph Chan * mixer widget 1161c577b8a1SJoseph Chan */ 1162c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1163f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1164f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1165f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1166f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1167f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1168c577b8a1SJoseph Chan 1169c577b8a1SJoseph Chan /* 1170c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1171c577b8a1SJoseph Chan */ 1172c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1173c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1174c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1175c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1176c577b8a1SJoseph Chan 1177bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1178bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1179c577b8a1SJoseph Chan /* PW9 Output enable */ 1180c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1181f7278fd0SJosepch Chan { } 1182c577b8a1SJoseph Chan }; 1183c577b8a1SJoseph Chan 1184c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1185c577b8a1SJoseph Chan struct hda_codec *codec, 1186c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1187c577b8a1SJoseph Chan { 1188c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 118917314379SLydia Wang int idle = substream->pstr->substream_opened == 1 119017314379SLydia Wang && substream->ref_count == 0; 119117314379SLydia Wang analog_low_current_mode(codec, idle); 11929a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 11939a08160bSTakashi Iwai hinfo); 1194c577b8a1SJoseph Chan } 1195c577b8a1SJoseph Chan 11960aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 11970aa62aefSHarald Welte unsigned int stream_tag, 11980aa62aefSHarald Welte unsigned int format, 11990aa62aefSHarald Welte struct snd_pcm_substream *substream) 12000aa62aefSHarald Welte { 12010aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12020aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 12030aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 12040aa62aefSHarald Welte int chs = substream->runtime->channels; 12050aa62aefSHarald Welte int i; 12060aa62aefSHarald Welte 12070aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12080aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12090aa62aefSHarald Welte if (chs == 2 && 12100aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12110aa62aefSHarald Welte format) && 12120aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 12130aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12140aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12150aa62aefSHarald Welte * be updated */ 12160aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 12170aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12180aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12190aa62aefSHarald Welte codec->spdif_ctls & 12200aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12210aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12220aa62aefSHarald Welte stream_tag, 0, format); 12230aa62aefSHarald Welte /* turn on again (if needed) */ 12240aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 12250aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12260aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12270aa62aefSHarald Welte codec->spdif_ctls & 0xff); 12280aa62aefSHarald Welte } else { 12290aa62aefSHarald Welte mout->dig_out_used = 0; 12300aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12310aa62aefSHarald Welte 0, 0, 0); 12320aa62aefSHarald Welte } 12330aa62aefSHarald Welte } 12340aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12350aa62aefSHarald Welte 12360aa62aefSHarald Welte /* front */ 12370aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12380aa62aefSHarald Welte 0, format); 12390aa62aefSHarald Welte 1240eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1241eb7188caSLydia Wang && !spec->hp_independent_mode) 12420aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12430aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12440aa62aefSHarald Welte 0, format); 12450aa62aefSHarald Welte 12460aa62aefSHarald Welte /* extra outputs copied from front */ 12470aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12480aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12490aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12500aa62aefSHarald Welte mout->extra_out_nid[i], 12510aa62aefSHarald Welte stream_tag, 0, format); 12520aa62aefSHarald Welte 12530aa62aefSHarald Welte /* surrounds */ 12540aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12550aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12560aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12570aa62aefSHarald Welte i * 2, format); 12580aa62aefSHarald Welte else /* copy front */ 12590aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12600aa62aefSHarald Welte 0, format); 12610aa62aefSHarald Welte } 12620aa62aefSHarald Welte } 12630aa62aefSHarald Welte 12640aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 12650aa62aefSHarald Welte struct hda_codec *codec, 12660aa62aefSHarald Welte unsigned int stream_tag, 12670aa62aefSHarald Welte unsigned int format, 12680aa62aefSHarald Welte struct snd_pcm_substream *substream) 12690aa62aefSHarald Welte { 12700aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12710aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 12720aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 12730aa62aefSHarald Welte 12740aa62aefSHarald Welte if (substream->number == 0) 12750aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 12760aa62aefSHarald Welte substream); 12770aa62aefSHarald Welte else { 12780aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 12790aa62aefSHarald Welte spec->hp_independent_mode) 12800aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 12810aa62aefSHarald Welte stream_tag, 0, format); 12820aa62aefSHarald Welte } 12831f2e99feSLydia Wang vt1708_start_hp_work(spec); 12840aa62aefSHarald Welte return 0; 12850aa62aefSHarald Welte } 12860aa62aefSHarald Welte 12870aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 12880aa62aefSHarald Welte struct hda_codec *codec, 12890aa62aefSHarald Welte struct snd_pcm_substream *substream) 12900aa62aefSHarald Welte { 12910aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12920aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 12930aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 12940aa62aefSHarald Welte int i; 12950aa62aefSHarald Welte 12960aa62aefSHarald Welte if (substream->number == 0) { 12970aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 12980aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 12990aa62aefSHarald Welte 13000aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13010aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13020aa62aefSHarald Welte 0, 0, 0); 13030aa62aefSHarald Welte 13040aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13050aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13060aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13070aa62aefSHarald Welte mout->extra_out_nid[i], 13080aa62aefSHarald Welte 0, 0, 0); 13090aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13100aa62aefSHarald Welte if (mout->dig_out_nid && 13110aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13120aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13130aa62aefSHarald Welte 0, 0, 0); 13140aa62aefSHarald Welte mout->dig_out_used = 0; 13150aa62aefSHarald Welte } 13160aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13170aa62aefSHarald Welte } else { 13180aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13190aa62aefSHarald Welte spec->hp_independent_mode) 13200aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13210aa62aefSHarald Welte 0, 0, 0); 13220aa62aefSHarald Welte } 13231f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13240aa62aefSHarald Welte return 0; 13250aa62aefSHarald Welte } 13260aa62aefSHarald Welte 1327c577b8a1SJoseph Chan /* 1328c577b8a1SJoseph Chan * Digital out 1329c577b8a1SJoseph Chan */ 1330c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1331c577b8a1SJoseph Chan struct hda_codec *codec, 1332c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1333c577b8a1SJoseph Chan { 1334c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1335c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1336c577b8a1SJoseph Chan } 1337c577b8a1SJoseph Chan 1338c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1339c577b8a1SJoseph Chan struct hda_codec *codec, 1340c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1341c577b8a1SJoseph Chan { 1342c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1343c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1344c577b8a1SJoseph Chan } 1345c577b8a1SJoseph Chan 13465691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 134798aa34c0SHarald Welte struct hda_codec *codec, 134898aa34c0SHarald Welte unsigned int stream_tag, 134998aa34c0SHarald Welte unsigned int format, 135098aa34c0SHarald Welte struct snd_pcm_substream *substream) 135198aa34c0SHarald Welte { 135298aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13539da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13549da29271STakashi Iwai stream_tag, format, substream); 13559da29271STakashi Iwai } 13565691ec7fSHarald Welte 13579da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 13589da29271STakashi Iwai struct hda_codec *codec, 13599da29271STakashi Iwai struct snd_pcm_substream *substream) 13609da29271STakashi Iwai { 13619da29271STakashi Iwai struct via_spec *spec = codec->spec; 13629da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 136398aa34c0SHarald Welte return 0; 136498aa34c0SHarald Welte } 136598aa34c0SHarald Welte 1366c577b8a1SJoseph Chan /* 1367c577b8a1SJoseph Chan * Analog capture 1368c577b8a1SJoseph Chan */ 1369c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1370c577b8a1SJoseph Chan struct hda_codec *codec, 1371c577b8a1SJoseph Chan unsigned int stream_tag, 1372c577b8a1SJoseph Chan unsigned int format, 1373c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1374c577b8a1SJoseph Chan { 1375c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1376c577b8a1SJoseph Chan 1377c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1378c577b8a1SJoseph Chan stream_tag, 0, format); 1379c577b8a1SJoseph Chan return 0; 1380c577b8a1SJoseph Chan } 1381c577b8a1SJoseph Chan 1382c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1383c577b8a1SJoseph Chan struct hda_codec *codec, 1384c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1385c577b8a1SJoseph Chan { 1386c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1387888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1388c577b8a1SJoseph Chan return 0; 1389c577b8a1SJoseph Chan } 1390c577b8a1SJoseph Chan 1391c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 13920aa62aefSHarald Welte .substreams = 2, 1393c577b8a1SJoseph Chan .channels_min = 2, 1394c577b8a1SJoseph Chan .channels_max = 8, 1395c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1396c577b8a1SJoseph Chan .ops = { 1397c577b8a1SJoseph Chan .open = via_playback_pcm_open, 13980aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 13990aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1400c577b8a1SJoseph Chan }, 1401c577b8a1SJoseph Chan }; 1402c577b8a1SJoseph Chan 1403bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1404c873cc25SLydia Wang .substreams = 2, 1405bc9b5623STakashi Iwai .channels_min = 2, 1406bc9b5623STakashi Iwai .channels_max = 8, 1407bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1408bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1409bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1410bc9b5623STakashi Iwai * disable the 24bit format, so far. 1411bc9b5623STakashi Iwai */ 1412bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1413bc9b5623STakashi Iwai .ops = { 1414bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1415c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1416c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1417bc9b5623STakashi Iwai }, 1418bc9b5623STakashi Iwai }; 1419bc9b5623STakashi Iwai 1420c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1421c577b8a1SJoseph Chan .substreams = 2, 1422c577b8a1SJoseph Chan .channels_min = 2, 1423c577b8a1SJoseph Chan .channels_max = 2, 1424c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1425c577b8a1SJoseph Chan .ops = { 1426c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1427c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1428c577b8a1SJoseph Chan }, 1429c577b8a1SJoseph Chan }; 1430c577b8a1SJoseph Chan 1431c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1432c577b8a1SJoseph Chan .substreams = 1, 1433c577b8a1SJoseph Chan .channels_min = 2, 1434c577b8a1SJoseph Chan .channels_max = 2, 1435c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1436c577b8a1SJoseph Chan .ops = { 1437c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14386b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14399da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14409da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1441c577b8a1SJoseph Chan }, 1442c577b8a1SJoseph Chan }; 1443c577b8a1SJoseph Chan 1444c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1445c577b8a1SJoseph Chan .substreams = 1, 1446c577b8a1SJoseph Chan .channels_min = 2, 1447c577b8a1SJoseph Chan .channels_max = 2, 1448c577b8a1SJoseph Chan }; 1449c577b8a1SJoseph Chan 1450c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1451c577b8a1SJoseph Chan { 1452c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 14535b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 14545b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 14555b0cb1d8SJaroslav Kysela int err, i; 1456c577b8a1SJoseph Chan 1457c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1458c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1459c577b8a1SJoseph Chan if (err < 0) 1460c577b8a1SJoseph Chan return err; 1461c577b8a1SJoseph Chan } 1462c577b8a1SJoseph Chan 1463c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1464c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1465c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1466c577b8a1SJoseph Chan if (err < 0) 1467c577b8a1SJoseph Chan return err; 14689a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 14699a08160bSTakashi Iwai &spec->multiout); 14709a08160bSTakashi Iwai if (err < 0) 14719a08160bSTakashi Iwai return err; 14729a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1473c577b8a1SJoseph Chan } 1474c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1475c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1476c577b8a1SJoseph Chan if (err < 0) 1477c577b8a1SJoseph Chan return err; 1478c577b8a1SJoseph Chan } 147917314379SLydia Wang 14805b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 14815b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 14825b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 148321949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 14845b0cb1d8SJaroslav Kysela if (err < 0) 14855b0cb1d8SJaroslav Kysela return err; 14865b0cb1d8SJaroslav Kysela } 14875b0cb1d8SJaroslav Kysela 14885b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 14895b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 14905b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 14915b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 14925b0cb1d8SJaroslav Kysela continue; 14935b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 14945b0cb1d8SJaroslav Kysela if (kctl == NULL) 14955b0cb1d8SJaroslav Kysela continue; 14965b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 14975b0cb1d8SJaroslav Kysela knew->subdevice); 14985b0cb1d8SJaroslav Kysela } 14995b0cb1d8SJaroslav Kysela } 15005b0cb1d8SJaroslav Kysela 150117314379SLydia Wang /* init power states */ 15023e95b9abSLydia Wang set_widgets_power_state(codec); 150317314379SLydia Wang analog_low_current_mode(codec, 1); 150417314379SLydia Wang 1505603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1506c577b8a1SJoseph Chan return 0; 1507c577b8a1SJoseph Chan } 1508c577b8a1SJoseph Chan 1509c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1510c577b8a1SJoseph Chan { 1511c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1512c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1513c577b8a1SJoseph Chan 1514c577b8a1SJoseph Chan codec->num_pcms = 1; 1515c577b8a1SJoseph Chan codec->pcm_info = info; 1516c577b8a1SJoseph Chan 1517c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1518377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1519377ff31aSLydia Wang *(spec->stream_analog_playback); 1520377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1521377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1522c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1523c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1524c577b8a1SJoseph Chan 1525c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1526c577b8a1SJoseph Chan spec->multiout.max_channels; 1527c577b8a1SJoseph Chan 1528c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1529c577b8a1SJoseph Chan codec->num_pcms++; 1530c577b8a1SJoseph Chan info++; 1531c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 15327ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1533c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1534c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1535c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1536c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1537c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1538c577b8a1SJoseph Chan } 1539c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1540c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1541c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1542c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1543c577b8a1SJoseph Chan spec->dig_in_nid; 1544c577b8a1SJoseph Chan } 1545c577b8a1SJoseph Chan } 1546c577b8a1SJoseph Chan 1547c577b8a1SJoseph Chan return 0; 1548c577b8a1SJoseph Chan } 1549c577b8a1SJoseph Chan 1550c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1551c577b8a1SJoseph Chan { 1552c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1553c577b8a1SJoseph Chan 1554c577b8a1SJoseph Chan if (!spec) 1555c577b8a1SJoseph Chan return; 1556c577b8a1SJoseph Chan 1557603c4019STakashi Iwai via_free_kctls(codec); 15581f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1559c577b8a1SJoseph Chan kfree(codec->spec); 1560c577b8a1SJoseph Chan } 1561c577b8a1SJoseph Chan 156269e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 156369e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 156469e52a80SHarald Welte { 1565dcf34c8cSLydia Wang unsigned int present = 0; 156669e52a80SHarald Welte struct via_spec *spec = codec->spec; 156769e52a80SHarald Welte 1568d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1569dcf34c8cSLydia Wang 1570dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1571dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 1572dcf34c8cSLydia Wang /* auto mute */ 1573dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 1574dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 1575dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1576dcf34c8cSLydia Wang /* notify change */ 1577dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 1578dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1579dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 1580dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1581dcf34c8cSLydia Wang &id); 1582dcf34c8cSLydia Wang } 158369e52a80SHarald Welte } 158469e52a80SHarald Welte 1585f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1586f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1587f3db423dSLydia Wang { 1588f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1589f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1590f3db423dSLydia Wang 1591f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1592f3db423dSLydia Wang return; 1593f3db423dSLydia Wang 1594d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1595d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1596f3db423dSLydia Wang 1597f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1598f3db423dSLydia Wang if (lineout_present) { 1599f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1600f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 1601f3db423dSLydia Wang return; 1602f3db423dSLydia Wang } 1603f3db423dSLydia Wang 1604d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1605f3db423dSLydia Wang 1606f3db423dSLydia Wang if (!spec->hp_independent_mode) 1607f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1608f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1609f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 1610f3db423dSLydia Wang } 1611f3db423dSLydia Wang 161269e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 161369e52a80SHarald Welte { 161469e52a80SHarald Welte unsigned int gpio_data; 161569e52a80SHarald Welte unsigned int vol_counter; 161669e52a80SHarald Welte unsigned int vol; 161769e52a80SHarald Welte unsigned int master_vol; 161869e52a80SHarald Welte 161969e52a80SHarald Welte struct via_spec *spec = codec->spec; 162069e52a80SHarald Welte 162169e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 162269e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 162369e52a80SHarald Welte 162469e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 162569e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 162669e52a80SHarald Welte 162769e52a80SHarald Welte vol = vol_counter & 0x1F; 162869e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 162969e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 163069e52a80SHarald Welte AC_AMP_GET_INPUT); 163169e52a80SHarald Welte 163269e52a80SHarald Welte if (gpio_data == 0x02) { 163369e52a80SHarald Welte /* unmute line out */ 163469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 163569e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 163669e52a80SHarald Welte 163769e52a80SHarald Welte if (vol_counter & 0x20) { 163869e52a80SHarald Welte /* decrease volume */ 163969e52a80SHarald Welte if (vol > master_vol) 164069e52a80SHarald Welte vol = master_vol; 164169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 164269e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 164369e52a80SHarald Welte master_vol-vol); 164469e52a80SHarald Welte } else { 164569e52a80SHarald Welte /* increase volume */ 164669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 164769e52a80SHarald Welte HDA_AMP_VOLMASK, 164869e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 164969e52a80SHarald Welte (master_vol+vol)); 165069e52a80SHarald Welte } 165169e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 165269e52a80SHarald Welte /* mute line out */ 165369e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 165469e52a80SHarald Welte spec->autocfg.line_out_pins[0], 165569e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 165669e52a80SHarald Welte HDA_AMP_MUTE); 165769e52a80SHarald Welte } 165869e52a80SHarald Welte } 165969e52a80SHarald Welte 166025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 166125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 166225eaba2fSLydia Wang { 166325eaba2fSLydia Wang unsigned int hp_present; 166425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 166525eaba2fSLydia Wang 1666ab6734e7SLydia Wang if (spec->codec_type != VT2002P && spec->codec_type != VT1812) 166725eaba2fSLydia Wang return; 166825eaba2fSLydia Wang 1669d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 167025eaba2fSLydia Wang 167125eaba2fSLydia Wang if (!spec->hp_independent_mode) { 167225eaba2fSLydia Wang struct snd_ctl_elem_id id; 167325eaba2fSLydia Wang snd_hda_codec_amp_stereo( 167425eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 167525eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 167625eaba2fSLydia Wang /* notify change */ 167725eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 167825eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 167925eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 168025eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 168125eaba2fSLydia Wang &id); 168225eaba2fSLydia Wang } 168325eaba2fSLydia Wang } 168425eaba2fSLydia Wang 168525eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 168625eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 168725eaba2fSLydia Wang { 168801a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 168901a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 169001a1796bSakpm@linux-foundation.org */ 169101a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 169225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 169325eaba2fSLydia Wang int i; 169425eaba2fSLydia Wang 169525eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 169625eaba2fSLydia Wang return; 169725eaba2fSLydia Wang 1698d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 169925eaba2fSLydia Wang 1700d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 170125eaba2fSLydia Wang 170225eaba2fSLydia Wang if (!spec->hp_independent_mode) { 170325eaba2fSLydia Wang /* Mute Line-Outs */ 170425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 170525eaba2fSLydia Wang snd_hda_codec_amp_stereo( 170625eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 170725eaba2fSLydia Wang HDA_OUTPUT, 0, 170825eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 170925eaba2fSLydia Wang if (hp_present) 171025eaba2fSLydia Wang present = hp_present; 171125eaba2fSLydia Wang } 171225eaba2fSLydia Wang /* Speakers */ 171325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 171425eaba2fSLydia Wang snd_hda_codec_amp_stereo( 171525eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 171625eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 171725eaba2fSLydia Wang } 171825eaba2fSLydia Wang 171925eaba2fSLydia Wang 172069e52a80SHarald Welte /* unsolicited event for jack sensing */ 172169e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 172269e52a80SHarald Welte unsigned int res) 172369e52a80SHarald Welte { 172469e52a80SHarald Welte res >>= 26; 1725a34df19aSLydia Wang if (res & VIA_HP_EVENT) 172669e52a80SHarald Welte via_hp_automute(codec); 1727a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 172869e52a80SHarald Welte via_gpio_control(codec); 1729a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 17303e95b9abSLydia Wang set_widgets_power_state(codec); 1731f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 1732f3db423dSLydia Wang via_mono_automute(codec); 173325eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 173425eaba2fSLydia Wang via_speaker_automute(codec); 173525eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 173625eaba2fSLydia Wang via_hp_bind_automute(codec); 173769e52a80SHarald Welte } 173869e52a80SHarald Welte 1739c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1740c577b8a1SJoseph Chan { 1741c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 174269e52a80SHarald Welte int i; 174369e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 174469e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 174569e52a80SHarald Welte 1746f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1747f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 174855d1d6c1STakashi Iwai if (spec->dig_in_pin) { 174955d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1750f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 175112b74c80STakashi Iwai PIN_OUT); 175255d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1753f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1754f7278fd0SJosepch Chan } 175512b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 175612b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 175712b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1758f7278fd0SJosepch Chan 17599da29271STakashi Iwai /* assign slave outs */ 17609da29271STakashi Iwai if (spec->slave_dig_outs[0]) 17619da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 17625691ec7fSHarald Welte 1763c577b8a1SJoseph Chan return 0; 1764c577b8a1SJoseph Chan } 1765c577b8a1SJoseph Chan 17661f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 17671f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 17681f2e99feSLydia Wang { 17691f2e99feSLydia Wang struct via_spec *spec = codec->spec; 17701f2e99feSLydia Wang vt1708_stop_hp_work(spec); 17711f2e99feSLydia Wang return 0; 17721f2e99feSLydia Wang } 17731f2e99feSLydia Wang #endif 17741f2e99feSLydia Wang 1775cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1776cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1777cb53c626STakashi Iwai { 1778cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1779cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1780cb53c626STakashi Iwai } 1781cb53c626STakashi Iwai #endif 1782cb53c626STakashi Iwai 1783c577b8a1SJoseph Chan /* 1784c577b8a1SJoseph Chan */ 1785c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 1786c577b8a1SJoseph Chan .build_controls = via_build_controls, 1787c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1788c577b8a1SJoseph Chan .init = via_init, 1789c577b8a1SJoseph Chan .free = via_free, 17901f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 17911f2e99feSLydia Wang .suspend = via_suspend, 17921f2e99feSLydia Wang #endif 1793cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1794cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1795cb53c626STakashi Iwai #endif 1796c577b8a1SJoseph Chan }; 1797c577b8a1SJoseph Chan 1798c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 1799c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 1800c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1801c577b8a1SJoseph Chan { 1802c577b8a1SJoseph Chan int i; 1803c577b8a1SJoseph Chan hda_nid_t nid; 1804c577b8a1SJoseph Chan 1805c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 1806c577b8a1SJoseph Chan 1807c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1808c577b8a1SJoseph Chan 1809c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 1810c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1811c577b8a1SJoseph Chan if (nid) { 1812c577b8a1SJoseph Chan /* config dac list */ 1813c577b8a1SJoseph Chan switch (i) { 1814c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1815c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 1816c577b8a1SJoseph Chan break; 1817c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1818c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 1819c577b8a1SJoseph Chan break; 1820c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1821fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 1822c577b8a1SJoseph Chan break; 1823c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1824fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 1825c577b8a1SJoseph Chan break; 1826c577b8a1SJoseph Chan } 1827c577b8a1SJoseph Chan } 1828c577b8a1SJoseph Chan } 1829c577b8a1SJoseph Chan 1830c577b8a1SJoseph Chan return 0; 1831c577b8a1SJoseph Chan } 1832c577b8a1SJoseph Chan 1833c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1834c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 1835c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1836c577b8a1SJoseph Chan { 1837c577b8a1SJoseph Chan char name[32]; 1838ea734963STakashi Iwai static const char * const chname[4] = { 1839ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 1840ea734963STakashi Iwai }; 18419645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 1842c577b8a1SJoseph Chan int i, err; 1843c577b8a1SJoseph Chan 1844c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1845c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1846c577b8a1SJoseph Chan 1847c577b8a1SJoseph Chan if (!nid) 1848c577b8a1SJoseph Chan continue; 1849c577b8a1SJoseph Chan 18509645c203SLydia Wang nid_vol = nid_vols[i]; 1851c577b8a1SJoseph Chan 1852c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1853c577b8a1SJoseph Chan /* Center/LFE */ 1854c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1855c577b8a1SJoseph Chan "Center Playback Volume", 1856f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1857f7278fd0SJosepch Chan HDA_OUTPUT)); 1858c577b8a1SJoseph Chan if (err < 0) 1859c577b8a1SJoseph Chan return err; 1860c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1861c577b8a1SJoseph Chan "LFE Playback Volume", 1862f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1863f7278fd0SJosepch Chan HDA_OUTPUT)); 1864c577b8a1SJoseph Chan if (err < 0) 1865c577b8a1SJoseph Chan return err; 1866c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1867c577b8a1SJoseph Chan "Center Playback Switch", 1868f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1869f7278fd0SJosepch Chan HDA_OUTPUT)); 1870c577b8a1SJoseph Chan if (err < 0) 1871c577b8a1SJoseph Chan return err; 1872c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1873c577b8a1SJoseph Chan "LFE Playback Switch", 1874f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1875f7278fd0SJosepch Chan HDA_OUTPUT)); 1876c577b8a1SJoseph Chan if (err < 0) 1877c577b8a1SJoseph Chan return err; 1878c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 1879c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1880c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1881c577b8a1SJoseph Chan "Master Front Playback Volume", 18829645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1883f7278fd0SJosepch Chan HDA_INPUT)); 1884c577b8a1SJoseph Chan if (err < 0) 1885c577b8a1SJoseph Chan return err; 1886c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1887c577b8a1SJoseph Chan "Master Front Playback Switch", 18889645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1889f7278fd0SJosepch Chan HDA_INPUT)); 1890c577b8a1SJoseph Chan if (err < 0) 1891c577b8a1SJoseph Chan return err; 1892c577b8a1SJoseph Chan 1893c577b8a1SJoseph Chan /* add control to PW3 */ 1894c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1895c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1896f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1897f7278fd0SJosepch Chan HDA_OUTPUT)); 1898c577b8a1SJoseph Chan if (err < 0) 1899c577b8a1SJoseph Chan return err; 1900c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1901c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1902f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1903f7278fd0SJosepch Chan HDA_OUTPUT)); 1904c577b8a1SJoseph Chan if (err < 0) 1905c577b8a1SJoseph Chan return err; 1906c577b8a1SJoseph Chan } else { 1907c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1908c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1909f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1910f7278fd0SJosepch Chan HDA_OUTPUT)); 1911c577b8a1SJoseph Chan if (err < 0) 1912c577b8a1SJoseph Chan return err; 1913c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1914c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1915f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1916f7278fd0SJosepch Chan HDA_OUTPUT)); 1917c577b8a1SJoseph Chan if (err < 0) 1918c577b8a1SJoseph Chan return err; 1919c577b8a1SJoseph Chan } 1920c577b8a1SJoseph Chan } 1921c577b8a1SJoseph Chan 1922c577b8a1SJoseph Chan return 0; 1923c577b8a1SJoseph Chan } 1924c577b8a1SJoseph Chan 19250aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 19260aa62aefSHarald Welte { 19270aa62aefSHarald Welte int i; 19280aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1929ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 19300aa62aefSHarald Welte 19310aa62aefSHarald Welte /* for hp mode select */ 193210a20af7STakashi Iwai for (i = 0; texts[i]; i++) 193310a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 19340aa62aefSHarald Welte 19350aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 19360aa62aefSHarald Welte } 19370aa62aefSHarald Welte 1938c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1939c577b8a1SJoseph Chan { 1940c577b8a1SJoseph Chan int err; 1941c577b8a1SJoseph Chan 1942c577b8a1SJoseph Chan if (!pin) 1943c577b8a1SJoseph Chan return 0; 1944c577b8a1SJoseph Chan 1945c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 1946cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 1947c577b8a1SJoseph Chan 1948c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1949c577b8a1SJoseph Chan "Headphone Playback Volume", 1950c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1951c577b8a1SJoseph Chan if (err < 0) 1952c577b8a1SJoseph Chan return err; 1953c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1954c577b8a1SJoseph Chan "Headphone Playback Switch", 1955c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1956c577b8a1SJoseph Chan if (err < 0) 1957c577b8a1SJoseph Chan return err; 1958c577b8a1SJoseph Chan 19590aa62aefSHarald Welte create_hp_imux(spec); 19600aa62aefSHarald Welte 1961c577b8a1SJoseph Chan return 0; 1962c577b8a1SJoseph Chan } 1963c577b8a1SJoseph Chan 1964c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 196510a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, 1966f3268512STakashi Iwai const struct auto_pin_cfg *cfg, 1967f3268512STakashi Iwai hda_nid_t cap_nid, 1968f3268512STakashi Iwai hda_nid_t pin_idxs[], int num_idxs) 1969c577b8a1SJoseph Chan { 197010a20af7STakashi Iwai struct via_spec *spec = codec->spec; 19710aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 19727b315bb4STakashi Iwai int i, err, idx, type, type_idx = 0; 1973c577b8a1SJoseph Chan 1974c577b8a1SJoseph Chan /* for internal loopback recording select */ 1975f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 1976f3268512STakashi Iwai if (pin_idxs[idx] == 0xff) { 197710a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 1978f3268512STakashi Iwai break; 1979f3268512STakashi Iwai } 1980f3268512STakashi Iwai } 1981c577b8a1SJoseph Chan 19827b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 198310a20af7STakashi Iwai const char *label; 19847b315bb4STakashi Iwai type = cfg->inputs[i].type; 1985f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 19867b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 1987c577b8a1SJoseph Chan break; 1988f3268512STakashi Iwai if (idx >= num_idxs) 1989f3268512STakashi Iwai continue; 19907b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 19917b315bb4STakashi Iwai type_idx++; 19927b315bb4STakashi Iwai else 19937b315bb4STakashi Iwai type_idx = 0; 199410a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 199516922281SLydia Wang if (spec->codec_type == VT1708S || 199616922281SLydia Wang spec->codec_type == VT1702 || 199716922281SLydia Wang spec->codec_type == VT1716S) 199816922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 199916922281SLydia Wang idx+1, cap_nid); 200016922281SLydia Wang else 200116922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 200216922281SLydia Wang idx, cap_nid); 2003c577b8a1SJoseph Chan if (err < 0) 2004c577b8a1SJoseph Chan return err; 200510a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2006c577b8a1SJoseph Chan } 2007c577b8a1SJoseph Chan return 0; 2008c577b8a1SJoseph Chan } 2009c577b8a1SJoseph Chan 2010f3268512STakashi Iwai /* create playback/capture controls for input pins */ 201110a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, 2012f3268512STakashi Iwai const struct auto_pin_cfg *cfg) 2013f3268512STakashi Iwai { 2014f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; 201510a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, 2016f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2017f3268512STakashi Iwai } 2018f3268512STakashi Iwai 2019cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2020cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2021cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2022cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2023cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2024cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2025cb53c626STakashi Iwai { } /* end */ 2026cb53c626STakashi Iwai }; 2027cb53c626STakashi Iwai #endif 2028cb53c626STakashi Iwai 202976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 203076d9b0ddSHarald Welte { 203176d9b0ddSHarald Welte unsigned int def_conf; 203276d9b0ddSHarald Welte unsigned char seqassoc; 203376d9b0ddSHarald Welte 20342f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 203576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 203676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 203782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 203882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 203976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 20402f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 204176d9b0ddSHarald Welte } 204276d9b0ddSHarald Welte 204376d9b0ddSHarald Welte return; 204476d9b0ddSHarald Welte } 204576d9b0ddSHarald Welte 20461f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 20471f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20481f2e99feSLydia Wang { 20491f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20501f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20511f2e99feSLydia Wang 20521f2e99feSLydia Wang if (spec->codec_type != VT1708) 20531f2e99feSLydia Wang return 0; 20541f2e99feSLydia Wang spec->vt1708_jack_detectect = 20551f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 20561f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 20571f2e99feSLydia Wang return 0; 20581f2e99feSLydia Wang } 20591f2e99feSLydia Wang 20601f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 20611f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20621f2e99feSLydia Wang { 20631f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20641f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20651f2e99feSLydia Wang int change; 20661f2e99feSLydia Wang 20671f2e99feSLydia Wang if (spec->codec_type != VT1708) 20681f2e99feSLydia Wang return 0; 20691f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 20701f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 20711f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 20721f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 20731f2e99feSLydia Wang mute_aa_path(codec, 1); 20741f2e99feSLydia Wang notify_aa_path_ctls(codec); 20751f2e99feSLydia Wang } 20761f2e99feSLydia Wang return change; 20771f2e99feSLydia Wang } 20781f2e99feSLydia Wang 20791f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 20801f2e99feSLydia Wang { 20811f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20821f2e99feSLydia Wang .name = "Jack Detect", 20831f2e99feSLydia Wang .count = 1, 20841f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 20851f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 20861f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 20871f2e99feSLydia Wang }, 20881f2e99feSLydia Wang {} /* end */ 20891f2e99feSLydia Wang }; 20901f2e99feSLydia Wang 2091c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2092c577b8a1SJoseph Chan { 2093c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2094c577b8a1SJoseph Chan int err; 2095c577b8a1SJoseph Chan 209676d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 209776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 209876d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 209976d9b0ddSHarald Welte 2100c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2101c577b8a1SJoseph Chan if (err < 0) 2102c577b8a1SJoseph Chan return err; 2103c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2104c577b8a1SJoseph Chan if (err < 0) 2105c577b8a1SJoseph Chan return err; 2106c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2107c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2108c577b8a1SJoseph Chan 2109c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2110c577b8a1SJoseph Chan if (err < 0) 2111c577b8a1SJoseph Chan return err; 2112c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2113c577b8a1SJoseph Chan if (err < 0) 2114c577b8a1SJoseph Chan return err; 211510a20af7STakashi Iwai err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); 2116c577b8a1SJoseph Chan if (err < 0) 2117c577b8a1SJoseph Chan return err; 21181f2e99feSLydia Wang /* add jack detect on/off control */ 21191f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 21201f2e99feSLydia Wang if (err < 0) 21211f2e99feSLydia Wang return err; 2122c577b8a1SJoseph Chan 2123c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2124c577b8a1SJoseph Chan 21250852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2126c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 212755d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2128c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2129c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2130c577b8a1SJoseph Chan 2131603c4019STakashi Iwai if (spec->kctls.list) 2132603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2133c577b8a1SJoseph Chan 213469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2135c577b8a1SJoseph Chan 21360aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 21370aa62aefSHarald Welte 2138f8fdd495SHarald Welte if (spec->hp_mux) 21393d83e577STakashi Iwai via_hp_build(codec); 2140c577b8a1SJoseph Chan 21415b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2142c577b8a1SJoseph Chan return 1; 2143c577b8a1SJoseph Chan } 2144c577b8a1SJoseph Chan 2145c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2146c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2147c577b8a1SJoseph Chan { 214825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 214925eaba2fSLydia Wang 2150c577b8a1SJoseph Chan via_init(codec); 2151c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2152c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2153c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2154ab6734e7SLydia Wang if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { 215525eaba2fSLydia Wang via_hp_bind_automute(codec); 215625eaba2fSLydia Wang } else { 215725eaba2fSLydia Wang via_hp_automute(codec); 215825eaba2fSLydia Wang via_speaker_automute(codec); 215925eaba2fSLydia Wang } 216025eaba2fSLydia Wang 2161c577b8a1SJoseph Chan return 0; 2162c577b8a1SJoseph Chan } 2163c577b8a1SJoseph Chan 21641f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 21651f2e99feSLydia Wang { 21661f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 21671f2e99feSLydia Wang vt1708_hp_work.work); 21681f2e99feSLydia Wang if (spec->codec_type != VT1708) 21691f2e99feSLydia Wang return; 21701f2e99feSLydia Wang /* if jack state toggled */ 21711f2e99feSLydia Wang if (spec->vt1708_hp_present 2172d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 21731f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 21741f2e99feSLydia Wang via_hp_automute(spec->codec); 21751f2e99feSLydia Wang } 21761f2e99feSLydia Wang vt1708_start_hp_work(spec); 21771f2e99feSLydia Wang } 21781f2e99feSLydia Wang 2179337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2180337b9d02STakashi Iwai { 2181337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2182337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2183337b9d02STakashi Iwai unsigned int type; 2184337b9d02STakashi Iwai int i, n; 2185337b9d02STakashi Iwai 2186337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2187337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2188337b9d02STakashi Iwai while (nid) { 2189a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 21901c55d521STakashi Iwai if (type == AC_WID_PIN) 21911c55d521STakashi Iwai break; 2192337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2193337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2194337b9d02STakashi Iwai if (n <= 0) 2195337b9d02STakashi Iwai break; 2196337b9d02STakashi Iwai if (n > 1) { 2197337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2198337b9d02STakashi Iwai break; 2199337b9d02STakashi Iwai } 2200337b9d02STakashi Iwai nid = conn[0]; 2201337b9d02STakashi Iwai } 2202337b9d02STakashi Iwai } 22031c55d521STakashi Iwai return 0; 2204337b9d02STakashi Iwai } 2205337b9d02STakashi Iwai 2206c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2207c577b8a1SJoseph Chan { 2208c577b8a1SJoseph Chan struct via_spec *spec; 2209c577b8a1SJoseph Chan int err; 2210c577b8a1SJoseph Chan 2211c577b8a1SJoseph Chan /* create a codec specific record */ 22125b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2213c577b8a1SJoseph Chan if (spec == NULL) 2214c577b8a1SJoseph Chan return -ENOMEM; 2215c577b8a1SJoseph Chan 2216c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2217c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2218c577b8a1SJoseph Chan if (err < 0) { 2219c577b8a1SJoseph Chan via_free(codec); 2220c577b8a1SJoseph Chan return err; 2221c577b8a1SJoseph Chan } else if (!err) { 2222c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2223c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2224c577b8a1SJoseph Chan } 2225c577b8a1SJoseph Chan 2226c577b8a1SJoseph Chan 2227c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2228c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2229bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2230bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2231bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2232c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2233c577b8a1SJoseph Chan 2234c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2235c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2236c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2237c577b8a1SJoseph Chan 2238c577b8a1SJoseph Chan 2239c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2240c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2241c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 22420f67a611STakashi Iwai get_mux_nids(codec); 2243c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2244c577b8a1SJoseph Chan spec->num_mixers++; 2245c577b8a1SJoseph Chan } 2246c577b8a1SJoseph Chan 2247c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2248c577b8a1SJoseph Chan 2249c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2250cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2251cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2252cb53c626STakashi Iwai #endif 22531f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2254c577b8a1SJoseph Chan return 0; 2255c577b8a1SJoseph Chan } 2256c577b8a1SJoseph Chan 2257c577b8a1SJoseph Chan /* capture mixer elements */ 2258c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2259c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2260c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2261c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2262c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2263c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2264c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2265c577b8a1SJoseph Chan { 2266c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2267c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2268c577b8a1SJoseph Chan * So call somewhat different.. 2269c577b8a1SJoseph Chan */ 2270c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2271c577b8a1SJoseph Chan .name = "Input Source", 2272c577b8a1SJoseph Chan .count = 1, 2273c577b8a1SJoseph Chan .info = via_mux_enum_info, 2274c577b8a1SJoseph Chan .get = via_mux_enum_get, 2275c577b8a1SJoseph Chan .put = via_mux_enum_put, 2276c577b8a1SJoseph Chan }, 2277c577b8a1SJoseph Chan { } /* end */ 2278c577b8a1SJoseph Chan }; 2279c577b8a1SJoseph Chan 228069e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2281a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2282a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 228369e52a80SHarald Welte { } 228469e52a80SHarald Welte }; 228569e52a80SHarald Welte 2286c577b8a1SJoseph Chan /* 2287c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2288c577b8a1SJoseph Chan */ 2289c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2290c577b8a1SJoseph Chan /* 2291c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2292c577b8a1SJoseph Chan */ 2293c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2294c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2295c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2296c577b8a1SJoseph Chan 2297c577b8a1SJoseph Chan 2298f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2299c577b8a1SJoseph Chan * mixer widget 2300c577b8a1SJoseph Chan */ 2301c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2302f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2303f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2304f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2305f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2306f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2307c577b8a1SJoseph Chan 2308c577b8a1SJoseph Chan /* 2309c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2310c577b8a1SJoseph Chan */ 2311c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2312c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2313c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2314c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2315c577b8a1SJoseph Chan 2316c577b8a1SJoseph Chan /* 2317c577b8a1SJoseph Chan * Unmute PW3 and PW4 2318c577b8a1SJoseph Chan */ 2319c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2320c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2321c577b8a1SJoseph Chan 2322bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2323bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2324c577b8a1SJoseph Chan /* PW9 Output enable */ 2325c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2326c577b8a1SJoseph Chan { } 2327c577b8a1SJoseph Chan }; 2328c577b8a1SJoseph Chan 2329c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2330c577b8a1SJoseph Chan .substreams = 1, 2331c577b8a1SJoseph Chan .channels_min = 2, 2332c577b8a1SJoseph Chan .channels_max = 10, 2333c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2334c577b8a1SJoseph Chan .ops = { 2335c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2336c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2337c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2338c577b8a1SJoseph Chan }, 2339c577b8a1SJoseph Chan }; 2340c577b8a1SJoseph Chan 2341c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2342c577b8a1SJoseph Chan .substreams = 1, 2343c577b8a1SJoseph Chan .channels_min = 2, 2344c577b8a1SJoseph Chan .channels_max = 6, 2345c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2346c577b8a1SJoseph Chan .ops = { 2347c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2348c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2349c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2350c577b8a1SJoseph Chan }, 2351c577b8a1SJoseph Chan }; 2352c577b8a1SJoseph Chan 2353c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2354c577b8a1SJoseph Chan .substreams = 2, 2355c577b8a1SJoseph Chan .channels_min = 2, 2356c577b8a1SJoseph Chan .channels_max = 2, 2357c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2358c577b8a1SJoseph Chan .ops = { 2359c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2360c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2361c577b8a1SJoseph Chan }, 2362c577b8a1SJoseph Chan }; 2363c577b8a1SJoseph Chan 2364c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2365c577b8a1SJoseph Chan .substreams = 1, 2366c577b8a1SJoseph Chan .channels_min = 2, 2367c577b8a1SJoseph Chan .channels_max = 2, 2368c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2369c577b8a1SJoseph Chan .ops = { 2370c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2371c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2372c577b8a1SJoseph Chan }, 2373c577b8a1SJoseph Chan }; 2374c577b8a1SJoseph Chan 2375c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2376c577b8a1SJoseph Chan .substreams = 1, 2377c577b8a1SJoseph Chan .channels_min = 2, 2378c577b8a1SJoseph Chan .channels_max = 2, 2379c577b8a1SJoseph Chan }; 2380c577b8a1SJoseph Chan 2381c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2382c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2383c577b8a1SJoseph Chan { 2384c577b8a1SJoseph Chan int i; 2385c577b8a1SJoseph Chan hda_nid_t nid; 2386c577b8a1SJoseph Chan 2387c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2388c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2389c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2390c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2391c577b8a1SJoseph Chan 2392c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2393c577b8a1SJoseph Chan 2394c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2395c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2396c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2397c577b8a1SJoseph Chan if (nid) { 2398c577b8a1SJoseph Chan /* config dac list */ 2399c577b8a1SJoseph Chan switch (i) { 2400c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2401c577b8a1SJoseph Chan /* AOW0 */ 2402c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2403c577b8a1SJoseph Chan break; 2404c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2405c577b8a1SJoseph Chan /* AOW2 */ 2406c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2407c577b8a1SJoseph Chan break; 2408c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2409c577b8a1SJoseph Chan /* AOW3 */ 2410fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2411c577b8a1SJoseph Chan break; 2412c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2413c577b8a1SJoseph Chan /* AOW1 */ 2414fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2415c577b8a1SJoseph Chan break; 2416c577b8a1SJoseph Chan default: 2417c577b8a1SJoseph Chan break; 2418c577b8a1SJoseph Chan } 2419c577b8a1SJoseph Chan } 2420c577b8a1SJoseph Chan } 2421c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2422c577b8a1SJoseph Chan 2423c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2424c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2425c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2426c577b8a1SJoseph Chan if (nid) { 2427c577b8a1SJoseph Chan /* config dac list */ 2428c577b8a1SJoseph Chan switch (i) { 2429c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2430c577b8a1SJoseph Chan /* AOW0 */ 2431c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2432c577b8a1SJoseph Chan break; 2433c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2434c577b8a1SJoseph Chan /* AOW2 */ 2435c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2436c577b8a1SJoseph Chan break; 2437c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2438c577b8a1SJoseph Chan /* AOW1 */ 2439c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2440c577b8a1SJoseph Chan break; 2441c577b8a1SJoseph Chan default: 2442c577b8a1SJoseph Chan break; 2443c577b8a1SJoseph Chan } 2444c577b8a1SJoseph Chan } 2445c577b8a1SJoseph Chan } 2446c577b8a1SJoseph Chan } 2447c577b8a1SJoseph Chan 2448c577b8a1SJoseph Chan return 0; 2449c577b8a1SJoseph Chan } 2450c577b8a1SJoseph Chan 2451c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2452c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2453c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2454c577b8a1SJoseph Chan { 2455c577b8a1SJoseph Chan char name[32]; 2456ea734963STakashi Iwai static const char * const chname[4] = { 2457ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2458ea734963STakashi Iwai }; 24594483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2460c577b8a1SJoseph Chan int i, err; 2461c577b8a1SJoseph Chan 2462c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2463c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2464c577b8a1SJoseph Chan 2465c577b8a1SJoseph Chan if (!nid) 2466c577b8a1SJoseph Chan continue; 2467c577b8a1SJoseph Chan 24684483a2f5SLydia Wang nid_vol = nid_vols[i]; 24694483a2f5SLydia Wang 2470c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2471c577b8a1SJoseph Chan /* Center/LFE */ 2472c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2473c577b8a1SJoseph Chan "Center Playback Volume", 24744483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2475f7278fd0SJosepch Chan HDA_OUTPUT)); 2476c577b8a1SJoseph Chan if (err < 0) 2477c577b8a1SJoseph Chan return err; 2478c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2479c577b8a1SJoseph Chan "LFE Playback Volume", 24804483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2481f7278fd0SJosepch Chan HDA_OUTPUT)); 2482c577b8a1SJoseph Chan if (err < 0) 2483c577b8a1SJoseph Chan return err; 2484c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2485c577b8a1SJoseph Chan "Center Playback Switch", 24864483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2487f7278fd0SJosepch Chan HDA_OUTPUT)); 2488c577b8a1SJoseph Chan if (err < 0) 2489c577b8a1SJoseph Chan return err; 2490c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2491c577b8a1SJoseph Chan "LFE Playback Switch", 24924483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2493f7278fd0SJosepch Chan HDA_OUTPUT)); 2494c577b8a1SJoseph Chan if (err < 0) 2495c577b8a1SJoseph Chan return err; 2496c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 24974483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2498c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2499c577b8a1SJoseph Chan "Master Front Playback Volume", 25004483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2501f7278fd0SJosepch Chan HDA_INPUT)); 2502c577b8a1SJoseph Chan if (err < 0) 2503c577b8a1SJoseph Chan return err; 2504c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2505c577b8a1SJoseph Chan "Master Front Playback Switch", 25064483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2507f7278fd0SJosepch Chan HDA_INPUT)); 2508c577b8a1SJoseph Chan if (err < 0) 2509c577b8a1SJoseph Chan return err; 2510c577b8a1SJoseph Chan 2511c577b8a1SJoseph Chan /* add control to PW3 */ 2512c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2513c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2514f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2515f7278fd0SJosepch Chan HDA_OUTPUT)); 2516c577b8a1SJoseph Chan if (err < 0) 2517c577b8a1SJoseph Chan return err; 2518c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2519c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2520f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2521f7278fd0SJosepch Chan HDA_OUTPUT)); 2522c577b8a1SJoseph Chan if (err < 0) 2523c577b8a1SJoseph Chan return err; 2524c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2525c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2526c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 25274483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2528f7278fd0SJosepch Chan HDA_OUTPUT)); 2529c577b8a1SJoseph Chan if (err < 0) 2530c577b8a1SJoseph Chan return err; 2531c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2532c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 25334483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2534f7278fd0SJosepch Chan HDA_OUTPUT)); 2535c577b8a1SJoseph Chan if (err < 0) 2536c577b8a1SJoseph Chan return err; 2537c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2538c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2539c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 25404483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2541f7278fd0SJosepch Chan HDA_OUTPUT)); 2542c577b8a1SJoseph Chan if (err < 0) 2543c577b8a1SJoseph Chan return err; 2544c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2545c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 25464483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2547f7278fd0SJosepch Chan HDA_OUTPUT)); 2548c577b8a1SJoseph Chan if (err < 0) 2549c577b8a1SJoseph Chan return err; 2550c577b8a1SJoseph Chan } 2551c577b8a1SJoseph Chan } 2552c577b8a1SJoseph Chan 2553c577b8a1SJoseph Chan return 0; 2554c577b8a1SJoseph Chan } 2555c577b8a1SJoseph Chan 2556c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2557c577b8a1SJoseph Chan { 2558c577b8a1SJoseph Chan int err; 2559c577b8a1SJoseph Chan 2560c577b8a1SJoseph Chan if (!pin) 2561c577b8a1SJoseph Chan return 0; 2562c577b8a1SJoseph Chan 2563c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2564c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2565c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2566c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2567cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2568c577b8a1SJoseph Chan 2569c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2570c577b8a1SJoseph Chan "Headphone Playback Volume", 2571c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2572c577b8a1SJoseph Chan if (err < 0) 2573c577b8a1SJoseph Chan return err; 2574c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2575c577b8a1SJoseph Chan "Headphone Playback Switch", 2576c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2577c577b8a1SJoseph Chan if (err < 0) 2578c577b8a1SJoseph Chan return err; 2579c577b8a1SJoseph Chan 2580c577b8a1SJoseph Chan return 0; 2581c577b8a1SJoseph Chan } 2582c577b8a1SJoseph Chan 2583c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 258410a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, 2585c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2586c577b8a1SJoseph Chan { 2587f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; 258810a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, 2589f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2590c577b8a1SJoseph Chan } 2591c577b8a1SJoseph Chan 2592c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2593c577b8a1SJoseph Chan { 2594c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2595c577b8a1SJoseph Chan int err; 2596c577b8a1SJoseph Chan 2597c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2598c577b8a1SJoseph Chan if (err < 0) 2599c577b8a1SJoseph Chan return err; 2600c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2601c577b8a1SJoseph Chan if (err < 0) 2602c577b8a1SJoseph Chan return err; 2603c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2604c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2605c577b8a1SJoseph Chan 2606c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2607c577b8a1SJoseph Chan if (err < 0) 2608c577b8a1SJoseph Chan return err; 2609c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2610c577b8a1SJoseph Chan if (err < 0) 2611c577b8a1SJoseph Chan return err; 261210a20af7STakashi Iwai err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); 2613c577b8a1SJoseph Chan if (err < 0) 2614c577b8a1SJoseph Chan return err; 2615c577b8a1SJoseph Chan 2616c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2617c577b8a1SJoseph Chan 26180852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2619c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 262055d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2621c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2622c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2623c577b8a1SJoseph Chan 2624603c4019STakashi Iwai if (spec->kctls.list) 2625603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2626c577b8a1SJoseph Chan 26270aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2628c577b8a1SJoseph Chan 2629f8fdd495SHarald Welte if (spec->hp_mux) 26303d83e577STakashi Iwai via_hp_build(codec); 2631f8fdd495SHarald Welte 26325b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2633c577b8a1SJoseph Chan return 1; 2634c577b8a1SJoseph Chan } 2635c577b8a1SJoseph Chan 2636cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2637cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 2638cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2639cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2640cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2641cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2642cb53c626STakashi Iwai { } /* end */ 2643cb53c626STakashi Iwai }; 2644cb53c626STakashi Iwai #endif 2645cb53c626STakashi Iwai 2646c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2647c577b8a1SJoseph Chan { 2648c577b8a1SJoseph Chan struct via_spec *spec; 2649c577b8a1SJoseph Chan int err; 2650c577b8a1SJoseph Chan 2651c577b8a1SJoseph Chan /* create a codec specific record */ 26525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2653c577b8a1SJoseph Chan if (spec == NULL) 2654c577b8a1SJoseph Chan return -ENOMEM; 2655c577b8a1SJoseph Chan 2656c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2657c577b8a1SJoseph Chan if (err < 0) { 2658c577b8a1SJoseph Chan via_free(codec); 2659c577b8a1SJoseph Chan return err; 2660c577b8a1SJoseph Chan } else if (!err) { 2661c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2662c577b8a1SJoseph Chan "Using genenic mode...\n"); 2663c577b8a1SJoseph Chan } 2664c577b8a1SJoseph Chan 266569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 266669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2667c577b8a1SJoseph Chan 2668c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2669c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2670c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2671c577b8a1SJoseph Chan 2672c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2673c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2674c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2675c577b8a1SJoseph Chan 2676c577b8a1SJoseph Chan 2677c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2678c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2679c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2680337b9d02STakashi Iwai get_mux_nids(codec); 2681c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2682c577b8a1SJoseph Chan spec->num_mixers++; 2683c577b8a1SJoseph Chan } 2684c577b8a1SJoseph Chan 2685c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2686c577b8a1SJoseph Chan 2687c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 268869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2689cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2690cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2691cb53c626STakashi Iwai #endif 2692c577b8a1SJoseph Chan 2693c577b8a1SJoseph Chan return 0; 2694c577b8a1SJoseph Chan } 2695c577b8a1SJoseph Chan /* 2696c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2697c577b8a1SJoseph Chan */ 2698c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2699c577b8a1SJoseph Chan /* 2700c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2701c577b8a1SJoseph Chan */ 2702c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2703c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2704c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2705c577b8a1SJoseph Chan 2706c577b8a1SJoseph Chan 2707c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2708c577b8a1SJoseph Chan * mixer widget 2709c577b8a1SJoseph Chan */ 2710c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2711c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2712c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2713c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2714c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2715c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2716c577b8a1SJoseph Chan 2717c577b8a1SJoseph Chan /* 2718c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2719c577b8a1SJoseph Chan */ 2720c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2721c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2722c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2723c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2724c577b8a1SJoseph Chan 2725c577b8a1SJoseph Chan /* 2726c577b8a1SJoseph Chan * Unmute PW3 and PW4 2727c577b8a1SJoseph Chan */ 2728c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2729c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2730c577b8a1SJoseph Chan 2731c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2732c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2733c577b8a1SJoseph Chan /* PW9 Output enable */ 2734c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2735c577b8a1SJoseph Chan { } 2736c577b8a1SJoseph Chan }; 2737c577b8a1SJoseph Chan 2738c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2739c577b8a1SJoseph Chan { 2740c577b8a1SJoseph Chan struct via_spec *spec; 2741c577b8a1SJoseph Chan int err; 2742c577b8a1SJoseph Chan 2743c577b8a1SJoseph Chan /* create a codec specific record */ 27445b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2745c577b8a1SJoseph Chan if (spec == NULL) 2746c577b8a1SJoseph Chan return -ENOMEM; 2747c577b8a1SJoseph Chan 2748c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2749c577b8a1SJoseph Chan if (err < 0) { 2750c577b8a1SJoseph Chan via_free(codec); 2751c577b8a1SJoseph Chan return err; 2752c577b8a1SJoseph Chan } else if (!err) { 2753c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2754c577b8a1SJoseph Chan "Using genenic mode...\n"); 2755c577b8a1SJoseph Chan } 2756c577b8a1SJoseph Chan 275769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 275869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2759c577b8a1SJoseph Chan 2760c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2761c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2762c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2763c577b8a1SJoseph Chan 2764c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2765c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2766c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2767c577b8a1SJoseph Chan 2768c577b8a1SJoseph Chan 2769c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2770c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2771c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2772337b9d02STakashi Iwai get_mux_nids(codec); 2773c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2774c577b8a1SJoseph Chan spec->num_mixers++; 2775c577b8a1SJoseph Chan } 2776c577b8a1SJoseph Chan 2777c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2778c577b8a1SJoseph Chan 2779c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 278069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2781cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2782cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2783cb53c626STakashi Iwai #endif 2784f7278fd0SJosepch Chan return 0; 2785f7278fd0SJosepch Chan } 2786f7278fd0SJosepch Chan 2787f7278fd0SJosepch Chan /* capture mixer elements */ 2788f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2789f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2790f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2791f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2792f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2793f7278fd0SJosepch Chan { 2794f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2795f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2796f7278fd0SJosepch Chan * So call somewhat different.. 2797f7278fd0SJosepch Chan */ 2798f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2799f7278fd0SJosepch Chan .name = "Input Source", 2800f7278fd0SJosepch Chan .count = 1, 2801f7278fd0SJosepch Chan .info = via_mux_enum_info, 2802f7278fd0SJosepch Chan .get = via_mux_enum_get, 2803f7278fd0SJosepch Chan .put = via_mux_enum_put, 2804f7278fd0SJosepch Chan }, 2805f7278fd0SJosepch Chan { } /* end */ 2806f7278fd0SJosepch Chan }; 2807f7278fd0SJosepch Chan /* 2808f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2809f7278fd0SJosepch Chan */ 2810f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2811f7278fd0SJosepch Chan /* 2812f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2813f7278fd0SJosepch Chan */ 2814f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2815f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2816f7278fd0SJosepch Chan 2817f7278fd0SJosepch Chan 2818f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2819f7278fd0SJosepch Chan * mixer widget 2820f7278fd0SJosepch Chan */ 2821f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2822f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2823f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2824f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2825f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2826f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2827f7278fd0SJosepch Chan 2828f7278fd0SJosepch Chan /* 2829f7278fd0SJosepch Chan * Set up output mixers 2830f7278fd0SJosepch Chan */ 2831f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2832f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2833f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2834f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2835f7278fd0SJosepch Chan 2836f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2837bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2838f7278fd0SJosepch Chan /* PW9 Output enable */ 2839f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2840f7278fd0SJosepch Chan /* PW10 Input enable */ 2841f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2842f7278fd0SJosepch Chan { } 2843f7278fd0SJosepch Chan }; 2844f7278fd0SJosepch Chan 2845f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2846f7278fd0SJosepch Chan /* 2847f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2848f7278fd0SJosepch Chan */ 2849f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2850f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2851f7278fd0SJosepch Chan 2852f7278fd0SJosepch Chan 2853f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2854f7278fd0SJosepch Chan * mixer widget 2855f7278fd0SJosepch Chan */ 2856f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2857f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2858f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2859f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2860f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2861f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2862f7278fd0SJosepch Chan 2863f7278fd0SJosepch Chan /* 2864f7278fd0SJosepch Chan * Set up output mixers 2865f7278fd0SJosepch Chan */ 2866f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2867f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2868f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2869f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2870f7278fd0SJosepch Chan 2871f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2872f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2873f7278fd0SJosepch Chan /* PW9 Output enable */ 2874f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2875f7278fd0SJosepch Chan /* PW10 Input enable */ 2876f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2877f7278fd0SJosepch Chan { } 2878f7278fd0SJosepch Chan }; 2879f7278fd0SJosepch Chan 288069e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 2881a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2882a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2883a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2884a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2885a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2886a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2887a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2888a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2889a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 289069e52a80SHarald Welte { } 289169e52a80SHarald Welte }; 289269e52a80SHarald Welte 289317314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 289417314379SLydia Wang struct hda_codec *codec, 289517314379SLydia Wang struct snd_pcm_substream *substream) 289617314379SLydia Wang { 289717314379SLydia Wang int idle = substream->pstr->substream_opened == 1 289817314379SLydia Wang && substream->ref_count == 0; 289917314379SLydia Wang 290017314379SLydia Wang analog_low_current_mode(codec, idle); 290117314379SLydia Wang return 0; 290217314379SLydia Wang } 290317314379SLydia Wang 2904f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 29050aa62aefSHarald Welte .substreams = 2, 2906f7278fd0SJosepch Chan .channels_min = 2, 2907f7278fd0SJosepch Chan .channels_max = 8, 2908f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2909f7278fd0SJosepch Chan .ops = { 2910f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29110aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 291217314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 291317314379SLydia Wang .close = via_pcm_open_close 2914f7278fd0SJosepch Chan }, 2915f7278fd0SJosepch Chan }; 2916f7278fd0SJosepch Chan 2917f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 29180aa62aefSHarald Welte .substreams = 2, 2919f7278fd0SJosepch Chan .channels_min = 2, 2920f7278fd0SJosepch Chan .channels_max = 4, 2921f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2922f7278fd0SJosepch Chan .ops = { 2923f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29240aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 29250aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 2926f7278fd0SJosepch Chan }, 2927f7278fd0SJosepch Chan }; 2928f7278fd0SJosepch Chan 2929f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 2930f7278fd0SJosepch Chan .substreams = 2, 2931f7278fd0SJosepch Chan .channels_min = 2, 2932f7278fd0SJosepch Chan .channels_max = 2, 2933f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 2934f7278fd0SJosepch Chan .ops = { 293517314379SLydia Wang .open = via_pcm_open_close, 2936f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 293717314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 293817314379SLydia Wang .close = via_pcm_open_close 2939f7278fd0SJosepch Chan }, 2940f7278fd0SJosepch Chan }; 2941f7278fd0SJosepch Chan 2942f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 2943f7278fd0SJosepch Chan .substreams = 1, 2944f7278fd0SJosepch Chan .channels_min = 2, 2945f7278fd0SJosepch Chan .channels_max = 2, 2946f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 2947f7278fd0SJosepch Chan .ops = { 2948f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 2949f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 29509da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 29519da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 2952f7278fd0SJosepch Chan }, 2953f7278fd0SJosepch Chan }; 2954f7278fd0SJosepch Chan 2955f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 2956f7278fd0SJosepch Chan .substreams = 1, 2957f7278fd0SJosepch Chan .channels_min = 2, 2958f7278fd0SJosepch Chan .channels_max = 2, 2959f7278fd0SJosepch Chan }; 2960f7278fd0SJosepch Chan 2961f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 2962f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 2963f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2964f7278fd0SJosepch Chan { 2965f7278fd0SJosepch Chan int i; 2966f7278fd0SJosepch Chan hda_nid_t nid; 2967f7278fd0SJosepch Chan 2968f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 2969f7278fd0SJosepch Chan 2970f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 2971f7278fd0SJosepch Chan 2972f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 2973f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 2974f7278fd0SJosepch Chan if (nid) { 2975f7278fd0SJosepch Chan /* config dac list */ 2976f7278fd0SJosepch Chan switch (i) { 2977f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 2978f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 2979f7278fd0SJosepch Chan break; 2980f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 2981f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 2982f7278fd0SJosepch Chan break; 2983f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 2984fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2985f7278fd0SJosepch Chan break; 2986f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 2987fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 2988f7278fd0SJosepch Chan break; 2989f7278fd0SJosepch Chan } 2990f7278fd0SJosepch Chan } 2991f7278fd0SJosepch Chan } 2992f7278fd0SJosepch Chan 2993f7278fd0SJosepch Chan return 0; 2994f7278fd0SJosepch Chan } 2995f7278fd0SJosepch Chan 2996f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 2997f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 2998f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 2999f7278fd0SJosepch Chan { 3000f7278fd0SJosepch Chan char name[32]; 3001ea734963STakashi Iwai static const char * const chname[4] = { 3002ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3003ea734963STakashi Iwai }; 3004fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3005f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3006f7278fd0SJosepch Chan int i, err; 3007f7278fd0SJosepch Chan 3008f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3009f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3010f7278fd0SJosepch Chan 3011f7278fd0SJosepch Chan if (!nid) 3012f7278fd0SJosepch Chan continue; 3013f7278fd0SJosepch Chan 3014f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3015f7278fd0SJosepch Chan 3016f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3017f7278fd0SJosepch Chan /* Center/LFE */ 3018f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3019f7278fd0SJosepch Chan "Center Playback Volume", 3020f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3021f7278fd0SJosepch Chan HDA_OUTPUT)); 3022f7278fd0SJosepch Chan if (err < 0) 3023f7278fd0SJosepch Chan return err; 3024f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3025f7278fd0SJosepch Chan "LFE Playback Volume", 3026f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3027f7278fd0SJosepch Chan HDA_OUTPUT)); 3028f7278fd0SJosepch Chan if (err < 0) 3029f7278fd0SJosepch Chan return err; 3030f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3031f7278fd0SJosepch Chan "Center Playback Switch", 3032f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3033f7278fd0SJosepch Chan HDA_OUTPUT)); 3034f7278fd0SJosepch Chan if (err < 0) 3035f7278fd0SJosepch Chan return err; 3036f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3037f7278fd0SJosepch Chan "LFE Playback Switch", 3038f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3039f7278fd0SJosepch Chan HDA_OUTPUT)); 3040f7278fd0SJosepch Chan if (err < 0) 3041f7278fd0SJosepch Chan return err; 3042f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3043f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3044f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3045f7278fd0SJosepch Chan "Master Front Playback Volume", 3046f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3047f7278fd0SJosepch Chan HDA_INPUT)); 3048f7278fd0SJosepch Chan if (err < 0) 3049f7278fd0SJosepch Chan return err; 3050f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3051f7278fd0SJosepch Chan "Master Front Playback Switch", 3052f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3053f7278fd0SJosepch Chan HDA_INPUT)); 3054f7278fd0SJosepch Chan if (err < 0) 3055f7278fd0SJosepch Chan return err; 3056f7278fd0SJosepch Chan 3057f7278fd0SJosepch Chan /* add control to PW3 */ 3058f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3059f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3060f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3061f7278fd0SJosepch Chan HDA_OUTPUT)); 3062f7278fd0SJosepch Chan if (err < 0) 3063f7278fd0SJosepch Chan return err; 3064f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3065f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3066f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3067f7278fd0SJosepch Chan HDA_OUTPUT)); 3068f7278fd0SJosepch Chan if (err < 0) 3069f7278fd0SJosepch Chan return err; 3070f7278fd0SJosepch Chan } else { 3071f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3072f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3073f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3074f7278fd0SJosepch Chan HDA_OUTPUT)); 3075f7278fd0SJosepch Chan if (err < 0) 3076f7278fd0SJosepch Chan return err; 3077f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3078f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3079f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3080f7278fd0SJosepch Chan HDA_OUTPUT)); 3081f7278fd0SJosepch Chan if (err < 0) 3082f7278fd0SJosepch Chan return err; 3083f7278fd0SJosepch Chan } 3084f7278fd0SJosepch Chan } 3085f7278fd0SJosepch Chan 3086f7278fd0SJosepch Chan return 0; 3087f7278fd0SJosepch Chan } 3088f7278fd0SJosepch Chan 3089f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3090f7278fd0SJosepch Chan { 3091f7278fd0SJosepch Chan int err; 3092f7278fd0SJosepch Chan 3093f7278fd0SJosepch Chan if (!pin) 3094f7278fd0SJosepch Chan return 0; 3095f7278fd0SJosepch Chan 3096f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3097cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3098f7278fd0SJosepch Chan 3099f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3100f7278fd0SJosepch Chan "Headphone Playback Volume", 3101f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3102f7278fd0SJosepch Chan if (err < 0) 3103f7278fd0SJosepch Chan return err; 3104f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3105f7278fd0SJosepch Chan "Headphone Playback Switch", 3106f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3107f7278fd0SJosepch Chan if (err < 0) 3108f7278fd0SJosepch Chan return err; 3109f7278fd0SJosepch Chan 31100aa62aefSHarald Welte create_hp_imux(spec); 31110aa62aefSHarald Welte 3112f7278fd0SJosepch Chan return 0; 3113f7278fd0SJosepch Chan } 3114f7278fd0SJosepch Chan 3115f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 311610a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, 3117f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3118f7278fd0SJosepch Chan { 3119f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; 312010a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3121f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3122f7278fd0SJosepch Chan } 3123f7278fd0SJosepch Chan 3124f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3125f7278fd0SJosepch Chan { 3126f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3127f7278fd0SJosepch Chan int err; 3128f7278fd0SJosepch Chan 3129f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3130f7278fd0SJosepch Chan if (err < 0) 3131f7278fd0SJosepch Chan return err; 3132f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3133f7278fd0SJosepch Chan if (err < 0) 3134f7278fd0SJosepch Chan return err; 3135f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3136f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3137f7278fd0SJosepch Chan 3138f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3139f7278fd0SJosepch Chan if (err < 0) 3140f7278fd0SJosepch Chan return err; 3141f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3142f7278fd0SJosepch Chan if (err < 0) 3143f7278fd0SJosepch Chan return err; 314410a20af7STakashi Iwai err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); 3145f7278fd0SJosepch Chan if (err < 0) 3146f7278fd0SJosepch Chan return err; 3147f7278fd0SJosepch Chan 3148f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3149f7278fd0SJosepch Chan 31500852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3151f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 315255d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3153f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3154f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3155f7278fd0SJosepch Chan 3156603c4019STakashi Iwai if (spec->kctls.list) 3157603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3158f7278fd0SJosepch Chan 31590aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 31600aa62aefSHarald Welte 3161f8fdd495SHarald Welte if (spec->hp_mux) 31623d83e577STakashi Iwai via_hp_build(codec); 3163f7278fd0SJosepch Chan 31645b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3165f7278fd0SJosepch Chan return 1; 3166f7278fd0SJosepch Chan } 3167f7278fd0SJosepch Chan 3168f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3169f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3170f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3171f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3172f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3173f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3174f7278fd0SJosepch Chan { } /* end */ 3175f7278fd0SJosepch Chan }; 3176f7278fd0SJosepch Chan #endif 31773e95b9abSLydia Wang 31783e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 31793e95b9abSLydia Wang { 31803e95b9abSLydia Wang struct via_spec *spec = codec->spec; 31813e95b9abSLydia Wang int imux_is_smixer; 31823e95b9abSLydia Wang unsigned int parm; 31833e95b9abSLydia Wang int is_8ch = 0; 3184*bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 3185*bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 31863e95b9abSLydia Wang is_8ch = 1; 31873e95b9abSLydia Wang 31883e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 31893e95b9abSLydia Wang imux_is_smixer = 31903e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 31913e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 31923e95b9abSLydia Wang /* inputs */ 31933e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 31943e95b9abSLydia Wang parm = AC_PWRST_D3; 31953e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 31963e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 31973e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 31983e95b9abSLydia Wang if (imux_is_smixer) 31993e95b9abSLydia Wang parm = AC_PWRST_D0; 32003e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 32013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 32023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 32033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 32043e95b9abSLydia Wang 32053e95b9abSLydia Wang /* outputs */ 32063e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 32073e95b9abSLydia Wang parm = AC_PWRST_D3; 32083e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 32093e95b9abSLydia Wang if (spec->smart51_enabled) 32103e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 32123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 32133e95b9abSLydia Wang 32143e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 32153e95b9abSLydia Wang if (is_8ch) { 32163e95b9abSLydia Wang parm = AC_PWRST_D3; 32173e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 32183e95b9abSLydia Wang if (spec->smart51_enabled) 32193e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 32213e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 32233e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3224*bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 3225*bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 3226*bc92df7fSLydia Wang parm = AC_PWRST_D3; 3227*bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 3228*bc92df7fSLydia Wang if (spec->smart51_enabled) 3229*bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 3230*bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 3231*bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3232*bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3233*bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32343e95b9abSLydia Wang } 32353e95b9abSLydia Wang 32363e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 32373e95b9abSLydia Wang parm = AC_PWRST_D3; 32383e95b9abSLydia Wang /* force to D0 for internal Speaker */ 32393e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 32403e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 32413e95b9abSLydia Wang if (is_8ch) 32423e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 32433e95b9abSLydia Wang 32443e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 32453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 32463e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 32473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 32483e95b9abSLydia Wang if (is_8ch) { 32493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 32503e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 32523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3253*bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 3254*bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3255*bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32563e95b9abSLydia Wang } 32573e95b9abSLydia Wang 3258518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3259f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3260f7278fd0SJosepch Chan { 3261f7278fd0SJosepch Chan struct via_spec *spec; 3262f7278fd0SJosepch Chan int err; 3263f7278fd0SJosepch Chan 3264518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3265518bf3baSLydia Wang return patch_vt1708S(codec); 3266f7278fd0SJosepch Chan /* create a codec specific record */ 32675b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3268f7278fd0SJosepch Chan if (spec == NULL) 3269f7278fd0SJosepch Chan return -ENOMEM; 3270f7278fd0SJosepch Chan 3271f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3272f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3273f7278fd0SJosepch Chan if (err < 0) { 3274f7278fd0SJosepch Chan via_free(codec); 3275f7278fd0SJosepch Chan return err; 3276f7278fd0SJosepch Chan } else if (!err) { 3277f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3278f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3279f7278fd0SJosepch Chan } 3280f7278fd0SJosepch Chan 328169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 328269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3283f7278fd0SJosepch Chan 3284f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3285f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3286f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3287f7278fd0SJosepch Chan 3288f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3289f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3290f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3291f7278fd0SJosepch Chan 3292f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3293f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3294f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3295337b9d02STakashi Iwai get_mux_nids(codec); 3296f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3297f7278fd0SJosepch Chan spec->num_mixers++; 3298f7278fd0SJosepch Chan } 3299f7278fd0SJosepch Chan 3300f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3301f7278fd0SJosepch Chan 3302f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 330369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3304f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3305f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3306f7278fd0SJosepch Chan #endif 3307f7278fd0SJosepch Chan 33083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 33093e95b9abSLydia Wang 3310f7278fd0SJosepch Chan return 0; 3311f7278fd0SJosepch Chan } 3312f7278fd0SJosepch Chan 3313f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3314f7278fd0SJosepch Chan { 3315f7278fd0SJosepch Chan struct via_spec *spec; 3316f7278fd0SJosepch Chan int err; 3317f7278fd0SJosepch Chan 3318f7278fd0SJosepch Chan /* create a codec specific record */ 33195b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3320f7278fd0SJosepch Chan if (spec == NULL) 3321f7278fd0SJosepch Chan return -ENOMEM; 3322f7278fd0SJosepch Chan 3323f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3324f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3325f7278fd0SJosepch Chan if (err < 0) { 3326f7278fd0SJosepch Chan via_free(codec); 3327f7278fd0SJosepch Chan return err; 3328f7278fd0SJosepch Chan } else if (!err) { 3329f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3330f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3331f7278fd0SJosepch Chan } 3332f7278fd0SJosepch Chan 333369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 333469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3335f7278fd0SJosepch Chan 3336f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3337f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3338f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3339f7278fd0SJosepch Chan 3340f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3341f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3342f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3343f7278fd0SJosepch Chan 3344f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3345f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3346f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3347337b9d02STakashi Iwai get_mux_nids(codec); 3348f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3349f7278fd0SJosepch Chan spec->num_mixers++; 3350f7278fd0SJosepch Chan } 3351f7278fd0SJosepch Chan 3352f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3353f7278fd0SJosepch Chan 3354f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 335569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3356f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3357f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3358f7278fd0SJosepch Chan #endif 3359c577b8a1SJoseph Chan 33603e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 33613e95b9abSLydia Wang 3362c577b8a1SJoseph Chan return 0; 3363c577b8a1SJoseph Chan } 3364c577b8a1SJoseph Chan 3365d949cac1SHarald Welte /* Patch for VT1708S */ 3366d949cac1SHarald Welte 3367d949cac1SHarald Welte /* capture mixer elements */ 3368d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3369d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3370d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3371d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3372d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 33736369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 33746369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 33756369bcfcSLydia Wang HDA_INPUT), 3376d949cac1SHarald Welte { 3377d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3378d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3379d949cac1SHarald Welte * So call somewhat different.. 3380d949cac1SHarald Welte */ 3381d949cac1SHarald Welte /* .name = "Capture Source", */ 3382d949cac1SHarald Welte .name = "Input Source", 3383d949cac1SHarald Welte .count = 1, 3384d949cac1SHarald Welte .info = via_mux_enum_info, 3385d949cac1SHarald Welte .get = via_mux_enum_get, 3386d949cac1SHarald Welte .put = via_mux_enum_put, 3387d949cac1SHarald Welte }, 3388d949cac1SHarald Welte { } /* end */ 3389d949cac1SHarald Welte }; 3390d949cac1SHarald Welte 3391d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3392d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3393d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3394d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3395d949cac1SHarald Welte 3396d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3397d949cac1SHarald Welte * analog-loopback mixer widget */ 3398d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3399d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3400d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3401d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3402d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3403d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3404d949cac1SHarald Welte 3405d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3406d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 34075691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3408d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 34095691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3410d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3411d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3412bc7e7e5cSLydia Wang /* don't bybass mixer */ 3413bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3414d949cac1SHarald Welte { } 3415d949cac1SHarald Welte }; 3416d949cac1SHarald Welte 341769e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3418a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3419a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3420a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3421a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3422a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3423a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3424a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3425a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3426a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 342769e52a80SHarald Welte { } 342869e52a80SHarald Welte }; 342969e52a80SHarald Welte 3430*bc92df7fSLydia Wang static struct hda_verb vt1705_uniwill_init_verbs[] = { 3431*bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3432*bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3433*bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3434*bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3435*bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3436*bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3437*bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3438*bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3439*bc92df7fSLydia Wang { } 3440*bc92df7fSLydia Wang }; 3441*bc92df7fSLydia Wang 3442d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3443d949cac1SHarald Welte .substreams = 2, 3444d949cac1SHarald Welte .channels_min = 2, 3445d949cac1SHarald Welte .channels_max = 8, 3446d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3447d949cac1SHarald Welte .ops = { 3448d949cac1SHarald Welte .open = via_playback_pcm_open, 3449c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3450c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 345117314379SLydia Wang .close = via_pcm_open_close 3452d949cac1SHarald Welte }, 3453d949cac1SHarald Welte }; 3454d949cac1SHarald Welte 3455*bc92df7fSLydia Wang static struct hda_pcm_stream vt1705_pcm_analog_playback = { 3456*bc92df7fSLydia Wang .substreams = 2, 3457*bc92df7fSLydia Wang .channels_min = 2, 3458*bc92df7fSLydia Wang .channels_max = 6, 3459*bc92df7fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3460*bc92df7fSLydia Wang .ops = { 3461*bc92df7fSLydia Wang .open = via_playback_pcm_open, 3462*bc92df7fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3463*bc92df7fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3464*bc92df7fSLydia Wang .close = via_pcm_open_close 3465*bc92df7fSLydia Wang }, 3466*bc92df7fSLydia Wang }; 3467*bc92df7fSLydia Wang 3468d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3469d949cac1SHarald Welte .substreams = 2, 3470d949cac1SHarald Welte .channels_min = 2, 3471d949cac1SHarald Welte .channels_max = 2, 3472d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3473d949cac1SHarald Welte .ops = { 347417314379SLydia Wang .open = via_pcm_open_close, 3475d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 347617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 347717314379SLydia Wang .close = via_pcm_open_close 3478d949cac1SHarald Welte }, 3479d949cac1SHarald Welte }; 3480d949cac1SHarald Welte 3481d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 34829da29271STakashi Iwai .substreams = 1, 3483d949cac1SHarald Welte .channels_min = 2, 3484d949cac1SHarald Welte .channels_max = 2, 3485d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3486d949cac1SHarald Welte .ops = { 3487d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3488d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 34899da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 34909da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3491d949cac1SHarald Welte }, 3492d949cac1SHarald Welte }; 3493d949cac1SHarald Welte 3494d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3495d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3496d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3497d949cac1SHarald Welte { 3498d949cac1SHarald Welte int i; 3499d949cac1SHarald Welte hda_nid_t nid; 3500d949cac1SHarald Welte 3501d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3502d949cac1SHarald Welte 3503d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3504d949cac1SHarald Welte 3505d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3506d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3507d949cac1SHarald Welte if (nid) { 3508d949cac1SHarald Welte /* config dac list */ 3509d949cac1SHarald Welte switch (i) { 3510d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3511d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3512d949cac1SHarald Welte break; 3513d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3514*bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3515*bc92df7fSLydia Wang spec->multiout.dac_nids[i] = 0x25; 3516*bc92df7fSLydia Wang else 3517d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3518d949cac1SHarald Welte break; 3519d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3520d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3521d949cac1SHarald Welte break; 3522d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3523d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3524d949cac1SHarald Welte break; 3525d949cac1SHarald Welte } 3526d949cac1SHarald Welte } 3527d949cac1SHarald Welte } 3528d949cac1SHarald Welte 352932e0191dSClemens Ladisch /* for Smart 5.1, line/mic inputs double as output pins */ 353032e0191dSClemens Ladisch if (cfg->line_outs == 1) { 353132e0191dSClemens Ladisch spec->multiout.num_dacs = 3; 353232e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_SURROUND] = 0x11; 3533*bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3534*bc92df7fSLydia Wang spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x25; 3535*bc92df7fSLydia Wang else 353632e0191dSClemens Ladisch spec->multiout.dac_nids[AUTO_SEQ_CENLFE] = 0x24; 353732e0191dSClemens Ladisch } 353832e0191dSClemens Ladisch 3539d949cac1SHarald Welte return 0; 3540d949cac1SHarald Welte } 3541d949cac1SHarald Welte 3542d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3543*bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, 3544d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3545d949cac1SHarald Welte { 3546*bc92df7fSLydia Wang struct via_spec *spec = codec->spec; 3547d949cac1SHarald Welte char name[32]; 3548ea734963STakashi Iwai static const char * const chname[4] = { 3549ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3550ea734963STakashi Iwai }; 3551*bc92df7fSLydia Wang hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, 3552*bc92df7fSLydia Wang {0x10, 0x11, 0x25, 0} }; 3553*bc92df7fSLydia Wang hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, 3554*bc92df7fSLydia Wang {0x1C, 0x18, 0x27, 0} }; 3555d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3556d949cac1SHarald Welte int i, err; 3557d949cac1SHarald Welte 3558d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3559d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3560d949cac1SHarald Welte 356132e0191dSClemens Ladisch /* for Smart 5.1, there are always at least six channels */ 356232e0191dSClemens Ladisch if (!nid && i > AUTO_SEQ_CENLFE) 3563d949cac1SHarald Welte continue; 3564d949cac1SHarald Welte 3565*bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3566*bc92df7fSLydia Wang nid_vol = nid_vols[1][i]; 3567*bc92df7fSLydia Wang nid_mute = nid_mutes[1][i]; 3568*bc92df7fSLydia Wang } else { 3569*bc92df7fSLydia Wang nid_vol = nid_vols[0][i]; 3570*bc92df7fSLydia Wang nid_mute = nid_mutes[0][i]; 3571*bc92df7fSLydia Wang } 3572*bc92df7fSLydia Wang if (!nid_vol && !nid_mute) 3573*bc92df7fSLydia Wang continue; 3574d949cac1SHarald Welte 3575d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3576d949cac1SHarald Welte /* Center/LFE */ 3577d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3578d949cac1SHarald Welte "Center Playback Volume", 3579d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3580d949cac1SHarald Welte HDA_OUTPUT)); 3581d949cac1SHarald Welte if (err < 0) 3582d949cac1SHarald Welte return err; 3583d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3584d949cac1SHarald Welte "LFE Playback Volume", 3585d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3586d949cac1SHarald Welte HDA_OUTPUT)); 3587d949cac1SHarald Welte if (err < 0) 3588d949cac1SHarald Welte return err; 3589d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3590d949cac1SHarald Welte "Center Playback Switch", 3591d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3592d949cac1SHarald Welte 1, 0, 3593d949cac1SHarald Welte HDA_OUTPUT)); 3594d949cac1SHarald Welte if (err < 0) 3595d949cac1SHarald Welte return err; 3596d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3597d949cac1SHarald Welte "LFE Playback Switch", 3598d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3599d949cac1SHarald Welte 2, 0, 3600d949cac1SHarald Welte HDA_OUTPUT)); 3601d949cac1SHarald Welte if (err < 0) 3602d949cac1SHarald Welte return err; 3603d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3604d949cac1SHarald Welte /* add control to mixer index 0 */ 3605d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3606d949cac1SHarald Welte "Master Front Playback Volume", 3607d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3608d949cac1SHarald Welte HDA_INPUT)); 3609d949cac1SHarald Welte if (err < 0) 3610d949cac1SHarald Welte return err; 3611d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3612d949cac1SHarald Welte "Master Front Playback Switch", 3613d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3614d949cac1SHarald Welte HDA_INPUT)); 3615d949cac1SHarald Welte if (err < 0) 3616d949cac1SHarald Welte return err; 3617d949cac1SHarald Welte 3618d949cac1SHarald Welte /* Front */ 3619d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3620d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3621d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3622d949cac1SHarald Welte HDA_OUTPUT)); 3623d949cac1SHarald Welte if (err < 0) 3624d949cac1SHarald Welte return err; 3625d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3626d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3627d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3628d949cac1SHarald Welte 3, 0, 3629d949cac1SHarald Welte HDA_OUTPUT)); 3630d949cac1SHarald Welte if (err < 0) 3631d949cac1SHarald Welte return err; 3632d949cac1SHarald Welte } else { 3633d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3634d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3635d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3636d949cac1SHarald Welte HDA_OUTPUT)); 3637d949cac1SHarald Welte if (err < 0) 3638d949cac1SHarald Welte return err; 3639d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3640d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3641d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3642d949cac1SHarald Welte 3, 0, 3643d949cac1SHarald Welte HDA_OUTPUT)); 3644d949cac1SHarald Welte if (err < 0) 3645d949cac1SHarald Welte return err; 3646d949cac1SHarald Welte } 3647d949cac1SHarald Welte } 3648d949cac1SHarald Welte 3649d949cac1SHarald Welte return 0; 3650d949cac1SHarald Welte } 3651d949cac1SHarald Welte 3652d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3653d949cac1SHarald Welte { 3654d949cac1SHarald Welte int err; 3655d949cac1SHarald Welte 3656d949cac1SHarald Welte if (!pin) 3657d949cac1SHarald Welte return 0; 3658d949cac1SHarald Welte 3659d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3660cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3661d949cac1SHarald Welte 3662d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3663d949cac1SHarald Welte "Headphone Playback Volume", 3664d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3665d949cac1SHarald Welte if (err < 0) 3666d949cac1SHarald Welte return err; 3667d949cac1SHarald Welte 3668d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3669d949cac1SHarald Welte "Headphone Playback Switch", 3670d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3671d949cac1SHarald Welte if (err < 0) 3672d949cac1SHarald Welte return err; 3673d949cac1SHarald Welte 36740aa62aefSHarald Welte create_hp_imux(spec); 36750aa62aefSHarald Welte 3676d949cac1SHarald Welte return 0; 3677d949cac1SHarald Welte } 3678d949cac1SHarald Welte 3679d949cac1SHarald Welte /* create playback/capture controls for input pins */ 368010a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, 3681d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3682d949cac1SHarald Welte { 3683f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 368410a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3685f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3686d949cac1SHarald Welte } 3687d949cac1SHarald Welte 36889da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 36899da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 36909da29271STakashi Iwai { 36919da29271STakashi Iwai struct via_spec *spec = codec->spec; 36929da29271STakashi Iwai int i; 36939da29271STakashi Iwai 36949da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 36959da29271STakashi Iwai hda_nid_t nid; 36969da29271STakashi Iwai int conn; 36979da29271STakashi Iwai 36989da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 36999da29271STakashi Iwai if (!nid) 37009da29271STakashi Iwai continue; 37019da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 37029da29271STakashi Iwai if (conn < 1) 37039da29271STakashi Iwai continue; 37049da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 37059da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 37069da29271STakashi Iwai else { 37079da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 37089da29271STakashi Iwai break; /* at most two dig outs */ 37099da29271STakashi Iwai } 37109da29271STakashi Iwai } 37119da29271STakashi Iwai } 37129da29271STakashi Iwai 3713d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3714d949cac1SHarald Welte { 3715d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3716d949cac1SHarald Welte int err; 3717d949cac1SHarald Welte 37189da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3719d949cac1SHarald Welte if (err < 0) 3720d949cac1SHarald Welte return err; 3721d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3722d949cac1SHarald Welte if (err < 0) 3723d949cac1SHarald Welte return err; 3724d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3725d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3726d949cac1SHarald Welte 3727*bc92df7fSLydia Wang err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); 3728d949cac1SHarald Welte if (err < 0) 3729d949cac1SHarald Welte return err; 3730d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3731d949cac1SHarald Welte if (err < 0) 3732d949cac1SHarald Welte return err; 373310a20af7STakashi Iwai err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); 3734d949cac1SHarald Welte if (err < 0) 3735d949cac1SHarald Welte return err; 3736d949cac1SHarald Welte 3737d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3738d949cac1SHarald Welte 37399da29271STakashi Iwai fill_dig_outs(codec); 374098aa34c0SHarald Welte 3741603c4019STakashi Iwai if (spec->kctls.list) 3742603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3743d949cac1SHarald Welte 37440aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 37450aa62aefSHarald Welte 3746f8fdd495SHarald Welte if (spec->hp_mux) 37473d83e577STakashi Iwai via_hp_build(codec); 3748d949cac1SHarald Welte 37495b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3750d949cac1SHarald Welte return 1; 3751d949cac1SHarald Welte } 3752d949cac1SHarald Welte 3753d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3754d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 3755d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3756d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3757d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3758d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3759d949cac1SHarald Welte { } /* end */ 3760d949cac1SHarald Welte }; 3761d949cac1SHarald Welte #endif 3762d949cac1SHarald Welte 37636369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 37646369bcfcSLydia Wang int offset, int num_steps, int step_size) 37656369bcfcSLydia Wang { 37666369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 37676369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 37686369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 37696369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 37706369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 37716369bcfcSLydia Wang } 37726369bcfcSLydia Wang 3773d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3774d949cac1SHarald Welte { 3775d949cac1SHarald Welte struct via_spec *spec; 3776d949cac1SHarald Welte int err; 3777d949cac1SHarald Welte 3778d949cac1SHarald Welte /* create a codec specific record */ 37795b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3780d949cac1SHarald Welte if (spec == NULL) 3781d949cac1SHarald Welte return -ENOMEM; 3782d949cac1SHarald Welte 3783d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3784d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3785d949cac1SHarald Welte if (err < 0) { 3786d949cac1SHarald Welte via_free(codec); 3787d949cac1SHarald Welte return err; 3788d949cac1SHarald Welte } else if (!err) { 3789d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3790d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3791d949cac1SHarald Welte } 3792d949cac1SHarald Welte 379369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3794*bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3795*bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3796*bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3797*bc92df7fSLydia Wang else 3798*bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3799*bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3800d949cac1SHarald Welte 380136dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 380236dd5c4aSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3803*bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3804*bc92df7fSLydia Wang spec->stream_name_analog = "VT1705 Analog"; 380536dd5c4aSLydia Wang else 3806d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 3807*bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3808*bc92df7fSLydia Wang spec->stream_analog_playback = &vt1705_pcm_analog_playback; 3809*bc92df7fSLydia Wang else 3810d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3811d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3812d949cac1SHarald Welte 381336dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 381436dd5c4aSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3815*bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3816*bc92df7fSLydia Wang spec->stream_name_digital = "VT1705 Digital"; 381736dd5c4aSLydia Wang else 3818d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 3819d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3820d949cac1SHarald Welte 3821d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 3822d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 3823d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 3824337b9d02STakashi Iwai get_mux_nids(codec); 38256369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 38266369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3827d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3828d949cac1SHarald Welte spec->num_mixers++; 3829d949cac1SHarald Welte } 3830d949cac1SHarald Welte 3831d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3832d949cac1SHarald Welte 3833d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 383469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3835d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3836d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3837d949cac1SHarald Welte #endif 3838d949cac1SHarald Welte 3839518bf3baSLydia Wang /* correct names for VT1708BCE */ 3840518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3841518bf3baSLydia Wang kfree(codec->chip_name); 3842518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3843518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3844518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3845518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3846518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 3847518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 3848518bf3baSLydia Wang } 3849970f630fSLydia Wang /* correct names for VT1818S */ 3850970f630fSLydia Wang if (codec->vendor_id == 0x11060440) { 3851970f630fSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3852970f630fSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3853970f630fSLydia Wang } 3854*bc92df7fSLydia Wang /* correct names for VT1705 */ 3855*bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3856*bc92df7fSLydia Wang kfree(codec->chip_name); 3857*bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3858*bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3859*bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3860*bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3861*bc92df7fSLydia Wang } 38623e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3863d949cac1SHarald Welte return 0; 3864d949cac1SHarald Welte } 3865d949cac1SHarald Welte 3866d949cac1SHarald Welte /* Patch for VT1702 */ 3867d949cac1SHarald Welte 3868d949cac1SHarald Welte /* capture mixer elements */ 3869d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 3870d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3871d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3872d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3873d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3874d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3875d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3876d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3877d949cac1SHarald Welte HDA_INPUT), 3878d949cac1SHarald Welte { 3879d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3880d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3881d949cac1SHarald Welte * So call somewhat different.. 3882d949cac1SHarald Welte */ 3883d949cac1SHarald Welte /* .name = "Capture Source", */ 3884d949cac1SHarald Welte .name = "Input Source", 3885d949cac1SHarald Welte .count = 1, 3886d949cac1SHarald Welte .info = via_mux_enum_info, 3887d949cac1SHarald Welte .get = via_mux_enum_get, 3888d949cac1SHarald Welte .put = via_mux_enum_put, 3889d949cac1SHarald Welte }, 3890d949cac1SHarald Welte { } /* end */ 3891d949cac1SHarald Welte }; 3892d949cac1SHarald Welte 3893d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 3894d949cac1SHarald Welte /* 3895d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3896d949cac1SHarald Welte */ 3897d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3898d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3899d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3900d949cac1SHarald Welte 3901d949cac1SHarald Welte 3902d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3903d949cac1SHarald Welte * mixer widget 3904d949cac1SHarald Welte */ 3905d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3906d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3907d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3908d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3909d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3910d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3911d949cac1SHarald Welte 3912d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3913d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3914d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3915d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3916d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3917bc7e7e5cSLydia Wang /* mixer enable */ 3918bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3919bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3920bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3921d949cac1SHarald Welte { } 3922d949cac1SHarald Welte }; 3923d949cac1SHarald Welte 392469e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 3925a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3926a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3927a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3928a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3929a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3930a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 393169e52a80SHarald Welte { } 393269e52a80SHarald Welte }; 393369e52a80SHarald Welte 3934d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 39350aa62aefSHarald Welte .substreams = 2, 3936d949cac1SHarald Welte .channels_min = 2, 3937d949cac1SHarald Welte .channels_max = 2, 3938d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3939d949cac1SHarald Welte .ops = { 3940d949cac1SHarald Welte .open = via_playback_pcm_open, 39410aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 394217314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 394317314379SLydia Wang .close = via_pcm_open_close 3944d949cac1SHarald Welte }, 3945d949cac1SHarald Welte }; 3946d949cac1SHarald Welte 3947d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 3948d949cac1SHarald Welte .substreams = 3, 3949d949cac1SHarald Welte .channels_min = 2, 3950d949cac1SHarald Welte .channels_max = 2, 3951d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 3952d949cac1SHarald Welte .ops = { 395317314379SLydia Wang .open = via_pcm_open_close, 3954d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 395517314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 395617314379SLydia Wang .close = via_pcm_open_close 3957d949cac1SHarald Welte }, 3958d949cac1SHarald Welte }; 3959d949cac1SHarald Welte 3960d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 39615691ec7fSHarald Welte .substreams = 2, 3962d949cac1SHarald Welte .channels_min = 2, 3963d949cac1SHarald Welte .channels_max = 2, 3964d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3965d949cac1SHarald Welte .ops = { 3966d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3967d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 39689da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 39699da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3970d949cac1SHarald Welte }, 3971d949cac1SHarald Welte }; 3972d949cac1SHarald Welte 3973d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3974d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 3975d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3976d949cac1SHarald Welte { 3977d949cac1SHarald Welte spec->multiout.num_dacs = 1; 3978d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3979d949cac1SHarald Welte 3980d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 3981d949cac1SHarald Welte /* config dac list */ 3982d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 3983d949cac1SHarald Welte } 3984d949cac1SHarald Welte 3985d949cac1SHarald Welte return 0; 3986d949cac1SHarald Welte } 3987d949cac1SHarald Welte 3988d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3989d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 3990d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3991d949cac1SHarald Welte { 3992d949cac1SHarald Welte int err; 3993d949cac1SHarald Welte 3994d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 3995d949cac1SHarald Welte return -1; 3996d949cac1SHarald Welte 3997d949cac1SHarald Welte /* add control to mixer index 0 */ 3998d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3999d949cac1SHarald Welte "Master Front Playback Volume", 4000d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4001d949cac1SHarald Welte if (err < 0) 4002d949cac1SHarald Welte return err; 4003d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4004d949cac1SHarald Welte "Master Front Playback Switch", 4005d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4006d949cac1SHarald Welte if (err < 0) 4007d949cac1SHarald Welte return err; 4008d949cac1SHarald Welte 4009d949cac1SHarald Welte /* Front */ 4010d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4011d949cac1SHarald Welte "Front Playback Volume", 4012d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4013d949cac1SHarald Welte if (err < 0) 4014d949cac1SHarald Welte return err; 4015d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4016d949cac1SHarald Welte "Front Playback Switch", 4017d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4018d949cac1SHarald Welte if (err < 0) 4019d949cac1SHarald Welte return err; 4020d949cac1SHarald Welte 4021d949cac1SHarald Welte return 0; 4022d949cac1SHarald Welte } 4023d949cac1SHarald Welte 4024d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4025d949cac1SHarald Welte { 40260713efebSLydia Wang int err, i; 40270713efebSLydia Wang struct hda_input_mux *imux; 4028ea734963STakashi Iwai static const char * const texts[] = { "ON", "OFF", NULL}; 4029d949cac1SHarald Welte if (!pin) 4030d949cac1SHarald Welte return 0; 4031d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4032cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4033d949cac1SHarald Welte 4034d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4035d949cac1SHarald Welte "Headphone Playback Volume", 4036d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4037d949cac1SHarald Welte if (err < 0) 4038d949cac1SHarald Welte return err; 4039d949cac1SHarald Welte 4040d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4041d949cac1SHarald Welte "Headphone Playback Switch", 4042d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4043d949cac1SHarald Welte if (err < 0) 4044d949cac1SHarald Welte return err; 4045d949cac1SHarald Welte 40460713efebSLydia Wang imux = &spec->private_imux[1]; 40470aa62aefSHarald Welte 40480713efebSLydia Wang /* for hp mode select */ 404910a20af7STakashi Iwai for (i = 0; texts[i]; i++) 405010a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 40510713efebSLydia Wang 40520713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4053d949cac1SHarald Welte return 0; 4054d949cac1SHarald Welte } 4055d949cac1SHarald Welte 4056d949cac1SHarald Welte /* create playback/capture controls for input pins */ 405710a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, 4058d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4059d949cac1SHarald Welte { 4060f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; 406110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, 4062f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4063d949cac1SHarald Welte } 4064d949cac1SHarald Welte 4065d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4066d949cac1SHarald Welte { 4067d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4068d949cac1SHarald Welte int err; 4069d949cac1SHarald Welte 40709da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4071d949cac1SHarald Welte if (err < 0) 4072d949cac1SHarald Welte return err; 4073d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4074d949cac1SHarald Welte if (err < 0) 4075d949cac1SHarald Welte return err; 4076d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4077d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4078d949cac1SHarald Welte 4079d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4080d949cac1SHarald Welte if (err < 0) 4081d949cac1SHarald Welte return err; 4082d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4083d949cac1SHarald Welte if (err < 0) 4084d949cac1SHarald Welte return err; 4085c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4086c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4087c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4088c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4089c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4090c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 409110a20af7STakashi Iwai err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); 4092d949cac1SHarald Welte if (err < 0) 4093d949cac1SHarald Welte return err; 4094d949cac1SHarald Welte 4095d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4096d949cac1SHarald Welte 40979da29271STakashi Iwai fill_dig_outs(codec); 409898aa34c0SHarald Welte 4099603c4019STakashi Iwai if (spec->kctls.list) 4100603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4101d949cac1SHarald Welte 41020aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 41030aa62aefSHarald Welte 4104f8fdd495SHarald Welte if (spec->hp_mux) 41053d83e577STakashi Iwai via_hp_build(codec); 4106d949cac1SHarald Welte 4107d949cac1SHarald Welte return 1; 4108d949cac1SHarald Welte } 4109d949cac1SHarald Welte 4110d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4111d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4112d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4113d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4114d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4115d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4116d949cac1SHarald Welte { } /* end */ 4117d949cac1SHarald Welte }; 4118d949cac1SHarald Welte #endif 4119d949cac1SHarald Welte 41203e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 41213e95b9abSLydia Wang { 41223e95b9abSLydia Wang int imux_is_smixer = 41233e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 41243e95b9abSLydia Wang unsigned int parm; 41253e95b9abSLydia Wang /* inputs */ 41263e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 41273e95b9abSLydia Wang parm = AC_PWRST_D3; 41283e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 41293e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 41303e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 41313e95b9abSLydia Wang if (imux_is_smixer) 41323e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 41333e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 41343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 41353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 41363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 41373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 41383e95b9abSLydia Wang 41393e95b9abSLydia Wang /* outputs */ 41403e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 41413e95b9abSLydia Wang parm = AC_PWRST_D3; 41423e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 41433e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 41443e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 41453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 41463e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 41473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 41483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 41493e95b9abSLydia Wang } 41503e95b9abSLydia Wang 4151d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4152d949cac1SHarald Welte { 4153d949cac1SHarald Welte struct via_spec *spec; 4154d949cac1SHarald Welte int err; 4155d949cac1SHarald Welte 4156d949cac1SHarald Welte /* create a codec specific record */ 41575b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4158d949cac1SHarald Welte if (spec == NULL) 4159d949cac1SHarald Welte return -ENOMEM; 4160d949cac1SHarald Welte 4161d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4162d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4163d949cac1SHarald Welte if (err < 0) { 4164d949cac1SHarald Welte via_free(codec); 4165d949cac1SHarald Welte return err; 4166d949cac1SHarald Welte } else if (!err) { 4167d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4168d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4169d949cac1SHarald Welte } 4170d949cac1SHarald Welte 417169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 417269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4173d949cac1SHarald Welte 4174d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4175d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4176d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4177d949cac1SHarald Welte 4178d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4179d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4180d949cac1SHarald Welte 4181d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4182d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4183d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4184337b9d02STakashi Iwai get_mux_nids(codec); 4185d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4186d949cac1SHarald Welte spec->num_mixers++; 4187d949cac1SHarald Welte } 4188d949cac1SHarald Welte 4189d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4190d949cac1SHarald Welte 4191d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 419269e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4193d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4194d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4195d949cac1SHarald Welte #endif 4196d949cac1SHarald Welte 41973e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 4198d949cac1SHarald Welte return 0; 4199d949cac1SHarald Welte } 4200d949cac1SHarald Welte 4201eb7188caSLydia Wang /* Patch for VT1718S */ 4202eb7188caSLydia Wang 4203eb7188caSLydia Wang /* capture mixer elements */ 4204eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4205eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4206eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4207eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4208eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4209eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4210eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4211eb7188caSLydia Wang HDA_INPUT), 4212eb7188caSLydia Wang { 4213eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4214eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4215eb7188caSLydia Wang * So call somewhat different.. 4216eb7188caSLydia Wang */ 4217eb7188caSLydia Wang .name = "Input Source", 4218eb7188caSLydia Wang .count = 2, 4219eb7188caSLydia Wang .info = via_mux_enum_info, 4220eb7188caSLydia Wang .get = via_mux_enum_get, 4221eb7188caSLydia Wang .put = via_mux_enum_put, 4222eb7188caSLydia Wang }, 4223eb7188caSLydia Wang { } /* end */ 4224eb7188caSLydia Wang }; 4225eb7188caSLydia Wang 4226eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4227eb7188caSLydia Wang /* 4228eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4229eb7188caSLydia Wang */ 4230eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4231eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4232eb7188caSLydia Wang 4233eb7188caSLydia Wang 4234eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4235eb7188caSLydia Wang * mixer widget 4236eb7188caSLydia Wang */ 4237eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4238eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4239eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4240eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4241eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4242eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4243eb7188caSLydia Wang 4244eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4245eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4246eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4247eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4248eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4249eb7188caSLydia Wang /* PW11 Input enable */ 4250eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4251eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4252eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4253eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4254eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4255eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4256eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4257eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4258eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4259eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4260eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4261eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4262eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4263eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4264eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4265eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4266eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4267eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4268eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4269eb7188caSLydia Wang { } 4270eb7188caSLydia Wang }; 4271eb7188caSLydia Wang 4272eb7188caSLydia Wang 4273eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4274eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4275eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4276eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4277eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4278eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4279eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4280eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4281eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4282eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4283eb7188caSLydia Wang { } 4284eb7188caSLydia Wang }; 4285eb7188caSLydia Wang 4286eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4287eb7188caSLydia Wang .substreams = 2, 4288eb7188caSLydia Wang .channels_min = 2, 4289eb7188caSLydia Wang .channels_max = 10, 4290eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4291eb7188caSLydia Wang .ops = { 4292eb7188caSLydia Wang .open = via_playback_pcm_open, 4293eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4294eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4295eb7188caSLydia Wang .close = via_pcm_open_close, 4296eb7188caSLydia Wang }, 4297eb7188caSLydia Wang }; 4298eb7188caSLydia Wang 4299eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4300eb7188caSLydia Wang .substreams = 2, 4301eb7188caSLydia Wang .channels_min = 2, 4302eb7188caSLydia Wang .channels_max = 2, 4303eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4304eb7188caSLydia Wang .ops = { 4305eb7188caSLydia Wang .open = via_pcm_open_close, 4306eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4307eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4308eb7188caSLydia Wang .close = via_pcm_open_close, 4309eb7188caSLydia Wang }, 4310eb7188caSLydia Wang }; 4311eb7188caSLydia Wang 4312eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4313eb7188caSLydia Wang .substreams = 2, 4314eb7188caSLydia Wang .channels_min = 2, 4315eb7188caSLydia Wang .channels_max = 2, 4316eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4317eb7188caSLydia Wang .ops = { 4318eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4319eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4320eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4321eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4322eb7188caSLydia Wang }, 4323eb7188caSLydia Wang }; 4324eb7188caSLydia Wang 4325eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4326eb7188caSLydia Wang .substreams = 1, 4327eb7188caSLydia Wang .channels_min = 2, 4328eb7188caSLydia Wang .channels_max = 2, 4329eb7188caSLydia Wang }; 4330eb7188caSLydia Wang 4331eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4332eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4333eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4334eb7188caSLydia Wang { 4335eb7188caSLydia Wang int i; 4336eb7188caSLydia Wang hda_nid_t nid; 4337eb7188caSLydia Wang 4338eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4339eb7188caSLydia Wang 4340eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4341eb7188caSLydia Wang 4342eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4343eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4344eb7188caSLydia Wang if (nid) { 4345eb7188caSLydia Wang /* config dac list */ 4346eb7188caSLydia Wang switch (i) { 4347eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4348eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4349eb7188caSLydia Wang break; 4350eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4351eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4352eb7188caSLydia Wang break; 4353eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4354eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4355eb7188caSLydia Wang break; 4356eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4357eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4358eb7188caSLydia Wang break; 4359eb7188caSLydia Wang } 4360eb7188caSLydia Wang } 4361eb7188caSLydia Wang } 4362eb7188caSLydia Wang 4363eb7188caSLydia Wang return 0; 4364eb7188caSLydia Wang } 4365eb7188caSLydia Wang 4366eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4367eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4368eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4369eb7188caSLydia Wang { 4370eb7188caSLydia Wang char name[32]; 4371ea734963STakashi Iwai static const char * const chname[4] = { 4372ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 4373ea734963STakashi Iwai }; 4374eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4375eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4376eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4377eb7188caSLydia Wang int i, err; 4378eb7188caSLydia Wang 4379eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4380eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4381eb7188caSLydia Wang 4382eb7188caSLydia Wang if (!nid) 4383eb7188caSLydia Wang continue; 4384eb7188caSLydia Wang nid_vol = nid_vols[i]; 4385eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4386eb7188caSLydia Wang 4387eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4388eb7188caSLydia Wang /* Center/LFE */ 4389eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4390eb7188caSLydia Wang "Center Playback Volume", 4391eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4392eb7188caSLydia Wang HDA_OUTPUT)); 4393eb7188caSLydia Wang if (err < 0) 4394eb7188caSLydia Wang return err; 4395eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4396eb7188caSLydia Wang "LFE Playback Volume", 4397eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4398eb7188caSLydia Wang HDA_OUTPUT)); 4399eb7188caSLydia Wang if (err < 0) 4400eb7188caSLydia Wang return err; 4401eb7188caSLydia Wang err = via_add_control( 4402eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4403eb7188caSLydia Wang "Center Playback Switch", 4404eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4405eb7188caSLydia Wang HDA_OUTPUT)); 4406eb7188caSLydia Wang if (err < 0) 4407eb7188caSLydia Wang return err; 4408eb7188caSLydia Wang err = via_add_control( 4409eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4410eb7188caSLydia Wang "LFE Playback Switch", 4411eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4412eb7188caSLydia Wang HDA_OUTPUT)); 4413eb7188caSLydia Wang if (err < 0) 4414eb7188caSLydia Wang return err; 4415eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4416eb7188caSLydia Wang /* Front */ 4417eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4418eb7188caSLydia Wang err = via_add_control( 4419eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4420eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4421eb7188caSLydia Wang if (err < 0) 4422eb7188caSLydia Wang return err; 4423eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4424eb7188caSLydia Wang err = via_add_control( 4425eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4426eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4427eb7188caSLydia Wang HDA_OUTPUT)); 4428eb7188caSLydia Wang if (err < 0) 4429eb7188caSLydia Wang return err; 4430eb7188caSLydia Wang } else { 4431eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4432eb7188caSLydia Wang err = via_add_control( 4433eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4434eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4435eb7188caSLydia Wang if (err < 0) 4436eb7188caSLydia Wang return err; 4437eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4438eb7188caSLydia Wang err = via_add_control( 4439eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4440eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4441eb7188caSLydia Wang HDA_OUTPUT)); 4442eb7188caSLydia Wang if (err < 0) 4443eb7188caSLydia Wang return err; 4444eb7188caSLydia Wang } 4445eb7188caSLydia Wang } 4446eb7188caSLydia Wang return 0; 4447eb7188caSLydia Wang } 4448eb7188caSLydia Wang 4449eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4450eb7188caSLydia Wang { 4451eb7188caSLydia Wang int err; 4452eb7188caSLydia Wang 4453eb7188caSLydia Wang if (!pin) 4454eb7188caSLydia Wang return 0; 4455eb7188caSLydia Wang 4456eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4457eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4458eb7188caSLydia Wang 4459eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4460eb7188caSLydia Wang "Headphone Playback Volume", 4461eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4462eb7188caSLydia Wang if (err < 0) 4463eb7188caSLydia Wang return err; 4464eb7188caSLydia Wang 4465eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4466eb7188caSLydia Wang "Headphone Playback Switch", 4467eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4468eb7188caSLydia Wang if (err < 0) 4469eb7188caSLydia Wang return err; 4470eb7188caSLydia Wang 4471eb7188caSLydia Wang create_hp_imux(spec); 4472eb7188caSLydia Wang return 0; 4473eb7188caSLydia Wang } 4474eb7188caSLydia Wang 4475eb7188caSLydia Wang /* create playback/capture controls for input pins */ 447610a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, 4477eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4478eb7188caSLydia Wang { 4479f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; 448010a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 4481f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4482eb7188caSLydia Wang } 4483eb7188caSLydia Wang 4484eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4485eb7188caSLydia Wang { 4486eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4487eb7188caSLydia Wang int err; 4488eb7188caSLydia Wang 4489eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4490eb7188caSLydia Wang 4491eb7188caSLydia Wang if (err < 0) 4492eb7188caSLydia Wang return err; 4493eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4494eb7188caSLydia Wang if (err < 0) 4495eb7188caSLydia Wang return err; 4496eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4497eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4498eb7188caSLydia Wang 4499eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4500eb7188caSLydia Wang if (err < 0) 4501eb7188caSLydia Wang return err; 4502eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4503eb7188caSLydia Wang if (err < 0) 4504eb7188caSLydia Wang return err; 450510a20af7STakashi Iwai err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4506eb7188caSLydia Wang if (err < 0) 4507eb7188caSLydia Wang return err; 4508eb7188caSLydia Wang 4509eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4510eb7188caSLydia Wang 4511eb7188caSLydia Wang fill_dig_outs(codec); 4512eb7188caSLydia Wang 4513eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4514eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4515eb7188caSLydia Wang 4516eb7188caSLydia Wang if (spec->kctls.list) 4517eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4518eb7188caSLydia Wang 4519eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4520eb7188caSLydia Wang 4521eb7188caSLydia Wang if (spec->hp_mux) 45223d83e577STakashi Iwai via_hp_build(codec); 4523eb7188caSLydia Wang 45245b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4525eb7188caSLydia Wang 4526eb7188caSLydia Wang return 1; 4527eb7188caSLydia Wang } 4528eb7188caSLydia Wang 4529eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4530eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4531eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4532eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4533eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4534eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4535eb7188caSLydia Wang { } /* end */ 4536eb7188caSLydia Wang }; 4537eb7188caSLydia Wang #endif 4538eb7188caSLydia Wang 45393e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 45403e95b9abSLydia Wang { 45413e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45423e95b9abSLydia Wang int imux_is_smixer; 45433e95b9abSLydia Wang unsigned int parm; 45443e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 45453e95b9abSLydia Wang imux_is_smixer = 45463e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 45473e95b9abSLydia Wang /* inputs */ 45483e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45493e95b9abSLydia Wang parm = AC_PWRST_D3; 45503e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45513e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45523e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45533e95b9abSLydia Wang if (imux_is_smixer) 45543e95b9abSLydia Wang parm = AC_PWRST_D0; 45553e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 45563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45603e95b9abSLydia Wang 45613e95b9abSLydia Wang /* outputs */ 45623e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 45633e95b9abSLydia Wang parm = AC_PWRST_D3; 45643e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 45653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 45663e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 45673e95b9abSLydia Wang 45683e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 45693e95b9abSLydia Wang parm = AC_PWRST_D3; 45703e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 45713e95b9abSLydia Wang if (spec->smart51_enabled) 45723e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45733e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 45743e95b9abSLydia Wang 45753e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 45763e95b9abSLydia Wang parm = AC_PWRST_D3; 45773e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 45783e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 45793e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 45813e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 45823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 45833e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 45843e95b9abSLydia Wang 45853e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 45863e95b9abSLydia Wang parm = AC_PWRST_D3; 45873e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 45883e95b9abSLydia Wang if (spec->smart51_enabled) 45893e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 45913e95b9abSLydia Wang 45923e95b9abSLydia Wang if (spec->hp_independent_mode) { 45933e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 45943e95b9abSLydia Wang parm = AC_PWRST_D3; 45953e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 45973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 45983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 45993e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46003e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 46013e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46023e95b9abSLydia Wang } 46033e95b9abSLydia Wang } 46043e95b9abSLydia Wang 4605eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4606eb7188caSLydia Wang { 4607eb7188caSLydia Wang struct via_spec *spec; 4608eb7188caSLydia Wang int err; 4609eb7188caSLydia Wang 4610eb7188caSLydia Wang /* create a codec specific record */ 46115b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4612eb7188caSLydia Wang if (spec == NULL) 4613eb7188caSLydia Wang return -ENOMEM; 4614eb7188caSLydia Wang 4615eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4616eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4617eb7188caSLydia Wang if (err < 0) { 4618eb7188caSLydia Wang via_free(codec); 4619eb7188caSLydia Wang return err; 4620eb7188caSLydia Wang } else if (!err) { 4621eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4622eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4623eb7188caSLydia Wang } 4624eb7188caSLydia Wang 4625eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4626eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4627eb7188caSLydia Wang 4628bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4629bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4630bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4631bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4632bb3c6bfcSLydia Wang else 4633eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4634eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4635eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4636eb7188caSLydia Wang 4637bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4638bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4639bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4640bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4641bb3c6bfcSLydia Wang else 4642eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4643eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4644bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4645eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4646eb7188caSLydia Wang 4647eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4648eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4649eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4650eb7188caSLydia Wang get_mux_nids(codec); 4651bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4652bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4653eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4654eb7188caSLydia Wang spec->num_mixers++; 4655eb7188caSLydia Wang } 4656eb7188caSLydia Wang 4657eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4658eb7188caSLydia Wang 4659eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 46600f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4661eb7188caSLydia Wang 4662eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4663eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4664eb7188caSLydia Wang #endif 4665eb7188caSLydia Wang 46663e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 46673e95b9abSLydia Wang 4668eb7188caSLydia Wang return 0; 4669eb7188caSLydia Wang } 4670f3db423dSLydia Wang 4671f3db423dSLydia Wang /* Patch for VT1716S */ 4672f3db423dSLydia Wang 4673f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4674f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4675f3db423dSLydia Wang { 4676f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4677f3db423dSLydia Wang uinfo->count = 1; 4678f3db423dSLydia Wang uinfo->value.integer.min = 0; 4679f3db423dSLydia Wang uinfo->value.integer.max = 1; 4680f3db423dSLydia Wang return 0; 4681f3db423dSLydia Wang } 4682f3db423dSLydia Wang 4683f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4684f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4685f3db423dSLydia Wang { 4686f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4687f3db423dSLydia Wang int index = 0; 4688f3db423dSLydia Wang 4689f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4690f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4691f3db423dSLydia Wang if (index != -1) 4692f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4693f3db423dSLydia Wang 4694f3db423dSLydia Wang return 0; 4695f3db423dSLydia Wang } 4696f3db423dSLydia Wang 4697f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4698f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4699f3db423dSLydia Wang { 4700f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4701f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4702f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4703f3db423dSLydia Wang 4704f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4705f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4706f3db423dSLydia Wang spec->dmic_enabled = index; 47073e95b9abSLydia Wang set_widgets_power_state(codec); 4708f3db423dSLydia Wang return 1; 4709f3db423dSLydia Wang } 4710f3db423dSLydia Wang 4711f3db423dSLydia Wang /* capture mixer elements */ 4712f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4713f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4714f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4715f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4716f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4717f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4718f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4719f3db423dSLydia Wang HDA_INPUT), 4720f3db423dSLydia Wang { 4721f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4722f3db423dSLydia Wang .name = "Input Source", 4723f3db423dSLydia Wang .count = 1, 4724f3db423dSLydia Wang .info = via_mux_enum_info, 4725f3db423dSLydia Wang .get = via_mux_enum_get, 4726f3db423dSLydia Wang .put = via_mux_enum_put, 4727f3db423dSLydia Wang }, 4728f3db423dSLydia Wang { } /* end */ 4729f3db423dSLydia Wang }; 4730f3db423dSLydia Wang 4731f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4732f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4733f3db423dSLydia Wang { 4734f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4735f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 47365b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 4737f3db423dSLydia Wang .count = 1, 4738f3db423dSLydia Wang .info = vt1716s_dmic_info, 4739f3db423dSLydia Wang .get = vt1716s_dmic_get, 4740f3db423dSLydia Wang .put = vt1716s_dmic_put, 4741f3db423dSLydia Wang }, 4742f3db423dSLydia Wang {} /* end */ 4743f3db423dSLydia Wang }; 4744f3db423dSLydia Wang 4745f3db423dSLydia Wang 4746f3db423dSLydia Wang /* mono-out mixer elements */ 4747f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4748f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4749f3db423dSLydia Wang { } /* end */ 4750f3db423dSLydia Wang }; 4751f3db423dSLydia Wang 4752f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 4753f3db423dSLydia Wang /* 4754f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4755f3db423dSLydia Wang */ 4756f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4757f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4758f3db423dSLydia Wang 4759f3db423dSLydia Wang 4760f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4761f3db423dSLydia Wang * mixer widget 4762f3db423dSLydia Wang */ 4763f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4764f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4765f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4766f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4767f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4768f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4769f3db423dSLydia Wang 4770f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4771f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4772f3db423dSLydia Wang 4773f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4774f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4775f3db423dSLydia Wang 4776f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4777f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4778f3db423dSLydia Wang 4779f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4780f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4781f3db423dSLydia Wang 4782f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4783f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4784f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4785f3db423dSLydia Wang 4786f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4787f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4788f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4789f3db423dSLydia Wang /* PW12 Output enable */ 4790f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4791f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4792f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4793f3db423dSLydia Wang /* don't bybass mixer */ 4794f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4795f3db423dSLydia Wang /* Enable mono output */ 4796f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4797f3db423dSLydia Wang { } 4798f3db423dSLydia Wang }; 4799f3db423dSLydia Wang 4800f3db423dSLydia Wang 4801f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 4802f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4803f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4804f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4805f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4806f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4807f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4808f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4809f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4810f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4811f3db423dSLydia Wang { } 4812f3db423dSLydia Wang }; 4813f3db423dSLydia Wang 4814f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4815f3db423dSLydia Wang .substreams = 2, 4816f3db423dSLydia Wang .channels_min = 2, 4817f3db423dSLydia Wang .channels_max = 6, 4818f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4819f3db423dSLydia Wang .ops = { 4820f3db423dSLydia Wang .open = via_playback_pcm_open, 4821f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4822f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4823f3db423dSLydia Wang .close = via_pcm_open_close, 4824f3db423dSLydia Wang }, 4825f3db423dSLydia Wang }; 4826f3db423dSLydia Wang 4827f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4828f3db423dSLydia Wang .substreams = 2, 4829f3db423dSLydia Wang .channels_min = 2, 4830f3db423dSLydia Wang .channels_max = 2, 4831f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4832f3db423dSLydia Wang .ops = { 4833f3db423dSLydia Wang .open = via_pcm_open_close, 4834f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4835f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4836f3db423dSLydia Wang .close = via_pcm_open_close, 4837f3db423dSLydia Wang }, 4838f3db423dSLydia Wang }; 4839f3db423dSLydia Wang 4840f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4841f3db423dSLydia Wang .substreams = 2, 4842f3db423dSLydia Wang .channels_min = 2, 4843f3db423dSLydia Wang .channels_max = 2, 4844f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4845f3db423dSLydia Wang .ops = { 4846f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4847f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4848f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4849f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4850f3db423dSLydia Wang }, 4851f3db423dSLydia Wang }; 4852f3db423dSLydia Wang 4853f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4854f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 4855f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4856f3db423dSLydia Wang { int i; 4857f3db423dSLydia Wang hda_nid_t nid; 4858f3db423dSLydia Wang 4859f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4860f3db423dSLydia Wang 4861f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4862f3db423dSLydia Wang 4863f3db423dSLydia Wang for (i = 0; i < 3; i++) { 4864f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4865f3db423dSLydia Wang if (nid) { 4866f3db423dSLydia Wang /* config dac list */ 4867f3db423dSLydia Wang switch (i) { 4868f3db423dSLydia Wang case AUTO_SEQ_FRONT: 4869f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 4870f3db423dSLydia Wang break; 4871f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 4872f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 4873f3db423dSLydia Wang break; 4874f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 4875f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 4876f3db423dSLydia Wang break; 4877f3db423dSLydia Wang } 4878f3db423dSLydia Wang } 4879f3db423dSLydia Wang } 4880f3db423dSLydia Wang 4881f3db423dSLydia Wang return 0; 4882f3db423dSLydia Wang } 4883f3db423dSLydia Wang 4884f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 4885f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 4886f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4887f3db423dSLydia Wang { 4888f3db423dSLydia Wang char name[32]; 4889ea734963STakashi Iwai static const char * const chname[3] = { 4890ea734963STakashi Iwai "Front", "Surround", "C/LFE" 4891ea734963STakashi Iwai }; 4892f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 4893f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 4894f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 4895f3db423dSLydia Wang int i, err; 4896f3db423dSLydia Wang 4897f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 4898f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4899f3db423dSLydia Wang 4900f3db423dSLydia Wang if (!nid) 4901f3db423dSLydia Wang continue; 4902f3db423dSLydia Wang 4903f3db423dSLydia Wang nid_vol = nid_vols[i]; 4904f3db423dSLydia Wang nid_mute = nid_mutes[i]; 4905f3db423dSLydia Wang 4906f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4907f3db423dSLydia Wang err = via_add_control( 4908f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4909f3db423dSLydia Wang "Center Playback Volume", 4910f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 4911f3db423dSLydia Wang if (err < 0) 4912f3db423dSLydia Wang return err; 4913f3db423dSLydia Wang err = via_add_control( 4914f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4915f3db423dSLydia Wang "LFE Playback Volume", 4916f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 4917f3db423dSLydia Wang if (err < 0) 4918f3db423dSLydia Wang return err; 4919f3db423dSLydia Wang err = via_add_control( 4920f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4921f3db423dSLydia Wang "Center Playback Switch", 4922f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4923f3db423dSLydia Wang HDA_OUTPUT)); 4924f3db423dSLydia Wang if (err < 0) 4925f3db423dSLydia Wang return err; 4926f3db423dSLydia Wang err = via_add_control( 4927f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4928f3db423dSLydia Wang "LFE Playback Switch", 4929f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4930f3db423dSLydia Wang HDA_OUTPUT)); 4931f3db423dSLydia Wang if (err < 0) 4932f3db423dSLydia Wang return err; 4933f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4934f3db423dSLydia Wang 4935f3db423dSLydia Wang err = via_add_control( 4936f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4937f3db423dSLydia Wang "Master Front Playback Volume", 4938f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4939f3db423dSLydia Wang if (err < 0) 4940f3db423dSLydia Wang return err; 4941f3db423dSLydia Wang err = via_add_control( 4942f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4943f3db423dSLydia Wang "Master Front Playback Switch", 4944f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4945f3db423dSLydia Wang if (err < 0) 4946f3db423dSLydia Wang return err; 4947f3db423dSLydia Wang 4948f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4949f3db423dSLydia Wang err = via_add_control( 4950f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4951f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4952f3db423dSLydia Wang if (err < 0) 4953f3db423dSLydia Wang return err; 4954f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4955f3db423dSLydia Wang err = via_add_control( 4956f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4957f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4958f3db423dSLydia Wang HDA_OUTPUT)); 4959f3db423dSLydia Wang if (err < 0) 4960f3db423dSLydia Wang return err; 4961f3db423dSLydia Wang } else { 4962f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4963f3db423dSLydia Wang err = via_add_control( 4964f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4965f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4966f3db423dSLydia Wang if (err < 0) 4967f3db423dSLydia Wang return err; 4968f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4969f3db423dSLydia Wang err = via_add_control( 4970f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4971f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4972f3db423dSLydia Wang HDA_OUTPUT)); 4973f3db423dSLydia Wang if (err < 0) 4974f3db423dSLydia Wang return err; 4975f3db423dSLydia Wang } 4976f3db423dSLydia Wang } 4977f3db423dSLydia Wang return 0; 4978f3db423dSLydia Wang } 4979f3db423dSLydia Wang 4980f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4981f3db423dSLydia Wang { 4982f3db423dSLydia Wang int err; 4983f3db423dSLydia Wang 4984f3db423dSLydia Wang if (!pin) 4985f3db423dSLydia Wang return 0; 4986f3db423dSLydia Wang 4987f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 4988f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 4989f3db423dSLydia Wang 4990f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4991f3db423dSLydia Wang "Headphone Playback Volume", 4992f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 4993f3db423dSLydia Wang if (err < 0) 4994f3db423dSLydia Wang return err; 4995f3db423dSLydia Wang 4996f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4997f3db423dSLydia Wang "Headphone Playback Switch", 4998f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4999f3db423dSLydia Wang if (err < 0) 5000f3db423dSLydia Wang return err; 5001f3db423dSLydia Wang 5002f3db423dSLydia Wang create_hp_imux(spec); 5003f3db423dSLydia Wang return 0; 5004f3db423dSLydia Wang } 5005f3db423dSLydia Wang 5006f3db423dSLydia Wang /* create playback/capture controls for input pins */ 500710a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, 5008f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5009f3db423dSLydia Wang { 5010f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 501110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 5012f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5013f3db423dSLydia Wang } 5014f3db423dSLydia Wang 5015f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5016f3db423dSLydia Wang { 5017f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5018f3db423dSLydia Wang int err; 5019f3db423dSLydia Wang 5020f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5021f3db423dSLydia Wang if (err < 0) 5022f3db423dSLydia Wang return err; 5023f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5024f3db423dSLydia Wang if (err < 0) 5025f3db423dSLydia Wang return err; 5026f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5027f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5028f3db423dSLydia Wang 5029f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5030f3db423dSLydia Wang if (err < 0) 5031f3db423dSLydia Wang return err; 5032f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5033f3db423dSLydia Wang if (err < 0) 5034f3db423dSLydia Wang return err; 503510a20af7STakashi Iwai err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); 5036f3db423dSLydia Wang if (err < 0) 5037f3db423dSLydia Wang return err; 5038f3db423dSLydia Wang 5039f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5040f3db423dSLydia Wang 5041f3db423dSLydia Wang fill_dig_outs(codec); 5042f3db423dSLydia Wang 5043f3db423dSLydia Wang if (spec->kctls.list) 5044f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5045f3db423dSLydia Wang 5046f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5047f3db423dSLydia Wang 5048f3db423dSLydia Wang if (spec->hp_mux) 50493d83e577STakashi Iwai via_hp_build(codec); 5050f3db423dSLydia Wang 50515b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5052f3db423dSLydia Wang 5053f3db423dSLydia Wang return 1; 5054f3db423dSLydia Wang } 5055f3db423dSLydia Wang 5056f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5057f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5058f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5059f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5060f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5061f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5062f3db423dSLydia Wang { } /* end */ 5063f3db423dSLydia Wang }; 5064f3db423dSLydia Wang #endif 5065f3db423dSLydia Wang 50663e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 50673e95b9abSLydia Wang { 50683e95b9abSLydia Wang struct via_spec *spec = codec->spec; 50693e95b9abSLydia Wang int imux_is_smixer; 50703e95b9abSLydia Wang unsigned int parm; 50713e95b9abSLydia Wang unsigned int mono_out, present; 50723e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 50733e95b9abSLydia Wang imux_is_smixer = 50743e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 50753e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 50763e95b9abSLydia Wang /* inputs */ 50773e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 50783e95b9abSLydia Wang parm = AC_PWRST_D3; 50793e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 50803e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 50813e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 50823e95b9abSLydia Wang if (imux_is_smixer) 50833e95b9abSLydia Wang parm = AC_PWRST_D0; 50843e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 50853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 50863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 50873e95b9abSLydia Wang 50883e95b9abSLydia Wang parm = AC_PWRST_D3; 50893e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 50903e95b9abSLydia Wang /* PW11 (22h) */ 50913e95b9abSLydia Wang if (spec->dmic_enabled) 50923e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 50933e95b9abSLydia Wang else 50943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 50953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 50963e95b9abSLydia Wang 50973e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 50983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 50993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 51003e95b9abSLydia Wang 51013e95b9abSLydia Wang /* outputs */ 51023e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 51033e95b9abSLydia Wang parm = AC_PWRST_D3; 51043e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 51053e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 51063e95b9abSLydia Wang if (spec->smart51_enabled) 51073e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 51083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 51093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 51103e95b9abSLydia Wang 51113e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 51123e95b9abSLydia Wang parm = AC_PWRST_D3; 51133e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 51143e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 51153e95b9abSLydia Wang if (spec->smart51_enabled) 51163e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 51173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 51183e95b9abSLydia Wang 51193e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 51203e95b9abSLydia Wang if (spec->smart51_enabled) 51213e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 51233e95b9abSLydia Wang 51243e95b9abSLydia Wang /* Mono out */ 51253e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 51263e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 51273e95b9abSLydia Wang 51283e95b9abSLydia Wang if (present) 51293e95b9abSLydia Wang mono_out = 0; 51303e95b9abSLydia Wang else { 51313e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 51323e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 51333e95b9abSLydia Wang mono_out = 0; 51343e95b9abSLydia Wang else 51353e95b9abSLydia Wang mono_out = 1; 51363e95b9abSLydia Wang } 51373e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 51383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 51393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 51403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 51413e95b9abSLydia Wang 51423e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 51433e95b9abSLydia Wang parm = AC_PWRST_D3; 51443e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 51453e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 51463e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 51473e95b9abSLydia Wang if (spec->hp_independent_mode) 51483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 51493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 51503e95b9abSLydia Wang 51513e95b9abSLydia Wang /* force to D0 for internal Speaker */ 51523e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 51533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 51543e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 51553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 51563e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 51573e95b9abSLydia Wang } 51583e95b9abSLydia Wang 5159f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5160f3db423dSLydia Wang { 5161f3db423dSLydia Wang struct via_spec *spec; 5162f3db423dSLydia Wang int err; 5163f3db423dSLydia Wang 5164f3db423dSLydia Wang /* create a codec specific record */ 51655b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5166f3db423dSLydia Wang if (spec == NULL) 5167f3db423dSLydia Wang return -ENOMEM; 5168f3db423dSLydia Wang 5169f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5170f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5171f3db423dSLydia Wang if (err < 0) { 5172f3db423dSLydia Wang via_free(codec); 5173f3db423dSLydia Wang return err; 5174f3db423dSLydia Wang } else if (!err) { 5175f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5176f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5177f3db423dSLydia Wang } 5178f3db423dSLydia Wang 5179f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5180f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5181f3db423dSLydia Wang 5182f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5183f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5184f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5185f3db423dSLydia Wang 5186f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5187f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5188f3db423dSLydia Wang 5189f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5190f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5191f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5192f3db423dSLydia Wang get_mux_nids(codec); 5193f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5194f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5195f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5196f3db423dSLydia Wang spec->num_mixers++; 5197f3db423dSLydia Wang } 5198f3db423dSLydia Wang 5199f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5200f3db423dSLydia Wang spec->num_mixers++; 5201f3db423dSLydia Wang 5202f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5203f3db423dSLydia Wang 5204f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5205f3db423dSLydia Wang 5206f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 52070f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5208f3db423dSLydia Wang 5209f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5210f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5211f3db423dSLydia Wang #endif 5212f3db423dSLydia Wang 52133e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 5214f3db423dSLydia Wang return 0; 5215f3db423dSLydia Wang } 521625eaba2fSLydia Wang 521725eaba2fSLydia Wang /* for vt2002P */ 521825eaba2fSLydia Wang 521925eaba2fSLydia Wang /* capture mixer elements */ 522025eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 522125eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 522225eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 522325eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 522425eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 522525eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 522625eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 522725eaba2fSLydia Wang HDA_INPUT), 522825eaba2fSLydia Wang { 522925eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 523025eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 523125eaba2fSLydia Wang * So call somewhat different.. 523225eaba2fSLydia Wang */ 523325eaba2fSLydia Wang /* .name = "Capture Source", */ 523425eaba2fSLydia Wang .name = "Input Source", 523525eaba2fSLydia Wang .count = 2, 523625eaba2fSLydia Wang .info = via_mux_enum_info, 523725eaba2fSLydia Wang .get = via_mux_enum_get, 523825eaba2fSLydia Wang .put = via_mux_enum_put, 523925eaba2fSLydia Wang }, 524025eaba2fSLydia Wang { } /* end */ 524125eaba2fSLydia Wang }; 524225eaba2fSLydia Wang 524325eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 524425eaba2fSLydia Wang /* 524525eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 524625eaba2fSLydia Wang */ 524725eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 524825eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 524925eaba2fSLydia Wang 525025eaba2fSLydia Wang 525125eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 525225eaba2fSLydia Wang * mixer widget 525325eaba2fSLydia Wang */ 525425eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 525525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 525625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 525725eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 525825eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 525925eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 526025eaba2fSLydia Wang 526125eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 526225eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 526325eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 526425eaba2fSLydia Wang 526525eaba2fSLydia Wang /* PW9 Output enable */ 526625eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 526725eaba2fSLydia Wang 526825eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 526925eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 527025eaba2fSLydia Wang 527125eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 527225eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527325eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527425eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527525eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527625eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527725eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527825eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527925eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 528025eaba2fSLydia Wang 528125eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 528225eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 528325eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 528425eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 528525eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 528625eaba2fSLydia Wang 528725eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 528825eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 528925eaba2fSLydia Wang 529025eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 529125eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 529225eaba2fSLydia Wang { } 529325eaba2fSLydia Wang }; 529425eaba2fSLydia Wang 529525eaba2fSLydia Wang 529625eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 529725eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 529825eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 529925eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 530025eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 530125eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 530225eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 530325eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 530425eaba2fSLydia Wang { } 530525eaba2fSLydia Wang }; 530625eaba2fSLydia Wang 530725eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 530825eaba2fSLydia Wang .substreams = 2, 530925eaba2fSLydia Wang .channels_min = 2, 531025eaba2fSLydia Wang .channels_max = 2, 531125eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 531225eaba2fSLydia Wang .ops = { 531325eaba2fSLydia Wang .open = via_playback_pcm_open, 531425eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 531525eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 531625eaba2fSLydia Wang .close = via_pcm_open_close, 531725eaba2fSLydia Wang }, 531825eaba2fSLydia Wang }; 531925eaba2fSLydia Wang 532025eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 532125eaba2fSLydia Wang .substreams = 2, 532225eaba2fSLydia Wang .channels_min = 2, 532325eaba2fSLydia Wang .channels_max = 2, 532425eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 532525eaba2fSLydia Wang .ops = { 532625eaba2fSLydia Wang .open = via_pcm_open_close, 532725eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 532825eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 532925eaba2fSLydia Wang .close = via_pcm_open_close, 533025eaba2fSLydia Wang }, 533125eaba2fSLydia Wang }; 533225eaba2fSLydia Wang 533325eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 533425eaba2fSLydia Wang .substreams = 1, 533525eaba2fSLydia Wang .channels_min = 2, 533625eaba2fSLydia Wang .channels_max = 2, 533725eaba2fSLydia Wang /* NID is set in via_build_pcms */ 533825eaba2fSLydia Wang .ops = { 533925eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 534025eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 534125eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 534225eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 534325eaba2fSLydia Wang }, 534425eaba2fSLydia Wang }; 534525eaba2fSLydia Wang 534625eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 534725eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 534825eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 534925eaba2fSLydia Wang { 535025eaba2fSLydia Wang spec->multiout.num_dacs = 1; 535125eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 535225eaba2fSLydia Wang if (cfg->line_out_pins[0]) 535325eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 535425eaba2fSLydia Wang return 0; 535525eaba2fSLydia Wang } 535625eaba2fSLydia Wang 535725eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 535825eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 535925eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 536025eaba2fSLydia Wang { 536125eaba2fSLydia Wang int err; 536225eaba2fSLydia Wang 536325eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 536425eaba2fSLydia Wang return -1; 536525eaba2fSLydia Wang 536625eaba2fSLydia Wang 536725eaba2fSLydia Wang /* Line-Out: PortE */ 536825eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 536925eaba2fSLydia Wang "Master Front Playback Volume", 537025eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 537125eaba2fSLydia Wang if (err < 0) 537225eaba2fSLydia Wang return err; 537325eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 537425eaba2fSLydia Wang "Master Front Playback Switch", 537525eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); 537625eaba2fSLydia Wang if (err < 0) 537725eaba2fSLydia Wang return err; 537825eaba2fSLydia Wang 537925eaba2fSLydia Wang return 0; 538025eaba2fSLydia Wang } 538125eaba2fSLydia Wang 538225eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 538325eaba2fSLydia Wang { 538425eaba2fSLydia Wang int err; 538525eaba2fSLydia Wang 538625eaba2fSLydia Wang if (!pin) 538725eaba2fSLydia Wang return 0; 538825eaba2fSLydia Wang 538925eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 539025eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 539125eaba2fSLydia Wang 539225eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 539325eaba2fSLydia Wang "Headphone Playback Volume", 539425eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 539525eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 539625eaba2fSLydia Wang if (err < 0) 539725eaba2fSLydia Wang return err; 539825eaba2fSLydia Wang 539925eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 540025eaba2fSLydia Wang "Headphone Playback Switch", 540125eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 540225eaba2fSLydia Wang if (err < 0) 540325eaba2fSLydia Wang return err; 540425eaba2fSLydia Wang 540525eaba2fSLydia Wang create_hp_imux(spec); 540625eaba2fSLydia Wang return 0; 540725eaba2fSLydia Wang } 540825eaba2fSLydia Wang 540925eaba2fSLydia Wang /* create playback/capture controls for input pins */ 541010a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, 541125eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 541225eaba2fSLydia Wang { 541310a20af7STakashi Iwai struct via_spec *spec = codec->spec; 541425eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5415f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; 5416f3268512STakashi Iwai int err; 541725eaba2fSLydia Wang 541810a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5419f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 542025eaba2fSLydia Wang if (err < 0) 542125eaba2fSLydia Wang return err; 542225eaba2fSLydia Wang /* build volume/mute control of loopback */ 54237b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); 542425eaba2fSLydia Wang if (err < 0) 542525eaba2fSLydia Wang return err; 542625eaba2fSLydia Wang 542725eaba2fSLydia Wang /* for digital mic select */ 542810a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); 542925eaba2fSLydia Wang 543025eaba2fSLydia Wang return 0; 543125eaba2fSLydia Wang } 543225eaba2fSLydia Wang 543325eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 543425eaba2fSLydia Wang { 543525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 543625eaba2fSLydia Wang int err; 543725eaba2fSLydia Wang 543825eaba2fSLydia Wang 543925eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 544025eaba2fSLydia Wang if (err < 0) 544125eaba2fSLydia Wang return err; 544225eaba2fSLydia Wang 544325eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 544425eaba2fSLydia Wang if (err < 0) 544525eaba2fSLydia Wang return err; 544625eaba2fSLydia Wang 544725eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 544825eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 544925eaba2fSLydia Wang 545025eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 545125eaba2fSLydia Wang if (err < 0) 545225eaba2fSLydia Wang return err; 545325eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 545425eaba2fSLydia Wang if (err < 0) 545525eaba2fSLydia Wang return err; 545610a20af7STakashi Iwai err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); 545725eaba2fSLydia Wang if (err < 0) 545825eaba2fSLydia Wang return err; 545925eaba2fSLydia Wang 546025eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 546125eaba2fSLydia Wang 546225eaba2fSLydia Wang fill_dig_outs(codec); 546325eaba2fSLydia Wang 546425eaba2fSLydia Wang if (spec->kctls.list) 546525eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 546625eaba2fSLydia Wang 546725eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 546825eaba2fSLydia Wang 546925eaba2fSLydia Wang if (spec->hp_mux) 54703d83e577STakashi Iwai via_hp_build(codec); 547125eaba2fSLydia Wang 547225eaba2fSLydia Wang return 1; 547325eaba2fSLydia Wang } 547425eaba2fSLydia Wang 547525eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 547625eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 547725eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 547825eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 547925eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 548025eaba2fSLydia Wang { } /* end */ 548125eaba2fSLydia Wang }; 548225eaba2fSLydia Wang #endif 548325eaba2fSLydia Wang 54843e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 54853e95b9abSLydia Wang { 54863e95b9abSLydia Wang struct via_spec *spec = codec->spec; 54873e95b9abSLydia Wang int imux_is_smixer; 54883e95b9abSLydia Wang unsigned int parm; 54893e95b9abSLydia Wang unsigned int present; 54903e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 54913e95b9abSLydia Wang imux_is_smixer = 54923e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 54933e95b9abSLydia Wang /* inputs */ 54943e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 54953e95b9abSLydia Wang parm = AC_PWRST_D3; 54963e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 54973e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 54983e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 54993e95b9abSLydia Wang parm = AC_PWRST_D0; 55003e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 55013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 55023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 55033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 55043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 55053e95b9abSLydia Wang 55063e95b9abSLydia Wang /* outputs */ 55073e95b9abSLydia Wang /* AOW0 (8h)*/ 55083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 55093e95b9abSLydia Wang 55103e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 55113e95b9abSLydia Wang parm = AC_PWRST_D3; 55123e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 55133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 55143e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 55163e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55173e95b9abSLydia Wang 55183e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 55193e95b9abSLydia Wang parm = AC_PWRST_D3; 55203e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 55213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 55223e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 55243e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55253e95b9abSLydia Wang 55263e95b9abSLydia Wang if (spec->hp_independent_mode) 55273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 55283e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 55293e95b9abSLydia Wang 55303e95b9abSLydia Wang /* Class-D */ 55313e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 55323e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 55333e95b9abSLydia Wang 55343e95b9abSLydia Wang parm = AC_PWRST_D3; 55353e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 55363e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 55373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 55383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 55403e95b9abSLydia Wang 55413e95b9abSLydia Wang /* Mono Out */ 55423e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 55433e95b9abSLydia Wang 55443e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 55453e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 55463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 55473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 55493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 55513e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55523e95b9abSLydia Wang 55533e95b9abSLydia Wang /* MW9 (21h) */ 55543e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 55553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 55563e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 55573e95b9abSLydia Wang else 55583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 55593e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 55603e95b9abSLydia Wang } 556125eaba2fSLydia Wang 556225eaba2fSLydia Wang /* patch for vt2002P */ 556325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 556425eaba2fSLydia Wang { 556525eaba2fSLydia Wang struct via_spec *spec; 556625eaba2fSLydia Wang int err; 556725eaba2fSLydia Wang 556825eaba2fSLydia Wang /* create a codec specific record */ 55695b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 557025eaba2fSLydia Wang if (spec == NULL) 557125eaba2fSLydia Wang return -ENOMEM; 557225eaba2fSLydia Wang 557325eaba2fSLydia Wang /* automatic parse from the BIOS config */ 557425eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 557525eaba2fSLydia Wang if (err < 0) { 557625eaba2fSLydia Wang via_free(codec); 557725eaba2fSLydia Wang return err; 557825eaba2fSLydia Wang } else if (!err) { 557925eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 558025eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 558125eaba2fSLydia Wang } 558225eaba2fSLydia Wang 558325eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; 558425eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; 558525eaba2fSLydia Wang 558625eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 558725eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 558825eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 558925eaba2fSLydia Wang 559025eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 559125eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 559225eaba2fSLydia Wang 559325eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 559425eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 559525eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 559625eaba2fSLydia Wang get_mux_nids(codec); 559725eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 559825eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 559925eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 560025eaba2fSLydia Wang spec->num_mixers++; 560125eaba2fSLydia Wang } 560225eaba2fSLydia Wang 560325eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 560425eaba2fSLydia Wang 560525eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 56060f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 560725eaba2fSLydia Wang 560825eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 560925eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 561025eaba2fSLydia Wang #endif 561125eaba2fSLydia Wang 56123e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 561325eaba2fSLydia Wang return 0; 561425eaba2fSLydia Wang } 5615ab6734e7SLydia Wang 5616ab6734e7SLydia Wang /* for vt1812 */ 5617ab6734e7SLydia Wang 5618ab6734e7SLydia Wang /* capture mixer elements */ 5619ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = { 5620ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5621ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5622ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5623ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5624ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5625ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5626ab6734e7SLydia Wang HDA_INPUT), 5627ab6734e7SLydia Wang { 5628ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5629ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5630ab6734e7SLydia Wang * So call somewhat different.. 5631ab6734e7SLydia Wang */ 5632ab6734e7SLydia Wang .name = "Input Source", 5633ab6734e7SLydia Wang .count = 2, 5634ab6734e7SLydia Wang .info = via_mux_enum_info, 5635ab6734e7SLydia Wang .get = via_mux_enum_get, 5636ab6734e7SLydia Wang .put = via_mux_enum_put, 5637ab6734e7SLydia Wang }, 5638ab6734e7SLydia Wang { } /* end */ 5639ab6734e7SLydia Wang }; 5640ab6734e7SLydia Wang 5641ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = { 5642ab6734e7SLydia Wang /* 5643ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5644ab6734e7SLydia Wang */ 5645ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5646ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5647ab6734e7SLydia Wang 5648ab6734e7SLydia Wang 5649ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5650ab6734e7SLydia Wang * mixer widget 5651ab6734e7SLydia Wang */ 5652ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5653ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5654ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5655ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5656ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5657ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5658ab6734e7SLydia Wang 5659ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5660ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5661ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5662ab6734e7SLydia Wang 5663ab6734e7SLydia Wang /* PW9 Output enable */ 5664ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5665ab6734e7SLydia Wang 5666ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5667ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5668ab6734e7SLydia Wang 5669ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5670ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5671ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5672ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5673ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5674ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5675ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5676ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5677ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5678ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5679ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5680ab6734e7SLydia Wang 5681ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5682ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5683ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5684ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5685ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5686ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5687ab6734e7SLydia Wang 5688ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5689ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5690ab6734e7SLydia Wang { } 5691ab6734e7SLydia Wang }; 5692ab6734e7SLydia Wang 5693ab6734e7SLydia Wang 5694ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = { 5695ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5696ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5697ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5698ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5699ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5700ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5701ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5702ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5703ab6734e7SLydia Wang { } 5704ab6734e7SLydia Wang }; 5705ab6734e7SLydia Wang 5706ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = { 5707ab6734e7SLydia Wang .substreams = 2, 5708ab6734e7SLydia Wang .channels_min = 2, 5709ab6734e7SLydia Wang .channels_max = 2, 5710ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5711ab6734e7SLydia Wang .ops = { 5712ab6734e7SLydia Wang .open = via_playback_pcm_open, 5713ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5714ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5715ab6734e7SLydia Wang .close = via_pcm_open_close, 5716ab6734e7SLydia Wang }, 5717ab6734e7SLydia Wang }; 5718ab6734e7SLydia Wang 5719ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = { 5720ab6734e7SLydia Wang .substreams = 2, 5721ab6734e7SLydia Wang .channels_min = 2, 5722ab6734e7SLydia Wang .channels_max = 2, 5723ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5724ab6734e7SLydia Wang .ops = { 5725ab6734e7SLydia Wang .open = via_pcm_open_close, 5726ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5727ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5728ab6734e7SLydia Wang .close = via_pcm_open_close, 5729ab6734e7SLydia Wang }, 5730ab6734e7SLydia Wang }; 5731ab6734e7SLydia Wang 5732ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = { 5733ab6734e7SLydia Wang .substreams = 1, 5734ab6734e7SLydia Wang .channels_min = 2, 5735ab6734e7SLydia Wang .channels_max = 2, 5736ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5737ab6734e7SLydia Wang .ops = { 5738ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5739ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5740ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5741ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5742ab6734e7SLydia Wang }, 5743ab6734e7SLydia Wang }; 5744ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5745ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5746ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5747ab6734e7SLydia Wang { 5748ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5749ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5750ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5751ab6734e7SLydia Wang spec->multiout.dac_nids[0] = 0x8; 5752ab6734e7SLydia Wang return 0; 5753ab6734e7SLydia Wang } 5754ab6734e7SLydia Wang 5755ab6734e7SLydia Wang 5756ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5757ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5758ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5759ab6734e7SLydia Wang { 5760ab6734e7SLydia Wang int err; 5761ab6734e7SLydia Wang 5762ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5763ab6734e7SLydia Wang return -1; 5764ab6734e7SLydia Wang 5765ab6734e7SLydia Wang /* Line-Out: PortE */ 5766ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 57673d83e577STakashi Iwai "Front Playback Volume", 5768ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5769ab6734e7SLydia Wang if (err < 0) 5770ab6734e7SLydia Wang return err; 5771ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 57723d83e577STakashi Iwai "Front Playback Switch", 5773ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5774ab6734e7SLydia Wang if (err < 0) 5775ab6734e7SLydia Wang return err; 5776ab6734e7SLydia Wang 5777ab6734e7SLydia Wang return 0; 5778ab6734e7SLydia Wang } 5779ab6734e7SLydia Wang 5780ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5781ab6734e7SLydia Wang { 5782ab6734e7SLydia Wang int err; 5783ab6734e7SLydia Wang 5784ab6734e7SLydia Wang if (!pin) 5785ab6734e7SLydia Wang return 0; 5786ab6734e7SLydia Wang 5787ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 5788ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 5789ab6734e7SLydia Wang 5790ab6734e7SLydia Wang 5791ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5792ab6734e7SLydia Wang "Headphone Playback Volume", 5793ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 5794ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5795ab6734e7SLydia Wang if (err < 0) 5796ab6734e7SLydia Wang return err; 5797ab6734e7SLydia Wang 5798ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5799ab6734e7SLydia Wang "Headphone Playback Switch", 5800ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5801ab6734e7SLydia Wang if (err < 0) 5802ab6734e7SLydia Wang return err; 5803ab6734e7SLydia Wang 5804ab6734e7SLydia Wang create_hp_imux(spec); 5805ab6734e7SLydia Wang return 0; 5806ab6734e7SLydia Wang } 5807ab6734e7SLydia Wang 5808ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 580910a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, 5810ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5811ab6734e7SLydia Wang { 581210a20af7STakashi Iwai struct via_spec *spec = codec->spec; 5813ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5814f3268512STakashi Iwai static hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; 5815f3268512STakashi Iwai int err; 5816ab6734e7SLydia Wang 581710a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5818f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5819ab6734e7SLydia Wang if (err < 0) 5820ab6734e7SLydia Wang return err; 5821f3268512STakashi Iwai 5822ab6734e7SLydia Wang /* build volume/mute control of loopback */ 58237b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); 5824ab6734e7SLydia Wang if (err < 0) 5825ab6734e7SLydia Wang return err; 5826ab6734e7SLydia Wang 5827ab6734e7SLydia Wang /* for digital mic select */ 582810a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); 5829ab6734e7SLydia Wang 5830ab6734e7SLydia Wang return 0; 5831ab6734e7SLydia Wang } 5832ab6734e7SLydia Wang 5833ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 5834ab6734e7SLydia Wang { 5835ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 5836ab6734e7SLydia Wang int err; 5837ab6734e7SLydia Wang 5838ab6734e7SLydia Wang 5839ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5840ab6734e7SLydia Wang if (err < 0) 5841ab6734e7SLydia Wang return err; 5842ab6734e7SLydia Wang fill_dig_outs(codec); 5843ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 5844ab6734e7SLydia Wang if (err < 0) 5845ab6734e7SLydia Wang return err; 5846ab6734e7SLydia Wang 5847ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 5848ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 5849ab6734e7SLydia Wang 5850ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 5851ab6734e7SLydia Wang if (err < 0) 5852ab6734e7SLydia Wang return err; 5853ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5854ab6734e7SLydia Wang if (err < 0) 5855ab6734e7SLydia Wang return err; 585610a20af7STakashi Iwai err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); 5857ab6734e7SLydia Wang if (err < 0) 5858ab6734e7SLydia Wang return err; 5859ab6734e7SLydia Wang 5860ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5861ab6734e7SLydia Wang 5862ab6734e7SLydia Wang fill_dig_outs(codec); 5863ab6734e7SLydia Wang 5864ab6734e7SLydia Wang if (spec->kctls.list) 5865ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5866ab6734e7SLydia Wang 5867ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 5868ab6734e7SLydia Wang 5869ab6734e7SLydia Wang if (spec->hp_mux) 58703d83e577STakashi Iwai via_hp_build(codec); 5871ab6734e7SLydia Wang 5872ab6734e7SLydia Wang return 1; 5873ab6734e7SLydia Wang } 5874ab6734e7SLydia Wang 5875ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5876ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = { 5877ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 5878ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 5879ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 5880ab6734e7SLydia Wang { } /* end */ 5881ab6734e7SLydia Wang }; 5882ab6734e7SLydia Wang #endif 5883ab6734e7SLydia Wang 58843e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 58853e95b9abSLydia Wang { 58863e95b9abSLydia Wang struct via_spec *spec = codec->spec; 58873e95b9abSLydia Wang int imux_is_smixer = 58883e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 58893e95b9abSLydia Wang unsigned int parm; 58903e95b9abSLydia Wang unsigned int present; 58913e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 58923e95b9abSLydia Wang imux_is_smixer = 58933e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 58943e95b9abSLydia Wang /* inputs */ 58953e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 58963e95b9abSLydia Wang parm = AC_PWRST_D3; 58973e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 58983e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 58993e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 59003e95b9abSLydia Wang parm = AC_PWRST_D0; 59013e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 59023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 59033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 59043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 59053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 59063e95b9abSLydia Wang 59073e95b9abSLydia Wang /* outputs */ 59083e95b9abSLydia Wang /* AOW0 (8h)*/ 59093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 59103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59113e95b9abSLydia Wang 59123e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 59133e95b9abSLydia Wang parm = AC_PWRST_D3; 59143e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 59153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 59163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 59173e95b9abSLydia Wang 59183e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 59193e95b9abSLydia Wang parm = AC_PWRST_D3; 59203e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 59213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 59223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 59233e95b9abSLydia Wang if (spec->hp_independent_mode) 59243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 59253e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59263e95b9abSLydia Wang 59273e95b9abSLydia Wang /* Internal Speaker */ 59283e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 59293e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 59303e95b9abSLydia Wang 59313e95b9abSLydia Wang parm = AC_PWRST_D3; 59323e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 59333e95b9abSLydia Wang if (present) { 59343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 59353e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 59363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 59373e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 59383e95b9abSLydia Wang } else { 59393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 59403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 59423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59433e95b9abSLydia Wang } 59443e95b9abSLydia Wang 59453e95b9abSLydia Wang 59463e95b9abSLydia Wang /* Mono Out */ 59473e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 59483e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 59493e95b9abSLydia Wang 59503e95b9abSLydia Wang parm = AC_PWRST_D3; 59513e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 59523e95b9abSLydia Wang if (present) { 59533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 59543e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 59553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 59563e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 59573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 59583e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 59593e95b9abSLydia Wang } else { 59603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 59613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 59633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 59653e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59663e95b9abSLydia Wang } 59673e95b9abSLydia Wang 59683e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 59693e95b9abSLydia Wang parm = AC_PWRST_D3; 59703e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 59713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 59723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 59733e95b9abSLydia Wang 59743e95b9abSLydia Wang } 5975ab6734e7SLydia Wang 5976ab6734e7SLydia Wang /* patch for vt1812 */ 5977ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 5978ab6734e7SLydia Wang { 5979ab6734e7SLydia Wang struct via_spec *spec; 5980ab6734e7SLydia Wang int err; 5981ab6734e7SLydia Wang 5982ab6734e7SLydia Wang /* create a codec specific record */ 59835b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5984ab6734e7SLydia Wang if (spec == NULL) 5985ab6734e7SLydia Wang return -ENOMEM; 5986ab6734e7SLydia Wang 5987ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 5988ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 5989ab6734e7SLydia Wang if (err < 0) { 5990ab6734e7SLydia Wang via_free(codec); 5991ab6734e7SLydia Wang return err; 5992ab6734e7SLydia Wang } else if (!err) { 5993ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5994ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 5995ab6734e7SLydia Wang } 5996ab6734e7SLydia Wang 5997ab6734e7SLydia Wang 5998ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 5999ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6000ab6734e7SLydia Wang 6001ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 6002ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6003ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6004ab6734e7SLydia Wang 6005ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 6006ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6007ab6734e7SLydia Wang 6008ab6734e7SLydia Wang 6009ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 6010ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 6011ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 6012ab6734e7SLydia Wang get_mux_nids(codec); 6013ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6014ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6015ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6016ab6734e7SLydia Wang spec->num_mixers++; 6017ab6734e7SLydia Wang } 6018ab6734e7SLydia Wang 6019ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6020ab6734e7SLydia Wang 6021ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 60220f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 6023ab6734e7SLydia Wang 6024ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6025ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6026ab6734e7SLydia Wang #endif 6027ab6734e7SLydia Wang 60283e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 6029ab6734e7SLydia Wang return 0; 6030ab6734e7SLydia Wang } 6031ab6734e7SLydia Wang 6032c577b8a1SJoseph Chan /* 6033c577b8a1SJoseph Chan * patch entries 6034c577b8a1SJoseph Chan */ 60351289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 60363218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 60373218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 60383218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 60393218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 60403218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6041f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 60423218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6043f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 60443218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6045f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 60463218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6047f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 60483218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6049f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 60503218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6051f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 60523218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6053f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 60543218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6055f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 60563218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6057f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 60583218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6059f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 60603218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6061f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 60623218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6063f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 60643218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6065f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60663218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6067f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60683218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6069f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60703218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6071f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 60723218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6073d949cac1SHarald Welte .patch = patch_vt1708S}, 60743218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6075d949cac1SHarald Welte .patch = patch_vt1708S}, 60763218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6077d949cac1SHarald Welte .patch = patch_vt1708S}, 60783218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6079d949cac1SHarald Welte .patch = patch_vt1708S}, 6080*bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 6081d949cac1SHarald Welte .patch = patch_vt1708S}, 60823218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6083d949cac1SHarald Welte .patch = patch_vt1708S}, 60843218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6085d949cac1SHarald Welte .patch = patch_vt1708S}, 60863218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6087d949cac1SHarald Welte .patch = patch_vt1708S}, 60883218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6089d949cac1SHarald Welte .patch = patch_vt1702}, 60903218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6091d949cac1SHarald Welte .patch = patch_vt1702}, 60923218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6093d949cac1SHarald Welte .patch = patch_vt1702}, 60943218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6095d949cac1SHarald Welte .patch = patch_vt1702}, 60963218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6097d949cac1SHarald Welte .patch = patch_vt1702}, 60983218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6099d949cac1SHarald Welte .patch = patch_vt1702}, 61003218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6101d949cac1SHarald Welte .patch = patch_vt1702}, 61023218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6103d949cac1SHarald Welte .patch = patch_vt1702}, 6104eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6105eb7188caSLydia Wang .patch = patch_vt1718S}, 6106eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6107eb7188caSLydia Wang .patch = patch_vt1718S}, 6108bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6109bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6110bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6111bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6112f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6113f3db423dSLydia Wang .patch = patch_vt1716S}, 6114f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6115f3db423dSLydia Wang .patch = patch_vt1716S}, 611625eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 611725eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6118ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 611936dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 612036dd5c4aSLydia Wang .patch = patch_vt1708S}, 6121c577b8a1SJoseph Chan {} /* terminator */ 6122c577b8a1SJoseph Chan }; 61231289e9e8STakashi Iwai 61241289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 61251289e9e8STakashi Iwai 61261289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 61271289e9e8STakashi Iwai .preset = snd_hda_preset_via, 61281289e9e8STakashi Iwai .owner = THIS_MODULE, 61291289e9e8STakashi Iwai }; 61301289e9e8STakashi Iwai 61311289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 61321289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 61331289e9e8STakashi Iwai 61341289e9e8STakashi Iwai static int __init patch_via_init(void) 61351289e9e8STakashi Iwai { 61361289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 61371289e9e8STakashi Iwai } 61381289e9e8STakashi Iwai 61391289e9e8STakashi Iwai static void __exit patch_via_exit(void) 61401289e9e8STakashi Iwai { 61411289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 61421289e9e8STakashi Iwai } 61431289e9e8STakashi Iwai 61441289e9e8STakashi Iwai module_init(patch_via_init) 61451289e9e8STakashi Iwai module_exit(patch_via_exit) 6146