1c577b8a1SJoseph Chan /* 2c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec 3c577b8a1SJoseph Chan * 48e86597fSLydia Wang * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec 5c577b8a1SJoseph Chan * 68e86597fSLydia Wang * (C) 2006-2009 VIA Technology, Inc. 78e86597fSLydia Wang * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> 8c577b8a1SJoseph Chan * 9c577b8a1SJoseph Chan * This driver is free software; you can redistribute it and/or modify 10c577b8a1SJoseph Chan * it under the terms of the GNU General Public License as published by 11c577b8a1SJoseph Chan * the Free Software Foundation; either version 2 of the License, or 12c577b8a1SJoseph Chan * (at your option) any later version. 13c577b8a1SJoseph Chan * 14c577b8a1SJoseph Chan * This driver is distributed in the hope that it will be useful, 15c577b8a1SJoseph Chan * but WITHOUT ANY WARRANTY; without even the implied warranty of 16c577b8a1SJoseph Chan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17c577b8a1SJoseph Chan * GNU General Public License for more details. 18c577b8a1SJoseph Chan * 19c577b8a1SJoseph Chan * You should have received a copy of the GNU General Public License 20c577b8a1SJoseph Chan * along with this program; if not, write to the Free Software 21c577b8a1SJoseph Chan * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22c577b8a1SJoseph Chan */ 23c577b8a1SJoseph Chan 24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ 25c577b8a1SJoseph Chan /* */ 26c577b8a1SJoseph Chan /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ 27c577b8a1SJoseph Chan /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ 28c577b8a1SJoseph Chan /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ 29c577b8a1SJoseph Chan /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ 30f7278fd0SJosepch Chan /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ 31f7278fd0SJosepch Chan /* 2007-09-17 Lydia Wang Add VT1708B codec support */ 3276d9b0ddSHarald Welte /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ 33fb4cb772SHarald Welte /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ 34d949cac1SHarald Welte /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ 3569e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ 360aa62aefSHarald Welte /* 2008-04-09 Lydia Wang Add Independent HP feature */ 3798aa34c0SHarald Welte /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ 38d7426329SHarald Welte /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ 398e86597fSLydia Wang /* 2009-02-16 Logan Li Add support for VT1718S */ 408e86597fSLydia Wang /* 2009-03-13 Logan Li Add support for VT1716S */ 418e86597fSLydia Wang /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ 428e86597fSLydia Wang /* 2009-07-08 Lydia Wang Add support for VT2002P */ 438e86597fSLydia Wang /* 2009-07-21 Lydia Wang Add support for VT1812 */ 4436dd5c4aSLydia Wang /* 2009-09-19 Lydia Wang Add support for VT1818S */ 45c577b8a1SJoseph Chan /* */ 46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan 49c577b8a1SJoseph Chan #include <linux/init.h> 50c577b8a1SJoseph Chan #include <linux/delay.h> 51c577b8a1SJoseph Chan #include <linux/slab.h> 52c577b8a1SJoseph Chan #include <sound/core.h> 530aa62aefSHarald Welte #include <sound/asoundef.h> 54c577b8a1SJoseph Chan #include "hda_codec.h" 55c577b8a1SJoseph Chan #include "hda_local.h" 56c577b8a1SJoseph Chan 575b0cb1d8SJaroslav Kysela #define NID_MAPPING (-1) 585b0cb1d8SJaroslav Kysela 59c577b8a1SJoseph Chan /* amp values */ 60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 62c577b8a1SJoseph Chan 63c577b8a1SJoseph Chan /* Pin Widget NID */ 64c577b8a1SJoseph Chan #define VT1708_HP_NID 0x13 65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID 0x14 66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID 0x16 67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN 0x26 6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 70c577b8a1SJoseph Chan 71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID 0x28 72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID 0x13 73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID 0x17 74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN 0x25 75f7278fd0SJosepch Chan 76f7278fd0SJosepch Chan #define VT1708B_HP_NID 0x25 77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID 0x12 78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID 0x15 79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN 0x21 80c577b8a1SJoseph Chan 81d949cac1SHarald Welte #define VT1708S_HP_NID 0x25 82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID 0x12 83d949cac1SHarald Welte 84d949cac1SHarald Welte #define VT1702_HP_NID 0x17 85d949cac1SHarald Welte #define VT1702_DIGOUT_NID 0x11 86d949cac1SHarald Welte 87d7426329SHarald Welte enum VIA_HDA_CODEC { 88d7426329SHarald Welte UNKNOWN = -1, 89d7426329SHarald Welte VT1708, 90d7426329SHarald Welte VT1709_10CH, 91d7426329SHarald Welte VT1709_6CH, 92d7426329SHarald Welte VT1708B_8CH, 93d7426329SHarald Welte VT1708B_4CH, 94d7426329SHarald Welte VT1708S, 95518bf3baSLydia Wang VT1708BCE, 96d7426329SHarald Welte VT1702, 97eb7188caSLydia Wang VT1718S, 98f3db423dSLydia Wang VT1716S, 9925eaba2fSLydia Wang VT2002P, 100ab6734e7SLydia Wang VT1812, 10111890956SLydia Wang VT1802, 102d7426329SHarald Welte CODEC_TYPES, 103d7426329SHarald Welte }; 104d7426329SHarald Welte 10511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 10611890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 10711890956SLydia Wang (spec)->codec_type == VT1812 ||\ 10811890956SLydia Wang (spec)->codec_type == VT1802) 10911890956SLydia Wang 1101f2e99feSLydia Wang struct via_spec { 1111f2e99feSLydia Wang /* codec parameterization */ 11290dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1131f2e99feSLydia Wang unsigned int num_mixers; 1141f2e99feSLydia Wang 11590dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1161f2e99feSLydia Wang unsigned int num_iverbs; 1171f2e99feSLydia Wang 1181f2e99feSLydia Wang char *stream_name_analog; 11990dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1211f2e99feSLydia Wang 1221f2e99feSLydia Wang char *stream_name_digital; 12390dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 12490dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1251f2e99feSLydia Wang 1261f2e99feSLydia Wang /* playback */ 1271f2e99feSLydia Wang struct hda_multi_out multiout; 1281f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1291f2e99feSLydia Wang 1301f2e99feSLydia Wang /* capture */ 1311f2e99feSLydia Wang unsigned int num_adc_nids; 13290dd48a1STakashi Iwai const hda_nid_t *adc_nids; 1331f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1341f2e99feSLydia Wang hda_nid_t dig_in_nid; 1351f2e99feSLydia Wang hda_nid_t dig_in_pin; 1361f2e99feSLydia Wang 1371f2e99feSLydia Wang /* capture source */ 1381f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1391f2e99feSLydia Wang unsigned int cur_mux[3]; 1401f2e99feSLydia Wang 1411f2e99feSLydia Wang /* PCM information */ 1421f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1431f2e99feSLydia Wang 1441f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1451f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1461f2e99feSLydia Wang struct snd_array kctls; 1471f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1481f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1491f2e99feSLydia Wang 1501f2e99feSLydia Wang /* HP mode source */ 1511f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1521f2e99feSLydia Wang unsigned int hp_independent_mode; 1531f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1541f2e99feSLydia Wang unsigned int smart51_enabled; 155f3db423dSLydia Wang unsigned int dmic_enabled; 156*24088a58STakashi Iwai unsigned int no_pin_power_ctl; 1571f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1581f2e99feSLydia Wang 1591f2e99feSLydia Wang /* work to check hp jack state */ 1601f2e99feSLydia Wang struct hda_codec *codec; 1611f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1621f2e99feSLydia Wang int vt1708_jack_detectect; 1631f2e99feSLydia Wang int vt1708_hp_present; 1643e95b9abSLydia Wang 1653e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1663e95b9abSLydia Wang 1671f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1681f2e99feSLydia Wang struct hda_loopback_check loopback; 1691f2e99feSLydia Wang #endif 1701f2e99feSLydia Wang }; 1711f2e99feSLydia Wang 1720341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1735b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1745b0cb1d8SJaroslav Kysela { 1755b0cb1d8SJaroslav Kysela struct via_spec *spec; 1765b0cb1d8SJaroslav Kysela 1775b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1785b0cb1d8SJaroslav Kysela if (spec == NULL) 1795b0cb1d8SJaroslav Kysela return NULL; 1805b0cb1d8SJaroslav Kysela 1815b0cb1d8SJaroslav Kysela codec->spec = spec; 1825b0cb1d8SJaroslav Kysela spec->codec = codec; 1830341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1840341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1850341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1860341ccd7SLydia Wang spec->codec_type = VT1708S; 1875b0cb1d8SJaroslav Kysela return spec; 1885b0cb1d8SJaroslav Kysela } 1895b0cb1d8SJaroslav Kysela 190744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 191d7426329SHarald Welte { 192744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 193d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 194d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 195d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 196d7426329SHarald Welte 197d7426329SHarald Welte /* get codec type */ 198d7426329SHarald Welte if (ven_id != 0x1106) 199d7426329SHarald Welte codec_type = UNKNOWN; 200d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 201d7426329SHarald Welte codec_type = VT1708; 202d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 203d7426329SHarald Welte codec_type = VT1709_10CH; 204d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 205d7426329SHarald Welte codec_type = VT1709_6CH; 206518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 207d7426329SHarald Welte codec_type = VT1708B_8CH; 208518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 209518bf3baSLydia Wang codec_type = VT1708BCE; 210518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 211d7426329SHarald Welte codec_type = VT1708B_4CH; 212d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 213d7426329SHarald Welte && (dev_id >> 12) < 8) 214d7426329SHarald Welte codec_type = VT1708S; 215d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 216d7426329SHarald Welte && (dev_id >> 12) < 8) 217d7426329SHarald Welte codec_type = VT1702; 218eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 219eb7188caSLydia Wang && (dev_id >> 12) < 8) 220eb7188caSLydia Wang codec_type = VT1718S; 221f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 222f3db423dSLydia Wang codec_type = VT1716S; 223bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 224bb3c6bfcSLydia Wang codec_type = VT1718S; 22525eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 22625eaba2fSLydia Wang codec_type = VT2002P; 227ab6734e7SLydia Wang else if (dev_id == 0x0448) 228ab6734e7SLydia Wang codec_type = VT1812; 22936dd5c4aSLydia Wang else if (dev_id == 0x0440) 23036dd5c4aSLydia Wang codec_type = VT1708S; 23111890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 23211890956SLydia Wang codec_type = VT1802; 233d7426329SHarald Welte else 234d7426329SHarald Welte codec_type = UNKNOWN; 235d7426329SHarald Welte return codec_type; 236d7426329SHarald Welte }; 237d7426329SHarald Welte 238ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 23969e52a80SHarald Welte #define VIA_HP_EVENT 0x01 24069e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 241ec7e7e42SLydia Wang #define VIA_MONO_EVENT 0x03 242ec7e7e42SLydia Wang #define VIA_SPEAKER_EVENT 0x04 243ec7e7e42SLydia Wang #define VIA_BIND_HP_EVENT 0x05 24469e52a80SHarald Welte 245c577b8a1SJoseph Chan enum { 246c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 247c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 248f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 24925eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 250c577b8a1SJoseph Chan }; 251c577b8a1SJoseph Chan 252c577b8a1SJoseph Chan enum { 253eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 254c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 255c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 256c577b8a1SJoseph Chan AUTO_SEQ_SIDE 257c577b8a1SJoseph Chan }; 258c577b8a1SJoseph Chan 259f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2601f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2611f2e99feSLydia Wang 2621f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2631f2e99feSLydia Wang { 2641f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2651f2e99feSLydia Wang return; 2661f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2671f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2681f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2691f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2701f2e99feSLydia Wang msecs_to_jiffies(100)); 2711f2e99feSLydia Wang } 2721f2e99feSLydia Wang 2731f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2741f2e99feSLydia Wang { 2751f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2761f2e99feSLydia Wang return; 2771f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2781f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2791f2e99feSLydia Wang return; 2801f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2811f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2825b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2831f2e99feSLydia Wang } 284f5271101SLydia Wang 2853e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2863e95b9abSLydia Wang { 2873e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2883e95b9abSLydia Wang if (spec->set_widgets_power_state) 2893e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2903e95b9abSLydia Wang } 29125eaba2fSLydia Wang 292f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 293f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 294f5271101SLydia Wang { 295f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 296f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 297f5271101SLydia Wang 2983e95b9abSLydia Wang set_widgets_power_state(codec); 299f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3001f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3011f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3021f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3031f2e99feSLydia Wang else 3041f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3051f2e99feSLydia Wang } 306f5271101SLydia Wang return change; 307f5271101SLydia Wang } 308f5271101SLydia Wang 309f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 310f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 311f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 312f5271101SLydia Wang .name = NULL, \ 313f5271101SLydia Wang .index = 0, \ 314f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 315f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 316f5271101SLydia Wang .put = analog_input_switch_put, \ 317f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 318f5271101SLydia Wang 31925eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 32025eaba2fSLydia Wang 32125eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 32225eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 32325eaba2fSLydia Wang { 32425eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 32525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 32625eaba2fSLydia Wang int i; 32725eaba2fSLydia Wang int change = 0; 32825eaba2fSLydia Wang 32925eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 33025eaba2fSLydia Wang int lmute, rmute; 33125eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 33225eaba2fSLydia Wang snd_printd("Invalid control!\n"); 33325eaba2fSLydia Wang return change; 33425eaba2fSLydia Wang } 33525eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 33625eaba2fSLydia Wang ucontrol); 33725eaba2fSLydia Wang /* Get mute value */ 33825eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 33925eaba2fSLydia Wang valp++; 34025eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 34125eaba2fSLydia Wang 34225eaba2fSLydia Wang /* Set hp pins */ 34325eaba2fSLydia Wang if (!spec->hp_independent_mode) { 34425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 34525eaba2fSLydia Wang snd_hda_codec_amp_update( 34625eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 34725eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 34825eaba2fSLydia Wang lmute); 34925eaba2fSLydia Wang snd_hda_codec_amp_update( 35025eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 35125eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35225eaba2fSLydia Wang rmute); 35325eaba2fSLydia Wang } 35425eaba2fSLydia Wang } 35525eaba2fSLydia Wang 35625eaba2fSLydia Wang if (!lmute && !rmute) { 35725eaba2fSLydia Wang /* Line Outs */ 35825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 35925eaba2fSLydia Wang snd_hda_codec_amp_stereo( 36025eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 36125eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 36225eaba2fSLydia Wang /* Speakers */ 36325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 36425eaba2fSLydia Wang snd_hda_codec_amp_stereo( 36525eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 36625eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 36725eaba2fSLydia Wang /* unmute */ 36825eaba2fSLydia Wang via_hp_bind_automute(codec); 36925eaba2fSLydia Wang 37025eaba2fSLydia Wang } else { 37125eaba2fSLydia Wang if (lmute) { 37225eaba2fSLydia Wang /* Mute all left channels */ 37325eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 37425eaba2fSLydia Wang snd_hda_codec_amp_update( 37525eaba2fSLydia Wang codec, 37625eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 37725eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37825eaba2fSLydia Wang lmute); 37925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 38025eaba2fSLydia Wang snd_hda_codec_amp_update( 38125eaba2fSLydia Wang codec, 38225eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 38325eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 38425eaba2fSLydia Wang lmute); 38525eaba2fSLydia Wang } 38625eaba2fSLydia Wang if (rmute) { 38725eaba2fSLydia Wang /* mute all right channels */ 38825eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 38925eaba2fSLydia Wang snd_hda_codec_amp_update( 39025eaba2fSLydia Wang codec, 39125eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 39225eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39325eaba2fSLydia Wang rmute); 39425eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 39525eaba2fSLydia Wang snd_hda_codec_amp_update( 39625eaba2fSLydia Wang codec, 39725eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 39825eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 39925eaba2fSLydia Wang rmute); 40025eaba2fSLydia Wang } 40125eaba2fSLydia Wang } 40225eaba2fSLydia Wang return change; 40325eaba2fSLydia Wang } 40425eaba2fSLydia Wang 40525eaba2fSLydia Wang #define BIND_PIN_MUTE \ 40625eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 40725eaba2fSLydia Wang .name = NULL, \ 40825eaba2fSLydia Wang .index = 0, \ 40925eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 41025eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 41125eaba2fSLydia Wang .put = bind_pin_switch_put, \ 41225eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 41325eaba2fSLydia Wang 41490dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 415c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 416c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 417f5271101SLydia Wang ANALOG_INPUT_MUTE, 41825eaba2fSLydia Wang BIND_PIN_MUTE, 419c577b8a1SJoseph Chan }; 420c577b8a1SJoseph Chan 42190dd48a1STakashi Iwai static const hda_nid_t vt1708_adc_nids[2] = { 422c577b8a1SJoseph Chan /* ADC1-2 */ 423c577b8a1SJoseph Chan 0x15, 0x27 424c577b8a1SJoseph Chan }; 425c577b8a1SJoseph Chan 42690dd48a1STakashi Iwai static const hda_nid_t vt1709_adc_nids[3] = { 427c577b8a1SJoseph Chan /* ADC1-2 */ 428c577b8a1SJoseph Chan 0x14, 0x15, 0x16 429c577b8a1SJoseph Chan }; 430c577b8a1SJoseph Chan 43190dd48a1STakashi Iwai static const hda_nid_t vt1708B_adc_nids[2] = { 432f7278fd0SJosepch Chan /* ADC1-2 */ 433f7278fd0SJosepch Chan 0x13, 0x14 434f7278fd0SJosepch Chan }; 435f7278fd0SJosepch Chan 43690dd48a1STakashi Iwai static const hda_nid_t vt1708S_adc_nids[2] = { 437d949cac1SHarald Welte /* ADC1-2 */ 438d949cac1SHarald Welte 0x13, 0x14 439d949cac1SHarald Welte }; 440d949cac1SHarald Welte 44190dd48a1STakashi Iwai static const hda_nid_t vt1702_adc_nids[3] = { 442d949cac1SHarald Welte /* ADC1-2 */ 443d949cac1SHarald Welte 0x12, 0x20, 0x1F 444d949cac1SHarald Welte }; 445d949cac1SHarald Welte 44690dd48a1STakashi Iwai static const hda_nid_t vt1718S_adc_nids[2] = { 447eb7188caSLydia Wang /* ADC1-2 */ 448eb7188caSLydia Wang 0x10, 0x11 449eb7188caSLydia Wang }; 450eb7188caSLydia Wang 45190dd48a1STakashi Iwai static const hda_nid_t vt1716S_adc_nids[2] = { 452f3db423dSLydia Wang /* ADC1-2 */ 453f3db423dSLydia Wang 0x13, 0x14 454f3db423dSLydia Wang }; 455f3db423dSLydia Wang 45690dd48a1STakashi Iwai static const hda_nid_t vt2002P_adc_nids[2] = { 45725eaba2fSLydia Wang /* ADC1-2 */ 45825eaba2fSLydia Wang 0x10, 0x11 45925eaba2fSLydia Wang }; 46025eaba2fSLydia Wang 46190dd48a1STakashi Iwai static const hda_nid_t vt1812_adc_nids[2] = { 462ab6734e7SLydia Wang /* ADC1-2 */ 463ab6734e7SLydia Wang 0x10, 0x11 464ab6734e7SLydia Wang }; 465ab6734e7SLydia Wang 466ab6734e7SLydia Wang 467c577b8a1SJoseph Chan /* add dynamic controls */ 4687b315bb4STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 4697b315bb4STakashi Iwai int idx, unsigned long val) 470c577b8a1SJoseph Chan { 471c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 472c577b8a1SJoseph Chan 473603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 474603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 475c577b8a1SJoseph Chan if (!knew) 476c577b8a1SJoseph Chan return -ENOMEM; 47771eb7dccSLydia Wang *knew = via_control_templates[type]; 478c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 479c577b8a1SJoseph Chan if (!knew->name) 480c577b8a1SJoseph Chan return -ENOMEM; 4814d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4825e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 483c577b8a1SJoseph Chan knew->private_value = val; 484c577b8a1SJoseph Chan return 0; 485c577b8a1SJoseph Chan } 486c577b8a1SJoseph Chan 4877b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4887b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4897b315bb4STakashi Iwai 4905b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, 49190dd48a1STakashi Iwai const struct snd_kcontrol_new *tmpl) 4925b0cb1d8SJaroslav Kysela { 4935b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 4945b0cb1d8SJaroslav Kysela 4955b0cb1d8SJaroslav Kysela snd_array_init(&spec->kctls, sizeof(*knew), 32); 4965b0cb1d8SJaroslav Kysela knew = snd_array_new(&spec->kctls); 4975b0cb1d8SJaroslav Kysela if (!knew) 4985b0cb1d8SJaroslav Kysela return NULL; 4995b0cb1d8SJaroslav Kysela *knew = *tmpl; 5005b0cb1d8SJaroslav Kysela knew->name = kstrdup(tmpl->name, GFP_KERNEL); 5015b0cb1d8SJaroslav Kysela if (!knew->name) 5025b0cb1d8SJaroslav Kysela return NULL; 503b331439dSTakashi Iwai return knew; 5045b0cb1d8SJaroslav Kysela } 5055b0cb1d8SJaroslav Kysela 506603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 507603c4019STakashi Iwai { 508603c4019STakashi Iwai struct via_spec *spec = codec->spec; 509603c4019STakashi Iwai 510603c4019STakashi Iwai if (spec->kctls.list) { 511603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 512603c4019STakashi Iwai int i; 513603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 514603c4019STakashi Iwai kfree(kctl[i].name); 515603c4019STakashi Iwai } 516603c4019STakashi Iwai snd_array_free(&spec->kctls); 517603c4019STakashi Iwai } 518603c4019STakashi Iwai 519c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 5209510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 5217b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 522c577b8a1SJoseph Chan { 523c577b8a1SJoseph Chan char name[32]; 524c577b8a1SJoseph Chan int err; 525c577b8a1SJoseph Chan 526c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 5277b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 528c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 529c577b8a1SJoseph Chan if (err < 0) 530c577b8a1SJoseph Chan return err; 531c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 5327b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 533c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 534c577b8a1SJoseph Chan if (err < 0) 535c577b8a1SJoseph Chan return err; 536c577b8a1SJoseph Chan return 0; 537c577b8a1SJoseph Chan } 538c577b8a1SJoseph Chan 539c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 540c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 541c577b8a1SJoseph Chan int dac_idx) 542c577b8a1SJoseph Chan { 543c577b8a1SJoseph Chan /* set as output */ 544c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 545c577b8a1SJoseph Chan pin_type); 546c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 547c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 548d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 549d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 550d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 551c577b8a1SJoseph Chan } 552c577b8a1SJoseph Chan 553c577b8a1SJoseph Chan 554c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 555c577b8a1SJoseph Chan { 556c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 557c577b8a1SJoseph Chan int i; 558c577b8a1SJoseph Chan 559c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 560c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 561c577b8a1SJoseph Chan if (nid) 562c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 563c577b8a1SJoseph Chan } 564c577b8a1SJoseph Chan } 565c577b8a1SJoseph Chan 566c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 567c577b8a1SJoseph Chan { 568c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 569c577b8a1SJoseph Chan hda_nid_t pin; 57025eaba2fSLydia Wang int i; 571c577b8a1SJoseph Chan 57225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 57325eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 574c577b8a1SJoseph Chan if (pin) /* connect to front */ 575c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 576c577b8a1SJoseph Chan } 57725eaba2fSLydia Wang } 578c577b8a1SJoseph Chan 57932e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 58032e0191dSClemens Ladisch 581c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 582c577b8a1SJoseph Chan { 583c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5847b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 58532e0191dSClemens Ladisch unsigned int ctl; 586c577b8a1SJoseph Chan int i; 587c577b8a1SJoseph Chan 5887b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5897b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 59032e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 59132e0191dSClemens Ladisch ctl = PIN_OUT; 59230649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 59332e0191dSClemens Ladisch ctl = PIN_VREF50; 59432e0191dSClemens Ladisch else 59532e0191dSClemens Ladisch ctl = PIN_IN; 596c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 59732e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 598c577b8a1SJoseph Chan } 599c577b8a1SJoseph Chan } 600f5271101SLydia Wang 601f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 602f5271101SLydia Wang unsigned int *affected_parm) 603f5271101SLydia Wang { 604f5271101SLydia Wang unsigned parm; 605f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 606f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 607f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 608f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 6091564b287SLydia Wang struct via_spec *spec = codec->spec; 610*24088a58STakashi Iwai unsigned present = 0; 611*24088a58STakashi Iwai 612*24088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 613*24088a58STakashi Iwai if (!no_presence) 614*24088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 6151564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 6161564b287SLydia Wang || ((no_presence || present) 6171564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 618f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 619f5271101SLydia Wang parm = AC_PWRST_D0; 620f5271101SLydia Wang } else 621f5271101SLydia Wang parm = AC_PWRST_D3; 622f5271101SLydia Wang 623f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 624f5271101SLydia Wang } 625f5271101SLydia Wang 626*24088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 627*24088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 628*24088a58STakashi Iwai { 629*24088a58STakashi Iwai static const char * const texts[] = { 630*24088a58STakashi Iwai "Disabled", "Enabled" 631*24088a58STakashi Iwai }; 632*24088a58STakashi Iwai 633*24088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 634*24088a58STakashi Iwai uinfo->count = 1; 635*24088a58STakashi Iwai uinfo->value.enumerated.items = 2; 636*24088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 637*24088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 638*24088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 639*24088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 640*24088a58STakashi Iwai return 0; 641*24088a58STakashi Iwai } 642*24088a58STakashi Iwai 643*24088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 644*24088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 645*24088a58STakashi Iwai { 646*24088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 647*24088a58STakashi Iwai struct via_spec *spec = codec->spec; 648*24088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 649*24088a58STakashi Iwai return 0; 650*24088a58STakashi Iwai } 651*24088a58STakashi Iwai 652*24088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 653*24088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 654*24088a58STakashi Iwai { 655*24088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 656*24088a58STakashi Iwai struct via_spec *spec = codec->spec; 657*24088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 658*24088a58STakashi Iwai 659*24088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 660*24088a58STakashi Iwai return 0; 661*24088a58STakashi Iwai spec->no_pin_power_ctl = val; 662*24088a58STakashi Iwai set_widgets_power_state(codec); 663*24088a58STakashi Iwai return 1; 664*24088a58STakashi Iwai } 665*24088a58STakashi Iwai 666*24088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 667*24088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 668*24088a58STakashi Iwai .name = "Dynamic Power-Control", 669*24088a58STakashi Iwai .info = via_pin_power_ctl_info, 670*24088a58STakashi Iwai .get = via_pin_power_ctl_get, 671*24088a58STakashi Iwai .put = via_pin_power_ctl_put, 672*24088a58STakashi Iwai }; 673*24088a58STakashi Iwai 674*24088a58STakashi Iwai 675c577b8a1SJoseph Chan /* 676c577b8a1SJoseph Chan * input MUX handling 677c577b8a1SJoseph Chan */ 678c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 679c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 680c577b8a1SJoseph Chan { 681c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 682c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 683c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 684c577b8a1SJoseph Chan } 685c577b8a1SJoseph Chan 686c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 687c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 688c577b8a1SJoseph Chan { 689c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 690c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 691c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 692c577b8a1SJoseph Chan 693c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 694c577b8a1SJoseph Chan return 0; 695c577b8a1SJoseph Chan } 696c577b8a1SJoseph Chan 697c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 698c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 699c577b8a1SJoseph Chan { 700c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 701c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 702c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 703bff5fbf5SLydia Wang int ret; 704c577b8a1SJoseph Chan 705337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 706337b9d02STakashi Iwai return -EINVAL; 707a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 708a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 709a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 710a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 711a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 712bff5fbf5SLydia Wang 713bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 714bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 715bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 716a80e6e3cSLydia Wang /* update jack power state */ 7173e95b9abSLydia Wang set_widgets_power_state(codec); 718a80e6e3cSLydia Wang 719bff5fbf5SLydia Wang return ret; 720c577b8a1SJoseph Chan } 721c577b8a1SJoseph Chan 7220aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 7230aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 7240aa62aefSHarald Welte { 7250aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7260aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7270aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 7280aa62aefSHarald Welte } 7290aa62aefSHarald Welte 7300aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7310aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7320aa62aefSHarald Welte { 7330aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7345b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 735eb7188caSLydia Wang unsigned int pinsel; 736eb7188caSLydia Wang 737eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 738eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 7390aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 7400aa62aefSHarald Welte 0x00); 7410aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 7420aa62aefSHarald Welte 7430aa62aefSHarald Welte return 0; 7440aa62aefSHarald Welte } 7450aa62aefSHarald Welte 7460713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7470713efebSLydia Wang { 7480713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7490713efebSLydia Wang if (ctl) { 7500713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7510713efebSLydia Wang ctl->vd[0].access |= active 7520713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7530713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7540713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7550713efebSLydia Wang } 7560713efebSLydia Wang } 7570713efebSLydia Wang 7585b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7595b0cb1d8SJaroslav Kysela { 7605b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7615b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7625b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7635b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7645b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 765e87885feSLydia Wang case VT2002P: return 0x19; 766e87885feSLydia Wang case VT1802: return 0x15; 767e87885feSLydia Wang case VT1812: return 0x15; 7685b0cb1d8SJaroslav Kysela default: return 0; 7695b0cb1d8SJaroslav Kysela } 7705b0cb1d8SJaroslav Kysela } 7715b0cb1d8SJaroslav Kysela 772cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 773cdc1784dSLydia Wang { 774cdc1784dSLydia Wang /* mute side channel */ 775cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 776e87885feSLydia Wang unsigned int parm; 7775b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 778cdc1784dSLydia Wang 779e87885feSLydia Wang if (sw3) { 780e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 781e87885feSLydia Wang parm = spec->hp_independent_mode ? 782e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 783e87885feSLydia Wang else 784e87885feSLydia Wang parm = spec->hp_independent_mode ? 785e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 786e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 787e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 788e87885feSLydia Wang if (spec->codec_type == VT1812) 789e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 790e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 791e87885feSLydia Wang } 792cdc1784dSLydia Wang return 0; 793cdc1784dSLydia Wang } 794cdc1784dSLydia Wang 7950aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7960aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7970aa62aefSHarald Welte { 7980aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7990aa62aefSHarald Welte struct via_spec *spec = codec->spec; 8005b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 8010aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 802cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 803cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 804cdc1784dSLydia Wang ? 1 : 0; 805ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 806ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 807ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 808ce0e5a9eSLydia Wang else 809ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 810ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 8110aa62aefSHarald Welte 812ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 813ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 814ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 815cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 816cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 817cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 8180aa62aefSHarald Welte 0, 0, 0); 8190aa62aefSHarald Welte 820cdc1784dSLydia Wang update_side_mute_status(codec); 8210713efebSLydia Wang /* update HP volume/swtich active state */ 8220713efebSLydia Wang if (spec->codec_type == VT1708S 823eb7188caSLydia Wang || spec->codec_type == VT1702 824f3db423dSLydia Wang || spec->codec_type == VT1718S 82525eaba2fSLydia Wang || spec->codec_type == VT1716S 82611890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 8270713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 8280713efebSLydia Wang spec->hp_independent_mode); 8290713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 8300713efebSLydia Wang spec->hp_independent_mode); 8310713efebSLydia Wang } 832ce0e5a9eSLydia Wang /* update jack power state */ 8333e95b9abSLydia Wang set_widgets_power_state(codec); 8340aa62aefSHarald Welte return 0; 8350aa62aefSHarald Welte } 8360aa62aefSHarald Welte 83790dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 8380aa62aefSHarald Welte { 8390aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8400aa62aefSHarald Welte .name = "Independent HP", 8410aa62aefSHarald Welte .info = via_independent_hp_info, 8420aa62aefSHarald Welte .get = via_independent_hp_get, 8430aa62aefSHarald Welte .put = via_independent_hp_put, 8440aa62aefSHarald Welte }, 8455b0cb1d8SJaroslav Kysela { 8465b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8475b0cb1d8SJaroslav Kysela .name = "Independent HP", 8485b0cb1d8SJaroslav Kysela }, 8490aa62aefSHarald Welte }; 8500aa62aefSHarald Welte 8513d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8525b0cb1d8SJaroslav Kysela { 8533d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8545b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8555b0cb1d8SJaroslav Kysela hda_nid_t nid; 8563d83e577STakashi Iwai int nums; 8573d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8585b0cb1d8SJaroslav Kysela 8595b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8605b0cb1d8SJaroslav Kysela case VT1718S: 8615b0cb1d8SJaroslav Kysela nid = 0x34; 8625b0cb1d8SJaroslav Kysela break; 8635b0cb1d8SJaroslav Kysela case VT2002P: 86411890956SLydia Wang case VT1802: 8655b0cb1d8SJaroslav Kysela nid = 0x35; 8665b0cb1d8SJaroslav Kysela break; 8675b0cb1d8SJaroslav Kysela case VT1812: 8685b0cb1d8SJaroslav Kysela nid = 0x3d; 8695b0cb1d8SJaroslav Kysela break; 8705b0cb1d8SJaroslav Kysela default: 8715b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8725b0cb1d8SJaroslav Kysela break; 8735b0cb1d8SJaroslav Kysela } 8745b0cb1d8SJaroslav Kysela 875ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 876ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 877ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8783d83e577STakashi Iwai if (nums <= 1) 8793d83e577STakashi Iwai return 0; 880ee3c35c0SLydia Wang } 8813d83e577STakashi Iwai 8823d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8833d83e577STakashi Iwai if (knew == NULL) 8843d83e577STakashi Iwai return -ENOMEM; 8853d83e577STakashi Iwai 8865b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8875b0cb1d8SJaroslav Kysela knew->private_value = nid; 8885b0cb1d8SJaroslav Kysela 8895b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8905b0cb1d8SJaroslav Kysela if (knew == NULL) 8915b0cb1d8SJaroslav Kysela return -ENOMEM; 8925b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8935b0cb1d8SJaroslav Kysela 8945b0cb1d8SJaroslav Kysela return 0; 8955b0cb1d8SJaroslav Kysela } 8965b0cb1d8SJaroslav Kysela 8971564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8981564b287SLydia Wang { 8991564b287SLydia Wang int i; 9001564b287SLydia Wang struct snd_ctl_elem_id id; 901525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 902525566cbSLydia Wang struct snd_kcontrol *ctl; 9031564b287SLydia Wang 9041564b287SLydia Wang memset(&id, 0, sizeof(id)); 9051564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 9061564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 9071564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 908525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 909525566cbSLydia Wang if (ctl) 910525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 911525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 912525566cbSLydia Wang &ctl->id); 9131564b287SLydia Wang } 9141564b287SLydia Wang } 9151564b287SLydia Wang 9161564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 9171564b287SLydia Wang { 9181564b287SLydia Wang struct via_spec *spec = codec->spec; 9191564b287SLydia Wang hda_nid_t nid_mixer; 9201564b287SLydia Wang int start_idx; 9211564b287SLydia Wang int end_idx; 9221564b287SLydia Wang int i; 9231564b287SLydia Wang /* get nid of MW0 and start & end index */ 9241564b287SLydia Wang switch (spec->codec_type) { 9251564b287SLydia Wang case VT1708: 9261564b287SLydia Wang nid_mixer = 0x17; 9271564b287SLydia Wang start_idx = 2; 9281564b287SLydia Wang end_idx = 4; 9291564b287SLydia Wang break; 9301564b287SLydia Wang case VT1709_10CH: 9311564b287SLydia Wang case VT1709_6CH: 9321564b287SLydia Wang nid_mixer = 0x18; 9331564b287SLydia Wang start_idx = 2; 9341564b287SLydia Wang end_idx = 4; 9351564b287SLydia Wang break; 9361564b287SLydia Wang case VT1708B_8CH: 9371564b287SLydia Wang case VT1708B_4CH: 9381564b287SLydia Wang case VT1708S: 939f3db423dSLydia Wang case VT1716S: 9401564b287SLydia Wang nid_mixer = 0x16; 9411564b287SLydia Wang start_idx = 2; 9421564b287SLydia Wang end_idx = 4; 9431564b287SLydia Wang break; 944ab657e0cSLydia Wang case VT1718S: 945ab657e0cSLydia Wang nid_mixer = 0x21; 946ab657e0cSLydia Wang start_idx = 1; 947ab657e0cSLydia Wang end_idx = 3; 948ab657e0cSLydia Wang break; 9491564b287SLydia Wang default: 9501564b287SLydia Wang return; 9511564b287SLydia Wang } 9521564b287SLydia Wang /* check AA path's mute status */ 9531564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9541564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 9551564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 9561564b287SLydia Wang HDA_AMP_MUTE, val); 9571564b287SLydia Wang } 9581564b287SLydia Wang } 9591564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 9601564b287SLydia Wang { 9617b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9627b315bb4STakashi Iwai int i; 9637b315bb4STakashi Iwai 9647b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9657b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 96686e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 9671564b287SLydia Wang } 9687b315bb4STakashi Iwai return 0; 9691564b287SLydia Wang } 9701564b287SLydia Wang 9711564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9721564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9731564b287SLydia Wang { 9741564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9751564b287SLydia Wang uinfo->count = 1; 9761564b287SLydia Wang uinfo->value.integer.min = 0; 9771564b287SLydia Wang uinfo->value.integer.max = 1; 9781564b287SLydia Wang return 0; 9791564b287SLydia Wang } 9801564b287SLydia Wang 9811564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9821564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9831564b287SLydia Wang { 9841564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9851564b287SLydia Wang struct via_spec *spec = codec->spec; 9867b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9871564b287SLydia Wang int on = 1; 9881564b287SLydia Wang int i; 9891564b287SLydia Wang 9907b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9917b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9927b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 9937b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 99486e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9957b315bb4STakashi Iwai continue; 99686e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9977b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9981564b287SLydia Wang continue; /* ignore FMic for independent HP */ 9997b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 10001564b287SLydia Wang on = 0; 10011564b287SLydia Wang } 10021564b287SLydia Wang *ucontrol->value.integer.value = on; 10031564b287SLydia Wang return 0; 10041564b287SLydia Wang } 10051564b287SLydia Wang 10061564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 10071564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 10081564b287SLydia Wang { 10091564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 10101564b287SLydia Wang struct via_spec *spec = codec->spec; 10117b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10121564b287SLydia Wang int out_in = *ucontrol->value.integer.value 10131564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 10141564b287SLydia Wang int i; 10151564b287SLydia Wang 10167b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10177b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 10187b315bb4STakashi Iwai unsigned int parm; 10197b315bb4STakashi Iwai 102086e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 10217b315bb4STakashi Iwai continue; 102286e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 10237b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 10241564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 10257b315bb4STakashi Iwai 10267b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 10271564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 10281564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 10291564b287SLydia Wang parm |= out_in; 10301564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 10311564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 10321564b287SLydia Wang parm); 10331564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 10341564b287SLydia Wang mute_aa_path(codec, 1); 10351564b287SLydia Wang notify_aa_path_ctls(codec); 10361564b287SLydia Wang } 10377b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 1038eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1039eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1040eb7188caSLydia Wang HDA_AMP_UNMUTE); 10411564b287SLydia Wang } 104286e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 1043f3db423dSLydia Wang if (spec->codec_type == VT1708S 1044f3db423dSLydia Wang || spec->codec_type == VT1716S) { 10451564b287SLydia Wang /* input = index 1 (AOW3) */ 10461564b287SLydia Wang snd_hda_codec_write( 10471564b287SLydia Wang codec, nid, 0, 10481564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 10491564b287SLydia Wang snd_hda_codec_amp_stereo( 10501564b287SLydia Wang codec, nid, HDA_OUTPUT, 10511564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 10521564b287SLydia Wang } 10531564b287SLydia Wang } 10541564b287SLydia Wang } 10551564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10563e95b9abSLydia Wang set_widgets_power_state(codec); 10571564b287SLydia Wang return 1; 10581564b287SLydia Wang } 10591564b287SLydia Wang 10605f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10611564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10621564b287SLydia Wang .name = "Smart 5.1", 10631564b287SLydia Wang .count = 1, 10641564b287SLydia Wang .info = via_smart51_info, 10651564b287SLydia Wang .get = via_smart51_get, 10661564b287SLydia Wang .put = via_smart51_put, 10671564b287SLydia Wang }; 10681564b287SLydia Wang 10695b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 10705b0cb1d8SJaroslav Kysela { 10715b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10727b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10735b0cb1d8SJaroslav Kysela hda_nid_t nid; 10745b0cb1d8SJaroslav Kysela int i; 10755b0cb1d8SJaroslav Kysela 1076cb34c207SLydia Wang if (!cfg) 1077cb34c207SLydia Wang return 0; 1078cb34c207SLydia Wang if (cfg->line_outs > 2) 1079cb34c207SLydia Wang return 0; 1080cb34c207SLydia Wang 10815f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10825b0cb1d8SJaroslav Kysela if (knew == NULL) 10835b0cb1d8SJaroslav Kysela return -ENOMEM; 10845b0cb1d8SJaroslav Kysela 10857b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10867b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 108786e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 10885f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10897b315bb4STakashi Iwai break; 10905b0cb1d8SJaroslav Kysela } 10915b0cb1d8SJaroslav Kysela } 10925b0cb1d8SJaroslav Kysela 10935b0cb1d8SJaroslav Kysela return 0; 10945b0cb1d8SJaroslav Kysela } 10955b0cb1d8SJaroslav Kysela 1096c577b8a1SJoseph Chan /* capture mixer elements */ 109790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = { 1098c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1099c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1100c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1101c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1102c577b8a1SJoseph Chan { 1103c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1104c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1105c577b8a1SJoseph Chan * So call somewhat different.. 1106c577b8a1SJoseph Chan */ 1107c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1108c577b8a1SJoseph Chan .name = "Input Source", 1109c577b8a1SJoseph Chan .count = 1, 1110c577b8a1SJoseph Chan .info = via_mux_enum_info, 1111c577b8a1SJoseph Chan .get = via_mux_enum_get, 1112c577b8a1SJoseph Chan .put = via_mux_enum_put, 1113c577b8a1SJoseph Chan }, 1114c577b8a1SJoseph Chan { } /* end */ 1115c577b8a1SJoseph Chan }; 1116f5271101SLydia Wang 1117f5271101SLydia Wang /* check AA path's mute statue */ 1118f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1119f5271101SLydia Wang { 1120f5271101SLydia Wang int mute = 1; 1121f5271101SLydia Wang hda_nid_t nid_mixer; 1122f5271101SLydia Wang int start_idx; 1123f5271101SLydia Wang int end_idx; 1124f5271101SLydia Wang int i; 1125f5271101SLydia Wang struct via_spec *spec = codec->spec; 1126f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1127f5271101SLydia Wang switch (spec->codec_type) { 1128f5271101SLydia Wang case VT1708B_8CH: 1129f5271101SLydia Wang case VT1708B_4CH: 1130f5271101SLydia Wang case VT1708S: 1131f3db423dSLydia Wang case VT1716S: 1132f5271101SLydia Wang nid_mixer = 0x16; 1133f5271101SLydia Wang start_idx = 2; 1134f5271101SLydia Wang end_idx = 4; 1135f5271101SLydia Wang break; 1136f5271101SLydia Wang case VT1702: 1137f5271101SLydia Wang nid_mixer = 0x1a; 1138f5271101SLydia Wang start_idx = 1; 1139f5271101SLydia Wang end_idx = 3; 1140f5271101SLydia Wang break; 1141eb7188caSLydia Wang case VT1718S: 1142eb7188caSLydia Wang nid_mixer = 0x21; 1143eb7188caSLydia Wang start_idx = 1; 1144eb7188caSLydia Wang end_idx = 3; 1145eb7188caSLydia Wang break; 114625eaba2fSLydia Wang case VT2002P: 1147ab6734e7SLydia Wang case VT1812: 114811890956SLydia Wang case VT1802: 114925eaba2fSLydia Wang nid_mixer = 0x21; 115025eaba2fSLydia Wang start_idx = 0; 115125eaba2fSLydia Wang end_idx = 2; 115225eaba2fSLydia Wang break; 1153f5271101SLydia Wang default: 1154f5271101SLydia Wang return 0; 1155f5271101SLydia Wang } 1156f5271101SLydia Wang /* check AA path's mute status */ 1157f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1158f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1159f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1160f5271101SLydia Wang int shift = 8 * (i % 4); 1161f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1162f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1163f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1164f5271101SLydia Wang /* check mute status while the pin is connected */ 1165f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1166f5271101SLydia Wang HDA_INPUT, i) >> 7; 1167f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1168f5271101SLydia Wang HDA_INPUT, i) >> 7; 1169f5271101SLydia Wang if (!mute_l || !mute_r) { 1170f5271101SLydia Wang mute = 0; 1171f5271101SLydia Wang break; 1172f5271101SLydia Wang } 1173f5271101SLydia Wang } 1174f5271101SLydia Wang } 1175f5271101SLydia Wang return mute; 1176f5271101SLydia Wang } 1177f5271101SLydia Wang 1178f5271101SLydia Wang /* enter/exit analog low-current mode */ 1179f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1180f5271101SLydia Wang { 1181f5271101SLydia Wang struct via_spec *spec = codec->spec; 1182f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1183f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1184f5271101SLydia Wang unsigned int verb = 0; 1185f5271101SLydia Wang unsigned int parm = 0; 1186f5271101SLydia Wang 1187f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1188f5271101SLydia Wang enable = enable && saved_stream_idle; 1189f5271101SLydia Wang else { 1190f5271101SLydia Wang enable = enable && stream_idle; 1191f5271101SLydia Wang saved_stream_idle = stream_idle; 1192f5271101SLydia Wang } 1193f5271101SLydia Wang 1194f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1195f5271101SLydia Wang switch (spec->codec_type) { 1196f5271101SLydia Wang case VT1708B_8CH: 1197f5271101SLydia Wang case VT1708B_4CH: 1198f5271101SLydia Wang verb = 0xf70; 1199f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1200f5271101SLydia Wang break; 1201f5271101SLydia Wang case VT1708S: 1202eb7188caSLydia Wang case VT1718S: 1203f3db423dSLydia Wang case VT1716S: 1204f5271101SLydia Wang verb = 0xf73; 1205f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1206f5271101SLydia Wang break; 1207f5271101SLydia Wang case VT1702: 1208f5271101SLydia Wang verb = 0xf73; 1209f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1210f5271101SLydia Wang break; 121125eaba2fSLydia Wang case VT2002P: 1212ab6734e7SLydia Wang case VT1812: 121311890956SLydia Wang case VT1802: 121425eaba2fSLydia Wang verb = 0xf93; 121525eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 121625eaba2fSLydia Wang break; 1217f5271101SLydia Wang default: 1218f5271101SLydia Wang return; /* other codecs are not supported */ 1219f5271101SLydia Wang } 1220f5271101SLydia Wang /* send verb */ 1221f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1222f5271101SLydia Wang } 1223f5271101SLydia Wang 1224c577b8a1SJoseph Chan /* 1225c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1226c577b8a1SJoseph Chan */ 122790dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1228c577b8a1SJoseph Chan /* 1229c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1230c577b8a1SJoseph Chan */ 1231c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1232c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1233c577b8a1SJoseph Chan 1234c577b8a1SJoseph Chan 1235f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1236c577b8a1SJoseph Chan * mixer widget 1237c577b8a1SJoseph Chan */ 1238c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1239f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1240f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1241f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1242f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1243f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1244c577b8a1SJoseph Chan 1245c577b8a1SJoseph Chan /* 1246c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1247c577b8a1SJoseph Chan */ 1248c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1249c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1250c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1251c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1252c577b8a1SJoseph Chan 1253bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1254bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1255c577b8a1SJoseph Chan /* PW9 Output enable */ 1256c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1257aa266fccSLydia Wang /* power down jack detect function */ 1258aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1259f7278fd0SJosepch Chan { } 1260c577b8a1SJoseph Chan }; 1261c577b8a1SJoseph Chan 1262c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1263c577b8a1SJoseph Chan struct hda_codec *codec, 1264c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1265c577b8a1SJoseph Chan { 1266c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 126717314379SLydia Wang int idle = substream->pstr->substream_opened == 1 126817314379SLydia Wang && substream->ref_count == 0; 126917314379SLydia Wang analog_low_current_mode(codec, idle); 12709a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12719a08160bSTakashi Iwai hinfo); 1272c577b8a1SJoseph Chan } 1273c577b8a1SJoseph Chan 12740aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 12750aa62aefSHarald Welte unsigned int stream_tag, 12760aa62aefSHarald Welte unsigned int format, 12770aa62aefSHarald Welte struct snd_pcm_substream *substream) 12780aa62aefSHarald Welte { 12790aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12800aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1281dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12820aa62aefSHarald Welte int chs = substream->runtime->channels; 12830aa62aefSHarald Welte int i; 12847c935976SStephen Warren struct hda_spdif_out *spdif = 12857c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12860aa62aefSHarald Welte 12870aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12880aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12890aa62aefSHarald Welte if (chs == 2 && 12900aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12910aa62aefSHarald Welte format) && 12927c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12930aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12940aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12950aa62aefSHarald Welte * be updated */ 12967c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12970aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12980aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12997c935976SStephen Warren spdif->ctls & 13000aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 13010aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13020aa62aefSHarald Welte stream_tag, 0, format); 13030aa62aefSHarald Welte /* turn on again (if needed) */ 13047c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 13050aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 13060aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 13077c935976SStephen Warren spdif->ctls & 0xff); 13080aa62aefSHarald Welte } else { 13090aa62aefSHarald Welte mout->dig_out_used = 0; 13100aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13110aa62aefSHarald Welte 0, 0, 0); 13120aa62aefSHarald Welte } 13130aa62aefSHarald Welte } 13140aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13150aa62aefSHarald Welte 13160aa62aefSHarald Welte /* front */ 13170aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 13180aa62aefSHarald Welte 0, format); 13190aa62aefSHarald Welte 1320eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1321eb7188caSLydia Wang && !spec->hp_independent_mode) 13220aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 13230aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 13240aa62aefSHarald Welte 0, format); 13250aa62aefSHarald Welte 13260aa62aefSHarald Welte /* extra outputs copied from front */ 13270aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13280aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13290aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13300aa62aefSHarald Welte mout->extra_out_nid[i], 13310aa62aefSHarald Welte stream_tag, 0, format); 13320aa62aefSHarald Welte 13330aa62aefSHarald Welte /* surrounds */ 13340aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 13350aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 13360aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13370aa62aefSHarald Welte i * 2, format); 13380aa62aefSHarald Welte else /* copy front */ 13390aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 13400aa62aefSHarald Welte 0, format); 13410aa62aefSHarald Welte } 13420aa62aefSHarald Welte } 13430aa62aefSHarald Welte 13440aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 13450aa62aefSHarald Welte struct hda_codec *codec, 13460aa62aefSHarald Welte unsigned int stream_tag, 13470aa62aefSHarald Welte unsigned int format, 13480aa62aefSHarald Welte struct snd_pcm_substream *substream) 13490aa62aefSHarald Welte { 13500aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13510aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1352dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13530aa62aefSHarald Welte 13540aa62aefSHarald Welte if (substream->number == 0) 13550aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 13560aa62aefSHarald Welte substream); 13570aa62aefSHarald Welte else { 13580aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13590aa62aefSHarald Welte spec->hp_independent_mode) 13600aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13610aa62aefSHarald Welte stream_tag, 0, format); 13620aa62aefSHarald Welte } 13631f2e99feSLydia Wang vt1708_start_hp_work(spec); 13640aa62aefSHarald Welte return 0; 13650aa62aefSHarald Welte } 13660aa62aefSHarald Welte 13670aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13680aa62aefSHarald Welte struct hda_codec *codec, 13690aa62aefSHarald Welte struct snd_pcm_substream *substream) 13700aa62aefSHarald Welte { 13710aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13720aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1373dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13740aa62aefSHarald Welte int i; 13750aa62aefSHarald Welte 13760aa62aefSHarald Welte if (substream->number == 0) { 13770aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13780aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13790aa62aefSHarald Welte 13800aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13810aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13820aa62aefSHarald Welte 0, 0, 0); 13830aa62aefSHarald Welte 13840aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13850aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13860aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13870aa62aefSHarald Welte mout->extra_out_nid[i], 13880aa62aefSHarald Welte 0, 0, 0); 13890aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13900aa62aefSHarald Welte if (mout->dig_out_nid && 13910aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13920aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13930aa62aefSHarald Welte 0, 0, 0); 13940aa62aefSHarald Welte mout->dig_out_used = 0; 13950aa62aefSHarald Welte } 13960aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13970aa62aefSHarald Welte } else { 13980aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13990aa62aefSHarald Welte spec->hp_independent_mode) 14000aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 14010aa62aefSHarald Welte 0, 0, 0); 14020aa62aefSHarald Welte } 14031f2e99feSLydia Wang vt1708_stop_hp_work(spec); 14040aa62aefSHarald Welte return 0; 14050aa62aefSHarald Welte } 14060aa62aefSHarald Welte 1407c577b8a1SJoseph Chan /* 1408c577b8a1SJoseph Chan * Digital out 1409c577b8a1SJoseph Chan */ 1410c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1411c577b8a1SJoseph Chan struct hda_codec *codec, 1412c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1413c577b8a1SJoseph Chan { 1414c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1415c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1416c577b8a1SJoseph Chan } 1417c577b8a1SJoseph Chan 1418c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1419c577b8a1SJoseph Chan struct hda_codec *codec, 1420c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1421c577b8a1SJoseph Chan { 1422c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1423c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1424c577b8a1SJoseph Chan } 1425c577b8a1SJoseph Chan 14265691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 142798aa34c0SHarald Welte struct hda_codec *codec, 142898aa34c0SHarald Welte unsigned int stream_tag, 142998aa34c0SHarald Welte unsigned int format, 143098aa34c0SHarald Welte struct snd_pcm_substream *substream) 143198aa34c0SHarald Welte { 143298aa34c0SHarald Welte struct via_spec *spec = codec->spec; 14339da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 14349da29271STakashi Iwai stream_tag, format, substream); 14359da29271STakashi Iwai } 14365691ec7fSHarald Welte 14379da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 14389da29271STakashi Iwai struct hda_codec *codec, 14399da29271STakashi Iwai struct snd_pcm_substream *substream) 14409da29271STakashi Iwai { 14419da29271STakashi Iwai struct via_spec *spec = codec->spec; 14429da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 144398aa34c0SHarald Welte return 0; 144498aa34c0SHarald Welte } 144598aa34c0SHarald Welte 1446c577b8a1SJoseph Chan /* 1447c577b8a1SJoseph Chan * Analog capture 1448c577b8a1SJoseph Chan */ 1449c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1450c577b8a1SJoseph Chan struct hda_codec *codec, 1451c577b8a1SJoseph Chan unsigned int stream_tag, 1452c577b8a1SJoseph Chan unsigned int format, 1453c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1454c577b8a1SJoseph Chan { 1455c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1456c577b8a1SJoseph Chan 1457c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1458c577b8a1SJoseph Chan stream_tag, 0, format); 1459c577b8a1SJoseph Chan return 0; 1460c577b8a1SJoseph Chan } 1461c577b8a1SJoseph Chan 1462c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1463c577b8a1SJoseph Chan struct hda_codec *codec, 1464c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1465c577b8a1SJoseph Chan { 1466c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1467888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1468c577b8a1SJoseph Chan return 0; 1469c577b8a1SJoseph Chan } 1470c577b8a1SJoseph Chan 147190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_playback = { 14720aa62aefSHarald Welte .substreams = 2, 1473c577b8a1SJoseph Chan .channels_min = 2, 1474c577b8a1SJoseph Chan .channels_max = 8, 1475c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1476c577b8a1SJoseph Chan .ops = { 1477c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14780aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14790aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1480c577b8a1SJoseph Chan }, 1481c577b8a1SJoseph Chan }; 1482c577b8a1SJoseph Chan 148390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1484c873cc25SLydia Wang .substreams = 2, 1485bc9b5623STakashi Iwai .channels_min = 2, 1486bc9b5623STakashi Iwai .channels_max = 8, 1487bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1488bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1489bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1490bc9b5623STakashi Iwai * disable the 24bit format, so far. 1491bc9b5623STakashi Iwai */ 1492bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1493bc9b5623STakashi Iwai .ops = { 1494bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1495c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1496c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1497bc9b5623STakashi Iwai }, 1498bc9b5623STakashi Iwai }; 1499bc9b5623STakashi Iwai 150090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_capture = { 1501c577b8a1SJoseph Chan .substreams = 2, 1502c577b8a1SJoseph Chan .channels_min = 2, 1503c577b8a1SJoseph Chan .channels_max = 2, 1504c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1505c577b8a1SJoseph Chan .ops = { 1506c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1507c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1508c577b8a1SJoseph Chan }, 1509c577b8a1SJoseph Chan }; 1510c577b8a1SJoseph Chan 151190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_playback = { 1512c577b8a1SJoseph Chan .substreams = 1, 1513c577b8a1SJoseph Chan .channels_min = 2, 1514c577b8a1SJoseph Chan .channels_max = 2, 1515c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1516c577b8a1SJoseph Chan .ops = { 1517c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 15186b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 15199da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 15209da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1521c577b8a1SJoseph Chan }, 1522c577b8a1SJoseph Chan }; 1523c577b8a1SJoseph Chan 152490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_capture = { 1525c577b8a1SJoseph Chan .substreams = 1, 1526c577b8a1SJoseph Chan .channels_min = 2, 1527c577b8a1SJoseph Chan .channels_max = 2, 1528c577b8a1SJoseph Chan }; 1529c577b8a1SJoseph Chan 1530c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1531c577b8a1SJoseph Chan { 1532c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 15335b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 153490dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 15355b0cb1d8SJaroslav Kysela int err, i; 1536c577b8a1SJoseph Chan 1537*24088a58STakashi Iwai if (spec->set_widgets_power_state) 1538*24088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 1539*24088a58STakashi Iwai return -ENOMEM; 1540*24088a58STakashi Iwai 1541c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1542c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1543c577b8a1SJoseph Chan if (err < 0) 1544c577b8a1SJoseph Chan return err; 1545c577b8a1SJoseph Chan } 1546c577b8a1SJoseph Chan 1547c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1548c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 154974b654c9SStephen Warren spec->multiout.dig_out_nid, 1550c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1551c577b8a1SJoseph Chan if (err < 0) 1552c577b8a1SJoseph Chan return err; 15539a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15549a08160bSTakashi Iwai &spec->multiout); 15559a08160bSTakashi Iwai if (err < 0) 15569a08160bSTakashi Iwai return err; 15579a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1558c577b8a1SJoseph Chan } 1559c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1560c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1561c577b8a1SJoseph Chan if (err < 0) 1562c577b8a1SJoseph Chan return err; 1563c577b8a1SJoseph Chan } 156417314379SLydia Wang 15655b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15665b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15675b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 156821949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15695b0cb1d8SJaroslav Kysela if (err < 0) 15705b0cb1d8SJaroslav Kysela return err; 15715b0cb1d8SJaroslav Kysela } 15725b0cb1d8SJaroslav Kysela 15735b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15745b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15755b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15765b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15775b0cb1d8SJaroslav Kysela continue; 15785b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15795b0cb1d8SJaroslav Kysela if (kctl == NULL) 15805b0cb1d8SJaroslav Kysela continue; 15815b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15825b0cb1d8SJaroslav Kysela knew->subdevice); 15835b0cb1d8SJaroslav Kysela } 15845b0cb1d8SJaroslav Kysela } 15855b0cb1d8SJaroslav Kysela 158617314379SLydia Wang /* init power states */ 15873e95b9abSLydia Wang set_widgets_power_state(codec); 158817314379SLydia Wang analog_low_current_mode(codec, 1); 158917314379SLydia Wang 1590603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1591c577b8a1SJoseph Chan return 0; 1592c577b8a1SJoseph Chan } 1593c577b8a1SJoseph Chan 1594c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1595c577b8a1SJoseph Chan { 1596c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1597c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1598c577b8a1SJoseph Chan 1599c577b8a1SJoseph Chan codec->num_pcms = 1; 1600c577b8a1SJoseph Chan codec->pcm_info = info; 1601c577b8a1SJoseph Chan 1602c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1603377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1604377ff31aSLydia Wang *(spec->stream_analog_playback); 1605377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1606377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1607c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1608c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1609c577b8a1SJoseph Chan 1610c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1611c577b8a1SJoseph Chan spec->multiout.max_channels; 1612c577b8a1SJoseph Chan 1613c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1614c577b8a1SJoseph Chan codec->num_pcms++; 1615c577b8a1SJoseph Chan info++; 1616c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 16177ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1618c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1619c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1620c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1621c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1622c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1623c577b8a1SJoseph Chan } 1624c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1625c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1626c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1627c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1628c577b8a1SJoseph Chan spec->dig_in_nid; 1629c577b8a1SJoseph Chan } 1630c577b8a1SJoseph Chan } 1631c577b8a1SJoseph Chan 1632c577b8a1SJoseph Chan return 0; 1633c577b8a1SJoseph Chan } 1634c577b8a1SJoseph Chan 1635c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1636c577b8a1SJoseph Chan { 1637c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1638c577b8a1SJoseph Chan 1639c577b8a1SJoseph Chan if (!spec) 1640c577b8a1SJoseph Chan return; 1641c577b8a1SJoseph Chan 1642603c4019STakashi Iwai via_free_kctls(codec); 16431f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1644c577b8a1SJoseph Chan kfree(codec->spec); 1645c577b8a1SJoseph Chan } 1646c577b8a1SJoseph Chan 164769e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 164869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 164969e52a80SHarald Welte { 1650dcf34c8cSLydia Wang unsigned int present = 0; 165169e52a80SHarald Welte struct via_spec *spec = codec->spec; 165269e52a80SHarald Welte 1653d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1654dcf34c8cSLydia Wang 1655dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1656dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 1657dcf34c8cSLydia Wang /* auto mute */ 1658dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 1659dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 1660dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 1661dcf34c8cSLydia Wang /* notify change */ 1662dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 1663dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 1664dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 1665dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 1666dcf34c8cSLydia Wang &id); 1667dcf34c8cSLydia Wang } 166869e52a80SHarald Welte } 166969e52a80SHarald Welte 1670f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1671f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1672f3db423dSLydia Wang { 1673f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1674f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1675f3db423dSLydia Wang 1676f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1677f3db423dSLydia Wang return; 1678f3db423dSLydia Wang 1679d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1680d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1681f3db423dSLydia Wang 1682f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1683f3db423dSLydia Wang if (lineout_present) { 1684f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1685f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 1686f3db423dSLydia Wang return; 1687f3db423dSLydia Wang } 1688f3db423dSLydia Wang 1689d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1690f3db423dSLydia Wang 1691f3db423dSLydia Wang if (!spec->hp_independent_mode) 1692f3db423dSLydia Wang snd_hda_codec_amp_stereo( 1693f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1694f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 1695f3db423dSLydia Wang } 1696f3db423dSLydia Wang 169769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 169869e52a80SHarald Welte { 169969e52a80SHarald Welte unsigned int gpio_data; 170069e52a80SHarald Welte unsigned int vol_counter; 170169e52a80SHarald Welte unsigned int vol; 170269e52a80SHarald Welte unsigned int master_vol; 170369e52a80SHarald Welte 170469e52a80SHarald Welte struct via_spec *spec = codec->spec; 170569e52a80SHarald Welte 170669e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 170769e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 170869e52a80SHarald Welte 170969e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 171069e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 171169e52a80SHarald Welte 171269e52a80SHarald Welte vol = vol_counter & 0x1F; 171369e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 171469e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 171569e52a80SHarald Welte AC_AMP_GET_INPUT); 171669e52a80SHarald Welte 171769e52a80SHarald Welte if (gpio_data == 0x02) { 171869e52a80SHarald Welte /* unmute line out */ 171969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 172069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 172169e52a80SHarald Welte 172269e52a80SHarald Welte if (vol_counter & 0x20) { 172369e52a80SHarald Welte /* decrease volume */ 172469e52a80SHarald Welte if (vol > master_vol) 172569e52a80SHarald Welte vol = master_vol; 172669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 172769e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 172869e52a80SHarald Welte master_vol-vol); 172969e52a80SHarald Welte } else { 173069e52a80SHarald Welte /* increase volume */ 173169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 173269e52a80SHarald Welte HDA_AMP_VOLMASK, 173369e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 173469e52a80SHarald Welte (master_vol+vol)); 173569e52a80SHarald Welte } 173669e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 173769e52a80SHarald Welte /* mute line out */ 173869e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 173969e52a80SHarald Welte spec->autocfg.line_out_pins[0], 174069e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 174169e52a80SHarald Welte HDA_AMP_MUTE); 174269e52a80SHarald Welte } 174369e52a80SHarald Welte } 174469e52a80SHarald Welte 174525eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 174625eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 174725eaba2fSLydia Wang { 174825eaba2fSLydia Wang unsigned int hp_present; 174925eaba2fSLydia Wang struct via_spec *spec = codec->spec; 175025eaba2fSLydia Wang 175127439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 175225eaba2fSLydia Wang return; 175325eaba2fSLydia Wang 1754d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 175525eaba2fSLydia Wang 175625eaba2fSLydia Wang if (!spec->hp_independent_mode) { 175725eaba2fSLydia Wang struct snd_ctl_elem_id id; 175825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 175925eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 176025eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 176125eaba2fSLydia Wang /* notify change */ 176225eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 176325eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 176425eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 176525eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 176625eaba2fSLydia Wang &id); 176725eaba2fSLydia Wang } 176825eaba2fSLydia Wang } 176925eaba2fSLydia Wang 177025eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 177125eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 177225eaba2fSLydia Wang { 177301a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 177401a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 177501a1796bSakpm@linux-foundation.org */ 177601a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 177725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 177825eaba2fSLydia Wang int i; 177925eaba2fSLydia Wang 178025eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 178125eaba2fSLydia Wang return; 178225eaba2fSLydia Wang 1783d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 178425eaba2fSLydia Wang 1785d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 178625eaba2fSLydia Wang 178725eaba2fSLydia Wang if (!spec->hp_independent_mode) { 178825eaba2fSLydia Wang /* Mute Line-Outs */ 178925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 179025eaba2fSLydia Wang snd_hda_codec_amp_stereo( 179125eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 179225eaba2fSLydia Wang HDA_OUTPUT, 0, 179325eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 179425eaba2fSLydia Wang if (hp_present) 179525eaba2fSLydia Wang present = hp_present; 179625eaba2fSLydia Wang } 179725eaba2fSLydia Wang /* Speakers */ 179825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 179925eaba2fSLydia Wang snd_hda_codec_amp_stereo( 180025eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 180125eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 180225eaba2fSLydia Wang } 180325eaba2fSLydia Wang 180425eaba2fSLydia Wang 180569e52a80SHarald Welte /* unsolicited event for jack sensing */ 180669e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 180769e52a80SHarald Welte unsigned int res) 180869e52a80SHarald Welte { 180969e52a80SHarald Welte res >>= 26; 1810ec7e7e42SLydia Wang 1811a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 18123e95b9abSLydia Wang set_widgets_power_state(codec); 1813ec7e7e42SLydia Wang 1814ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1815ec7e7e42SLydia Wang 1816ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1817ec7e7e42SLydia Wang via_hp_automute(codec); 1818ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1819ec7e7e42SLydia Wang via_gpio_control(codec); 1820ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1821f3db423dSLydia Wang via_mono_automute(codec); 1822ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 182325eaba2fSLydia Wang via_speaker_automute(codec); 1824ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 182525eaba2fSLydia Wang via_hp_bind_automute(codec); 182669e52a80SHarald Welte } 182769e52a80SHarald Welte 1828c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1829c577b8a1SJoseph Chan { 1830c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 183169e52a80SHarald Welte int i; 183269e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 183369e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 183469e52a80SHarald Welte 1835f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1836f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 183755d1d6c1STakashi Iwai if (spec->dig_in_pin) { 183855d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1839f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 184012b74c80STakashi Iwai PIN_OUT); 184155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1842f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1843f7278fd0SJosepch Chan } 184412b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 184512b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 184612b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1847f7278fd0SJosepch Chan 18489da29271STakashi Iwai /* assign slave outs */ 18499da29271STakashi Iwai if (spec->slave_dig_outs[0]) 18509da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 18515691ec7fSHarald Welte 1852c577b8a1SJoseph Chan return 0; 1853c577b8a1SJoseph Chan } 1854c577b8a1SJoseph Chan 18551f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18561f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18571f2e99feSLydia Wang { 18581f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18591f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18601f2e99feSLydia Wang return 0; 18611f2e99feSLydia Wang } 18621f2e99feSLydia Wang #endif 18631f2e99feSLydia Wang 1864cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1865cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1866cb53c626STakashi Iwai { 1867cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1868cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1869cb53c626STakashi Iwai } 1870cb53c626STakashi Iwai #endif 1871cb53c626STakashi Iwai 1872c577b8a1SJoseph Chan /* 1873c577b8a1SJoseph Chan */ 187490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1875c577b8a1SJoseph Chan .build_controls = via_build_controls, 1876c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1877c577b8a1SJoseph Chan .init = via_init, 1878c577b8a1SJoseph Chan .free = via_free, 18791f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18801f2e99feSLydia Wang .suspend = via_suspend, 18811f2e99feSLydia Wang #endif 1882cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1883cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1884cb53c626STakashi Iwai #endif 1885c577b8a1SJoseph Chan }; 1886c577b8a1SJoseph Chan 1887c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 1888c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 1889c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1890c577b8a1SJoseph Chan { 1891c577b8a1SJoseph Chan int i; 1892c577b8a1SJoseph Chan hda_nid_t nid; 1893c577b8a1SJoseph Chan 1894c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 1895c577b8a1SJoseph Chan 1896c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1897c577b8a1SJoseph Chan 1898c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 1899c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1900c577b8a1SJoseph Chan if (nid) { 1901c577b8a1SJoseph Chan /* config dac list */ 1902c577b8a1SJoseph Chan switch (i) { 1903c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1904dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 1905c577b8a1SJoseph Chan break; 1906c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1907dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 1908c577b8a1SJoseph Chan break; 1909c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1910dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 1911c577b8a1SJoseph Chan break; 1912c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1913dda14410STakashi Iwai spec->private_dac_nids[i] = 0x13; 1914c577b8a1SJoseph Chan break; 1915c577b8a1SJoseph Chan } 1916c577b8a1SJoseph Chan } 1917c577b8a1SJoseph Chan } 1918c577b8a1SJoseph Chan 1919c577b8a1SJoseph Chan return 0; 1920c577b8a1SJoseph Chan } 1921c577b8a1SJoseph Chan 1922c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1923c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 1924c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1925c577b8a1SJoseph Chan { 1926c577b8a1SJoseph Chan char name[32]; 1927ea734963STakashi Iwai static const char * const chname[4] = { 1928ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 1929ea734963STakashi Iwai }; 19309645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 1931c577b8a1SJoseph Chan int i, err; 1932c577b8a1SJoseph Chan 1933c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1934c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1935c577b8a1SJoseph Chan 1936c577b8a1SJoseph Chan if (!nid) 1937c577b8a1SJoseph Chan continue; 1938c577b8a1SJoseph Chan 19399645c203SLydia Wang nid_vol = nid_vols[i]; 1940c577b8a1SJoseph Chan 1941c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1942c577b8a1SJoseph Chan /* Center/LFE */ 1943c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1944c577b8a1SJoseph Chan "Center Playback Volume", 1945f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1946f7278fd0SJosepch Chan HDA_OUTPUT)); 1947c577b8a1SJoseph Chan if (err < 0) 1948c577b8a1SJoseph Chan return err; 1949c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1950c577b8a1SJoseph Chan "LFE Playback Volume", 1951f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1952f7278fd0SJosepch Chan HDA_OUTPUT)); 1953c577b8a1SJoseph Chan if (err < 0) 1954c577b8a1SJoseph Chan return err; 1955c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1956c577b8a1SJoseph Chan "Center Playback Switch", 1957f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1958f7278fd0SJosepch Chan HDA_OUTPUT)); 1959c577b8a1SJoseph Chan if (err < 0) 1960c577b8a1SJoseph Chan return err; 1961c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1962c577b8a1SJoseph Chan "LFE Playback Switch", 1963f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1964f7278fd0SJosepch Chan HDA_OUTPUT)); 1965c577b8a1SJoseph Chan if (err < 0) 1966c577b8a1SJoseph Chan return err; 1967c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 1968c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1969c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1970c577b8a1SJoseph Chan "Master Front Playback Volume", 19719645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1972f7278fd0SJosepch Chan HDA_INPUT)); 1973c577b8a1SJoseph Chan if (err < 0) 1974c577b8a1SJoseph Chan return err; 1975c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1976c577b8a1SJoseph Chan "Master Front Playback Switch", 19779645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1978f7278fd0SJosepch Chan HDA_INPUT)); 1979c577b8a1SJoseph Chan if (err < 0) 1980c577b8a1SJoseph Chan return err; 1981c577b8a1SJoseph Chan 1982c577b8a1SJoseph Chan /* add control to PW3 */ 1983c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1984c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1985f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1986f7278fd0SJosepch Chan HDA_OUTPUT)); 1987c577b8a1SJoseph Chan if (err < 0) 1988c577b8a1SJoseph Chan return err; 1989c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1990c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1991f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1992f7278fd0SJosepch Chan HDA_OUTPUT)); 1993c577b8a1SJoseph Chan if (err < 0) 1994c577b8a1SJoseph Chan return err; 1995c577b8a1SJoseph Chan } else { 1996c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1997c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1998f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1999f7278fd0SJosepch Chan HDA_OUTPUT)); 2000c577b8a1SJoseph Chan if (err < 0) 2001c577b8a1SJoseph Chan return err; 2002c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2003c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2004f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2005f7278fd0SJosepch Chan HDA_OUTPUT)); 2006c577b8a1SJoseph Chan if (err < 0) 2007c577b8a1SJoseph Chan return err; 2008c577b8a1SJoseph Chan } 2009c577b8a1SJoseph Chan } 2010c577b8a1SJoseph Chan 2011c577b8a1SJoseph Chan return 0; 2012c577b8a1SJoseph Chan } 2013c577b8a1SJoseph Chan 20140aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 20150aa62aefSHarald Welte { 20160aa62aefSHarald Welte int i; 20170aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 2018ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 20190aa62aefSHarald Welte 20200aa62aefSHarald Welte /* for hp mode select */ 202110a20af7STakashi Iwai for (i = 0; texts[i]; i++) 202210a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 20230aa62aefSHarald Welte 20240aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 20250aa62aefSHarald Welte } 20260aa62aefSHarald Welte 2027c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2028c577b8a1SJoseph Chan { 2029c577b8a1SJoseph Chan int err; 2030c577b8a1SJoseph Chan 2031c577b8a1SJoseph Chan if (!pin) 2032c577b8a1SJoseph Chan return 0; 2033c577b8a1SJoseph Chan 2034c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 2035cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2036c577b8a1SJoseph Chan 2037c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2038c577b8a1SJoseph Chan "Headphone Playback Volume", 2039c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2040c577b8a1SJoseph Chan if (err < 0) 2041c577b8a1SJoseph Chan return err; 2042c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2043c577b8a1SJoseph Chan "Headphone Playback Switch", 2044c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2045c577b8a1SJoseph Chan if (err < 0) 2046c577b8a1SJoseph Chan return err; 2047c577b8a1SJoseph Chan 20480aa62aefSHarald Welte create_hp_imux(spec); 20490aa62aefSHarald Welte 2050c577b8a1SJoseph Chan return 0; 2051c577b8a1SJoseph Chan } 2052c577b8a1SJoseph Chan 2053c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 205410a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, 2055f3268512STakashi Iwai const struct auto_pin_cfg *cfg, 2056f3268512STakashi Iwai hda_nid_t cap_nid, 205790dd48a1STakashi Iwai const hda_nid_t pin_idxs[], 205890dd48a1STakashi Iwai int num_idxs) 2059c577b8a1SJoseph Chan { 206010a20af7STakashi Iwai struct via_spec *spec = codec->spec; 20610aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 20627b315bb4STakashi Iwai int i, err, idx, type, type_idx = 0; 2063c577b8a1SJoseph Chan 2064c577b8a1SJoseph Chan /* for internal loopback recording select */ 2065f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2066f3268512STakashi Iwai if (pin_idxs[idx] == 0xff) { 206710a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2068f3268512STakashi Iwai break; 2069f3268512STakashi Iwai } 2070f3268512STakashi Iwai } 2071c577b8a1SJoseph Chan 20727b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 207310a20af7STakashi Iwai const char *label; 20747b315bb4STakashi Iwai type = cfg->inputs[i].type; 2075f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 20767b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2077c577b8a1SJoseph Chan break; 2078f3268512STakashi Iwai if (idx >= num_idxs) 2079f3268512STakashi Iwai continue; 20807b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 20817b315bb4STakashi Iwai type_idx++; 20827b315bb4STakashi Iwai else 20837b315bb4STakashi Iwai type_idx = 0; 208410a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 208516922281SLydia Wang if (spec->codec_type == VT1708S || 208616922281SLydia Wang spec->codec_type == VT1702 || 208716922281SLydia Wang spec->codec_type == VT1716S) 208816922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 208916922281SLydia Wang idx+1, cap_nid); 209016922281SLydia Wang else 209116922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 209216922281SLydia Wang idx, cap_nid); 2093c577b8a1SJoseph Chan if (err < 0) 2094c577b8a1SJoseph Chan return err; 209510a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2096c577b8a1SJoseph Chan } 2097c577b8a1SJoseph Chan return 0; 2098c577b8a1SJoseph Chan } 2099c577b8a1SJoseph Chan 2100f3268512STakashi Iwai /* create playback/capture controls for input pins */ 210110a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, 2102f3268512STakashi Iwai const struct auto_pin_cfg *cfg) 2103f3268512STakashi Iwai { 210490dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0xff, 0x24, 0x1d, 0x1e, 0x21 }; 210510a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x17, pin_idxs, 2106f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2107f3268512STakashi Iwai } 2108f3268512STakashi Iwai 2109cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 211090dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2111cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2112cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2113cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2114cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2115cb53c626STakashi Iwai { } /* end */ 2116cb53c626STakashi Iwai }; 2117cb53c626STakashi Iwai #endif 2118cb53c626STakashi Iwai 211976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 212076d9b0ddSHarald Welte { 212176d9b0ddSHarald Welte unsigned int def_conf; 212276d9b0ddSHarald Welte unsigned char seqassoc; 212376d9b0ddSHarald Welte 21242f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 212576d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 212676d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 212782ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 212882ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 212976d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 21302f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 213176d9b0ddSHarald Welte } 213276d9b0ddSHarald Welte 213376d9b0ddSHarald Welte return; 213476d9b0ddSHarald Welte } 213576d9b0ddSHarald Welte 21361f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 21371f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21381f2e99feSLydia Wang { 21391f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21401f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21411f2e99feSLydia Wang 21421f2e99feSLydia Wang if (spec->codec_type != VT1708) 21431f2e99feSLydia Wang return 0; 21441f2e99feSLydia Wang spec->vt1708_jack_detectect = 21451f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 21461f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 21471f2e99feSLydia Wang return 0; 21481f2e99feSLydia Wang } 21491f2e99feSLydia Wang 21501f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 21511f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21521f2e99feSLydia Wang { 21531f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21541f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21551f2e99feSLydia Wang int change; 21561f2e99feSLydia Wang 21571f2e99feSLydia Wang if (spec->codec_type != VT1708) 21581f2e99feSLydia Wang return 0; 21591f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 21601f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 21611f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 21621f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 21631f2e99feSLydia Wang mute_aa_path(codec, 1); 21641f2e99feSLydia Wang notify_aa_path_ctls(codec); 21651f2e99feSLydia Wang } 21661f2e99feSLydia Wang return change; 21671f2e99feSLydia Wang } 21681f2e99feSLydia Wang 216990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detectect[] = { 21701f2e99feSLydia Wang { 21711f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 21721f2e99feSLydia Wang .name = "Jack Detect", 21731f2e99feSLydia Wang .count = 1, 21741f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 21751f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 21761f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 21771f2e99feSLydia Wang }, 21781f2e99feSLydia Wang {} /* end */ 21791f2e99feSLydia Wang }; 21801f2e99feSLydia Wang 2181c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2182c577b8a1SJoseph Chan { 2183c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2184c577b8a1SJoseph Chan int err; 2185c577b8a1SJoseph Chan 218676d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 218776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 218876d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 218976d9b0ddSHarald Welte 2190c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2191c577b8a1SJoseph Chan if (err < 0) 2192c577b8a1SJoseph Chan return err; 2193c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2194c577b8a1SJoseph Chan if (err < 0) 2195c577b8a1SJoseph Chan return err; 2196c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2197c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2198c577b8a1SJoseph Chan 2199c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2200c577b8a1SJoseph Chan if (err < 0) 2201c577b8a1SJoseph Chan return err; 2202c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2203c577b8a1SJoseph Chan if (err < 0) 2204c577b8a1SJoseph Chan return err; 220510a20af7STakashi Iwai err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); 2206c577b8a1SJoseph Chan if (err < 0) 2207c577b8a1SJoseph Chan return err; 22081f2e99feSLydia Wang /* add jack detect on/off control */ 22091f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 22101f2e99feSLydia Wang if (err < 0) 22111f2e99feSLydia Wang return err; 2212c577b8a1SJoseph Chan 2213c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2214c577b8a1SJoseph Chan 22150852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2216c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 221755d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2218c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2219c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2220c577b8a1SJoseph Chan 2221603c4019STakashi Iwai if (spec->kctls.list) 2222603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2223c577b8a1SJoseph Chan 222469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2225c577b8a1SJoseph Chan 22260aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 22270aa62aefSHarald Welte 2228f8fdd495SHarald Welte if (spec->hp_mux) 22293d83e577STakashi Iwai via_hp_build(codec); 2230c577b8a1SJoseph Chan 22315b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2232c577b8a1SJoseph Chan return 1; 2233c577b8a1SJoseph Chan } 2234c577b8a1SJoseph Chan 2235c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2236c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2237c577b8a1SJoseph Chan { 223825eaba2fSLydia Wang struct via_spec *spec = codec->spec; 223925eaba2fSLydia Wang 2240c577b8a1SJoseph Chan via_init(codec); 2241c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2242c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2243c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 224411890956SLydia Wang 224511890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 224625eaba2fSLydia Wang via_hp_bind_automute(codec); 224725eaba2fSLydia Wang } else { 224825eaba2fSLydia Wang via_hp_automute(codec); 224925eaba2fSLydia Wang via_speaker_automute(codec); 225025eaba2fSLydia Wang } 225125eaba2fSLydia Wang 2252c577b8a1SJoseph Chan return 0; 2253c577b8a1SJoseph Chan } 2254c577b8a1SJoseph Chan 22551f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 22561f2e99feSLydia Wang { 22571f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 22581f2e99feSLydia Wang vt1708_hp_work.work); 22591f2e99feSLydia Wang if (spec->codec_type != VT1708) 22601f2e99feSLydia Wang return; 22611f2e99feSLydia Wang /* if jack state toggled */ 22621f2e99feSLydia Wang if (spec->vt1708_hp_present 2263d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 22641f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 22651f2e99feSLydia Wang via_hp_automute(spec->codec); 22661f2e99feSLydia Wang } 22671f2e99feSLydia Wang vt1708_start_hp_work(spec); 22681f2e99feSLydia Wang } 22691f2e99feSLydia Wang 2270337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2271337b9d02STakashi Iwai { 2272337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2273337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2274337b9d02STakashi Iwai unsigned int type; 2275337b9d02STakashi Iwai int i, n; 2276337b9d02STakashi Iwai 2277337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2278337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2279337b9d02STakashi Iwai while (nid) { 2280a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22811c55d521STakashi Iwai if (type == AC_WID_PIN) 22821c55d521STakashi Iwai break; 2283337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2284337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2285337b9d02STakashi Iwai if (n <= 0) 2286337b9d02STakashi Iwai break; 2287337b9d02STakashi Iwai if (n > 1) { 2288337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2289337b9d02STakashi Iwai break; 2290337b9d02STakashi Iwai } 2291337b9d02STakashi Iwai nid = conn[0]; 2292337b9d02STakashi Iwai } 2293337b9d02STakashi Iwai } 22941c55d521STakashi Iwai return 0; 2295337b9d02STakashi Iwai } 2296337b9d02STakashi Iwai 2297c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2298c577b8a1SJoseph Chan { 2299c577b8a1SJoseph Chan struct via_spec *spec; 2300c577b8a1SJoseph Chan int err; 2301c577b8a1SJoseph Chan 2302c577b8a1SJoseph Chan /* create a codec specific record */ 23035b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2304c577b8a1SJoseph Chan if (spec == NULL) 2305c577b8a1SJoseph Chan return -ENOMEM; 2306c577b8a1SJoseph Chan 2307c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2308c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2309c577b8a1SJoseph Chan if (err < 0) { 2310c577b8a1SJoseph Chan via_free(codec); 2311c577b8a1SJoseph Chan return err; 2312c577b8a1SJoseph Chan } else if (!err) { 2313c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2314c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2315c577b8a1SJoseph Chan } 2316c577b8a1SJoseph Chan 2317c577b8a1SJoseph Chan 2318c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2319c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2320bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2321bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2322bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2323c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2324c577b8a1SJoseph Chan 2325c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2326c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2327c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2328c577b8a1SJoseph Chan 2329c577b8a1SJoseph Chan 2330c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2331c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2332c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 23330f67a611STakashi Iwai get_mux_nids(codec); 2334c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2335c577b8a1SJoseph Chan spec->num_mixers++; 2336c577b8a1SJoseph Chan } 2337c577b8a1SJoseph Chan 2338c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2339c577b8a1SJoseph Chan 2340c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2341cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2342cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2343cb53c626STakashi Iwai #endif 23441f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2345c577b8a1SJoseph Chan return 0; 2346c577b8a1SJoseph Chan } 2347c577b8a1SJoseph Chan 2348c577b8a1SJoseph Chan /* capture mixer elements */ 234990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = { 2350c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2351c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2352c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2353c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2354c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2355c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2356c577b8a1SJoseph Chan { 2357c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2358c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2359c577b8a1SJoseph Chan * So call somewhat different.. 2360c577b8a1SJoseph Chan */ 2361c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2362c577b8a1SJoseph Chan .name = "Input Source", 2363c577b8a1SJoseph Chan .count = 1, 2364c577b8a1SJoseph Chan .info = via_mux_enum_info, 2365c577b8a1SJoseph Chan .get = via_mux_enum_get, 2366c577b8a1SJoseph Chan .put = via_mux_enum_put, 2367c577b8a1SJoseph Chan }, 2368c577b8a1SJoseph Chan { } /* end */ 2369c577b8a1SJoseph Chan }; 2370c577b8a1SJoseph Chan 237190dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2372a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2373a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 237469e52a80SHarald Welte { } 237569e52a80SHarald Welte }; 237669e52a80SHarald Welte 2377c577b8a1SJoseph Chan /* 2378c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2379c577b8a1SJoseph Chan */ 238090dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2381c577b8a1SJoseph Chan /* 2382c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2383c577b8a1SJoseph Chan */ 2384c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2385c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2386c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2387c577b8a1SJoseph Chan 2388c577b8a1SJoseph Chan 2389f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2390c577b8a1SJoseph Chan * mixer widget 2391c577b8a1SJoseph Chan */ 2392c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2393f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2394f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2395f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2396f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2397f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2398c577b8a1SJoseph Chan 2399c577b8a1SJoseph Chan /* 2400c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2401c577b8a1SJoseph Chan */ 2402c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2403c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2404c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2405c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2406c577b8a1SJoseph Chan 2407c577b8a1SJoseph Chan /* 2408c577b8a1SJoseph Chan * Unmute PW3 and PW4 2409c577b8a1SJoseph Chan */ 2410c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2411c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2412c577b8a1SJoseph Chan 2413bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2414bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2415c577b8a1SJoseph Chan /* PW9 Output enable */ 2416c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2417c577b8a1SJoseph Chan { } 2418c577b8a1SJoseph Chan }; 2419c577b8a1SJoseph Chan 242090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2421c577b8a1SJoseph Chan .substreams = 1, 2422c577b8a1SJoseph Chan .channels_min = 2, 2423c577b8a1SJoseph Chan .channels_max = 10, 2424c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2425c577b8a1SJoseph Chan .ops = { 2426c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2427c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2428c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2429c577b8a1SJoseph Chan }, 2430c577b8a1SJoseph Chan }; 2431c577b8a1SJoseph Chan 243290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2433c577b8a1SJoseph Chan .substreams = 1, 2434c577b8a1SJoseph Chan .channels_min = 2, 2435c577b8a1SJoseph Chan .channels_max = 6, 2436c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2437c577b8a1SJoseph Chan .ops = { 2438c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2439c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2440c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2441c577b8a1SJoseph Chan }, 2442c577b8a1SJoseph Chan }; 2443c577b8a1SJoseph Chan 244490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_analog_capture = { 2445c577b8a1SJoseph Chan .substreams = 2, 2446c577b8a1SJoseph Chan .channels_min = 2, 2447c577b8a1SJoseph Chan .channels_max = 2, 2448c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2449c577b8a1SJoseph Chan .ops = { 2450c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2451c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2452c577b8a1SJoseph Chan }, 2453c577b8a1SJoseph Chan }; 2454c577b8a1SJoseph Chan 245590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_playback = { 2456c577b8a1SJoseph Chan .substreams = 1, 2457c577b8a1SJoseph Chan .channels_min = 2, 2458c577b8a1SJoseph Chan .channels_max = 2, 2459c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2460c577b8a1SJoseph Chan .ops = { 2461c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2462c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2463c577b8a1SJoseph Chan }, 2464c577b8a1SJoseph Chan }; 2465c577b8a1SJoseph Chan 246690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_capture = { 2467c577b8a1SJoseph Chan .substreams = 1, 2468c577b8a1SJoseph Chan .channels_min = 2, 2469c577b8a1SJoseph Chan .channels_max = 2, 2470c577b8a1SJoseph Chan }; 2471c577b8a1SJoseph Chan 2472c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2473c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2474c577b8a1SJoseph Chan { 2475c577b8a1SJoseph Chan int i; 2476c577b8a1SJoseph Chan hda_nid_t nid; 2477c577b8a1SJoseph Chan 2478c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2479c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2480c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2481c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2482c577b8a1SJoseph Chan 2483c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2484c577b8a1SJoseph Chan 2485c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2486c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2487c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2488c577b8a1SJoseph Chan if (nid) { 2489c577b8a1SJoseph Chan /* config dac list */ 2490c577b8a1SJoseph Chan switch (i) { 2491c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2492c577b8a1SJoseph Chan /* AOW0 */ 2493dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 2494c577b8a1SJoseph Chan break; 2495c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2496c577b8a1SJoseph Chan /* AOW2 */ 2497dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 2498c577b8a1SJoseph Chan break; 2499c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2500c577b8a1SJoseph Chan /* AOW3 */ 2501dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 2502c577b8a1SJoseph Chan break; 2503c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2504c577b8a1SJoseph Chan /* AOW1 */ 2505dda14410STakashi Iwai spec->private_dac_nids[i] = 0x27; 2506c577b8a1SJoseph Chan break; 2507c577b8a1SJoseph Chan default: 2508c577b8a1SJoseph Chan break; 2509c577b8a1SJoseph Chan } 2510c577b8a1SJoseph Chan } 2511c577b8a1SJoseph Chan } 2512dda14410STakashi Iwai spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2513c577b8a1SJoseph Chan 2514c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2515c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2516c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2517c577b8a1SJoseph Chan if (nid) { 2518c577b8a1SJoseph Chan /* config dac list */ 2519c577b8a1SJoseph Chan switch (i) { 2520c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2521c577b8a1SJoseph Chan /* AOW0 */ 2522dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 2523c577b8a1SJoseph Chan break; 2524c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2525c577b8a1SJoseph Chan /* AOW2 */ 2526dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 2527c577b8a1SJoseph Chan break; 2528c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2529c577b8a1SJoseph Chan /* AOW1 */ 2530dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 2531c577b8a1SJoseph Chan break; 2532c577b8a1SJoseph Chan default: 2533c577b8a1SJoseph Chan break; 2534c577b8a1SJoseph Chan } 2535c577b8a1SJoseph Chan } 2536c577b8a1SJoseph Chan } 2537c577b8a1SJoseph Chan } 2538c577b8a1SJoseph Chan 2539c577b8a1SJoseph Chan return 0; 2540c577b8a1SJoseph Chan } 2541c577b8a1SJoseph Chan 2542c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2543c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2544c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2545c577b8a1SJoseph Chan { 2546c577b8a1SJoseph Chan char name[32]; 2547ea734963STakashi Iwai static const char * const chname[4] = { 2548ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2549ea734963STakashi Iwai }; 25504483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2551c577b8a1SJoseph Chan int i, err; 2552c577b8a1SJoseph Chan 2553c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2554c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2555c577b8a1SJoseph Chan 2556c577b8a1SJoseph Chan if (!nid) 2557c577b8a1SJoseph Chan continue; 2558c577b8a1SJoseph Chan 25594483a2f5SLydia Wang nid_vol = nid_vols[i]; 25604483a2f5SLydia Wang 2561c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2562c577b8a1SJoseph Chan /* Center/LFE */ 2563c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2564c577b8a1SJoseph Chan "Center Playback Volume", 25654483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2566f7278fd0SJosepch Chan HDA_OUTPUT)); 2567c577b8a1SJoseph Chan if (err < 0) 2568c577b8a1SJoseph Chan return err; 2569c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2570c577b8a1SJoseph Chan "LFE Playback Volume", 25714483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2572f7278fd0SJosepch Chan HDA_OUTPUT)); 2573c577b8a1SJoseph Chan if (err < 0) 2574c577b8a1SJoseph Chan return err; 2575c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2576c577b8a1SJoseph Chan "Center Playback Switch", 25774483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2578f7278fd0SJosepch Chan HDA_OUTPUT)); 2579c577b8a1SJoseph Chan if (err < 0) 2580c577b8a1SJoseph Chan return err; 2581c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2582c577b8a1SJoseph Chan "LFE Playback Switch", 25834483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2584f7278fd0SJosepch Chan HDA_OUTPUT)); 2585c577b8a1SJoseph Chan if (err < 0) 2586c577b8a1SJoseph Chan return err; 2587c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 25884483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2589c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2590c577b8a1SJoseph Chan "Master Front Playback Volume", 25914483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2592f7278fd0SJosepch Chan HDA_INPUT)); 2593c577b8a1SJoseph Chan if (err < 0) 2594c577b8a1SJoseph Chan return err; 2595c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2596c577b8a1SJoseph Chan "Master Front Playback Switch", 25974483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2598f7278fd0SJosepch Chan HDA_INPUT)); 2599c577b8a1SJoseph Chan if (err < 0) 2600c577b8a1SJoseph Chan return err; 2601c577b8a1SJoseph Chan 2602c577b8a1SJoseph Chan /* add control to PW3 */ 2603c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2604c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2605f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2606f7278fd0SJosepch Chan HDA_OUTPUT)); 2607c577b8a1SJoseph Chan if (err < 0) 2608c577b8a1SJoseph Chan return err; 2609c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2610c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2611f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2612f7278fd0SJosepch Chan HDA_OUTPUT)); 2613c577b8a1SJoseph Chan if (err < 0) 2614c577b8a1SJoseph Chan return err; 2615c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2616c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2617c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 26184483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2619f7278fd0SJosepch Chan HDA_OUTPUT)); 2620c577b8a1SJoseph Chan if (err < 0) 2621c577b8a1SJoseph Chan return err; 2622c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2623c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 26244483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2625f7278fd0SJosepch Chan HDA_OUTPUT)); 2626c577b8a1SJoseph Chan if (err < 0) 2627c577b8a1SJoseph Chan return err; 2628c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2629c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2630c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 26314483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2632f7278fd0SJosepch Chan HDA_OUTPUT)); 2633c577b8a1SJoseph Chan if (err < 0) 2634c577b8a1SJoseph Chan return err; 2635c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2636c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 26374483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2638f7278fd0SJosepch Chan HDA_OUTPUT)); 2639c577b8a1SJoseph Chan if (err < 0) 2640c577b8a1SJoseph Chan return err; 2641c577b8a1SJoseph Chan } 2642c577b8a1SJoseph Chan } 2643c577b8a1SJoseph Chan 2644c577b8a1SJoseph Chan return 0; 2645c577b8a1SJoseph Chan } 2646c577b8a1SJoseph Chan 2647c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2648c577b8a1SJoseph Chan { 2649c577b8a1SJoseph Chan int err; 2650c577b8a1SJoseph Chan 2651c577b8a1SJoseph Chan if (!pin) 2652c577b8a1SJoseph Chan return 0; 2653c577b8a1SJoseph Chan 2654c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2655c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2656c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2657c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2658cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2659c577b8a1SJoseph Chan 2660c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2661c577b8a1SJoseph Chan "Headphone Playback Volume", 2662c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2663c577b8a1SJoseph Chan if (err < 0) 2664c577b8a1SJoseph Chan return err; 2665c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2666c577b8a1SJoseph Chan "Headphone Playback Switch", 2667c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2668c577b8a1SJoseph Chan if (err < 0) 2669c577b8a1SJoseph Chan return err; 2670c577b8a1SJoseph Chan 2671c577b8a1SJoseph Chan return 0; 2672c577b8a1SJoseph Chan } 2673c577b8a1SJoseph Chan 2674c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 267510a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, 2676c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2677c577b8a1SJoseph Chan { 267890dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0xff, 0x23, 0x1d, 0x1e, 0x21 }; 267910a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x18, pin_idxs, 2680f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 2681c577b8a1SJoseph Chan } 2682c577b8a1SJoseph Chan 2683c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2684c577b8a1SJoseph Chan { 2685c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2686c577b8a1SJoseph Chan int err; 2687c577b8a1SJoseph Chan 2688c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2689c577b8a1SJoseph Chan if (err < 0) 2690c577b8a1SJoseph Chan return err; 2691c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2692c577b8a1SJoseph Chan if (err < 0) 2693c577b8a1SJoseph Chan return err; 2694c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2695c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2696c577b8a1SJoseph Chan 2697c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2698c577b8a1SJoseph Chan if (err < 0) 2699c577b8a1SJoseph Chan return err; 2700c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2701c577b8a1SJoseph Chan if (err < 0) 2702c577b8a1SJoseph Chan return err; 270310a20af7STakashi Iwai err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); 2704c577b8a1SJoseph Chan if (err < 0) 2705c577b8a1SJoseph Chan return err; 2706c577b8a1SJoseph Chan 2707c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2708c577b8a1SJoseph Chan 27090852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2710c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 271155d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2712c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2713c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2714c577b8a1SJoseph Chan 2715603c4019STakashi Iwai if (spec->kctls.list) 2716603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2717c577b8a1SJoseph Chan 27180aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2719c577b8a1SJoseph Chan 2720f8fdd495SHarald Welte if (spec->hp_mux) 27213d83e577STakashi Iwai via_hp_build(codec); 2722f8fdd495SHarald Welte 27235b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2724c577b8a1SJoseph Chan return 1; 2725c577b8a1SJoseph Chan } 2726c577b8a1SJoseph Chan 2727cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 272890dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2729cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2730cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2731cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2732cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2733cb53c626STakashi Iwai { } /* end */ 2734cb53c626STakashi Iwai }; 2735cb53c626STakashi Iwai #endif 2736cb53c626STakashi Iwai 2737c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2738c577b8a1SJoseph Chan { 2739c577b8a1SJoseph Chan struct via_spec *spec; 2740c577b8a1SJoseph Chan int err; 2741c577b8a1SJoseph Chan 2742c577b8a1SJoseph Chan /* create a codec specific record */ 27435b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2744c577b8a1SJoseph Chan if (spec == NULL) 2745c577b8a1SJoseph Chan return -ENOMEM; 2746c577b8a1SJoseph Chan 2747c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2748c577b8a1SJoseph Chan if (err < 0) { 2749c577b8a1SJoseph Chan via_free(codec); 2750c577b8a1SJoseph Chan return err; 2751c577b8a1SJoseph Chan } else if (!err) { 2752c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2753c577b8a1SJoseph Chan "Using genenic mode...\n"); 2754c577b8a1SJoseph Chan } 2755c577b8a1SJoseph Chan 275669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 275769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2758c577b8a1SJoseph Chan 2759c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2760c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2761c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2762c577b8a1SJoseph Chan 2763c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2764c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2765c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2766c577b8a1SJoseph Chan 2767c577b8a1SJoseph Chan 2768c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2769c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2770c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2771337b9d02STakashi Iwai get_mux_nids(codec); 2772c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2773c577b8a1SJoseph Chan spec->num_mixers++; 2774c577b8a1SJoseph Chan } 2775c577b8a1SJoseph Chan 2776c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2777c577b8a1SJoseph Chan 2778c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 277969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2780cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2781cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2782cb53c626STakashi Iwai #endif 2783c577b8a1SJoseph Chan 2784c577b8a1SJoseph Chan return 0; 2785c577b8a1SJoseph Chan } 2786c577b8a1SJoseph Chan /* 2787c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2788c577b8a1SJoseph Chan */ 278990dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2790c577b8a1SJoseph Chan /* 2791c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2792c577b8a1SJoseph Chan */ 2793c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2794c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2795c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2796c577b8a1SJoseph Chan 2797c577b8a1SJoseph Chan 2798c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2799c577b8a1SJoseph Chan * mixer widget 2800c577b8a1SJoseph Chan */ 2801c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2802c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2803c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2804c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2805c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2806c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2807c577b8a1SJoseph Chan 2808c577b8a1SJoseph Chan /* 2809c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2810c577b8a1SJoseph Chan */ 2811c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2812c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2813c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2814c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2815c577b8a1SJoseph Chan 2816c577b8a1SJoseph Chan /* 2817c577b8a1SJoseph Chan * Unmute PW3 and PW4 2818c577b8a1SJoseph Chan */ 2819c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2820c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2821c577b8a1SJoseph Chan 2822c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2823c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2824c577b8a1SJoseph Chan /* PW9 Output enable */ 2825c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2826c577b8a1SJoseph Chan { } 2827c577b8a1SJoseph Chan }; 2828c577b8a1SJoseph Chan 2829c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2830c577b8a1SJoseph Chan { 2831c577b8a1SJoseph Chan struct via_spec *spec; 2832c577b8a1SJoseph Chan int err; 2833c577b8a1SJoseph Chan 2834c577b8a1SJoseph Chan /* create a codec specific record */ 28355b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2836c577b8a1SJoseph Chan if (spec == NULL) 2837c577b8a1SJoseph Chan return -ENOMEM; 2838c577b8a1SJoseph Chan 2839c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2840c577b8a1SJoseph Chan if (err < 0) { 2841c577b8a1SJoseph Chan via_free(codec); 2842c577b8a1SJoseph Chan return err; 2843c577b8a1SJoseph Chan } else if (!err) { 2844c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2845c577b8a1SJoseph Chan "Using genenic mode...\n"); 2846c577b8a1SJoseph Chan } 2847c577b8a1SJoseph Chan 284869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 284969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2850c577b8a1SJoseph Chan 2851c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 2852c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2853c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2854c577b8a1SJoseph Chan 2855c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 2856c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2857c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2858c577b8a1SJoseph Chan 2859c577b8a1SJoseph Chan 2860c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2861c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 2862c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 2863337b9d02STakashi Iwai get_mux_nids(codec); 2864c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2865c577b8a1SJoseph Chan spec->num_mixers++; 2866c577b8a1SJoseph Chan } 2867c577b8a1SJoseph Chan 2868c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2869c577b8a1SJoseph Chan 2870c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 287169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2872cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2873cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2874cb53c626STakashi Iwai #endif 2875f7278fd0SJosepch Chan return 0; 2876f7278fd0SJosepch Chan } 2877f7278fd0SJosepch Chan 2878f7278fd0SJosepch Chan /* capture mixer elements */ 287990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2880f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2881f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2882f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2883f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2884f7278fd0SJosepch Chan { 2885f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2886f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2887f7278fd0SJosepch Chan * So call somewhat different.. 2888f7278fd0SJosepch Chan */ 2889f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2890f7278fd0SJosepch Chan .name = "Input Source", 2891f7278fd0SJosepch Chan .count = 1, 2892f7278fd0SJosepch Chan .info = via_mux_enum_info, 2893f7278fd0SJosepch Chan .get = via_mux_enum_get, 2894f7278fd0SJosepch Chan .put = via_mux_enum_put, 2895f7278fd0SJosepch Chan }, 2896f7278fd0SJosepch Chan { } /* end */ 2897f7278fd0SJosepch Chan }; 2898f7278fd0SJosepch Chan /* 2899f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2900f7278fd0SJosepch Chan */ 290190dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2902f7278fd0SJosepch Chan /* 2903f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2904f7278fd0SJosepch Chan */ 2905f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2906f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2907f7278fd0SJosepch Chan 2908f7278fd0SJosepch Chan 2909f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2910f7278fd0SJosepch Chan * mixer widget 2911f7278fd0SJosepch Chan */ 2912f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2913f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2914f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2915f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2916f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2917f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2918f7278fd0SJosepch Chan 2919f7278fd0SJosepch Chan /* 2920f7278fd0SJosepch Chan * Set up output mixers 2921f7278fd0SJosepch Chan */ 2922f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2923f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2924f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2925f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2926f7278fd0SJosepch Chan 2927f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2928bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2929f7278fd0SJosepch Chan /* PW9 Output enable */ 2930f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2931f7278fd0SJosepch Chan /* PW10 Input enable */ 2932f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2933f7278fd0SJosepch Chan { } 2934f7278fd0SJosepch Chan }; 2935f7278fd0SJosepch Chan 293690dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2937f7278fd0SJosepch Chan /* 2938f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2939f7278fd0SJosepch Chan */ 2940f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2941f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2942f7278fd0SJosepch Chan 2943f7278fd0SJosepch Chan 2944f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2945f7278fd0SJosepch Chan * mixer widget 2946f7278fd0SJosepch Chan */ 2947f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2948f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2949f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2950f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2951f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2952f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2953f7278fd0SJosepch Chan 2954f7278fd0SJosepch Chan /* 2955f7278fd0SJosepch Chan * Set up output mixers 2956f7278fd0SJosepch Chan */ 2957f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2958f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2959f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2960f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2961f7278fd0SJosepch Chan 2962f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2963f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2964f7278fd0SJosepch Chan /* PW9 Output enable */ 2965f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2966f7278fd0SJosepch Chan /* PW10 Input enable */ 2967f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2968f7278fd0SJosepch Chan { } 2969f7278fd0SJosepch Chan }; 2970f7278fd0SJosepch Chan 297190dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2972a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2973a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2974a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2975a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2976a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2977a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2978a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2979a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2980a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 298169e52a80SHarald Welte { } 298269e52a80SHarald Welte }; 298369e52a80SHarald Welte 298417314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 298517314379SLydia Wang struct hda_codec *codec, 298617314379SLydia Wang struct snd_pcm_substream *substream) 298717314379SLydia Wang { 298817314379SLydia Wang int idle = substream->pstr->substream_opened == 1 298917314379SLydia Wang && substream->ref_count == 0; 299017314379SLydia Wang 299117314379SLydia Wang analog_low_current_mode(codec, idle); 299217314379SLydia Wang return 0; 299317314379SLydia Wang } 299417314379SLydia Wang 299590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 29960aa62aefSHarald Welte .substreams = 2, 2997f7278fd0SJosepch Chan .channels_min = 2, 2998f7278fd0SJosepch Chan .channels_max = 8, 2999f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3000f7278fd0SJosepch Chan .ops = { 3001f7278fd0SJosepch Chan .open = via_playback_pcm_open, 30020aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 300317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 300417314379SLydia Wang .close = via_pcm_open_close 3005f7278fd0SJosepch Chan }, 3006f7278fd0SJosepch Chan }; 3007f7278fd0SJosepch Chan 300890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 30090aa62aefSHarald Welte .substreams = 2, 3010f7278fd0SJosepch Chan .channels_min = 2, 3011f7278fd0SJosepch Chan .channels_max = 4, 3012f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3013f7278fd0SJosepch Chan .ops = { 3014f7278fd0SJosepch Chan .open = via_playback_pcm_open, 30150aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 30160aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 3017f7278fd0SJosepch Chan }, 3018f7278fd0SJosepch Chan }; 3019f7278fd0SJosepch Chan 302090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_analog_capture = { 3021f7278fd0SJosepch Chan .substreams = 2, 3022f7278fd0SJosepch Chan .channels_min = 2, 3023f7278fd0SJosepch Chan .channels_max = 2, 3024f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 3025f7278fd0SJosepch Chan .ops = { 302617314379SLydia Wang .open = via_pcm_open_close, 3027f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 302817314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 302917314379SLydia Wang .close = via_pcm_open_close 3030f7278fd0SJosepch Chan }, 3031f7278fd0SJosepch Chan }; 3032f7278fd0SJosepch Chan 303390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3034f7278fd0SJosepch Chan .substreams = 1, 3035f7278fd0SJosepch Chan .channels_min = 2, 3036f7278fd0SJosepch Chan .channels_max = 2, 3037f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3038f7278fd0SJosepch Chan .ops = { 3039f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3040f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 30419da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 30429da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3043f7278fd0SJosepch Chan }, 3044f7278fd0SJosepch Chan }; 3045f7278fd0SJosepch Chan 304690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3047f7278fd0SJosepch Chan .substreams = 1, 3048f7278fd0SJosepch Chan .channels_min = 2, 3049f7278fd0SJosepch Chan .channels_max = 2, 3050f7278fd0SJosepch Chan }; 3051f7278fd0SJosepch Chan 3052f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3053f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3054f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3055f7278fd0SJosepch Chan { 3056f7278fd0SJosepch Chan int i; 3057f7278fd0SJosepch Chan hda_nid_t nid; 3058f7278fd0SJosepch Chan 3059f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3060f7278fd0SJosepch Chan 3061f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3062f7278fd0SJosepch Chan 3063f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3064f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3065f7278fd0SJosepch Chan if (nid) { 3066f7278fd0SJosepch Chan /* config dac list */ 3067f7278fd0SJosepch Chan switch (i) { 3068f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3069dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 3070f7278fd0SJosepch Chan break; 3071f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3072dda14410STakashi Iwai spec->private_dac_nids[i] = 0x24; 3073f7278fd0SJosepch Chan break; 3074f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3075dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 3076f7278fd0SJosepch Chan break; 3077f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3078dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3079f7278fd0SJosepch Chan break; 3080f7278fd0SJosepch Chan } 3081f7278fd0SJosepch Chan } 3082f7278fd0SJosepch Chan } 3083f7278fd0SJosepch Chan 3084f7278fd0SJosepch Chan return 0; 3085f7278fd0SJosepch Chan } 3086f7278fd0SJosepch Chan 3087f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3088f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3089f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3090f7278fd0SJosepch Chan { 3091f7278fd0SJosepch Chan char name[32]; 3092ea734963STakashi Iwai static const char * const chname[4] = { 3093ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3094ea734963STakashi Iwai }; 3095fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3096f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3097f7278fd0SJosepch Chan int i, err; 3098f7278fd0SJosepch Chan 3099f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3100f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3101f7278fd0SJosepch Chan 3102f7278fd0SJosepch Chan if (!nid) 3103f7278fd0SJosepch Chan continue; 3104f7278fd0SJosepch Chan 3105f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3106f7278fd0SJosepch Chan 3107f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3108f7278fd0SJosepch Chan /* Center/LFE */ 3109f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3110f7278fd0SJosepch Chan "Center Playback Volume", 3111f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3112f7278fd0SJosepch Chan HDA_OUTPUT)); 3113f7278fd0SJosepch Chan if (err < 0) 3114f7278fd0SJosepch Chan return err; 3115f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3116f7278fd0SJosepch Chan "LFE Playback Volume", 3117f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3118f7278fd0SJosepch Chan HDA_OUTPUT)); 3119f7278fd0SJosepch Chan if (err < 0) 3120f7278fd0SJosepch Chan return err; 3121f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3122f7278fd0SJosepch Chan "Center Playback Switch", 3123f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3124f7278fd0SJosepch Chan HDA_OUTPUT)); 3125f7278fd0SJosepch Chan if (err < 0) 3126f7278fd0SJosepch Chan return err; 3127f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3128f7278fd0SJosepch Chan "LFE Playback Switch", 3129f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3130f7278fd0SJosepch Chan HDA_OUTPUT)); 3131f7278fd0SJosepch Chan if (err < 0) 3132f7278fd0SJosepch Chan return err; 3133f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3134f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3135f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3136f7278fd0SJosepch Chan "Master Front Playback Volume", 3137f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3138f7278fd0SJosepch Chan HDA_INPUT)); 3139f7278fd0SJosepch Chan if (err < 0) 3140f7278fd0SJosepch Chan return err; 3141f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3142f7278fd0SJosepch Chan "Master Front Playback Switch", 3143f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3144f7278fd0SJosepch Chan HDA_INPUT)); 3145f7278fd0SJosepch Chan if (err < 0) 3146f7278fd0SJosepch Chan return err; 3147f7278fd0SJosepch Chan 3148f7278fd0SJosepch Chan /* add control to PW3 */ 3149f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3150f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3151f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3152f7278fd0SJosepch Chan HDA_OUTPUT)); 3153f7278fd0SJosepch Chan if (err < 0) 3154f7278fd0SJosepch Chan return err; 3155f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3156f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3157f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3158f7278fd0SJosepch Chan HDA_OUTPUT)); 3159f7278fd0SJosepch Chan if (err < 0) 3160f7278fd0SJosepch Chan return err; 3161f7278fd0SJosepch Chan } else { 3162f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3163f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3164f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3165f7278fd0SJosepch Chan HDA_OUTPUT)); 3166f7278fd0SJosepch Chan if (err < 0) 3167f7278fd0SJosepch Chan return err; 3168f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3169f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3170f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3171f7278fd0SJosepch Chan HDA_OUTPUT)); 3172f7278fd0SJosepch Chan if (err < 0) 3173f7278fd0SJosepch Chan return err; 3174f7278fd0SJosepch Chan } 3175f7278fd0SJosepch Chan } 3176f7278fd0SJosepch Chan 3177f7278fd0SJosepch Chan return 0; 3178f7278fd0SJosepch Chan } 3179f7278fd0SJosepch Chan 3180f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3181f7278fd0SJosepch Chan { 3182f7278fd0SJosepch Chan int err; 3183f7278fd0SJosepch Chan 3184f7278fd0SJosepch Chan if (!pin) 3185f7278fd0SJosepch Chan return 0; 3186f7278fd0SJosepch Chan 3187f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3188cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3189f7278fd0SJosepch Chan 3190f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3191f7278fd0SJosepch Chan "Headphone Playback Volume", 3192f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3193f7278fd0SJosepch Chan if (err < 0) 3194f7278fd0SJosepch Chan return err; 3195f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3196f7278fd0SJosepch Chan "Headphone Playback Switch", 3197f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3198f7278fd0SJosepch Chan if (err < 0) 3199f7278fd0SJosepch Chan return err; 3200f7278fd0SJosepch Chan 32010aa62aefSHarald Welte create_hp_imux(spec); 32020aa62aefSHarald Welte 3203f7278fd0SJosepch Chan return 0; 3204f7278fd0SJosepch Chan } 3205f7278fd0SJosepch Chan 3206f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 320710a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, 3208f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3209f7278fd0SJosepch Chan { 321090dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0xff, 0x1f, 0x1a, 0x1b, 0x1e }; 321110a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3212f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3213f7278fd0SJosepch Chan } 3214f7278fd0SJosepch Chan 3215f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3216f7278fd0SJosepch Chan { 3217f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3218f7278fd0SJosepch Chan int err; 3219f7278fd0SJosepch Chan 3220f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3221f7278fd0SJosepch Chan if (err < 0) 3222f7278fd0SJosepch Chan return err; 3223f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3224f7278fd0SJosepch Chan if (err < 0) 3225f7278fd0SJosepch Chan return err; 3226f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3227f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3228f7278fd0SJosepch Chan 3229f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3230f7278fd0SJosepch Chan if (err < 0) 3231f7278fd0SJosepch Chan return err; 3232f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3233f7278fd0SJosepch Chan if (err < 0) 3234f7278fd0SJosepch Chan return err; 323510a20af7STakashi Iwai err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); 3236f7278fd0SJosepch Chan if (err < 0) 3237f7278fd0SJosepch Chan return err; 3238f7278fd0SJosepch Chan 3239f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3240f7278fd0SJosepch Chan 32410852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3242f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 324355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3244f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3245f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3246f7278fd0SJosepch Chan 3247603c4019STakashi Iwai if (spec->kctls.list) 3248603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3249f7278fd0SJosepch Chan 32500aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 32510aa62aefSHarald Welte 3252f8fdd495SHarald Welte if (spec->hp_mux) 32533d83e577STakashi Iwai via_hp_build(codec); 3254f7278fd0SJosepch Chan 32555b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3256f7278fd0SJosepch Chan return 1; 3257f7278fd0SJosepch Chan } 3258f7278fd0SJosepch Chan 3259f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 326090dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 3261f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3262f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3263f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3264f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3265f7278fd0SJosepch Chan { } /* end */ 3266f7278fd0SJosepch Chan }; 3267f7278fd0SJosepch Chan #endif 32683e95b9abSLydia Wang 32693e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 32703e95b9abSLydia Wang { 32713e95b9abSLydia Wang struct via_spec *spec = codec->spec; 32723e95b9abSLydia Wang int imux_is_smixer; 32733e95b9abSLydia Wang unsigned int parm; 32743e95b9abSLydia Wang int is_8ch = 0; 3275bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 3276bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 32773e95b9abSLydia Wang is_8ch = 1; 32783e95b9abSLydia Wang 32793e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 32803e95b9abSLydia Wang imux_is_smixer = 32813e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 32823e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 32833e95b9abSLydia Wang /* inputs */ 32843e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 32853e95b9abSLydia Wang parm = AC_PWRST_D3; 32863e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32873e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32883e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 32893e95b9abSLydia Wang if (imux_is_smixer) 32903e95b9abSLydia Wang parm = AC_PWRST_D0; 32913e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 32923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 32933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 32943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 32953e95b9abSLydia Wang 32963e95b9abSLydia Wang /* outputs */ 32973e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 32983e95b9abSLydia Wang parm = AC_PWRST_D3; 32993e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 33003e95b9abSLydia Wang if (spec->smart51_enabled) 33013e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 33023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 33033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 33043e95b9abSLydia Wang 33053e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 33063e95b9abSLydia Wang if (is_8ch) { 33073e95b9abSLydia Wang parm = AC_PWRST_D3; 33083e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 33093e95b9abSLydia Wang if (spec->smart51_enabled) 33103e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 33113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 33123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 33143e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3315bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 3316bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 3317bc92df7fSLydia Wang parm = AC_PWRST_D3; 3318bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 3319bc92df7fSLydia Wang if (spec->smart51_enabled) 3320bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 3321bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 3322bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3323bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3324bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33253e95b9abSLydia Wang } 33263e95b9abSLydia Wang 33273e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 33283e95b9abSLydia Wang parm = AC_PWRST_D3; 33293e95b9abSLydia Wang /* force to D0 for internal Speaker */ 33303e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 33313e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 33323e95b9abSLydia Wang if (is_8ch) 33333e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 33343e95b9abSLydia Wang 33353e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 33363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 33373e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 33383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33393e95b9abSLydia Wang if (is_8ch) { 33403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 33413e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 33433e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3344bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 3345bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3346bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33473e95b9abSLydia Wang } 33483e95b9abSLydia Wang 3349518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3350f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3351f7278fd0SJosepch Chan { 3352f7278fd0SJosepch Chan struct via_spec *spec; 3353f7278fd0SJosepch Chan int err; 3354f7278fd0SJosepch Chan 3355518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3356518bf3baSLydia Wang return patch_vt1708S(codec); 3357f7278fd0SJosepch Chan /* create a codec specific record */ 33585b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3359f7278fd0SJosepch Chan if (spec == NULL) 3360f7278fd0SJosepch Chan return -ENOMEM; 3361f7278fd0SJosepch Chan 3362f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3363f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3364f7278fd0SJosepch Chan if (err < 0) { 3365f7278fd0SJosepch Chan via_free(codec); 3366f7278fd0SJosepch Chan return err; 3367f7278fd0SJosepch Chan } else if (!err) { 3368f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3369f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3370f7278fd0SJosepch Chan } 3371f7278fd0SJosepch Chan 337269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 337369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3374f7278fd0SJosepch Chan 3375f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3376f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3377f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3378f7278fd0SJosepch Chan 3379f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3380f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3381f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3382f7278fd0SJosepch Chan 3383f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3384f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3385f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3386337b9d02STakashi Iwai get_mux_nids(codec); 3387f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3388f7278fd0SJosepch Chan spec->num_mixers++; 3389f7278fd0SJosepch Chan } 3390f7278fd0SJosepch Chan 3391f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3392f7278fd0SJosepch Chan 3393f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 339469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3395f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3396f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3397f7278fd0SJosepch Chan #endif 3398f7278fd0SJosepch Chan 33993e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 34003e95b9abSLydia Wang 3401f7278fd0SJosepch Chan return 0; 3402f7278fd0SJosepch Chan } 3403f7278fd0SJosepch Chan 3404f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3405f7278fd0SJosepch Chan { 3406f7278fd0SJosepch Chan struct via_spec *spec; 3407f7278fd0SJosepch Chan int err; 3408f7278fd0SJosepch Chan 3409f7278fd0SJosepch Chan /* create a codec specific record */ 34105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3411f7278fd0SJosepch Chan if (spec == NULL) 3412f7278fd0SJosepch Chan return -ENOMEM; 3413f7278fd0SJosepch Chan 3414f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3415f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3416f7278fd0SJosepch Chan if (err < 0) { 3417f7278fd0SJosepch Chan via_free(codec); 3418f7278fd0SJosepch Chan return err; 3419f7278fd0SJosepch Chan } else if (!err) { 3420f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3421f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3422f7278fd0SJosepch Chan } 3423f7278fd0SJosepch Chan 342469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 342569e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3426f7278fd0SJosepch Chan 3427f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3428f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3429f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3430f7278fd0SJosepch Chan 3431f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3432f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3433f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3434f7278fd0SJosepch Chan 3435f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3436f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3437f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3438337b9d02STakashi Iwai get_mux_nids(codec); 3439f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3440f7278fd0SJosepch Chan spec->num_mixers++; 3441f7278fd0SJosepch Chan } 3442f7278fd0SJosepch Chan 3443f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3444f7278fd0SJosepch Chan 3445f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 344669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3447f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3448f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3449f7278fd0SJosepch Chan #endif 3450c577b8a1SJoseph Chan 34513e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 34523e95b9abSLydia Wang 3453c577b8a1SJoseph Chan return 0; 3454c577b8a1SJoseph Chan } 3455c577b8a1SJoseph Chan 3456d949cac1SHarald Welte /* Patch for VT1708S */ 3457d949cac1SHarald Welte 3458d949cac1SHarald Welte /* capture mixer elements */ 345990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3460d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3461d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3462d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3463d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 34646369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 34656369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 34666369bcfcSLydia Wang HDA_INPUT), 3467d949cac1SHarald Welte { 3468d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3469d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3470d949cac1SHarald Welte * So call somewhat different.. 3471d949cac1SHarald Welte */ 3472d949cac1SHarald Welte /* .name = "Capture Source", */ 3473d949cac1SHarald Welte .name = "Input Source", 3474d949cac1SHarald Welte .count = 1, 3475d949cac1SHarald Welte .info = via_mux_enum_info, 3476d949cac1SHarald Welte .get = via_mux_enum_get, 3477d949cac1SHarald Welte .put = via_mux_enum_put, 3478d949cac1SHarald Welte }, 3479d949cac1SHarald Welte { } /* end */ 3480d949cac1SHarald Welte }; 3481d949cac1SHarald Welte 348290dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3483d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3484d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3485d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3486d949cac1SHarald Welte 3487d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3488d949cac1SHarald Welte * analog-loopback mixer widget */ 3489d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3490d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3491d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3492d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3493d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3494d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3495d949cac1SHarald Welte 3496d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3497d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 34985691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3499d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 35005691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3501d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3502d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3503bc7e7e5cSLydia Wang /* don't bybass mixer */ 3504bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3505d949cac1SHarald Welte { } 3506d949cac1SHarald Welte }; 3507d949cac1SHarald Welte 350890dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3509a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3510a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3511a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3512a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3513a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3514a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3515a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3516a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3517a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 351869e52a80SHarald Welte { } 351969e52a80SHarald Welte }; 352069e52a80SHarald Welte 352190dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3522bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3523bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3524bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3525bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3526bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3527bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3528bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3529bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3530bc92df7fSLydia Wang { } 3531bc92df7fSLydia Wang }; 3532bc92df7fSLydia Wang 353390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3534d949cac1SHarald Welte .substreams = 2, 3535d949cac1SHarald Welte .channels_min = 2, 3536d949cac1SHarald Welte .channels_max = 8, 3537d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3538d949cac1SHarald Welte .ops = { 3539d949cac1SHarald Welte .open = via_playback_pcm_open, 3540c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3541c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 354217314379SLydia Wang .close = via_pcm_open_close 3543d949cac1SHarald Welte }, 3544d949cac1SHarald Welte }; 3545d949cac1SHarald Welte 354690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1705_pcm_analog_playback = { 3547bc92df7fSLydia Wang .substreams = 2, 3548bc92df7fSLydia Wang .channels_min = 2, 3549bc92df7fSLydia Wang .channels_max = 6, 3550bc92df7fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3551bc92df7fSLydia Wang .ops = { 3552bc92df7fSLydia Wang .open = via_playback_pcm_open, 3553bc92df7fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3554bc92df7fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3555bc92df7fSLydia Wang .close = via_pcm_open_close 3556bc92df7fSLydia Wang }, 3557bc92df7fSLydia Wang }; 3558bc92df7fSLydia Wang 355990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3560d949cac1SHarald Welte .substreams = 2, 3561d949cac1SHarald Welte .channels_min = 2, 3562d949cac1SHarald Welte .channels_max = 2, 3563d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3564d949cac1SHarald Welte .ops = { 356517314379SLydia Wang .open = via_pcm_open_close, 3566d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 356717314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 356817314379SLydia Wang .close = via_pcm_open_close 3569d949cac1SHarald Welte }, 3570d949cac1SHarald Welte }; 3571d949cac1SHarald Welte 357290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { 35739da29271STakashi Iwai .substreams = 1, 3574d949cac1SHarald Welte .channels_min = 2, 3575d949cac1SHarald Welte .channels_max = 2, 3576d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3577d949cac1SHarald Welte .ops = { 3578d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3579d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 35809da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 35819da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3582d949cac1SHarald Welte }, 3583d949cac1SHarald Welte }; 3584d949cac1SHarald Welte 3585d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3586d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3587d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3588d949cac1SHarald Welte { 3589d949cac1SHarald Welte int i; 3590d949cac1SHarald Welte hda_nid_t nid; 3591d949cac1SHarald Welte 3592d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3593d949cac1SHarald Welte 3594d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3595d949cac1SHarald Welte 3596d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3597d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3598d949cac1SHarald Welte if (nid) { 3599d949cac1SHarald Welte /* config dac list */ 3600d949cac1SHarald Welte switch (i) { 3601d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3602dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 3603d949cac1SHarald Welte break; 3604d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3605bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3606dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3607bc92df7fSLydia Wang else 3608dda14410STakashi Iwai spec->private_dac_nids[i] = 0x24; 3609d949cac1SHarald Welte break; 3610d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3611dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 3612d949cac1SHarald Welte break; 3613d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3614dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3615d949cac1SHarald Welte break; 3616d949cac1SHarald Welte } 3617d949cac1SHarald Welte } 3618d949cac1SHarald Welte } 3619d949cac1SHarald Welte 362032e0191dSClemens Ladisch /* for Smart 5.1, line/mic inputs double as output pins */ 362132e0191dSClemens Ladisch if (cfg->line_outs == 1) { 362232e0191dSClemens Ladisch spec->multiout.num_dacs = 3; 3623dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11; 3624bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3625dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25; 3626bc92df7fSLydia Wang else 3627dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24; 362832e0191dSClemens Ladisch } 362932e0191dSClemens Ladisch 3630d949cac1SHarald Welte return 0; 3631d949cac1SHarald Welte } 3632d949cac1SHarald Welte 3633d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3634bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, 3635d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3636d949cac1SHarald Welte { 3637bc92df7fSLydia Wang struct via_spec *spec = codec->spec; 3638d949cac1SHarald Welte char name[32]; 3639ea734963STakashi Iwai static const char * const chname[4] = { 3640ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3641ea734963STakashi Iwai }; 3642bc92df7fSLydia Wang hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, 3643bc92df7fSLydia Wang {0x10, 0x11, 0x25, 0} }; 3644bc92df7fSLydia Wang hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, 3645bc92df7fSLydia Wang {0x1C, 0x18, 0x27, 0} }; 3646d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3647d949cac1SHarald Welte int i, err; 3648d949cac1SHarald Welte 3649d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3650d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3651d949cac1SHarald Welte 365232e0191dSClemens Ladisch /* for Smart 5.1, there are always at least six channels */ 365332e0191dSClemens Ladisch if (!nid && i > AUTO_SEQ_CENLFE) 3654d949cac1SHarald Welte continue; 3655d949cac1SHarald Welte 3656bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3657bc92df7fSLydia Wang nid_vol = nid_vols[1][i]; 3658bc92df7fSLydia Wang nid_mute = nid_mutes[1][i]; 3659bc92df7fSLydia Wang } else { 3660bc92df7fSLydia Wang nid_vol = nid_vols[0][i]; 3661bc92df7fSLydia Wang nid_mute = nid_mutes[0][i]; 3662bc92df7fSLydia Wang } 3663bc92df7fSLydia Wang if (!nid_vol && !nid_mute) 3664bc92df7fSLydia Wang continue; 3665d949cac1SHarald Welte 3666d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3667d949cac1SHarald Welte /* Center/LFE */ 3668d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3669d949cac1SHarald Welte "Center Playback Volume", 3670d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3671d949cac1SHarald Welte HDA_OUTPUT)); 3672d949cac1SHarald Welte if (err < 0) 3673d949cac1SHarald Welte return err; 3674d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3675d949cac1SHarald Welte "LFE Playback Volume", 3676d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3677d949cac1SHarald Welte HDA_OUTPUT)); 3678d949cac1SHarald Welte if (err < 0) 3679d949cac1SHarald Welte return err; 3680d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3681d949cac1SHarald Welte "Center Playback Switch", 3682d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3683d949cac1SHarald Welte 1, 0, 3684d949cac1SHarald Welte HDA_OUTPUT)); 3685d949cac1SHarald Welte if (err < 0) 3686d949cac1SHarald Welte return err; 3687d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3688d949cac1SHarald Welte "LFE Playback Switch", 3689d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3690d949cac1SHarald Welte 2, 0, 3691d949cac1SHarald Welte HDA_OUTPUT)); 3692d949cac1SHarald Welte if (err < 0) 3693d949cac1SHarald Welte return err; 3694d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3695d949cac1SHarald Welte /* add control to mixer index 0 */ 3696d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3697d949cac1SHarald Welte "Master Front Playback Volume", 3698d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3699d949cac1SHarald Welte HDA_INPUT)); 3700d949cac1SHarald Welte if (err < 0) 3701d949cac1SHarald Welte return err; 3702d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3703d949cac1SHarald Welte "Master Front Playback Switch", 3704d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3705d949cac1SHarald Welte HDA_INPUT)); 3706d949cac1SHarald Welte if (err < 0) 3707d949cac1SHarald Welte return err; 3708d949cac1SHarald Welte 3709d949cac1SHarald Welte /* Front */ 3710d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3711d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3712d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3713d949cac1SHarald Welte HDA_OUTPUT)); 3714d949cac1SHarald Welte if (err < 0) 3715d949cac1SHarald Welte return err; 3716d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3717d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3718d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3719d949cac1SHarald Welte 3, 0, 3720d949cac1SHarald Welte HDA_OUTPUT)); 3721d949cac1SHarald Welte if (err < 0) 3722d949cac1SHarald Welte return err; 3723d949cac1SHarald Welte } else { 3724d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3725d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3726d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3727d949cac1SHarald Welte HDA_OUTPUT)); 3728d949cac1SHarald Welte if (err < 0) 3729d949cac1SHarald Welte return err; 3730d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3731d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3732d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3733d949cac1SHarald Welte 3, 0, 3734d949cac1SHarald Welte HDA_OUTPUT)); 3735d949cac1SHarald Welte if (err < 0) 3736d949cac1SHarald Welte return err; 3737d949cac1SHarald Welte } 3738d949cac1SHarald Welte } 3739d949cac1SHarald Welte 3740d949cac1SHarald Welte return 0; 3741d949cac1SHarald Welte } 3742d949cac1SHarald Welte 3743d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3744d949cac1SHarald Welte { 3745d949cac1SHarald Welte int err; 3746d949cac1SHarald Welte 3747d949cac1SHarald Welte if (!pin) 3748d949cac1SHarald Welte return 0; 3749d949cac1SHarald Welte 3750d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3751cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3752d949cac1SHarald Welte 3753d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3754d949cac1SHarald Welte "Headphone Playback Volume", 3755d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3756d949cac1SHarald Welte if (err < 0) 3757d949cac1SHarald Welte return err; 3758d949cac1SHarald Welte 3759d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3760d949cac1SHarald Welte "Headphone Playback Switch", 3761d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3762d949cac1SHarald Welte if (err < 0) 3763d949cac1SHarald Welte return err; 3764d949cac1SHarald Welte 37650aa62aefSHarald Welte create_hp_imux(spec); 37660aa62aefSHarald Welte 3767d949cac1SHarald Welte return 0; 3768d949cac1SHarald Welte } 3769d949cac1SHarald Welte 3770d949cac1SHarald Welte /* create playback/capture controls for input pins */ 377110a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, 3772d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3773d949cac1SHarald Welte { 377490dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 377510a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 3776f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 3777d949cac1SHarald Welte } 3778d949cac1SHarald Welte 37799da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 37809da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 37819da29271STakashi Iwai { 37829da29271STakashi Iwai struct via_spec *spec = codec->spec; 37839da29271STakashi Iwai int i; 37849da29271STakashi Iwai 37859da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 37869da29271STakashi Iwai hda_nid_t nid; 37879da29271STakashi Iwai int conn; 37889da29271STakashi Iwai 37899da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 37909da29271STakashi Iwai if (!nid) 37919da29271STakashi Iwai continue; 37929da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 37939da29271STakashi Iwai if (conn < 1) 37949da29271STakashi Iwai continue; 37959da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 37969da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 37979da29271STakashi Iwai else { 37989da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 37999da29271STakashi Iwai break; /* at most two dig outs */ 38009da29271STakashi Iwai } 38019da29271STakashi Iwai } 38029da29271STakashi Iwai } 38039da29271STakashi Iwai 3804d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3805d949cac1SHarald Welte { 3806d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3807d949cac1SHarald Welte int err; 3808d949cac1SHarald Welte 38099da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3810d949cac1SHarald Welte if (err < 0) 3811d949cac1SHarald Welte return err; 3812d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3813d949cac1SHarald Welte if (err < 0) 3814d949cac1SHarald Welte return err; 3815d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3816d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3817d949cac1SHarald Welte 3818bc92df7fSLydia Wang err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); 3819d949cac1SHarald Welte if (err < 0) 3820d949cac1SHarald Welte return err; 3821d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3822d949cac1SHarald Welte if (err < 0) 3823d949cac1SHarald Welte return err; 382410a20af7STakashi Iwai err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); 3825d949cac1SHarald Welte if (err < 0) 3826d949cac1SHarald Welte return err; 3827d949cac1SHarald Welte 3828d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3829d949cac1SHarald Welte 38309da29271STakashi Iwai fill_dig_outs(codec); 383198aa34c0SHarald Welte 3832603c4019STakashi Iwai if (spec->kctls.list) 3833603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3834d949cac1SHarald Welte 38350aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 38360aa62aefSHarald Welte 3837f8fdd495SHarald Welte if (spec->hp_mux) 38383d83e577STakashi Iwai via_hp_build(codec); 3839d949cac1SHarald Welte 38405b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3841d949cac1SHarald Welte return 1; 3842d949cac1SHarald Welte } 3843d949cac1SHarald Welte 3844d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 384590dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3846d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3847d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3848d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3849d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3850d949cac1SHarald Welte { } /* end */ 3851d949cac1SHarald Welte }; 3852d949cac1SHarald Welte #endif 3853d949cac1SHarald Welte 38546369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 38556369bcfcSLydia Wang int offset, int num_steps, int step_size) 38566369bcfcSLydia Wang { 38576369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 38586369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 38596369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 38606369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 38616369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 38626369bcfcSLydia Wang } 38636369bcfcSLydia Wang 3864d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3865d949cac1SHarald Welte { 3866d949cac1SHarald Welte struct via_spec *spec; 3867d949cac1SHarald Welte int err; 3868d949cac1SHarald Welte 3869d949cac1SHarald Welte /* create a codec specific record */ 38705b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3871d949cac1SHarald Welte if (spec == NULL) 3872d949cac1SHarald Welte return -ENOMEM; 3873d949cac1SHarald Welte 3874d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3875d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3876d949cac1SHarald Welte if (err < 0) { 3877d949cac1SHarald Welte via_free(codec); 3878d949cac1SHarald Welte return err; 3879d949cac1SHarald Welte } else if (!err) { 3880d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3881d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3882d949cac1SHarald Welte } 3883d949cac1SHarald Welte 388469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3885bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3886bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3887bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3888bc92df7fSLydia Wang else 3889bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3890bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3891d949cac1SHarald Welte 389236dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 389336dd5c4aSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3894bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3895bc92df7fSLydia Wang spec->stream_name_analog = "VT1705 Analog"; 389636dd5c4aSLydia Wang else 3897d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 3898bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3899bc92df7fSLydia Wang spec->stream_analog_playback = &vt1705_pcm_analog_playback; 3900bc92df7fSLydia Wang else 3901d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3902d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3903d949cac1SHarald Welte 390436dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 390536dd5c4aSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3906bc92df7fSLydia Wang else if (codec->vendor_id == 0x11064397) 3907bc92df7fSLydia Wang spec->stream_name_digital = "VT1705 Digital"; 390836dd5c4aSLydia Wang else 3909d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 3910d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3911d949cac1SHarald Welte 3912d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 3913d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 3914d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 3915337b9d02STakashi Iwai get_mux_nids(codec); 39166369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 39176369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3918d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3919d949cac1SHarald Welte spec->num_mixers++; 3920d949cac1SHarald Welte } 3921d949cac1SHarald Welte 3922d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3923d949cac1SHarald Welte 3924d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 392569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3926d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3927d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3928d949cac1SHarald Welte #endif 3929d949cac1SHarald Welte 3930518bf3baSLydia Wang /* correct names for VT1708BCE */ 3931518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3932518bf3baSLydia Wang kfree(codec->chip_name); 3933518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3934518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3935518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3936518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3937518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 3938518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 3939518bf3baSLydia Wang } 3940970f630fSLydia Wang /* correct names for VT1818S */ 3941970f630fSLydia Wang if (codec->vendor_id == 0x11060440) { 3942970f630fSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 3943970f630fSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 3944970f630fSLydia Wang } 3945bc92df7fSLydia Wang /* correct names for VT1705 */ 3946bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3947bc92df7fSLydia Wang kfree(codec->chip_name); 3948bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3949bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3950bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3951bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3952bc92df7fSLydia Wang } 39533e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3954d949cac1SHarald Welte return 0; 3955d949cac1SHarald Welte } 3956d949cac1SHarald Welte 3957d949cac1SHarald Welte /* Patch for VT1702 */ 3958d949cac1SHarald Welte 3959d949cac1SHarald Welte /* capture mixer elements */ 396090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = { 3961d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3962d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3963d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3964d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3965d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3966d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3967d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3968d949cac1SHarald Welte HDA_INPUT), 3969d949cac1SHarald Welte { 3970d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3971d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3972d949cac1SHarald Welte * So call somewhat different.. 3973d949cac1SHarald Welte */ 3974d949cac1SHarald Welte /* .name = "Capture Source", */ 3975d949cac1SHarald Welte .name = "Input Source", 3976d949cac1SHarald Welte .count = 1, 3977d949cac1SHarald Welte .info = via_mux_enum_info, 3978d949cac1SHarald Welte .get = via_mux_enum_get, 3979d949cac1SHarald Welte .put = via_mux_enum_put, 3980d949cac1SHarald Welte }, 3981d949cac1SHarald Welte { } /* end */ 3982d949cac1SHarald Welte }; 3983d949cac1SHarald Welte 398490dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3985d949cac1SHarald Welte /* 3986d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3987d949cac1SHarald Welte */ 3988d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3989d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3990d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3991d949cac1SHarald Welte 3992d949cac1SHarald Welte 3993d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3994d949cac1SHarald Welte * mixer widget 3995d949cac1SHarald Welte */ 3996d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3997d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3998d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3999d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 4000d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 4001d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4002d949cac1SHarald Welte 4003d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 4004d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 4005d949cac1SHarald Welte /* PW6 PW7 Output enable */ 4006d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4007d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4008bc7e7e5cSLydia Wang /* mixer enable */ 4009bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 4010bc7e7e5cSLydia Wang /* GPIO 0~2 */ 4011bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 4012d949cac1SHarald Welte { } 4013d949cac1SHarald Welte }; 4014d949cac1SHarald Welte 401590dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 4016a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 4017a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4018a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4019a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4020a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4021a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 402269e52a80SHarald Welte { } 402369e52a80SHarald Welte }; 402469e52a80SHarald Welte 402590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_playback = { 40260aa62aefSHarald Welte .substreams = 2, 4027d949cac1SHarald Welte .channels_min = 2, 4028d949cac1SHarald Welte .channels_max = 2, 4029d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 4030d949cac1SHarald Welte .ops = { 4031d949cac1SHarald Welte .open = via_playback_pcm_open, 40320aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 403317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 403417314379SLydia Wang .close = via_pcm_open_close 4035d949cac1SHarald Welte }, 4036d949cac1SHarald Welte }; 4037d949cac1SHarald Welte 403890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_capture = { 4039d949cac1SHarald Welte .substreams = 3, 4040d949cac1SHarald Welte .channels_min = 2, 4041d949cac1SHarald Welte .channels_max = 2, 4042d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 4043d949cac1SHarald Welte .ops = { 404417314379SLydia Wang .open = via_pcm_open_close, 4045d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 404617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 404717314379SLydia Wang .close = via_pcm_open_close 4048d949cac1SHarald Welte }, 4049d949cac1SHarald Welte }; 4050d949cac1SHarald Welte 405190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_digital_playback = { 40525691ec7fSHarald Welte .substreams = 2, 4053d949cac1SHarald Welte .channels_min = 2, 4054d949cac1SHarald Welte .channels_max = 2, 4055d949cac1SHarald Welte /* NID is set in via_build_pcms */ 4056d949cac1SHarald Welte .ops = { 4057d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 4058d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 40599da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 40609da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 4061d949cac1SHarald Welte }, 4062d949cac1SHarald Welte }; 4063d949cac1SHarald Welte 4064d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 4065d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 4066d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4067d949cac1SHarald Welte { 4068d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4069d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4070d949cac1SHarald Welte 4071d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4072d949cac1SHarald Welte /* config dac list */ 4073dda14410STakashi Iwai spec->private_dac_nids[0] = 0x10; 4074d949cac1SHarald Welte } 4075d949cac1SHarald Welte 4076d949cac1SHarald Welte return 0; 4077d949cac1SHarald Welte } 4078d949cac1SHarald Welte 4079d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4080d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4081d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4082d949cac1SHarald Welte { 4083d949cac1SHarald Welte int err; 4084d949cac1SHarald Welte 4085d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4086d949cac1SHarald Welte return -1; 4087d949cac1SHarald Welte 4088d949cac1SHarald Welte /* add control to mixer index 0 */ 4089d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4090d949cac1SHarald Welte "Master Front Playback Volume", 4091d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4092d949cac1SHarald Welte if (err < 0) 4093d949cac1SHarald Welte return err; 4094d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4095d949cac1SHarald Welte "Master Front Playback Switch", 4096d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4097d949cac1SHarald Welte if (err < 0) 4098d949cac1SHarald Welte return err; 4099d949cac1SHarald Welte 4100d949cac1SHarald Welte /* Front */ 4101d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4102d949cac1SHarald Welte "Front Playback Volume", 4103d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4104d949cac1SHarald Welte if (err < 0) 4105d949cac1SHarald Welte return err; 4106d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4107d949cac1SHarald Welte "Front Playback Switch", 4108d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4109d949cac1SHarald Welte if (err < 0) 4110d949cac1SHarald Welte return err; 4111d949cac1SHarald Welte 4112d949cac1SHarald Welte return 0; 4113d949cac1SHarald Welte } 4114d949cac1SHarald Welte 4115d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4116d949cac1SHarald Welte { 41170713efebSLydia Wang int err, i; 41180713efebSLydia Wang struct hda_input_mux *imux; 4119ea734963STakashi Iwai static const char * const texts[] = { "ON", "OFF", NULL}; 4120d949cac1SHarald Welte if (!pin) 4121d949cac1SHarald Welte return 0; 4122d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4123cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4124d949cac1SHarald Welte 4125d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4126d949cac1SHarald Welte "Headphone Playback Volume", 4127d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4128d949cac1SHarald Welte if (err < 0) 4129d949cac1SHarald Welte return err; 4130d949cac1SHarald Welte 4131d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4132d949cac1SHarald Welte "Headphone Playback Switch", 4133d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4134d949cac1SHarald Welte if (err < 0) 4135d949cac1SHarald Welte return err; 4136d949cac1SHarald Welte 41370713efebSLydia Wang imux = &spec->private_imux[1]; 41380aa62aefSHarald Welte 41390713efebSLydia Wang /* for hp mode select */ 414010a20af7STakashi Iwai for (i = 0; texts[i]; i++) 414110a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 41420713efebSLydia Wang 41430713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4144d949cac1SHarald Welte return 0; 4145d949cac1SHarald Welte } 4146d949cac1SHarald Welte 4147d949cac1SHarald Welte /* create playback/capture controls for input pins */ 414810a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, 4149d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4150d949cac1SHarald Welte { 415190dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x14, 0x15, 0x18, 0xff }; 415210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a, pin_idxs, 4153f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4154d949cac1SHarald Welte } 4155d949cac1SHarald Welte 4156d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4157d949cac1SHarald Welte { 4158d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4159d949cac1SHarald Welte int err; 4160d949cac1SHarald Welte 41619da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4162d949cac1SHarald Welte if (err < 0) 4163d949cac1SHarald Welte return err; 4164d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4165d949cac1SHarald Welte if (err < 0) 4166d949cac1SHarald Welte return err; 4167d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4168d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4169d949cac1SHarald Welte 4170d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4171d949cac1SHarald Welte if (err < 0) 4172d949cac1SHarald Welte return err; 4173d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4174d949cac1SHarald Welte if (err < 0) 4175d949cac1SHarald Welte return err; 4176c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4177c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4178c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4179c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4180c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4181c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 418210a20af7STakashi Iwai err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); 4183d949cac1SHarald Welte if (err < 0) 4184d949cac1SHarald Welte return err; 4185d949cac1SHarald Welte 4186d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4187d949cac1SHarald Welte 41889da29271STakashi Iwai fill_dig_outs(codec); 418998aa34c0SHarald Welte 4190603c4019STakashi Iwai if (spec->kctls.list) 4191603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4192d949cac1SHarald Welte 41930aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 41940aa62aefSHarald Welte 4195f8fdd495SHarald Welte if (spec->hp_mux) 41963d83e577STakashi Iwai via_hp_build(codec); 4197d949cac1SHarald Welte 4198d949cac1SHarald Welte return 1; 4199d949cac1SHarald Welte } 4200d949cac1SHarald Welte 4201d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 420290dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 4203d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4204d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4205d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4206d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4207d949cac1SHarald Welte { } /* end */ 4208d949cac1SHarald Welte }; 4209d949cac1SHarald Welte #endif 4210d949cac1SHarald Welte 42113e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 42123e95b9abSLydia Wang { 42133e95b9abSLydia Wang int imux_is_smixer = 42143e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 42153e95b9abSLydia Wang unsigned int parm; 42163e95b9abSLydia Wang /* inputs */ 42173e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 42183e95b9abSLydia Wang parm = AC_PWRST_D3; 42193e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 42203e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 42213e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 42223e95b9abSLydia Wang if (imux_is_smixer) 42233e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 42243e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 42253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 42263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 42273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 42283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 42293e95b9abSLydia Wang 42303e95b9abSLydia Wang /* outputs */ 42313e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 42323e95b9abSLydia Wang parm = AC_PWRST_D3; 42333e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 42343e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 42353e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 42363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 42373e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 42383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 42393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 42403e95b9abSLydia Wang } 42413e95b9abSLydia Wang 4242d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4243d949cac1SHarald Welte { 4244d949cac1SHarald Welte struct via_spec *spec; 4245d949cac1SHarald Welte int err; 4246d949cac1SHarald Welte 4247d949cac1SHarald Welte /* create a codec specific record */ 42485b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4249d949cac1SHarald Welte if (spec == NULL) 4250d949cac1SHarald Welte return -ENOMEM; 4251d949cac1SHarald Welte 4252d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4253d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4254d949cac1SHarald Welte if (err < 0) { 4255d949cac1SHarald Welte via_free(codec); 4256d949cac1SHarald Welte return err; 4257d949cac1SHarald Welte } else if (!err) { 4258d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4259d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4260d949cac1SHarald Welte } 4261d949cac1SHarald Welte 426269e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 426369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4264d949cac1SHarald Welte 4265d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4266d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4267d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4268d949cac1SHarald Welte 4269d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4270d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4271d949cac1SHarald Welte 4272d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4273d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4274d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4275337b9d02STakashi Iwai get_mux_nids(codec); 4276d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4277d949cac1SHarald Welte spec->num_mixers++; 4278d949cac1SHarald Welte } 4279d949cac1SHarald Welte 4280d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4281d949cac1SHarald Welte 4282d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 428369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4284d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4285d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4286d949cac1SHarald Welte #endif 4287d949cac1SHarald Welte 42883e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 4289d949cac1SHarald Welte return 0; 4290d949cac1SHarald Welte } 4291d949cac1SHarald Welte 4292eb7188caSLydia Wang /* Patch for VT1718S */ 4293eb7188caSLydia Wang 4294eb7188caSLydia Wang /* capture mixer elements */ 429590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4296eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4297eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4298eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4299eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4300eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4301eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4302eb7188caSLydia Wang HDA_INPUT), 4303eb7188caSLydia Wang { 4304eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4305eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4306eb7188caSLydia Wang * So call somewhat different.. 4307eb7188caSLydia Wang */ 4308eb7188caSLydia Wang .name = "Input Source", 4309eb7188caSLydia Wang .count = 2, 4310eb7188caSLydia Wang .info = via_mux_enum_info, 4311eb7188caSLydia Wang .get = via_mux_enum_get, 4312eb7188caSLydia Wang .put = via_mux_enum_put, 4313eb7188caSLydia Wang }, 4314eb7188caSLydia Wang { } /* end */ 4315eb7188caSLydia Wang }; 4316eb7188caSLydia Wang 431790dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 4318eb7188caSLydia Wang /* 4319eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4320eb7188caSLydia Wang */ 4321eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4322eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4323eb7188caSLydia Wang 43244ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 43254ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 4326eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4327eb7188caSLydia Wang * mixer widget 4328eb7188caSLydia Wang */ 4329eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4330eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4331eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4332eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4333eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 43344ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 4335eb7188caSLydia Wang 4336eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4337eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4338eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4339eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4340eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4341eb7188caSLydia Wang /* PW11 Input enable */ 4342eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4343eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4344eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4345eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4346eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4347eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4348eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4349eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4350eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4351eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4352eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4353eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4354eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4355eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4356eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4357eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4358eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4359eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4360eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4361eb7188caSLydia Wang { } 4362eb7188caSLydia Wang }; 4363eb7188caSLydia Wang 4364eb7188caSLydia Wang 436590dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 4366eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4367eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4368eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4369eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4370eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4371eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4372eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4373eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4374eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4375eb7188caSLydia Wang { } 4376eb7188caSLydia Wang }; 4377eb7188caSLydia Wang 437890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4379eb7188caSLydia Wang .substreams = 2, 4380eb7188caSLydia Wang .channels_min = 2, 4381eb7188caSLydia Wang .channels_max = 10, 4382eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4383eb7188caSLydia Wang .ops = { 4384eb7188caSLydia Wang .open = via_playback_pcm_open, 4385eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4386eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4387eb7188caSLydia Wang .close = via_pcm_open_close, 4388eb7188caSLydia Wang }, 4389eb7188caSLydia Wang }; 4390eb7188caSLydia Wang 439190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4392eb7188caSLydia Wang .substreams = 2, 4393eb7188caSLydia Wang .channels_min = 2, 4394eb7188caSLydia Wang .channels_max = 2, 4395eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4396eb7188caSLydia Wang .ops = { 4397eb7188caSLydia Wang .open = via_pcm_open_close, 4398eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4399eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4400eb7188caSLydia Wang .close = via_pcm_open_close, 4401eb7188caSLydia Wang }, 4402eb7188caSLydia Wang }; 4403eb7188caSLydia Wang 440490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4405eb7188caSLydia Wang .substreams = 2, 4406eb7188caSLydia Wang .channels_min = 2, 4407eb7188caSLydia Wang .channels_max = 2, 4408eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4409eb7188caSLydia Wang .ops = { 4410eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4411eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4412eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4413eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4414eb7188caSLydia Wang }, 4415eb7188caSLydia Wang }; 4416eb7188caSLydia Wang 441790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4418eb7188caSLydia Wang .substreams = 1, 4419eb7188caSLydia Wang .channels_min = 2, 4420eb7188caSLydia Wang .channels_max = 2, 4421eb7188caSLydia Wang }; 4422eb7188caSLydia Wang 4423eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4424eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4425eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4426eb7188caSLydia Wang { 4427eb7188caSLydia Wang int i; 4428eb7188caSLydia Wang hda_nid_t nid; 4429eb7188caSLydia Wang 4430eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4431eb7188caSLydia Wang 4432eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4433eb7188caSLydia Wang 4434eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4435eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4436eb7188caSLydia Wang if (nid) { 4437eb7188caSLydia Wang /* config dac list */ 4438eb7188caSLydia Wang switch (i) { 4439eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4440dda14410STakashi Iwai spec->private_dac_nids[i] = 0x8; 4441eb7188caSLydia Wang break; 4442eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4443dda14410STakashi Iwai spec->private_dac_nids[i] = 0xa; 4444eb7188caSLydia Wang break; 4445eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4446dda14410STakashi Iwai spec->private_dac_nids[i] = 0x9; 4447eb7188caSLydia Wang break; 4448eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4449dda14410STakashi Iwai spec->private_dac_nids[i] = 0xb; 4450eb7188caSLydia Wang break; 4451eb7188caSLydia Wang } 4452eb7188caSLydia Wang } 4453eb7188caSLydia Wang } 4454eb7188caSLydia Wang 4455eb7188caSLydia Wang return 0; 4456eb7188caSLydia Wang } 4457eb7188caSLydia Wang 4458eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4459eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4460eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4461eb7188caSLydia Wang { 4462eb7188caSLydia Wang char name[32]; 4463ea734963STakashi Iwai static const char * const chname[4] = { 4464ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 4465ea734963STakashi Iwai }; 4466eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4467eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4468eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4469eb7188caSLydia Wang int i, err; 4470eb7188caSLydia Wang 4471eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4472eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4473eb7188caSLydia Wang 4474eb7188caSLydia Wang if (!nid) 4475eb7188caSLydia Wang continue; 4476eb7188caSLydia Wang nid_vol = nid_vols[i]; 4477eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4478eb7188caSLydia Wang 4479eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4480eb7188caSLydia Wang /* Center/LFE */ 4481eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4482eb7188caSLydia Wang "Center Playback Volume", 4483eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4484eb7188caSLydia Wang HDA_OUTPUT)); 4485eb7188caSLydia Wang if (err < 0) 4486eb7188caSLydia Wang return err; 4487eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4488eb7188caSLydia Wang "LFE Playback Volume", 4489eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4490eb7188caSLydia Wang HDA_OUTPUT)); 4491eb7188caSLydia Wang if (err < 0) 4492eb7188caSLydia Wang return err; 4493eb7188caSLydia Wang err = via_add_control( 4494eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4495eb7188caSLydia Wang "Center Playback Switch", 4496eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4497eb7188caSLydia Wang HDA_OUTPUT)); 4498eb7188caSLydia Wang if (err < 0) 4499eb7188caSLydia Wang return err; 4500eb7188caSLydia Wang err = via_add_control( 4501eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4502eb7188caSLydia Wang "LFE Playback Switch", 4503eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4504eb7188caSLydia Wang HDA_OUTPUT)); 4505eb7188caSLydia Wang if (err < 0) 4506eb7188caSLydia Wang return err; 4507eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4508eb7188caSLydia Wang /* Front */ 4509eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4510eb7188caSLydia Wang err = via_add_control( 4511eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4512eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4513eb7188caSLydia Wang if (err < 0) 4514eb7188caSLydia Wang return err; 4515eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4516eb7188caSLydia Wang err = via_add_control( 4517eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4518eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4519eb7188caSLydia Wang HDA_OUTPUT)); 4520eb7188caSLydia Wang if (err < 0) 4521eb7188caSLydia Wang return err; 4522eb7188caSLydia Wang } else { 4523eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4524eb7188caSLydia Wang err = via_add_control( 4525eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4526eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4527eb7188caSLydia Wang if (err < 0) 4528eb7188caSLydia Wang return err; 4529eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4530eb7188caSLydia Wang err = via_add_control( 4531eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4532eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4533eb7188caSLydia Wang HDA_OUTPUT)); 4534eb7188caSLydia Wang if (err < 0) 4535eb7188caSLydia Wang return err; 4536eb7188caSLydia Wang } 4537eb7188caSLydia Wang } 4538eb7188caSLydia Wang return 0; 4539eb7188caSLydia Wang } 4540eb7188caSLydia Wang 4541eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4542eb7188caSLydia Wang { 4543eb7188caSLydia Wang int err; 4544eb7188caSLydia Wang 4545eb7188caSLydia Wang if (!pin) 4546eb7188caSLydia Wang return 0; 4547eb7188caSLydia Wang 4548eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4549eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4550eb7188caSLydia Wang 4551eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4552eb7188caSLydia Wang "Headphone Playback Volume", 4553eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4554eb7188caSLydia Wang if (err < 0) 4555eb7188caSLydia Wang return err; 4556eb7188caSLydia Wang 4557eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4558eb7188caSLydia Wang "Headphone Playback Switch", 4559eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4560eb7188caSLydia Wang if (err < 0) 4561eb7188caSLydia Wang return err; 4562eb7188caSLydia Wang 4563eb7188caSLydia Wang create_hp_imux(spec); 4564eb7188caSLydia Wang return 0; 4565eb7188caSLydia Wang } 4566eb7188caSLydia Wang 4567eb7188caSLydia Wang /* create playback/capture controls for input pins */ 456810a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, 4569eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4570eb7188caSLydia Wang { 457190dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x2c, 0x2b, 0x2a, 0x29, 0, 0xff }; 457210a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 4573f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 4574eb7188caSLydia Wang } 4575eb7188caSLydia Wang 4576eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4577eb7188caSLydia Wang { 4578eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4579eb7188caSLydia Wang int err; 4580eb7188caSLydia Wang 4581eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4582eb7188caSLydia Wang 4583eb7188caSLydia Wang if (err < 0) 4584eb7188caSLydia Wang return err; 4585eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4586eb7188caSLydia Wang if (err < 0) 4587eb7188caSLydia Wang return err; 4588eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4589eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4590eb7188caSLydia Wang 4591eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4592eb7188caSLydia Wang if (err < 0) 4593eb7188caSLydia Wang return err; 4594eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4595eb7188caSLydia Wang if (err < 0) 4596eb7188caSLydia Wang return err; 459710a20af7STakashi Iwai err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4598eb7188caSLydia Wang if (err < 0) 4599eb7188caSLydia Wang return err; 4600eb7188caSLydia Wang 4601eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4602eb7188caSLydia Wang 4603eb7188caSLydia Wang fill_dig_outs(codec); 4604eb7188caSLydia Wang 4605eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4606eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4607eb7188caSLydia Wang 4608eb7188caSLydia Wang if (spec->kctls.list) 4609eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4610eb7188caSLydia Wang 4611eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4612eb7188caSLydia Wang 4613eb7188caSLydia Wang if (spec->hp_mux) 46143d83e577STakashi Iwai via_hp_build(codec); 4615eb7188caSLydia Wang 46165b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4617eb7188caSLydia Wang 4618eb7188caSLydia Wang return 1; 4619eb7188caSLydia Wang } 4620eb7188caSLydia Wang 4621eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 462290dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 4623eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4624eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4625eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4626eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4627eb7188caSLydia Wang { } /* end */ 4628eb7188caSLydia Wang }; 4629eb7188caSLydia Wang #endif 4630eb7188caSLydia Wang 46313e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 46323e95b9abSLydia Wang { 46333e95b9abSLydia Wang struct via_spec *spec = codec->spec; 46343e95b9abSLydia Wang int imux_is_smixer; 46353e95b9abSLydia Wang unsigned int parm; 46363e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 46373e95b9abSLydia Wang imux_is_smixer = 46383e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 46393e95b9abSLydia Wang /* inputs */ 46403e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 46413e95b9abSLydia Wang parm = AC_PWRST_D3; 46423e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 46433e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 46443e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 46453e95b9abSLydia Wang if (imux_is_smixer) 46463e95b9abSLydia Wang parm = AC_PWRST_D0; 46473e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 46483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 46493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 46503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 46513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 46523e95b9abSLydia Wang 46533e95b9abSLydia Wang /* outputs */ 46543e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 46553e95b9abSLydia Wang parm = AC_PWRST_D3; 46563e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 46573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 46583e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 46593e95b9abSLydia Wang 46603e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 46613e95b9abSLydia Wang parm = AC_PWRST_D3; 46623e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 46633e95b9abSLydia Wang if (spec->smart51_enabled) 46643e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 46653e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 46663e95b9abSLydia Wang 46673e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 46683e95b9abSLydia Wang parm = AC_PWRST_D3; 46693e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 46703e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 46713e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 46723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 46733e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 46743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 46753e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 46763e95b9abSLydia Wang 46773e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 46783e95b9abSLydia Wang parm = AC_PWRST_D3; 46793e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 46803e95b9abSLydia Wang if (spec->smart51_enabled) 46813e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 46823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 46833e95b9abSLydia Wang 46843e95b9abSLydia Wang if (spec->hp_independent_mode) { 46853e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 46863e95b9abSLydia Wang parm = AC_PWRST_D3; 46873e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 46883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 46893e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 46913e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46923e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 46933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46943e95b9abSLydia Wang } 46953e95b9abSLydia Wang } 46963e95b9abSLydia Wang 4697eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4698eb7188caSLydia Wang { 4699eb7188caSLydia Wang struct via_spec *spec; 4700eb7188caSLydia Wang int err; 4701eb7188caSLydia Wang 4702eb7188caSLydia Wang /* create a codec specific record */ 47035b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4704eb7188caSLydia Wang if (spec == NULL) 4705eb7188caSLydia Wang return -ENOMEM; 4706eb7188caSLydia Wang 4707eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4708eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4709eb7188caSLydia Wang if (err < 0) { 4710eb7188caSLydia Wang via_free(codec); 4711eb7188caSLydia Wang return err; 4712eb7188caSLydia Wang } else if (!err) { 4713eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4714eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4715eb7188caSLydia Wang } 4716eb7188caSLydia Wang 4717eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4718eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4719eb7188caSLydia Wang 4720bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4721bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4722bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4723bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4724bb3c6bfcSLydia Wang else 4725eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4726eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4727eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4728eb7188caSLydia Wang 4729bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4730bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4731bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4732bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4733bb3c6bfcSLydia Wang else 4734eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4735eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4736bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4737eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4738eb7188caSLydia Wang 4739eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4740eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4741eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4742eb7188caSLydia Wang get_mux_nids(codec); 4743bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4744bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4745eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4746eb7188caSLydia Wang spec->num_mixers++; 4747eb7188caSLydia Wang } 4748eb7188caSLydia Wang 4749eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4750eb7188caSLydia Wang 4751eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 47520f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4753eb7188caSLydia Wang 4754eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4755eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4756eb7188caSLydia Wang #endif 4757eb7188caSLydia Wang 47583e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 47593e95b9abSLydia Wang 4760eb7188caSLydia Wang return 0; 4761eb7188caSLydia Wang } 4762f3db423dSLydia Wang 4763f3db423dSLydia Wang /* Patch for VT1716S */ 4764f3db423dSLydia Wang 4765f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4766f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4767f3db423dSLydia Wang { 4768f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4769f3db423dSLydia Wang uinfo->count = 1; 4770f3db423dSLydia Wang uinfo->value.integer.min = 0; 4771f3db423dSLydia Wang uinfo->value.integer.max = 1; 4772f3db423dSLydia Wang return 0; 4773f3db423dSLydia Wang } 4774f3db423dSLydia Wang 4775f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4776f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4777f3db423dSLydia Wang { 4778f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4779f3db423dSLydia Wang int index = 0; 4780f3db423dSLydia Wang 4781f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4782f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4783f3db423dSLydia Wang if (index != -1) 4784f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4785f3db423dSLydia Wang 4786f3db423dSLydia Wang return 0; 4787f3db423dSLydia Wang } 4788f3db423dSLydia Wang 4789f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4790f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4791f3db423dSLydia Wang { 4792f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4793f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4794f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4795f3db423dSLydia Wang 4796f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4797f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4798f3db423dSLydia Wang spec->dmic_enabled = index; 47993e95b9abSLydia Wang set_widgets_power_state(codec); 4800f3db423dSLydia Wang return 1; 4801f3db423dSLydia Wang } 4802f3db423dSLydia Wang 4803f3db423dSLydia Wang /* capture mixer elements */ 480490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4805f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4806f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4807f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4808f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4809f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4810f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4811f3db423dSLydia Wang HDA_INPUT), 4812f3db423dSLydia Wang { 4813f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4814f3db423dSLydia Wang .name = "Input Source", 4815f3db423dSLydia Wang .count = 1, 4816f3db423dSLydia Wang .info = via_mux_enum_info, 4817f3db423dSLydia Wang .get = via_mux_enum_get, 4818f3db423dSLydia Wang .put = via_mux_enum_put, 4819f3db423dSLydia Wang }, 4820f3db423dSLydia Wang { } /* end */ 4821f3db423dSLydia Wang }; 4822f3db423dSLydia Wang 482390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4824f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4825f3db423dSLydia Wang { 4826f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4827f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 48285b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 4829f3db423dSLydia Wang .count = 1, 4830f3db423dSLydia Wang .info = vt1716s_dmic_info, 4831f3db423dSLydia Wang .get = vt1716s_dmic_get, 4832f3db423dSLydia Wang .put = vt1716s_dmic_put, 4833f3db423dSLydia Wang }, 4834f3db423dSLydia Wang {} /* end */ 4835f3db423dSLydia Wang }; 4836f3db423dSLydia Wang 4837f3db423dSLydia Wang 4838f3db423dSLydia Wang /* mono-out mixer elements */ 483990dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4840f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4841f3db423dSLydia Wang { } /* end */ 4842f3db423dSLydia Wang }; 4843f3db423dSLydia Wang 484490dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 4845f3db423dSLydia Wang /* 4846f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4847f3db423dSLydia Wang */ 4848f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4849f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4850f3db423dSLydia Wang 4851f3db423dSLydia Wang 4852f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4853f3db423dSLydia Wang * mixer widget 4854f3db423dSLydia Wang */ 4855f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4856f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4857f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4858f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4859f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4860f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4861f3db423dSLydia Wang 4862f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4863f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4864f3db423dSLydia Wang 4865f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4866f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4867f3db423dSLydia Wang 4868f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4869f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4870f3db423dSLydia Wang 4871f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4872f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4873f3db423dSLydia Wang 4874f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4875f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4876f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4877f3db423dSLydia Wang 4878f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4879f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4880f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4881f3db423dSLydia Wang /* PW12 Output enable */ 4882f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4883f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4884f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4885f3db423dSLydia Wang /* don't bybass mixer */ 4886f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4887f3db423dSLydia Wang /* Enable mono output */ 4888f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4889f3db423dSLydia Wang { } 4890f3db423dSLydia Wang }; 4891f3db423dSLydia Wang 4892f3db423dSLydia Wang 489390dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 4894f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4895f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4896f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4897f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4898f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4899f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4900f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4901f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4902f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4903f3db423dSLydia Wang { } 4904f3db423dSLydia Wang }; 4905f3db423dSLydia Wang 490690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4907f3db423dSLydia Wang .substreams = 2, 4908f3db423dSLydia Wang .channels_min = 2, 4909f3db423dSLydia Wang .channels_max = 6, 4910f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4911f3db423dSLydia Wang .ops = { 4912f3db423dSLydia Wang .open = via_playback_pcm_open, 4913f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4914f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4915f3db423dSLydia Wang .close = via_pcm_open_close, 4916f3db423dSLydia Wang }, 4917f3db423dSLydia Wang }; 4918f3db423dSLydia Wang 491990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4920f3db423dSLydia Wang .substreams = 2, 4921f3db423dSLydia Wang .channels_min = 2, 4922f3db423dSLydia Wang .channels_max = 2, 4923f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4924f3db423dSLydia Wang .ops = { 4925f3db423dSLydia Wang .open = via_pcm_open_close, 4926f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4927f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4928f3db423dSLydia Wang .close = via_pcm_open_close, 4929f3db423dSLydia Wang }, 4930f3db423dSLydia Wang }; 4931f3db423dSLydia Wang 493290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4933f3db423dSLydia Wang .substreams = 2, 4934f3db423dSLydia Wang .channels_min = 2, 4935f3db423dSLydia Wang .channels_max = 2, 4936f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4937f3db423dSLydia Wang .ops = { 4938f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4939f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4940f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4941f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4942f3db423dSLydia Wang }, 4943f3db423dSLydia Wang }; 4944f3db423dSLydia Wang 4945f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4946f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 4947f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4948f3db423dSLydia Wang { int i; 4949f3db423dSLydia Wang hda_nid_t nid; 4950f3db423dSLydia Wang 4951f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4952f3db423dSLydia Wang 4953f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4954f3db423dSLydia Wang 4955f3db423dSLydia Wang for (i = 0; i < 3; i++) { 4956f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4957f3db423dSLydia Wang if (nid) { 4958f3db423dSLydia Wang /* config dac list */ 4959f3db423dSLydia Wang switch (i) { 4960f3db423dSLydia Wang case AUTO_SEQ_FRONT: 4961dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 4962f3db423dSLydia Wang break; 4963f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 4964dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 4965f3db423dSLydia Wang break; 4966f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 4967dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 4968f3db423dSLydia Wang break; 4969f3db423dSLydia Wang } 4970f3db423dSLydia Wang } 4971f3db423dSLydia Wang } 4972f3db423dSLydia Wang 4973f3db423dSLydia Wang return 0; 4974f3db423dSLydia Wang } 4975f3db423dSLydia Wang 4976f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 4977f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 4978f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4979f3db423dSLydia Wang { 4980f3db423dSLydia Wang char name[32]; 4981ea734963STakashi Iwai static const char * const chname[3] = { 4982ea734963STakashi Iwai "Front", "Surround", "C/LFE" 4983ea734963STakashi Iwai }; 4984f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 4985f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 4986f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 4987f3db423dSLydia Wang int i, err; 4988f3db423dSLydia Wang 4989f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 4990f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4991f3db423dSLydia Wang 4992f3db423dSLydia Wang if (!nid) 4993f3db423dSLydia Wang continue; 4994f3db423dSLydia Wang 4995f3db423dSLydia Wang nid_vol = nid_vols[i]; 4996f3db423dSLydia Wang nid_mute = nid_mutes[i]; 4997f3db423dSLydia Wang 4998f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4999f3db423dSLydia Wang err = via_add_control( 5000f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5001f3db423dSLydia Wang "Center Playback Volume", 5002f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 5003f3db423dSLydia Wang if (err < 0) 5004f3db423dSLydia Wang return err; 5005f3db423dSLydia Wang err = via_add_control( 5006f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5007f3db423dSLydia Wang "LFE Playback Volume", 5008f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 5009f3db423dSLydia Wang if (err < 0) 5010f3db423dSLydia Wang return err; 5011f3db423dSLydia Wang err = via_add_control( 5012f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5013f3db423dSLydia Wang "Center Playback Switch", 5014f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 5015f3db423dSLydia Wang HDA_OUTPUT)); 5016f3db423dSLydia Wang if (err < 0) 5017f3db423dSLydia Wang return err; 5018f3db423dSLydia Wang err = via_add_control( 5019f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5020f3db423dSLydia Wang "LFE Playback Switch", 5021f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 5022f3db423dSLydia Wang HDA_OUTPUT)); 5023f3db423dSLydia Wang if (err < 0) 5024f3db423dSLydia Wang return err; 5025f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 5026f3db423dSLydia Wang 5027f3db423dSLydia Wang err = via_add_control( 5028f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5029f3db423dSLydia Wang "Master Front Playback Volume", 5030f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5031f3db423dSLydia Wang if (err < 0) 5032f3db423dSLydia Wang return err; 5033f3db423dSLydia Wang err = via_add_control( 5034f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5035f3db423dSLydia Wang "Master Front Playback Switch", 5036f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5037f3db423dSLydia Wang if (err < 0) 5038f3db423dSLydia Wang return err; 5039f3db423dSLydia Wang 5040f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5041f3db423dSLydia Wang err = via_add_control( 5042f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5043f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5044f3db423dSLydia Wang if (err < 0) 5045f3db423dSLydia Wang return err; 5046f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5047f3db423dSLydia Wang err = via_add_control( 5048f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5049f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5050f3db423dSLydia Wang HDA_OUTPUT)); 5051f3db423dSLydia Wang if (err < 0) 5052f3db423dSLydia Wang return err; 5053f3db423dSLydia Wang } else { 5054f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5055f3db423dSLydia Wang err = via_add_control( 5056f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5057f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5058f3db423dSLydia Wang if (err < 0) 5059f3db423dSLydia Wang return err; 5060f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5061f3db423dSLydia Wang err = via_add_control( 5062f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5063f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5064f3db423dSLydia Wang HDA_OUTPUT)); 5065f3db423dSLydia Wang if (err < 0) 5066f3db423dSLydia Wang return err; 5067f3db423dSLydia Wang } 5068f3db423dSLydia Wang } 5069f3db423dSLydia Wang return 0; 5070f3db423dSLydia Wang } 5071f3db423dSLydia Wang 5072f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5073f3db423dSLydia Wang { 5074f3db423dSLydia Wang int err; 5075f3db423dSLydia Wang 5076f3db423dSLydia Wang if (!pin) 5077f3db423dSLydia Wang return 0; 5078f3db423dSLydia Wang 5079f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 5080f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5081f3db423dSLydia Wang 5082f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5083f3db423dSLydia Wang "Headphone Playback Volume", 5084f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5085f3db423dSLydia Wang if (err < 0) 5086f3db423dSLydia Wang return err; 5087f3db423dSLydia Wang 5088f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5089f3db423dSLydia Wang "Headphone Playback Switch", 5090f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5091f3db423dSLydia Wang if (err < 0) 5092f3db423dSLydia Wang return err; 5093f3db423dSLydia Wang 5094f3db423dSLydia Wang create_hp_imux(spec); 5095f3db423dSLydia Wang return 0; 5096f3db423dSLydia Wang } 5097f3db423dSLydia Wang 5098f3db423dSLydia Wang /* create playback/capture controls for input pins */ 509910a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, 5100f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5101f3db423dSLydia Wang { 510290dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x1f, 0x1a, 0x1b, 0x1e, 0, 0xff }; 510310a20af7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16, pin_idxs, 5104f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 5105f3db423dSLydia Wang } 5106f3db423dSLydia Wang 5107f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5108f3db423dSLydia Wang { 5109f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5110f3db423dSLydia Wang int err; 5111f3db423dSLydia Wang 5112f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5113f3db423dSLydia Wang if (err < 0) 5114f3db423dSLydia Wang return err; 5115f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5116f3db423dSLydia Wang if (err < 0) 5117f3db423dSLydia Wang return err; 5118f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5119f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5120f3db423dSLydia Wang 5121f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5122f3db423dSLydia Wang if (err < 0) 5123f3db423dSLydia Wang return err; 5124f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5125f3db423dSLydia Wang if (err < 0) 5126f3db423dSLydia Wang return err; 512710a20af7STakashi Iwai err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); 5128f3db423dSLydia Wang if (err < 0) 5129f3db423dSLydia Wang return err; 5130f3db423dSLydia Wang 5131f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5132f3db423dSLydia Wang 5133f3db423dSLydia Wang fill_dig_outs(codec); 5134f3db423dSLydia Wang 5135f3db423dSLydia Wang if (spec->kctls.list) 5136f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5137f3db423dSLydia Wang 5138f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5139f3db423dSLydia Wang 5140f3db423dSLydia Wang if (spec->hp_mux) 51413d83e577STakashi Iwai via_hp_build(codec); 5142f3db423dSLydia Wang 51435b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5144f3db423dSLydia Wang 5145f3db423dSLydia Wang return 1; 5146f3db423dSLydia Wang } 5147f3db423dSLydia Wang 5148f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 514990dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 5150f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5151f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5152f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5153f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5154f3db423dSLydia Wang { } /* end */ 5155f3db423dSLydia Wang }; 5156f3db423dSLydia Wang #endif 5157f3db423dSLydia Wang 51583e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 51593e95b9abSLydia Wang { 51603e95b9abSLydia Wang struct via_spec *spec = codec->spec; 51613e95b9abSLydia Wang int imux_is_smixer; 51623e95b9abSLydia Wang unsigned int parm; 51633e95b9abSLydia Wang unsigned int mono_out, present; 51643e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 51653e95b9abSLydia Wang imux_is_smixer = 51663e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 51673e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 51683e95b9abSLydia Wang /* inputs */ 51693e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 51703e95b9abSLydia Wang parm = AC_PWRST_D3; 51713e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 51723e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 51733e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51743e95b9abSLydia Wang if (imux_is_smixer) 51753e95b9abSLydia Wang parm = AC_PWRST_D0; 51763e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 51773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 51783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 51793e95b9abSLydia Wang 51803e95b9abSLydia Wang parm = AC_PWRST_D3; 51813e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51823e95b9abSLydia Wang /* PW11 (22h) */ 51833e95b9abSLydia Wang if (spec->dmic_enabled) 51843e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 51853e95b9abSLydia Wang else 51863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 51873e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 51883e95b9abSLydia Wang 51893e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 51903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 51913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 51923e95b9abSLydia Wang 51933e95b9abSLydia Wang /* outputs */ 51943e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 51953e95b9abSLydia Wang parm = AC_PWRST_D3; 51963e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 51973e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 51983e95b9abSLydia Wang if (spec->smart51_enabled) 51993e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 52003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 52013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 52023e95b9abSLydia Wang 52033e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 52043e95b9abSLydia Wang parm = AC_PWRST_D3; 52053e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 52063e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 52073e95b9abSLydia Wang if (spec->smart51_enabled) 52083e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 52093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 52103e95b9abSLydia Wang 52113e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 52123e95b9abSLydia Wang if (spec->smart51_enabled) 52133e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 52143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 52153e95b9abSLydia Wang 52163e95b9abSLydia Wang /* Mono out */ 52173e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 52183e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 52193e95b9abSLydia Wang 52203e95b9abSLydia Wang if (present) 52213e95b9abSLydia Wang mono_out = 0; 52223e95b9abSLydia Wang else { 52233e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 52243e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 52253e95b9abSLydia Wang mono_out = 0; 52263e95b9abSLydia Wang else 52273e95b9abSLydia Wang mono_out = 1; 52283e95b9abSLydia Wang } 52293e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 52303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 52313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 52323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 52333e95b9abSLydia Wang 52343e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 52353e95b9abSLydia Wang parm = AC_PWRST_D3; 52363e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 52373e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 52383e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 52393e95b9abSLydia Wang if (spec->hp_independent_mode) 52403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 52413e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 52423e95b9abSLydia Wang 52433e95b9abSLydia Wang /* force to D0 for internal Speaker */ 52443e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 52453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 52463e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 52473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 52483e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 52493e95b9abSLydia Wang } 52503e95b9abSLydia Wang 5251f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5252f3db423dSLydia Wang { 5253f3db423dSLydia Wang struct via_spec *spec; 5254f3db423dSLydia Wang int err; 5255f3db423dSLydia Wang 5256f3db423dSLydia Wang /* create a codec specific record */ 52575b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5258f3db423dSLydia Wang if (spec == NULL) 5259f3db423dSLydia Wang return -ENOMEM; 5260f3db423dSLydia Wang 5261f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5262f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5263f3db423dSLydia Wang if (err < 0) { 5264f3db423dSLydia Wang via_free(codec); 5265f3db423dSLydia Wang return err; 5266f3db423dSLydia Wang } else if (!err) { 5267f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5268f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5269f3db423dSLydia Wang } 5270f3db423dSLydia Wang 5271f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5272f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5273f3db423dSLydia Wang 5274f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5275f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5276f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5277f3db423dSLydia Wang 5278f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5279f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5280f3db423dSLydia Wang 5281f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5282f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5283f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5284f3db423dSLydia Wang get_mux_nids(codec); 5285f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5286f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5287f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5288f3db423dSLydia Wang spec->num_mixers++; 5289f3db423dSLydia Wang } 5290f3db423dSLydia Wang 5291f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5292f3db423dSLydia Wang spec->num_mixers++; 5293f3db423dSLydia Wang 5294f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5295f3db423dSLydia Wang 5296f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5297f3db423dSLydia Wang 5298f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 52990f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5300f3db423dSLydia Wang 5301f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5302f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5303f3db423dSLydia Wang #endif 5304f3db423dSLydia Wang 53053e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 5306f3db423dSLydia Wang return 0; 5307f3db423dSLydia Wang } 530825eaba2fSLydia Wang 530925eaba2fSLydia Wang /* for vt2002P */ 531025eaba2fSLydia Wang 531125eaba2fSLydia Wang /* capture mixer elements */ 531290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { 531325eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 531425eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 531525eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 531625eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 531725eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 531825eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 531925eaba2fSLydia Wang HDA_INPUT), 532025eaba2fSLydia Wang { 532125eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 532225eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 532325eaba2fSLydia Wang * So call somewhat different.. 532425eaba2fSLydia Wang */ 532525eaba2fSLydia Wang /* .name = "Capture Source", */ 532625eaba2fSLydia Wang .name = "Input Source", 532725eaba2fSLydia Wang .count = 2, 532825eaba2fSLydia Wang .info = via_mux_enum_info, 532925eaba2fSLydia Wang .get = via_mux_enum_get, 533025eaba2fSLydia Wang .put = via_mux_enum_put, 533125eaba2fSLydia Wang }, 533225eaba2fSLydia Wang { } /* end */ 533325eaba2fSLydia Wang }; 533425eaba2fSLydia Wang 533590dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 5336eadb9a80SLydia Wang /* Class-D speaker related verbs */ 5337eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 5338eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 5339eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 534025eaba2fSLydia Wang /* 534125eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 534225eaba2fSLydia Wang */ 534325eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 534425eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 534525eaba2fSLydia Wang 534625eaba2fSLydia Wang 534725eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 534825eaba2fSLydia Wang * mixer widget 534925eaba2fSLydia Wang */ 535025eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 535125eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 535225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 535325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 535425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 535525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 535625eaba2fSLydia Wang 535725eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 535825eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 535925eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 536025eaba2fSLydia Wang 536125eaba2fSLydia Wang /* PW9 Output enable */ 536225eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 536325eaba2fSLydia Wang 536425eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 536525eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 536625eaba2fSLydia Wang 536725eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 536825eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 536925eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 537025eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 537125eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 537225eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 537325eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 537425eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 537525eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 537625eaba2fSLydia Wang 537725eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 537825eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 537925eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 538025eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 538125eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 538225eaba2fSLydia Wang 538325eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 538425eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 538525eaba2fSLydia Wang 538625eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 538725eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 538825eaba2fSLydia Wang { } 538925eaba2fSLydia Wang }; 539090dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 539111890956SLydia Wang /* 539211890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 539311890956SLydia Wang */ 539411890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 539511890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 539611890956SLydia Wang 539711890956SLydia Wang 539811890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 539911890956SLydia Wang * mixer widget 540011890956SLydia Wang */ 540111890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 540211890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 540311890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 540411890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 540511890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 540611890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 540711890956SLydia Wang 540811890956SLydia Wang /* MUX Indices: Mic = 0 */ 540911890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 541011890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 541111890956SLydia Wang 541211890956SLydia Wang /* PW9 Output enable */ 541311890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 541411890956SLydia Wang 541511890956SLydia Wang /* Enable Boost Volume backdoor */ 541611890956SLydia Wang {0x1, 0xfb9, 0x24}, 541711890956SLydia Wang 541811890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 541911890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 542011890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 542111890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 542211890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 542311890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 542411890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 542511890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 542611890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 542711890956SLydia Wang 542811890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 542911890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 543011890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 543111890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 543211890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 543311890956SLydia Wang 543411890956SLydia Wang /* set PW0 index=0 (MW0) */ 543511890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 543611890956SLydia Wang 543711890956SLydia Wang /* Enable AOW0 to MW9 */ 543811890956SLydia Wang {0x1, 0xfb8, 0x88}, 543911890956SLydia Wang { } 544011890956SLydia Wang }; 544125eaba2fSLydia Wang 544225eaba2fSLydia Wang 544390dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 544425eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 544525eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 544625eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 544725eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 544825eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 544925eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 545025eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 545125eaba2fSLydia Wang { } 545225eaba2fSLydia Wang }; 545390dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 545411890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 545511890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 545611890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 545711890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 545811890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 545911890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 546011890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 546111890956SLydia Wang { } 546211890956SLydia Wang }; 546325eaba2fSLydia Wang 546490dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_playback = { 546525eaba2fSLydia Wang .substreams = 2, 546625eaba2fSLydia Wang .channels_min = 2, 546725eaba2fSLydia Wang .channels_max = 2, 546825eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 546925eaba2fSLydia Wang .ops = { 547025eaba2fSLydia Wang .open = via_playback_pcm_open, 547125eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 547225eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 547325eaba2fSLydia Wang .close = via_pcm_open_close, 547425eaba2fSLydia Wang }, 547525eaba2fSLydia Wang }; 547625eaba2fSLydia Wang 547790dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_capture = { 547825eaba2fSLydia Wang .substreams = 2, 547925eaba2fSLydia Wang .channels_min = 2, 548025eaba2fSLydia Wang .channels_max = 2, 548125eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 548225eaba2fSLydia Wang .ops = { 548325eaba2fSLydia Wang .open = via_pcm_open_close, 548425eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 548525eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 548625eaba2fSLydia Wang .close = via_pcm_open_close, 548725eaba2fSLydia Wang }, 548825eaba2fSLydia Wang }; 548925eaba2fSLydia Wang 549090dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { 549125eaba2fSLydia Wang .substreams = 1, 549225eaba2fSLydia Wang .channels_min = 2, 549325eaba2fSLydia Wang .channels_max = 2, 549425eaba2fSLydia Wang /* NID is set in via_build_pcms */ 549525eaba2fSLydia Wang .ops = { 549625eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 549725eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 549825eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 549925eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 550025eaba2fSLydia Wang }, 550125eaba2fSLydia Wang }; 550225eaba2fSLydia Wang 550325eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 550425eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 550525eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 550625eaba2fSLydia Wang { 550725eaba2fSLydia Wang spec->multiout.num_dacs = 1; 550825eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 550925eaba2fSLydia Wang if (cfg->line_out_pins[0]) 5510dda14410STakashi Iwai spec->private_dac_nids[0] = 0x8; 551125eaba2fSLydia Wang return 0; 551225eaba2fSLydia Wang } 551325eaba2fSLydia Wang 551425eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 551525eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 551625eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 551725eaba2fSLydia Wang { 551825eaba2fSLydia Wang int err; 551911890956SLydia Wang hda_nid_t sw_nid; 552025eaba2fSLydia Wang 552125eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 552225eaba2fSLydia Wang return -1; 552325eaba2fSLydia Wang 552411890956SLydia Wang if (spec->codec_type == VT1802) 552511890956SLydia Wang sw_nid = 0x28; 552611890956SLydia Wang else 552711890956SLydia Wang sw_nid = 0x26; 552825eaba2fSLydia Wang 552925eaba2fSLydia Wang /* Line-Out: PortE */ 553025eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 553125eaba2fSLydia Wang "Master Front Playback Volume", 553225eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 553325eaba2fSLydia Wang if (err < 0) 553425eaba2fSLydia Wang return err; 553525eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 553625eaba2fSLydia Wang "Master Front Playback Switch", 553711890956SLydia Wang HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT)); 553825eaba2fSLydia Wang if (err < 0) 553925eaba2fSLydia Wang return err; 554025eaba2fSLydia Wang 554125eaba2fSLydia Wang return 0; 554225eaba2fSLydia Wang } 554325eaba2fSLydia Wang 554425eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 554525eaba2fSLydia Wang { 554625eaba2fSLydia Wang int err; 554725eaba2fSLydia Wang 554825eaba2fSLydia Wang if (!pin) 554925eaba2fSLydia Wang return 0; 555025eaba2fSLydia Wang 555125eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 555225eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 555325eaba2fSLydia Wang 555425eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 555525eaba2fSLydia Wang "Headphone Playback Volume", 555625eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 555725eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 555825eaba2fSLydia Wang if (err < 0) 555925eaba2fSLydia Wang return err; 556025eaba2fSLydia Wang 556125eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 556225eaba2fSLydia Wang "Headphone Playback Switch", 556325eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 556425eaba2fSLydia Wang if (err < 0) 556525eaba2fSLydia Wang return err; 556625eaba2fSLydia Wang 556725eaba2fSLydia Wang create_hp_imux(spec); 556825eaba2fSLydia Wang return 0; 556925eaba2fSLydia Wang } 557025eaba2fSLydia Wang 557125eaba2fSLydia Wang /* create playback/capture controls for input pins */ 557210a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, 557325eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 557425eaba2fSLydia Wang { 557510a20af7STakashi Iwai struct via_spec *spec = codec->spec; 557625eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 557790dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0xff }; 5578f3268512STakashi Iwai int err; 557925eaba2fSLydia Wang 558010a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 5581f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 558225eaba2fSLydia Wang if (err < 0) 558325eaba2fSLydia Wang return err; 558425eaba2fSLydia Wang /* build volume/mute control of loopback */ 55857b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 3, 0x21); 558625eaba2fSLydia Wang if (err < 0) 558725eaba2fSLydia Wang return err; 558825eaba2fSLydia Wang 558925eaba2fSLydia Wang /* for digital mic select */ 559010a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 4, NULL); 559125eaba2fSLydia Wang 559225eaba2fSLydia Wang return 0; 559325eaba2fSLydia Wang } 559425eaba2fSLydia Wang 559525eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 559625eaba2fSLydia Wang { 559725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 559825eaba2fSLydia Wang int err; 559925eaba2fSLydia Wang 560025eaba2fSLydia Wang 560125eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 560225eaba2fSLydia Wang if (err < 0) 560325eaba2fSLydia Wang return err; 560425eaba2fSLydia Wang 560525eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 560625eaba2fSLydia Wang if (err < 0) 560725eaba2fSLydia Wang return err; 560825eaba2fSLydia Wang 560925eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 561025eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 561125eaba2fSLydia Wang 561225eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 561325eaba2fSLydia Wang if (err < 0) 561425eaba2fSLydia Wang return err; 561525eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 561625eaba2fSLydia Wang if (err < 0) 561725eaba2fSLydia Wang return err; 561810a20af7STakashi Iwai err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); 561925eaba2fSLydia Wang if (err < 0) 562025eaba2fSLydia Wang return err; 562125eaba2fSLydia Wang 562225eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 562325eaba2fSLydia Wang 562425eaba2fSLydia Wang fill_dig_outs(codec); 562525eaba2fSLydia Wang 562625eaba2fSLydia Wang if (spec->kctls.list) 562725eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 562825eaba2fSLydia Wang 562925eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 563025eaba2fSLydia Wang 563125eaba2fSLydia Wang if (spec->hp_mux) 56323d83e577STakashi Iwai via_hp_build(codec); 563325eaba2fSLydia Wang 563425eaba2fSLydia Wang return 1; 563525eaba2fSLydia Wang } 563625eaba2fSLydia Wang 563725eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 563890dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 563925eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 564025eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 564125eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 564225eaba2fSLydia Wang { } /* end */ 564325eaba2fSLydia Wang }; 564425eaba2fSLydia Wang #endif 564525eaba2fSLydia Wang 56463e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 56473e95b9abSLydia Wang { 56483e95b9abSLydia Wang struct via_spec *spec = codec->spec; 56493e95b9abSLydia Wang int imux_is_smixer; 56503e95b9abSLydia Wang unsigned int parm; 56513e95b9abSLydia Wang unsigned int present; 56523e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 56533e95b9abSLydia Wang imux_is_smixer = 56543e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 56553e95b9abSLydia Wang /* inputs */ 56563e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 56573e95b9abSLydia Wang parm = AC_PWRST_D3; 56583e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 56593e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 56603e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 56613e95b9abSLydia Wang parm = AC_PWRST_D0; 56623e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 56633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 56643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 56653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 56663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 56673e95b9abSLydia Wang 56683e95b9abSLydia Wang /* outputs */ 56693e95b9abSLydia Wang /* AOW0 (8h)*/ 56703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 56713e95b9abSLydia Wang 567211890956SLydia Wang if (spec->codec_type == VT1802) { 567311890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 567411890956SLydia Wang parm = AC_PWRST_D3; 567511890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 567611890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 567711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 567811890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 567911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 568011890956SLydia Wang } else { 56813e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 56823e95b9abSLydia Wang parm = AC_PWRST_D3; 56833e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 56843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 56853e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 56873e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 568811890956SLydia Wang } 56893e95b9abSLydia Wang 569011890956SLydia Wang if (spec->codec_type == VT1802) { 569111890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 569211890956SLydia Wang parm = AC_PWRST_D3; 569311890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 569411890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 569511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 569611890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 569711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 569811890956SLydia Wang } else { 56993e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 57003e95b9abSLydia Wang parm = AC_PWRST_D3; 57013e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 57023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 57033e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 57043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 57053e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 570611890956SLydia Wang } 57073e95b9abSLydia Wang 57083e95b9abSLydia Wang if (spec->hp_independent_mode) 57093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 57103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 57113e95b9abSLydia Wang 57123e95b9abSLydia Wang /* Class-D */ 57133e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 57143e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 57153e95b9abSLydia Wang 57163e95b9abSLydia Wang parm = AC_PWRST_D3; 57173e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 57183e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 571911890956SLydia Wang if (spec->codec_type == VT1802) 572011890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 572111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 572211890956SLydia Wang else 57233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 57243e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 57253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 57263e95b9abSLydia Wang 57273e95b9abSLydia Wang /* Mono Out */ 57283e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 57293e95b9abSLydia Wang 57303e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 573111890956SLydia Wang if (spec->codec_type == VT1802) { 573211890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 573311890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 573411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 573511890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 573611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 573711890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 573811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 573911890956SLydia Wang } else { 57403e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 57413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 57423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 57433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 57443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 57453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 57463e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 574711890956SLydia Wang } 57483e95b9abSLydia Wang /* MW9 (21h) */ 57493e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 57503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 57513e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 57523e95b9abSLydia Wang else 57533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 57543e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 57553e95b9abSLydia Wang } 575625eaba2fSLydia Wang 575725eaba2fSLydia Wang /* patch for vt2002P */ 575825eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 575925eaba2fSLydia Wang { 576025eaba2fSLydia Wang struct via_spec *spec; 576125eaba2fSLydia Wang int err; 576225eaba2fSLydia Wang 576325eaba2fSLydia Wang /* create a codec specific record */ 57645b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 576525eaba2fSLydia Wang if (spec == NULL) 576625eaba2fSLydia Wang return -ENOMEM; 576725eaba2fSLydia Wang 576825eaba2fSLydia Wang /* automatic parse from the BIOS config */ 576925eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 577025eaba2fSLydia Wang if (err < 0) { 577125eaba2fSLydia Wang via_free(codec); 577225eaba2fSLydia Wang return err; 577325eaba2fSLydia Wang } else if (!err) { 577425eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 577525eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 577625eaba2fSLydia Wang } 577725eaba2fSLydia Wang 577811890956SLydia Wang if (spec->codec_type == VT1802) 577911890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 578011890956SLydia Wang vt1802_volume_init_verbs; 578111890956SLydia Wang else 578211890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 578311890956SLydia Wang vt2002P_volume_init_verbs; 578425eaba2fSLydia Wang 578511890956SLydia Wang if (spec->codec_type == VT1802) 578611890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 578711890956SLydia Wang vt1802_uniwill_init_verbs; 578811890956SLydia Wang else 578911890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 579011890956SLydia Wang vt2002P_uniwill_init_verbs; 579111890956SLydia Wang 579211890956SLydia Wang if (spec->codec_type == VT1802) 579311890956SLydia Wang spec->stream_name_analog = "VT1802 Analog"; 579411890956SLydia Wang else 579525eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 579625eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 579725eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 579825eaba2fSLydia Wang 579911890956SLydia Wang if (spec->codec_type == VT1802) 580011890956SLydia Wang spec->stream_name_digital = "VT1802 Digital"; 580111890956SLydia Wang else 580225eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 580325eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 580425eaba2fSLydia Wang 580525eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 580625eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 580725eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 580825eaba2fSLydia Wang get_mux_nids(codec); 580925eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 581025eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 581125eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 581225eaba2fSLydia Wang spec->num_mixers++; 581325eaba2fSLydia Wang } 581425eaba2fSLydia Wang 581525eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 581625eaba2fSLydia Wang 581725eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 58180f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 581925eaba2fSLydia Wang 582025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 582125eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 582225eaba2fSLydia Wang #endif 582325eaba2fSLydia Wang 58243e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 582525eaba2fSLydia Wang return 0; 582625eaba2fSLydia Wang } 5827ab6734e7SLydia Wang 5828ab6734e7SLydia Wang /* for vt1812 */ 5829ab6734e7SLydia Wang 5830ab6734e7SLydia Wang /* capture mixer elements */ 583190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = { 5832ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5833ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5834ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5835ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5836ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5837ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5838ab6734e7SLydia Wang HDA_INPUT), 5839ab6734e7SLydia Wang { 5840ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5841ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5842ab6734e7SLydia Wang * So call somewhat different.. 5843ab6734e7SLydia Wang */ 5844ab6734e7SLydia Wang .name = "Input Source", 5845ab6734e7SLydia Wang .count = 2, 5846ab6734e7SLydia Wang .info = via_mux_enum_info, 5847ab6734e7SLydia Wang .get = via_mux_enum_get, 5848ab6734e7SLydia Wang .put = via_mux_enum_put, 5849ab6734e7SLydia Wang }, 5850ab6734e7SLydia Wang { } /* end */ 5851ab6734e7SLydia Wang }; 5852ab6734e7SLydia Wang 585390dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 5854ab6734e7SLydia Wang /* 5855ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5856ab6734e7SLydia Wang */ 5857ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5858ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5859ab6734e7SLydia Wang 5860ab6734e7SLydia Wang 5861ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5862ab6734e7SLydia Wang * mixer widget 5863ab6734e7SLydia Wang */ 5864ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5865ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5866ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5867ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5868ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5869ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5870ab6734e7SLydia Wang 5871ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5872ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5873ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5874ab6734e7SLydia Wang 5875ab6734e7SLydia Wang /* PW9 Output enable */ 5876ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5877ab6734e7SLydia Wang 5878ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5879ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5880ab6734e7SLydia Wang 5881ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5882ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5883ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5884ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5885ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5886ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5887ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5888ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5889ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5890ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5891ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5892ab6734e7SLydia Wang 5893ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5894ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5895ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5896ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5897ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5898ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5899ab6734e7SLydia Wang 5900ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5901ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5902ab6734e7SLydia Wang { } 5903ab6734e7SLydia Wang }; 5904ab6734e7SLydia Wang 5905ab6734e7SLydia Wang 590690dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 5907ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5908ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5909ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5910ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5911ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5912ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5913ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5914ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5915ab6734e7SLydia Wang { } 5916ab6734e7SLydia Wang }; 5917ab6734e7SLydia Wang 591890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_playback = { 5919ab6734e7SLydia Wang .substreams = 2, 5920ab6734e7SLydia Wang .channels_min = 2, 5921ab6734e7SLydia Wang .channels_max = 2, 5922ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5923ab6734e7SLydia Wang .ops = { 5924ab6734e7SLydia Wang .open = via_playback_pcm_open, 5925ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5926ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5927ab6734e7SLydia Wang .close = via_pcm_open_close, 5928ab6734e7SLydia Wang }, 5929ab6734e7SLydia Wang }; 5930ab6734e7SLydia Wang 593190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_capture = { 5932ab6734e7SLydia Wang .substreams = 2, 5933ab6734e7SLydia Wang .channels_min = 2, 5934ab6734e7SLydia Wang .channels_max = 2, 5935ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5936ab6734e7SLydia Wang .ops = { 5937ab6734e7SLydia Wang .open = via_pcm_open_close, 5938ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5939ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5940ab6734e7SLydia Wang .close = via_pcm_open_close, 5941ab6734e7SLydia Wang }, 5942ab6734e7SLydia Wang }; 5943ab6734e7SLydia Wang 594490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_digital_playback = { 5945ab6734e7SLydia Wang .substreams = 1, 5946ab6734e7SLydia Wang .channels_min = 2, 5947ab6734e7SLydia Wang .channels_max = 2, 5948ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5949ab6734e7SLydia Wang .ops = { 5950ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5951ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5952ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5953ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5954ab6734e7SLydia Wang }, 5955ab6734e7SLydia Wang }; 5956ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5957ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5958ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5959ab6734e7SLydia Wang { 5960ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5961ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5962ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5963dda14410STakashi Iwai spec->private_dac_nids[0] = 0x8; 5964ab6734e7SLydia Wang return 0; 5965ab6734e7SLydia Wang } 5966ab6734e7SLydia Wang 5967ab6734e7SLydia Wang 5968ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5969ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5970ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5971ab6734e7SLydia Wang { 5972ab6734e7SLydia Wang int err; 5973ab6734e7SLydia Wang 5974ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5975ab6734e7SLydia Wang return -1; 5976ab6734e7SLydia Wang 5977ab6734e7SLydia Wang /* Line-Out: PortE */ 5978ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 59793d83e577STakashi Iwai "Front Playback Volume", 5980ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5981ab6734e7SLydia Wang if (err < 0) 5982ab6734e7SLydia Wang return err; 5983ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 59843d83e577STakashi Iwai "Front Playback Switch", 5985ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5986ab6734e7SLydia Wang if (err < 0) 5987ab6734e7SLydia Wang return err; 5988ab6734e7SLydia Wang 5989ab6734e7SLydia Wang return 0; 5990ab6734e7SLydia Wang } 5991ab6734e7SLydia Wang 5992ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5993ab6734e7SLydia Wang { 5994ab6734e7SLydia Wang int err; 5995ab6734e7SLydia Wang 5996ab6734e7SLydia Wang if (!pin) 5997ab6734e7SLydia Wang return 0; 5998ab6734e7SLydia Wang 5999ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 6000ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 6001ab6734e7SLydia Wang 6002ab6734e7SLydia Wang 6003ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 6004ab6734e7SLydia Wang "Headphone Playback Volume", 6005ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 6006ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 6007ab6734e7SLydia Wang if (err < 0) 6008ab6734e7SLydia Wang return err; 6009ab6734e7SLydia Wang 6010ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 6011ab6734e7SLydia Wang "Headphone Playback Switch", 6012ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 6013ab6734e7SLydia Wang if (err < 0) 6014ab6734e7SLydia Wang return err; 6015ab6734e7SLydia Wang 6016ab6734e7SLydia Wang create_hp_imux(spec); 6017ab6734e7SLydia Wang return 0; 6018ab6734e7SLydia Wang } 6019ab6734e7SLydia Wang 6020ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 602110a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, 6022ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 6023ab6734e7SLydia Wang { 602410a20af7STakashi Iwai struct via_spec *spec = codec->spec; 6025ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 602690dd48a1STakashi Iwai static const hda_nid_t pin_idxs[] = { 0x2b, 0x2a, 0x29, 0, 0, 0xff }; 6027f3268512STakashi Iwai int err; 6028ab6734e7SLydia Wang 602910a20af7STakashi Iwai err = vt_auto_create_analog_input_ctls(codec, cfg, 0x21, pin_idxs, 6030f3268512STakashi Iwai ARRAY_SIZE(pin_idxs)); 6031ab6734e7SLydia Wang if (err < 0) 6032ab6734e7SLydia Wang return err; 6033f3268512STakashi Iwai 6034ab6734e7SLydia Wang /* build volume/mute control of loopback */ 60357b315bb4STakashi Iwai err = via_new_analog_input(spec, "Stereo Mixer", 0, 5, 0x21); 6036ab6734e7SLydia Wang if (err < 0) 6037ab6734e7SLydia Wang return err; 6038ab6734e7SLydia Wang 6039ab6734e7SLydia Wang /* for digital mic select */ 604010a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Digital Mic", 6, NULL); 6041ab6734e7SLydia Wang 6042ab6734e7SLydia Wang return 0; 6043ab6734e7SLydia Wang } 6044ab6734e7SLydia Wang 6045ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 6046ab6734e7SLydia Wang { 6047ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 6048ab6734e7SLydia Wang int err; 6049ab6734e7SLydia Wang 6050ab6734e7SLydia Wang 6051ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 6052ab6734e7SLydia Wang if (err < 0) 6053ab6734e7SLydia Wang return err; 6054ab6734e7SLydia Wang fill_dig_outs(codec); 6055ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 6056ab6734e7SLydia Wang if (err < 0) 6057ab6734e7SLydia Wang return err; 6058ab6734e7SLydia Wang 6059ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 6060ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 6061ab6734e7SLydia Wang 6062ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 6063ab6734e7SLydia Wang if (err < 0) 6064ab6734e7SLydia Wang return err; 6065ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 6066ab6734e7SLydia Wang if (err < 0) 6067ab6734e7SLydia Wang return err; 606810a20af7STakashi Iwai err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); 6069ab6734e7SLydia Wang if (err < 0) 6070ab6734e7SLydia Wang return err; 6071ab6734e7SLydia Wang 6072ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 6073ab6734e7SLydia Wang 6074ab6734e7SLydia Wang fill_dig_outs(codec); 6075ab6734e7SLydia Wang 6076ab6734e7SLydia Wang if (spec->kctls.list) 6077ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 6078ab6734e7SLydia Wang 6079ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 6080ab6734e7SLydia Wang 6081ab6734e7SLydia Wang if (spec->hp_mux) 60823d83e577STakashi Iwai via_hp_build(codec); 6083ab6734e7SLydia Wang 6084ab6734e7SLydia Wang return 1; 6085ab6734e7SLydia Wang } 6086ab6734e7SLydia Wang 6087ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 608890dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 6089ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 6090ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 6091ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 6092ab6734e7SLydia Wang { } /* end */ 6093ab6734e7SLydia Wang }; 6094ab6734e7SLydia Wang #endif 6095ab6734e7SLydia Wang 60963e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 60973e95b9abSLydia Wang { 60983e95b9abSLydia Wang struct via_spec *spec = codec->spec; 60993e95b9abSLydia Wang int imux_is_smixer = 61003e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 61013e95b9abSLydia Wang unsigned int parm; 61023e95b9abSLydia Wang unsigned int present; 61033e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 61043e95b9abSLydia Wang imux_is_smixer = 61053e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 61063e95b9abSLydia Wang /* inputs */ 61073e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 61083e95b9abSLydia Wang parm = AC_PWRST_D3; 61093e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 61103e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 61113e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 61123e95b9abSLydia Wang parm = AC_PWRST_D0; 61133e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 61143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 61153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 61163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 61173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 61183e95b9abSLydia Wang 61193e95b9abSLydia Wang /* outputs */ 61203e95b9abSLydia Wang /* AOW0 (8h)*/ 61213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 61223e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61233e95b9abSLydia Wang 61243e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 61253e95b9abSLydia Wang parm = AC_PWRST_D3; 61263e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 61273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 61283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 61293e95b9abSLydia Wang 61303e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 61313e95b9abSLydia Wang parm = AC_PWRST_D3; 61323e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 61333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 61343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 61353e95b9abSLydia Wang if (spec->hp_independent_mode) 61363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 61373e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61383e95b9abSLydia Wang 61393e95b9abSLydia Wang /* Internal Speaker */ 61403e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 61413e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 61423e95b9abSLydia Wang 61433e95b9abSLydia Wang parm = AC_PWRST_D3; 61443e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 61453e95b9abSLydia Wang if (present) { 61463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 61473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 61493e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61503e95b9abSLydia Wang } else { 61513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 61523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 61543e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61553e95b9abSLydia Wang } 61563e95b9abSLydia Wang 61573e95b9abSLydia Wang 61583e95b9abSLydia Wang /* Mono Out */ 61593e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 61603e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 61613e95b9abSLydia Wang 61623e95b9abSLydia Wang parm = AC_PWRST_D3; 61633e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 61643e95b9abSLydia Wang if (present) { 61653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 61663e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 61683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 61703e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 61713e95b9abSLydia Wang } else { 61723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 61733e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 61753e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 61773e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 61783e95b9abSLydia Wang } 61793e95b9abSLydia Wang 61803e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 61813e95b9abSLydia Wang parm = AC_PWRST_D3; 61823e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 61833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 61843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 61853e95b9abSLydia Wang 61863e95b9abSLydia Wang } 6187ab6734e7SLydia Wang 6188ab6734e7SLydia Wang /* patch for vt1812 */ 6189ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 6190ab6734e7SLydia Wang { 6191ab6734e7SLydia Wang struct via_spec *spec; 6192ab6734e7SLydia Wang int err; 6193ab6734e7SLydia Wang 6194ab6734e7SLydia Wang /* create a codec specific record */ 61955b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 6196ab6734e7SLydia Wang if (spec == NULL) 6197ab6734e7SLydia Wang return -ENOMEM; 6198ab6734e7SLydia Wang 6199ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 6200ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 6201ab6734e7SLydia Wang if (err < 0) { 6202ab6734e7SLydia Wang via_free(codec); 6203ab6734e7SLydia Wang return err; 6204ab6734e7SLydia Wang } else if (!err) { 6205ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 6206ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 6207ab6734e7SLydia Wang } 6208ab6734e7SLydia Wang 6209ab6734e7SLydia Wang 6210ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 6211ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6212ab6734e7SLydia Wang 6213ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 6214ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6215ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6216ab6734e7SLydia Wang 6217ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 6218ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6219ab6734e7SLydia Wang 6220ab6734e7SLydia Wang 6221ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 6222ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 6223ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 6224ab6734e7SLydia Wang get_mux_nids(codec); 6225ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6226ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6227ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6228ab6734e7SLydia Wang spec->num_mixers++; 6229ab6734e7SLydia Wang } 6230ab6734e7SLydia Wang 6231ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6232ab6734e7SLydia Wang 6233ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 62340f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 6235ab6734e7SLydia Wang 6236ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6237ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6238ab6734e7SLydia Wang #endif 6239ab6734e7SLydia Wang 62403e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 6241ab6734e7SLydia Wang return 0; 6242ab6734e7SLydia Wang } 6243ab6734e7SLydia Wang 6244c577b8a1SJoseph Chan /* 6245c577b8a1SJoseph Chan * patch entries 6246c577b8a1SJoseph Chan */ 624790dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 62483218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 62493218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 62503218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 62513218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 62523218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6253f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62543218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6255f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62563218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6257f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62583218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6259f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62603218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6261f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62623218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6263f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62643218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6265f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62663218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6267f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62683218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6269f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62703218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6271f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62723218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6273f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62743218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6275f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62763218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6277f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62783218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6279f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62803218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6281f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62823218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6283f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62843218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6285d949cac1SHarald Welte .patch = patch_vt1708S}, 62863218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6287d949cac1SHarald Welte .patch = patch_vt1708S}, 62883218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6289d949cac1SHarald Welte .patch = patch_vt1708S}, 62903218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6291d949cac1SHarald Welte .patch = patch_vt1708S}, 6292bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 6293d949cac1SHarald Welte .patch = patch_vt1708S}, 62943218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6295d949cac1SHarald Welte .patch = patch_vt1708S}, 62963218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6297d949cac1SHarald Welte .patch = patch_vt1708S}, 62983218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6299d949cac1SHarald Welte .patch = patch_vt1708S}, 63003218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6301d949cac1SHarald Welte .patch = patch_vt1702}, 63023218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6303d949cac1SHarald Welte .patch = patch_vt1702}, 63043218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6305d949cac1SHarald Welte .patch = patch_vt1702}, 63063218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6307d949cac1SHarald Welte .patch = patch_vt1702}, 63083218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6309d949cac1SHarald Welte .patch = patch_vt1702}, 63103218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6311d949cac1SHarald Welte .patch = patch_vt1702}, 63123218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6313d949cac1SHarald Welte .patch = patch_vt1702}, 63143218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6315d949cac1SHarald Welte .patch = patch_vt1702}, 6316eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6317eb7188caSLydia Wang .patch = patch_vt1718S}, 6318eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6319eb7188caSLydia Wang .patch = patch_vt1718S}, 6320bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6321bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6322bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6323bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6324f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6325f3db423dSLydia Wang .patch = patch_vt1716S}, 6326f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6327f3db423dSLydia Wang .patch = patch_vt1716S}, 632825eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 632925eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6330ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 633136dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 633236dd5c4aSLydia Wang .patch = patch_vt1708S}, 633311890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 633411890956SLydia Wang .patch = patch_vt2002P}, 633511890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 633611890956SLydia Wang .patch = patch_vt2002P}, 6337c577b8a1SJoseph Chan {} /* terminator */ 6338c577b8a1SJoseph Chan }; 63391289e9e8STakashi Iwai 63401289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 63411289e9e8STakashi Iwai 63421289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 63431289e9e8STakashi Iwai .preset = snd_hda_preset_via, 63441289e9e8STakashi Iwai .owner = THIS_MODULE, 63451289e9e8STakashi Iwai }; 63461289e9e8STakashi Iwai 63471289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 63481289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 63491289e9e8STakashi Iwai 63501289e9e8STakashi Iwai static int __init patch_via_init(void) 63511289e9e8STakashi Iwai { 63521289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 63531289e9e8STakashi Iwai } 63541289e9e8STakashi Iwai 63551289e9e8STakashi Iwai static void __exit patch_via_exit(void) 63561289e9e8STakashi Iwai { 63571289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 63581289e9e8STakashi Iwai } 63591289e9e8STakashi Iwai 63601289e9e8STakashi Iwai module_init(patch_via_init) 63611289e9e8STakashi Iwai module_exit(patch_via_exit) 6362