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 11882673bc8STakashi Iwai char stream_name_analog[32]; 11990dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1211f2e99feSLydia Wang 12282673bc8STakashi Iwai char stream_name_digital[32]; 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; 132a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 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; 15624088a58STakashi 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; 162e06e5a29STakashi Iwai int vt1708_jack_detect; 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, 267e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 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, 281e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 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 421ab6734e7SLydia Wang 422c577b8a1SJoseph Chan /* add dynamic controls */ 423291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 424291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 425291c9e33STakashi Iwai const char *name) 426c577b8a1SJoseph Chan { 427c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 428c577b8a1SJoseph Chan 429603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 430603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 431c577b8a1SJoseph Chan if (!knew) 432291c9e33STakashi Iwai return NULL; 433291c9e33STakashi Iwai *knew = *tmpl; 434291c9e33STakashi Iwai if (!name) 435291c9e33STakashi Iwai name = tmpl->name; 436291c9e33STakashi Iwai if (name) { 437c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 438c577b8a1SJoseph Chan if (!knew->name) 439291c9e33STakashi Iwai return NULL; 440291c9e33STakashi Iwai } 441291c9e33STakashi Iwai return knew; 442291c9e33STakashi Iwai } 443291c9e33STakashi Iwai 444291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 445291c9e33STakashi Iwai int idx, unsigned long val) 446291c9e33STakashi Iwai { 447291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 448291c9e33STakashi Iwai 449291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 450291c9e33STakashi Iwai if (!knew) 451c577b8a1SJoseph Chan return -ENOMEM; 4524d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 4535e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 454c577b8a1SJoseph Chan knew->private_value = val; 455c577b8a1SJoseph Chan return 0; 456c577b8a1SJoseph Chan } 457c577b8a1SJoseph Chan 4587b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 4597b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 4607b315bb4STakashi Iwai 461291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 4625b0cb1d8SJaroslav Kysela 463603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 464603c4019STakashi Iwai { 465603c4019STakashi Iwai struct via_spec *spec = codec->spec; 466603c4019STakashi Iwai 467603c4019STakashi Iwai if (spec->kctls.list) { 468603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 469603c4019STakashi Iwai int i; 470603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 471603c4019STakashi Iwai kfree(kctl[i].name); 472603c4019STakashi Iwai } 473603c4019STakashi Iwai snd_array_free(&spec->kctls); 474603c4019STakashi Iwai } 475603c4019STakashi Iwai 476c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4779510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4787b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 479c577b8a1SJoseph Chan { 480c577b8a1SJoseph Chan char name[32]; 481c577b8a1SJoseph Chan int err; 482c577b8a1SJoseph Chan 483c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4847b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 485c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 486c577b8a1SJoseph Chan if (err < 0) 487c577b8a1SJoseph Chan return err; 488c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 4897b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 490c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 491c577b8a1SJoseph Chan if (err < 0) 492c577b8a1SJoseph Chan return err; 493c577b8a1SJoseph Chan return 0; 494c577b8a1SJoseph Chan } 495c577b8a1SJoseph Chan 496c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 497c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 498c577b8a1SJoseph Chan int dac_idx) 499c577b8a1SJoseph Chan { 500c577b8a1SJoseph Chan /* set as output */ 501c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 502c577b8a1SJoseph Chan pin_type); 503c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 504c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 505d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 506d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 507d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 508c577b8a1SJoseph Chan } 509c577b8a1SJoseph Chan 510c577b8a1SJoseph Chan 511c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 512c577b8a1SJoseph Chan { 513c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 514c577b8a1SJoseph Chan int i; 515c577b8a1SJoseph Chan 516c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 517c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 518c577b8a1SJoseph Chan if (nid) 519c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 520c577b8a1SJoseph Chan } 521c577b8a1SJoseph Chan } 522c577b8a1SJoseph Chan 523c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 524c577b8a1SJoseph Chan { 525c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 526c577b8a1SJoseph Chan hda_nid_t pin; 52725eaba2fSLydia Wang int i; 528c577b8a1SJoseph Chan 52925eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 53025eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 531c577b8a1SJoseph Chan if (pin) /* connect to front */ 532c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 533c577b8a1SJoseph Chan } 53425eaba2fSLydia Wang } 535c577b8a1SJoseph Chan 53632e0191dSClemens Ladisch static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 53732e0191dSClemens Ladisch 538c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 539c577b8a1SJoseph Chan { 540c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5417b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 54232e0191dSClemens Ladisch unsigned int ctl; 543c577b8a1SJoseph Chan int i; 544c577b8a1SJoseph Chan 5457b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5467b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 54732e0191dSClemens Ladisch if (spec->smart51_enabled && is_smart51_pins(spec, nid)) 54832e0191dSClemens Ladisch ctl = PIN_OUT; 54930649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 55032e0191dSClemens Ladisch ctl = PIN_VREF50; 55132e0191dSClemens Ladisch else 55232e0191dSClemens Ladisch ctl = PIN_IN; 553c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 55432e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 555c577b8a1SJoseph Chan } 556c577b8a1SJoseph Chan } 557f5271101SLydia Wang 558f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 559f5271101SLydia Wang unsigned int *affected_parm) 560f5271101SLydia Wang { 561f5271101SLydia Wang unsigned parm; 562f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 563f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 564f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 565f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5661564b287SLydia Wang struct via_spec *spec = codec->spec; 56724088a58STakashi Iwai unsigned present = 0; 56824088a58STakashi Iwai 56924088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 57024088a58STakashi Iwai if (!no_presence) 57124088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 5721564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5731564b287SLydia Wang || ((no_presence || present) 5741564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 575f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 576f5271101SLydia Wang parm = AC_PWRST_D0; 577f5271101SLydia Wang } else 578f5271101SLydia Wang parm = AC_PWRST_D3; 579f5271101SLydia Wang 580f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 581f5271101SLydia Wang } 582f5271101SLydia Wang 58324088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 58424088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 58524088a58STakashi Iwai { 58624088a58STakashi Iwai static const char * const texts[] = { 58724088a58STakashi Iwai "Disabled", "Enabled" 58824088a58STakashi Iwai }; 58924088a58STakashi Iwai 59024088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 59124088a58STakashi Iwai uinfo->count = 1; 59224088a58STakashi Iwai uinfo->value.enumerated.items = 2; 59324088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 59424088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 59524088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 59624088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 59724088a58STakashi Iwai return 0; 59824088a58STakashi Iwai } 59924088a58STakashi Iwai 60024088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 60124088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 60224088a58STakashi Iwai { 60324088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 60424088a58STakashi Iwai struct via_spec *spec = codec->spec; 60524088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 60624088a58STakashi Iwai return 0; 60724088a58STakashi Iwai } 60824088a58STakashi Iwai 60924088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 61024088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61124088a58STakashi Iwai { 61224088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61324088a58STakashi Iwai struct via_spec *spec = codec->spec; 61424088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 61524088a58STakashi Iwai 61624088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 61724088a58STakashi Iwai return 0; 61824088a58STakashi Iwai spec->no_pin_power_ctl = val; 61924088a58STakashi Iwai set_widgets_power_state(codec); 62024088a58STakashi Iwai return 1; 62124088a58STakashi Iwai } 62224088a58STakashi Iwai 62324088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 62424088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 62524088a58STakashi Iwai .name = "Dynamic Power-Control", 62624088a58STakashi Iwai .info = via_pin_power_ctl_info, 62724088a58STakashi Iwai .get = via_pin_power_ctl_get, 62824088a58STakashi Iwai .put = via_pin_power_ctl_put, 62924088a58STakashi Iwai }; 63024088a58STakashi Iwai 63124088a58STakashi Iwai 632c577b8a1SJoseph Chan /* 633c577b8a1SJoseph Chan * input MUX handling 634c577b8a1SJoseph Chan */ 635c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 636c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 637c577b8a1SJoseph Chan { 638c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 639c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 640c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 641c577b8a1SJoseph Chan } 642c577b8a1SJoseph Chan 643c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 644c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 645c577b8a1SJoseph Chan { 646c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 647c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 648c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 649c577b8a1SJoseph Chan 650c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 651c577b8a1SJoseph Chan return 0; 652c577b8a1SJoseph Chan } 653c577b8a1SJoseph Chan 654c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 655c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 656c577b8a1SJoseph Chan { 657c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 658c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 659c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 660bff5fbf5SLydia Wang int ret; 661c577b8a1SJoseph Chan 662337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 663337b9d02STakashi Iwai return -EINVAL; 664a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 665a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 666a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 667a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 668a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 669bff5fbf5SLydia Wang 670bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 671bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 672bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 673a80e6e3cSLydia Wang /* update jack power state */ 6743e95b9abSLydia Wang set_widgets_power_state(codec); 675a80e6e3cSLydia Wang 676bff5fbf5SLydia Wang return ret; 677c577b8a1SJoseph Chan } 678c577b8a1SJoseph Chan 6790aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6800aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6810aa62aefSHarald Welte { 6820aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6830aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6840aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 6850aa62aefSHarald Welte } 6860aa62aefSHarald Welte 6870aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 6880aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 6890aa62aefSHarald Welte { 6900aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6915b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 692eb7188caSLydia Wang unsigned int pinsel; 693eb7188caSLydia Wang 694eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 695eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 6960aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 6970aa62aefSHarald Welte 0x00); 6980aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 6990aa62aefSHarald Welte 7000aa62aefSHarald Welte return 0; 7010aa62aefSHarald Welte } 7020aa62aefSHarald Welte 7030713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 7040713efebSLydia Wang { 7050713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 7060713efebSLydia Wang if (ctl) { 7070713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7080713efebSLydia Wang ctl->vd[0].access |= active 7090713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 7100713efebSLydia Wang snd_ctl_notify(codec->bus->card, 7110713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 7120713efebSLydia Wang } 7130713efebSLydia Wang } 7140713efebSLydia Wang 7155b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 7165b0cb1d8SJaroslav Kysela { 7175b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 7185b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 7195b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 7205b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 7215b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 722e87885feSLydia Wang case VT2002P: return 0x19; 723e87885feSLydia Wang case VT1802: return 0x15; 724e87885feSLydia Wang case VT1812: return 0x15; 7255b0cb1d8SJaroslav Kysela default: return 0; 7265b0cb1d8SJaroslav Kysela } 7275b0cb1d8SJaroslav Kysela } 7285b0cb1d8SJaroslav Kysela 729cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 730cdc1784dSLydia Wang { 731cdc1784dSLydia Wang /* mute side channel */ 732cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 733e87885feSLydia Wang unsigned int parm; 7345b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 735cdc1784dSLydia Wang 736e87885feSLydia Wang if (sw3) { 737e87885feSLydia Wang if (VT2002P_COMPATIBLE(spec)) 738e87885feSLydia Wang parm = spec->hp_independent_mode ? 739e87885feSLydia Wang AMP_IN_MUTE(1) : AMP_IN_UNMUTE(1); 740e87885feSLydia Wang else 741e87885feSLydia Wang parm = spec->hp_independent_mode ? 742e87885feSLydia Wang AMP_OUT_MUTE : AMP_OUT_UNMUTE; 743e87885feSLydia Wang snd_hda_codec_write(codec, sw3, 0, 744e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 745e87885feSLydia Wang if (spec->codec_type == VT1812) 746e87885feSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 747e87885feSLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, parm); 748e87885feSLydia Wang } 749cdc1784dSLydia Wang return 0; 750cdc1784dSLydia Wang } 751cdc1784dSLydia Wang 7520aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7530aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7540aa62aefSHarald Welte { 7550aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7560aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7575b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7580aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 759cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 760cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 761cdc1784dSLydia Wang ? 1 : 0; 762ce0e5a9eSLydia Wang if (spec->codec_type == VT1718S) 763ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 764ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel ? 2 : 0); 765ce0e5a9eSLydia Wang else 766ce0e5a9eSLydia Wang snd_hda_codec_write(codec, nid, 0, 767ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 7680aa62aefSHarald Welte 769ce0e5a9eSLydia Wang if (spec->codec_type == VT1812) 770ce0e5a9eSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 771ce0e5a9eSLydia Wang AC_VERB_SET_CONNECT_SEL, pinsel); 772cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 773cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 774cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 7750aa62aefSHarald Welte 0, 0, 0); 7760aa62aefSHarald Welte 777cdc1784dSLydia Wang update_side_mute_status(codec); 7780713efebSLydia Wang /* update HP volume/swtich active state */ 7790713efebSLydia Wang if (spec->codec_type == VT1708S 780eb7188caSLydia Wang || spec->codec_type == VT1702 781f3db423dSLydia Wang || spec->codec_type == VT1718S 78225eaba2fSLydia Wang || spec->codec_type == VT1716S 78311890956SLydia Wang || VT2002P_COMPATIBLE(spec)) { 7840713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 7850713efebSLydia Wang spec->hp_independent_mode); 7860713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 7870713efebSLydia Wang spec->hp_independent_mode); 7880713efebSLydia Wang } 789ce0e5a9eSLydia Wang /* update jack power state */ 7903e95b9abSLydia Wang set_widgets_power_state(codec); 7910aa62aefSHarald Welte return 0; 7920aa62aefSHarald Welte } 7930aa62aefSHarald Welte 79490dd48a1STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer[2] = { 7950aa62aefSHarald Welte { 7960aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7970aa62aefSHarald Welte .name = "Independent HP", 7980aa62aefSHarald Welte .info = via_independent_hp_info, 7990aa62aefSHarald Welte .get = via_independent_hp_get, 8000aa62aefSHarald Welte .put = via_independent_hp_put, 8010aa62aefSHarald Welte }, 8025b0cb1d8SJaroslav Kysela { 8035b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 8045b0cb1d8SJaroslav Kysela .name = "Independent HP", 8055b0cb1d8SJaroslav Kysela }, 8060aa62aefSHarald Welte }; 8070aa62aefSHarald Welte 8083d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8095b0cb1d8SJaroslav Kysela { 8103d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8115b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8125b0cb1d8SJaroslav Kysela hda_nid_t nid; 8133d83e577STakashi Iwai int nums; 8143d83e577STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 8155b0cb1d8SJaroslav Kysela 8165b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 8175b0cb1d8SJaroslav Kysela case VT1718S: 8185b0cb1d8SJaroslav Kysela nid = 0x34; 8195b0cb1d8SJaroslav Kysela break; 8205b0cb1d8SJaroslav Kysela case VT2002P: 82111890956SLydia Wang case VT1802: 8225b0cb1d8SJaroslav Kysela nid = 0x35; 8235b0cb1d8SJaroslav Kysela break; 8245b0cb1d8SJaroslav Kysela case VT1812: 8255b0cb1d8SJaroslav Kysela nid = 0x3d; 8265b0cb1d8SJaroslav Kysela break; 8275b0cb1d8SJaroslav Kysela default: 8285b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 8295b0cb1d8SJaroslav Kysela break; 8305b0cb1d8SJaroslav Kysela } 8315b0cb1d8SJaroslav Kysela 832ee3c35c0SLydia Wang if (spec->codec_type != VT1708) { 833ee3c35c0SLydia Wang nums = snd_hda_get_connections(codec, nid, 834ee3c35c0SLydia Wang conn, HDA_MAX_CONNECTIONS); 8353d83e577STakashi Iwai if (nums <= 1) 8363d83e577STakashi Iwai return 0; 837ee3c35c0SLydia Wang } 8383d83e577STakashi Iwai 8393d83e577STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer[0]); 8403d83e577STakashi Iwai if (knew == NULL) 8413d83e577STakashi Iwai return -ENOMEM; 8423d83e577STakashi Iwai 8435b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8445b0cb1d8SJaroslav Kysela knew->private_value = nid; 8455b0cb1d8SJaroslav Kysela 8465b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 8475b0cb1d8SJaroslav Kysela if (knew == NULL) 8485b0cb1d8SJaroslav Kysela return -ENOMEM; 8495b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 8505b0cb1d8SJaroslav Kysela 8515b0cb1d8SJaroslav Kysela return 0; 8525b0cb1d8SJaroslav Kysela } 8535b0cb1d8SJaroslav Kysela 8541564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8551564b287SLydia Wang { 8561564b287SLydia Wang int i; 8571564b287SLydia Wang struct snd_ctl_elem_id id; 858525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 859525566cbSLydia Wang struct snd_kcontrol *ctl; 8601564b287SLydia Wang 8611564b287SLydia Wang memset(&id, 0, sizeof(id)); 8621564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 8631564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 8641564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 865525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 866525566cbSLydia Wang if (ctl) 867525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 868525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 869525566cbSLydia Wang &ctl->id); 8701564b287SLydia Wang } 8711564b287SLydia Wang } 8721564b287SLydia Wang 8731564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8741564b287SLydia Wang { 8751564b287SLydia Wang struct via_spec *spec = codec->spec; 8761564b287SLydia Wang hda_nid_t nid_mixer; 8771564b287SLydia Wang int start_idx; 8781564b287SLydia Wang int end_idx; 8791564b287SLydia Wang int i; 8801564b287SLydia Wang /* get nid of MW0 and start & end index */ 8811564b287SLydia Wang switch (spec->codec_type) { 8821564b287SLydia Wang case VT1708: 8831564b287SLydia Wang nid_mixer = 0x17; 8841564b287SLydia Wang start_idx = 2; 8851564b287SLydia Wang end_idx = 4; 8861564b287SLydia Wang break; 8871564b287SLydia Wang case VT1709_10CH: 8881564b287SLydia Wang case VT1709_6CH: 8891564b287SLydia Wang nid_mixer = 0x18; 8901564b287SLydia Wang start_idx = 2; 8911564b287SLydia Wang end_idx = 4; 8921564b287SLydia Wang break; 8931564b287SLydia Wang case VT1708B_8CH: 8941564b287SLydia Wang case VT1708B_4CH: 8951564b287SLydia Wang case VT1708S: 896f3db423dSLydia Wang case VT1716S: 8971564b287SLydia Wang nid_mixer = 0x16; 8981564b287SLydia Wang start_idx = 2; 8991564b287SLydia Wang end_idx = 4; 9001564b287SLydia Wang break; 901ab657e0cSLydia Wang case VT1718S: 902ab657e0cSLydia Wang nid_mixer = 0x21; 903ab657e0cSLydia Wang start_idx = 1; 904ab657e0cSLydia Wang end_idx = 3; 905ab657e0cSLydia Wang break; 9061564b287SLydia Wang default: 9071564b287SLydia Wang return; 9081564b287SLydia Wang } 9091564b287SLydia Wang /* check AA path's mute status */ 9101564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 9111564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 9121564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 9131564b287SLydia Wang HDA_AMP_MUTE, val); 9141564b287SLydia Wang } 9151564b287SLydia Wang } 9161564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 9171564b287SLydia Wang { 9187b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9197b315bb4STakashi Iwai int i; 9207b315bb4STakashi Iwai 9217b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9227b315bb4STakashi Iwai if (pin == cfg->inputs[i].pin) 92386e2959aSTakashi Iwai return cfg->inputs[i].type <= AUTO_PIN_LINE_IN; 9241564b287SLydia Wang } 9257b315bb4STakashi Iwai return 0; 9261564b287SLydia Wang } 9271564b287SLydia Wang 9281564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 9291564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 9301564b287SLydia Wang { 9311564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 9321564b287SLydia Wang uinfo->count = 1; 9331564b287SLydia Wang uinfo->value.integer.min = 0; 9341564b287SLydia Wang uinfo->value.integer.max = 1; 9351564b287SLydia Wang return 0; 9361564b287SLydia Wang } 9371564b287SLydia Wang 9381564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 9391564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9401564b287SLydia Wang { 9411564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9421564b287SLydia Wang struct via_spec *spec = codec->spec; 9437b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9441564b287SLydia Wang int on = 1; 9451564b287SLydia Wang int i; 9461564b287SLydia Wang 9477b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9487b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9497b315bb4STakashi Iwai int ctl = snd_hda_codec_read(codec, nid, 0, 9507b315bb4STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 95186e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9527b315bb4STakashi Iwai continue; 95386e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9547b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9551564b287SLydia Wang continue; /* ignore FMic for independent HP */ 9567b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 9571564b287SLydia Wang on = 0; 9581564b287SLydia Wang } 9591564b287SLydia Wang *ucontrol->value.integer.value = on; 9601564b287SLydia Wang return 0; 9611564b287SLydia Wang } 9621564b287SLydia Wang 9631564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 9641564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 9651564b287SLydia Wang { 9661564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 9671564b287SLydia Wang struct via_spec *spec = codec->spec; 9687b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9691564b287SLydia Wang int out_in = *ucontrol->value.integer.value 9701564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 9711564b287SLydia Wang int i; 9721564b287SLydia Wang 9737b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9747b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 9757b315bb4STakashi Iwai unsigned int parm; 9767b315bb4STakashi Iwai 97786e2959aSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 9787b315bb4STakashi Iwai continue; 97986e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9807b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9811564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 9827b315bb4STakashi Iwai 9837b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 9841564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9851564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 9861564b287SLydia Wang parm |= out_in; 9871564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 9881564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 9891564b287SLydia Wang parm); 9901564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 9911564b287SLydia Wang mute_aa_path(codec, 1); 9921564b287SLydia Wang notify_aa_path_ctls(codec); 9931564b287SLydia Wang } 9947b315bb4STakashi Iwai if (spec->codec_type == VT1718S) { 995eb7188caSLydia Wang snd_hda_codec_amp_stereo( 996eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 997eb7188caSLydia Wang HDA_AMP_UNMUTE); 9981564b287SLydia Wang } 99986e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC) { 1000f3db423dSLydia Wang if (spec->codec_type == VT1708S 1001f3db423dSLydia Wang || spec->codec_type == VT1716S) { 10021564b287SLydia Wang /* input = index 1 (AOW3) */ 10031564b287SLydia Wang snd_hda_codec_write( 10041564b287SLydia Wang codec, nid, 0, 10051564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 10061564b287SLydia Wang snd_hda_codec_amp_stereo( 10071564b287SLydia Wang codec, nid, HDA_OUTPUT, 10081564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 10091564b287SLydia Wang } 10101564b287SLydia Wang } 10111564b287SLydia Wang } 10121564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 10133e95b9abSLydia Wang set_widgets_power_state(codec); 10141564b287SLydia Wang return 1; 10151564b287SLydia Wang } 10161564b287SLydia Wang 10175f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 10181564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 10191564b287SLydia Wang .name = "Smart 5.1", 10201564b287SLydia Wang .count = 1, 10211564b287SLydia Wang .info = via_smart51_info, 10221564b287SLydia Wang .get = via_smart51_get, 10231564b287SLydia Wang .put = via_smart51_put, 10241564b287SLydia Wang }; 10251564b287SLydia Wang 10265b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 10275b0cb1d8SJaroslav Kysela { 10285b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 10297b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 10305b0cb1d8SJaroslav Kysela hda_nid_t nid; 10315b0cb1d8SJaroslav Kysela int i; 10325b0cb1d8SJaroslav Kysela 1033cb34c207SLydia Wang if (!cfg) 1034cb34c207SLydia Wang return 0; 1035cb34c207SLydia Wang if (cfg->line_outs > 2) 1036cb34c207SLydia Wang return 0; 1037cb34c207SLydia Wang 10385f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 10395b0cb1d8SJaroslav Kysela if (knew == NULL) 10405b0cb1d8SJaroslav Kysela return -ENOMEM; 10415b0cb1d8SJaroslav Kysela 10427b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 10437b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 104486e2959aSTakashi Iwai if (cfg->inputs[i].type <= AUTO_PIN_LINE_IN) { 10455f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 10467b315bb4STakashi Iwai break; 10475b0cb1d8SJaroslav Kysela } 10485b0cb1d8SJaroslav Kysela } 10495b0cb1d8SJaroslav Kysela 10505b0cb1d8SJaroslav Kysela return 0; 10515b0cb1d8SJaroslav Kysela } 10525b0cb1d8SJaroslav Kysela 1053c577b8a1SJoseph Chan /* capture mixer elements */ 105490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708_capture_mixer[] = { 1055c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1056c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1057c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1058c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1059c577b8a1SJoseph Chan { 1060c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1061c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1062c577b8a1SJoseph Chan * So call somewhat different.. 1063c577b8a1SJoseph Chan */ 1064c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1065c577b8a1SJoseph Chan .name = "Input Source", 1066c577b8a1SJoseph Chan .count = 1, 1067c577b8a1SJoseph Chan .info = via_mux_enum_info, 1068c577b8a1SJoseph Chan .get = via_mux_enum_get, 1069c577b8a1SJoseph Chan .put = via_mux_enum_put, 1070c577b8a1SJoseph Chan }, 1071c577b8a1SJoseph Chan { } /* end */ 1072c577b8a1SJoseph Chan }; 1073f5271101SLydia Wang 1074f5271101SLydia Wang /* check AA path's mute statue */ 1075f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1076f5271101SLydia Wang { 1077f5271101SLydia Wang int mute = 1; 1078f5271101SLydia Wang hda_nid_t nid_mixer; 1079f5271101SLydia Wang int start_idx; 1080f5271101SLydia Wang int end_idx; 1081f5271101SLydia Wang int i; 1082f5271101SLydia Wang struct via_spec *spec = codec->spec; 1083f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1084f5271101SLydia Wang switch (spec->codec_type) { 1085f5271101SLydia Wang case VT1708B_8CH: 1086f5271101SLydia Wang case VT1708B_4CH: 1087f5271101SLydia Wang case VT1708S: 1088f3db423dSLydia Wang case VT1716S: 1089f5271101SLydia Wang nid_mixer = 0x16; 1090f5271101SLydia Wang start_idx = 2; 1091f5271101SLydia Wang end_idx = 4; 1092f5271101SLydia Wang break; 1093f5271101SLydia Wang case VT1702: 1094f5271101SLydia Wang nid_mixer = 0x1a; 1095f5271101SLydia Wang start_idx = 1; 1096f5271101SLydia Wang end_idx = 3; 1097f5271101SLydia Wang break; 1098eb7188caSLydia Wang case VT1718S: 1099eb7188caSLydia Wang nid_mixer = 0x21; 1100eb7188caSLydia Wang start_idx = 1; 1101eb7188caSLydia Wang end_idx = 3; 1102eb7188caSLydia Wang break; 110325eaba2fSLydia Wang case VT2002P: 1104ab6734e7SLydia Wang case VT1812: 110511890956SLydia Wang case VT1802: 110625eaba2fSLydia Wang nid_mixer = 0x21; 110725eaba2fSLydia Wang start_idx = 0; 110825eaba2fSLydia Wang end_idx = 2; 110925eaba2fSLydia Wang break; 1110f5271101SLydia Wang default: 1111f5271101SLydia Wang return 0; 1112f5271101SLydia Wang } 1113f5271101SLydia Wang /* check AA path's mute status */ 1114f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1115f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1116f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1117f5271101SLydia Wang int shift = 8 * (i % 4); 1118f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1119f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1120f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1121f5271101SLydia Wang /* check mute status while the pin is connected */ 1122f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1123f5271101SLydia Wang HDA_INPUT, i) >> 7; 1124f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1125f5271101SLydia Wang HDA_INPUT, i) >> 7; 1126f5271101SLydia Wang if (!mute_l || !mute_r) { 1127f5271101SLydia Wang mute = 0; 1128f5271101SLydia Wang break; 1129f5271101SLydia Wang } 1130f5271101SLydia Wang } 1131f5271101SLydia Wang } 1132f5271101SLydia Wang return mute; 1133f5271101SLydia Wang } 1134f5271101SLydia Wang 1135f5271101SLydia Wang /* enter/exit analog low-current mode */ 1136f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1137f5271101SLydia Wang { 1138f5271101SLydia Wang struct via_spec *spec = codec->spec; 1139f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1140f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1141f5271101SLydia Wang unsigned int verb = 0; 1142f5271101SLydia Wang unsigned int parm = 0; 1143f5271101SLydia Wang 1144f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1145f5271101SLydia Wang enable = enable && saved_stream_idle; 1146f5271101SLydia Wang else { 1147f5271101SLydia Wang enable = enable && stream_idle; 1148f5271101SLydia Wang saved_stream_idle = stream_idle; 1149f5271101SLydia Wang } 1150f5271101SLydia Wang 1151f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1152f5271101SLydia Wang switch (spec->codec_type) { 1153f5271101SLydia Wang case VT1708B_8CH: 1154f5271101SLydia Wang case VT1708B_4CH: 1155f5271101SLydia Wang verb = 0xf70; 1156f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1157f5271101SLydia Wang break; 1158f5271101SLydia Wang case VT1708S: 1159eb7188caSLydia Wang case VT1718S: 1160f3db423dSLydia Wang case VT1716S: 1161f5271101SLydia Wang verb = 0xf73; 1162f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1163f5271101SLydia Wang break; 1164f5271101SLydia Wang case VT1702: 1165f5271101SLydia Wang verb = 0xf73; 1166f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1167f5271101SLydia Wang break; 116825eaba2fSLydia Wang case VT2002P: 1169ab6734e7SLydia Wang case VT1812: 117011890956SLydia Wang case VT1802: 117125eaba2fSLydia Wang verb = 0xf93; 117225eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 117325eaba2fSLydia Wang break; 1174f5271101SLydia Wang default: 1175f5271101SLydia Wang return; /* other codecs are not supported */ 1176f5271101SLydia Wang } 1177f5271101SLydia Wang /* send verb */ 1178f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1179f5271101SLydia Wang } 1180f5271101SLydia Wang 1181c577b8a1SJoseph Chan /* 1182c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1183c577b8a1SJoseph Chan */ 118490dd48a1STakashi Iwai static const struct hda_verb vt1708_volume_init_verbs[] = { 1185c577b8a1SJoseph Chan /* 1186c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1187c577b8a1SJoseph Chan */ 1188c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1189c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1190c577b8a1SJoseph Chan 1191c577b8a1SJoseph Chan 1192f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1193c577b8a1SJoseph Chan * mixer widget 1194c577b8a1SJoseph Chan */ 1195c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1196f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1197f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1198f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1199f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1200f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1201c577b8a1SJoseph Chan 1202c577b8a1SJoseph Chan /* 1203c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1204c577b8a1SJoseph Chan */ 1205c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1206c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1207c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1208c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1209c577b8a1SJoseph Chan 1210bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1211bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1212c577b8a1SJoseph Chan /* PW9 Output enable */ 1213c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1214aa266fccSLydia Wang /* power down jack detect function */ 1215aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1216f7278fd0SJosepch Chan { } 1217c577b8a1SJoseph Chan }; 1218c577b8a1SJoseph Chan 1219c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1220c577b8a1SJoseph Chan struct hda_codec *codec, 1221c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1222c577b8a1SJoseph Chan { 1223c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 122417314379SLydia Wang int idle = substream->pstr->substream_opened == 1 122517314379SLydia Wang && substream->ref_count == 0; 122617314379SLydia Wang analog_low_current_mode(codec, idle); 12279a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 12289a08160bSTakashi Iwai hinfo); 1229c577b8a1SJoseph Chan } 1230c577b8a1SJoseph Chan 12310aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 12320aa62aefSHarald Welte unsigned int stream_tag, 12330aa62aefSHarald Welte unsigned int format, 12340aa62aefSHarald Welte struct snd_pcm_substream *substream) 12350aa62aefSHarald Welte { 12360aa62aefSHarald Welte struct via_spec *spec = codec->spec; 12370aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1238dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 12390aa62aefSHarald Welte int chs = substream->runtime->channels; 12400aa62aefSHarald Welte int i; 12417c935976SStephen Warren struct hda_spdif_out *spdif = 12427c935976SStephen Warren snd_hda_spdif_out_of_nid(codec, spec->multiout.dig_out_nid); 12430aa62aefSHarald Welte 12440aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 12450aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 12460aa62aefSHarald Welte if (chs == 2 && 12470aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 12480aa62aefSHarald Welte format) && 12497c935976SStephen Warren !(spdif->status & IEC958_AES0_NONAUDIO)) { 12500aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 12510aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 12520aa62aefSHarald Welte * be updated */ 12537c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12540aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12550aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12567c935976SStephen Warren spdif->ctls & 12570aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 12580aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12590aa62aefSHarald Welte stream_tag, 0, format); 12600aa62aefSHarald Welte /* turn on again (if needed) */ 12617c935976SStephen Warren if (spdif->ctls & AC_DIG1_ENABLE) 12620aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 12630aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 12647c935976SStephen Warren spdif->ctls & 0xff); 12650aa62aefSHarald Welte } else { 12660aa62aefSHarald Welte mout->dig_out_used = 0; 12670aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 12680aa62aefSHarald Welte 0, 0, 0); 12690aa62aefSHarald Welte } 12700aa62aefSHarald Welte } 12710aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 12720aa62aefSHarald Welte 12730aa62aefSHarald Welte /* front */ 12740aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 12750aa62aefSHarald Welte 0, format); 12760aa62aefSHarald Welte 1277eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1278eb7188caSLydia Wang && !spec->hp_independent_mode) 12790aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 12800aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 12810aa62aefSHarald Welte 0, format); 12820aa62aefSHarald Welte 12830aa62aefSHarald Welte /* extra outputs copied from front */ 12840aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 12850aa62aefSHarald Welte if (mout->extra_out_nid[i]) 12860aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 12870aa62aefSHarald Welte mout->extra_out_nid[i], 12880aa62aefSHarald Welte stream_tag, 0, format); 12890aa62aefSHarald Welte 12900aa62aefSHarald Welte /* surrounds */ 12910aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 12920aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 12930aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12940aa62aefSHarald Welte i * 2, format); 12950aa62aefSHarald Welte else /* copy front */ 12960aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 12970aa62aefSHarald Welte 0, format); 12980aa62aefSHarald Welte } 12990aa62aefSHarald Welte } 13000aa62aefSHarald Welte 13010aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 13020aa62aefSHarald Welte struct hda_codec *codec, 13030aa62aefSHarald Welte unsigned int stream_tag, 13040aa62aefSHarald Welte unsigned int format, 13050aa62aefSHarald Welte struct snd_pcm_substream *substream) 13060aa62aefSHarald Welte { 13070aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13080aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1309dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13100aa62aefSHarald Welte 13110aa62aefSHarald Welte if (substream->number == 0) 13120aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 13130aa62aefSHarald Welte substream); 13140aa62aefSHarald Welte else { 13150aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13160aa62aefSHarald Welte spec->hp_independent_mode) 13170aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13180aa62aefSHarald Welte stream_tag, 0, format); 13190aa62aefSHarald Welte } 13201f2e99feSLydia Wang vt1708_start_hp_work(spec); 13210aa62aefSHarald Welte return 0; 13220aa62aefSHarald Welte } 13230aa62aefSHarald Welte 13240aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 13250aa62aefSHarald Welte struct hda_codec *codec, 13260aa62aefSHarald Welte struct snd_pcm_substream *substream) 13270aa62aefSHarald Welte { 13280aa62aefSHarald Welte struct via_spec *spec = codec->spec; 13290aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 1330dda14410STakashi Iwai const hda_nid_t *nids = mout->dac_nids; 13310aa62aefSHarald Welte int i; 13320aa62aefSHarald Welte 13330aa62aefSHarald Welte if (substream->number == 0) { 13340aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 13350aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 13360aa62aefSHarald Welte 13370aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 13380aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13390aa62aefSHarald Welte 0, 0, 0); 13400aa62aefSHarald Welte 13410aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 13420aa62aefSHarald Welte if (mout->extra_out_nid[i]) 13430aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 13440aa62aefSHarald Welte mout->extra_out_nid[i], 13450aa62aefSHarald Welte 0, 0, 0); 13460aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 13470aa62aefSHarald Welte if (mout->dig_out_nid && 13480aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 13490aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 13500aa62aefSHarald Welte 0, 0, 0); 13510aa62aefSHarald Welte mout->dig_out_used = 0; 13520aa62aefSHarald Welte } 13530aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 13540aa62aefSHarald Welte } else { 13550aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 13560aa62aefSHarald Welte spec->hp_independent_mode) 13570aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 13580aa62aefSHarald Welte 0, 0, 0); 13590aa62aefSHarald Welte } 13601f2e99feSLydia Wang vt1708_stop_hp_work(spec); 13610aa62aefSHarald Welte return 0; 13620aa62aefSHarald Welte } 13630aa62aefSHarald Welte 1364c577b8a1SJoseph Chan /* 1365c577b8a1SJoseph Chan * Digital out 1366c577b8a1SJoseph Chan */ 1367c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1368c577b8a1SJoseph Chan struct hda_codec *codec, 1369c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1370c577b8a1SJoseph Chan { 1371c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1372c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1373c577b8a1SJoseph Chan } 1374c577b8a1SJoseph Chan 1375c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1376c577b8a1SJoseph Chan struct hda_codec *codec, 1377c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1378c577b8a1SJoseph Chan { 1379c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1380c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1381c577b8a1SJoseph Chan } 1382c577b8a1SJoseph Chan 13835691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 138498aa34c0SHarald Welte struct hda_codec *codec, 138598aa34c0SHarald Welte unsigned int stream_tag, 138698aa34c0SHarald Welte unsigned int format, 138798aa34c0SHarald Welte struct snd_pcm_substream *substream) 138898aa34c0SHarald Welte { 138998aa34c0SHarald Welte struct via_spec *spec = codec->spec; 13909da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 13919da29271STakashi Iwai stream_tag, format, substream); 13929da29271STakashi Iwai } 13935691ec7fSHarald Welte 13949da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 13959da29271STakashi Iwai struct hda_codec *codec, 13969da29271STakashi Iwai struct snd_pcm_substream *substream) 13979da29271STakashi Iwai { 13989da29271STakashi Iwai struct via_spec *spec = codec->spec; 13999da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 140098aa34c0SHarald Welte return 0; 140198aa34c0SHarald Welte } 140298aa34c0SHarald Welte 1403c577b8a1SJoseph Chan /* 1404c577b8a1SJoseph Chan * Analog capture 1405c577b8a1SJoseph Chan */ 1406c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1407c577b8a1SJoseph Chan struct hda_codec *codec, 1408c577b8a1SJoseph Chan unsigned int stream_tag, 1409c577b8a1SJoseph Chan unsigned int format, 1410c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1411c577b8a1SJoseph Chan { 1412c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1413c577b8a1SJoseph Chan 1414c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1415c577b8a1SJoseph Chan stream_tag, 0, format); 1416c577b8a1SJoseph Chan return 0; 1417c577b8a1SJoseph Chan } 1418c577b8a1SJoseph Chan 1419c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1420c577b8a1SJoseph Chan struct hda_codec *codec, 1421c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1422c577b8a1SJoseph Chan { 1423c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1424888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1425c577b8a1SJoseph Chan return 0; 1426c577b8a1SJoseph Chan } 1427c577b8a1SJoseph Chan 142890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_playback = { 14290aa62aefSHarald Welte .substreams = 2, 1430c577b8a1SJoseph Chan .channels_min = 2, 1431c577b8a1SJoseph Chan .channels_max = 8, 1432c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1433c577b8a1SJoseph Chan .ops = { 1434c577b8a1SJoseph Chan .open = via_playback_pcm_open, 14350aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 14360aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1437c577b8a1SJoseph Chan }, 1438c577b8a1SJoseph Chan }; 1439c577b8a1SJoseph Chan 144090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1441c873cc25SLydia Wang .substreams = 2, 1442bc9b5623STakashi Iwai .channels_min = 2, 1443bc9b5623STakashi Iwai .channels_max = 8, 1444bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1445bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1446bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1447bc9b5623STakashi Iwai * disable the 24bit format, so far. 1448bc9b5623STakashi Iwai */ 1449bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1450bc9b5623STakashi Iwai .ops = { 1451bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1452c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1453c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1454bc9b5623STakashi Iwai }, 1455bc9b5623STakashi Iwai }; 1456bc9b5623STakashi Iwai 145790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_capture = { 1458c577b8a1SJoseph Chan .substreams = 2, 1459c577b8a1SJoseph Chan .channels_min = 2, 1460c577b8a1SJoseph Chan .channels_max = 2, 1461c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1462c577b8a1SJoseph Chan .ops = { 1463c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1464c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1465c577b8a1SJoseph Chan }, 1466c577b8a1SJoseph Chan }; 1467c577b8a1SJoseph Chan 146890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_playback = { 1469c577b8a1SJoseph Chan .substreams = 1, 1470c577b8a1SJoseph Chan .channels_min = 2, 1471c577b8a1SJoseph Chan .channels_max = 2, 1472c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1473c577b8a1SJoseph Chan .ops = { 1474c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 14756b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 14769da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 14779da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1478c577b8a1SJoseph Chan }, 1479c577b8a1SJoseph Chan }; 1480c577b8a1SJoseph Chan 148190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_digital_capture = { 1482c577b8a1SJoseph Chan .substreams = 1, 1483c577b8a1SJoseph Chan .channels_min = 2, 1484c577b8a1SJoseph Chan .channels_max = 2, 1485c577b8a1SJoseph Chan }; 1486c577b8a1SJoseph Chan 1487c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1488c577b8a1SJoseph Chan { 1489c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 14905b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 149190dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 14925b0cb1d8SJaroslav Kysela int err, i; 1493c577b8a1SJoseph Chan 149424088a58STakashi Iwai if (spec->set_widgets_power_state) 149524088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 149624088a58STakashi Iwai return -ENOMEM; 149724088a58STakashi Iwai 1498c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1499c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1500c577b8a1SJoseph Chan if (err < 0) 1501c577b8a1SJoseph Chan return err; 1502c577b8a1SJoseph Chan } 1503c577b8a1SJoseph Chan 1504c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1505c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 150674b654c9SStephen Warren spec->multiout.dig_out_nid, 1507c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1508c577b8a1SJoseph Chan if (err < 0) 1509c577b8a1SJoseph Chan return err; 15109a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 15119a08160bSTakashi Iwai &spec->multiout); 15129a08160bSTakashi Iwai if (err < 0) 15139a08160bSTakashi Iwai return err; 15149a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1515c577b8a1SJoseph Chan } 1516c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1517c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1518c577b8a1SJoseph Chan if (err < 0) 1519c577b8a1SJoseph Chan return err; 1520c577b8a1SJoseph Chan } 152117314379SLydia Wang 15225b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 15235b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 15245b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 152521949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 15265b0cb1d8SJaroslav Kysela if (err < 0) 15275b0cb1d8SJaroslav Kysela return err; 15285b0cb1d8SJaroslav Kysela } 15295b0cb1d8SJaroslav Kysela 15305b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 15315b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 15325b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 15335b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 15345b0cb1d8SJaroslav Kysela continue; 15355b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 15365b0cb1d8SJaroslav Kysela if (kctl == NULL) 15375b0cb1d8SJaroslav Kysela continue; 15385b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 15395b0cb1d8SJaroslav Kysela knew->subdevice); 15405b0cb1d8SJaroslav Kysela } 15415b0cb1d8SJaroslav Kysela } 15425b0cb1d8SJaroslav Kysela 154317314379SLydia Wang /* init power states */ 15443e95b9abSLydia Wang set_widgets_power_state(codec); 154517314379SLydia Wang analog_low_current_mode(codec, 1); 154617314379SLydia Wang 1547603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1548c577b8a1SJoseph Chan return 0; 1549c577b8a1SJoseph Chan } 1550c577b8a1SJoseph Chan 1551c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1552c577b8a1SJoseph Chan { 1553c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1554c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1555c577b8a1SJoseph Chan 1556c577b8a1SJoseph Chan codec->num_pcms = 1; 1557c577b8a1SJoseph Chan codec->pcm_info = info; 1558c577b8a1SJoseph Chan 155982673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 156082673bc8STakashi Iwai "%s Analog", codec->chip_name); 1561c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1562377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1563377ff31aSLydia Wang *(spec->stream_analog_playback); 1564377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1565377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1566c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1567c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1568c577b8a1SJoseph Chan 1569c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1570c577b8a1SJoseph Chan spec->multiout.max_channels; 1571c577b8a1SJoseph Chan 1572c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1573c577b8a1SJoseph Chan codec->num_pcms++; 1574c577b8a1SJoseph Chan info++; 157582673bc8STakashi Iwai snprintf(spec->stream_name_digital, 157682673bc8STakashi Iwai sizeof(spec->stream_name_digital), 157782673bc8STakashi Iwai "%s Digital", codec->chip_name); 1578c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 15797ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1580c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1581c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1582c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1583c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1584c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1585c577b8a1SJoseph Chan } 1586c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1587c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1588c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1589c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1590c577b8a1SJoseph Chan spec->dig_in_nid; 1591c577b8a1SJoseph Chan } 1592c577b8a1SJoseph Chan } 1593c577b8a1SJoseph Chan 1594c577b8a1SJoseph Chan return 0; 1595c577b8a1SJoseph Chan } 1596c577b8a1SJoseph Chan 1597c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1598c577b8a1SJoseph Chan { 1599c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1600c577b8a1SJoseph Chan 1601c577b8a1SJoseph Chan if (!spec) 1602c577b8a1SJoseph Chan return; 1603c577b8a1SJoseph Chan 1604603c4019STakashi Iwai via_free_kctls(codec); 16051f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1606c577b8a1SJoseph Chan kfree(codec->spec); 1607c577b8a1SJoseph Chan } 1608c577b8a1SJoseph Chan 160969e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 161069e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 161169e52a80SHarald Welte { 1612dcf34c8cSLydia Wang unsigned int present = 0; 161369e52a80SHarald Welte struct via_spec *spec = codec->spec; 161469e52a80SHarald Welte 1615d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1616dcf34c8cSLydia Wang 1617dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1618dcf34c8cSLydia Wang /* auto mute */ 1619*3e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 1620*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1621*3e0693e2STakashi Iwai present ? 0 : PIN_OUT); 1622dcf34c8cSLydia Wang } 162369e52a80SHarald Welte } 162469e52a80SHarald Welte 1625f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 1626f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 1627f3db423dSLydia Wang { 1628f3db423dSLydia Wang unsigned int hp_present, lineout_present; 1629f3db423dSLydia Wang struct via_spec *spec = codec->spec; 1630f3db423dSLydia Wang 1631f3db423dSLydia Wang if (spec->codec_type != VT1716S) 1632f3db423dSLydia Wang return; 1633f3db423dSLydia Wang 1634d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 1635d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 1636f3db423dSLydia Wang 1637f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 1638f3db423dSLydia Wang if (lineout_present) { 1639*3e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 1640*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1641*3e0693e2STakashi Iwai lineout_present ? 0 : PIN_OUT); 1642f3db423dSLydia Wang return; 1643f3db423dSLydia Wang } 1644f3db423dSLydia Wang 1645d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1646f3db423dSLydia Wang 1647f3db423dSLydia Wang if (!spec->hp_independent_mode) 1648*3e0693e2STakashi Iwai snd_hda_codec_write(codec, 0x2A, 0, 1649*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1650*3e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 1651f3db423dSLydia Wang } 1652f3db423dSLydia Wang 165369e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 165469e52a80SHarald Welte { 165569e52a80SHarald Welte unsigned int gpio_data; 165669e52a80SHarald Welte unsigned int vol_counter; 165769e52a80SHarald Welte unsigned int vol; 165869e52a80SHarald Welte unsigned int master_vol; 165969e52a80SHarald Welte 166069e52a80SHarald Welte struct via_spec *spec = codec->spec; 166169e52a80SHarald Welte 166269e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 166369e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 166469e52a80SHarald Welte 166569e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 166669e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 166769e52a80SHarald Welte 166869e52a80SHarald Welte vol = vol_counter & 0x1F; 166969e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 167069e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 167169e52a80SHarald Welte AC_AMP_GET_INPUT); 167269e52a80SHarald Welte 167369e52a80SHarald Welte if (gpio_data == 0x02) { 167469e52a80SHarald Welte /* unmute line out */ 1675*3e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 1676*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1677*3e0693e2STakashi Iwai PIN_OUT); 167869e52a80SHarald Welte if (vol_counter & 0x20) { 167969e52a80SHarald Welte /* decrease volume */ 168069e52a80SHarald Welte if (vol > master_vol) 168169e52a80SHarald Welte vol = master_vol; 168269e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 168369e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 168469e52a80SHarald Welte master_vol-vol); 168569e52a80SHarald Welte } else { 168669e52a80SHarald Welte /* increase volume */ 168769e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 168869e52a80SHarald Welte HDA_AMP_VOLMASK, 168969e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 169069e52a80SHarald Welte (master_vol+vol)); 169169e52a80SHarald Welte } 169269e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 169369e52a80SHarald Welte /* mute line out */ 1694*3e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 1695*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1696*3e0693e2STakashi Iwai 0); 169769e52a80SHarald Welte } 169869e52a80SHarald Welte } 169969e52a80SHarald Welte 170025eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 170125eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 170225eaba2fSLydia Wang { 170325eaba2fSLydia Wang unsigned int hp_present; 170425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 170525eaba2fSLydia Wang 170627439ce7SLydia Wang if (!VT2002P_COMPATIBLE(spec)) 170725eaba2fSLydia Wang return; 170825eaba2fSLydia Wang 1709d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 171025eaba2fSLydia Wang 171125eaba2fSLydia Wang if (!spec->hp_independent_mode) { 1712*3e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.speaker_pins[0], 0, 1713*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1714*3e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 171525eaba2fSLydia Wang } 171625eaba2fSLydia Wang } 171725eaba2fSLydia Wang 171825eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 171925eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 172025eaba2fSLydia Wang { 172101a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 172201a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 172301a1796bSakpm@linux-foundation.org */ 172401a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 172525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 172625eaba2fSLydia Wang int i; 172725eaba2fSLydia Wang 172825eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 172925eaba2fSLydia Wang return; 173025eaba2fSLydia Wang 1731d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 173225eaba2fSLydia Wang 1733d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 173425eaba2fSLydia Wang 173525eaba2fSLydia Wang if (!spec->hp_independent_mode) { 173625eaba2fSLydia Wang /* Mute Line-Outs */ 173725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 1738*3e0693e2STakashi Iwai snd_hda_codec_write(codec, 1739*3e0693e2STakashi Iwai spec->autocfg.line_out_pins[i], 0, 1740*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1741*3e0693e2STakashi Iwai hp_present ? 0 : PIN_OUT); 174225eaba2fSLydia Wang if (hp_present) 174325eaba2fSLydia Wang present = hp_present; 174425eaba2fSLydia Wang } 174525eaba2fSLydia Wang /* Speakers */ 174625eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 1747*3e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.speaker_pins[i], 0, 1748*3e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 1749*3e0693e2STakashi Iwai present ? 0 : PIN_OUT); 175025eaba2fSLydia Wang } 175125eaba2fSLydia Wang 175225eaba2fSLydia Wang 175369e52a80SHarald Welte /* unsolicited event for jack sensing */ 175469e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 175569e52a80SHarald Welte unsigned int res) 175669e52a80SHarald Welte { 175769e52a80SHarald Welte res >>= 26; 1758ec7e7e42SLydia Wang 1759a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 17603e95b9abSLydia Wang set_widgets_power_state(codec); 1761ec7e7e42SLydia Wang 1762ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1763ec7e7e42SLydia Wang 1764ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1765ec7e7e42SLydia Wang via_hp_automute(codec); 1766ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1767ec7e7e42SLydia Wang via_gpio_control(codec); 1768ec7e7e42SLydia Wang else if (res == VIA_MONO_EVENT) 1769f3db423dSLydia Wang via_mono_automute(codec); 1770ec7e7e42SLydia Wang else if (res == VIA_SPEAKER_EVENT) 177125eaba2fSLydia Wang via_speaker_automute(codec); 1772ec7e7e42SLydia Wang else if (res == VIA_BIND_HP_EVENT) 177325eaba2fSLydia Wang via_hp_bind_automute(codec); 177469e52a80SHarald Welte } 177569e52a80SHarald Welte 1776c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 1777c577b8a1SJoseph Chan { 1778c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 177969e52a80SHarald Welte int i; 178069e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 178169e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 178269e52a80SHarald Welte 1783f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 1784f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 178555d1d6c1STakashi Iwai if (spec->dig_in_pin) { 178655d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1787f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 178812b74c80STakashi Iwai PIN_OUT); 178955d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 1790f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 1791f7278fd0SJosepch Chan } 179212b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 179312b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 179412b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 1795f7278fd0SJosepch Chan 17969da29271STakashi Iwai /* assign slave outs */ 17979da29271STakashi Iwai if (spec->slave_dig_outs[0]) 17989da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 17995691ec7fSHarald Welte 1800c577b8a1SJoseph Chan return 0; 1801c577b8a1SJoseph Chan } 1802c577b8a1SJoseph Chan 18031f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18041f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 18051f2e99feSLydia Wang { 18061f2e99feSLydia Wang struct via_spec *spec = codec->spec; 18071f2e99feSLydia Wang vt1708_stop_hp_work(spec); 18081f2e99feSLydia Wang return 0; 18091f2e99feSLydia Wang } 18101f2e99feSLydia Wang #endif 18111f2e99feSLydia Wang 1812cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1813cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1814cb53c626STakashi Iwai { 1815cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1816cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1817cb53c626STakashi Iwai } 1818cb53c626STakashi Iwai #endif 1819cb53c626STakashi Iwai 1820c577b8a1SJoseph Chan /* 1821c577b8a1SJoseph Chan */ 182290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1823c577b8a1SJoseph Chan .build_controls = via_build_controls, 1824c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1825c577b8a1SJoseph Chan .init = via_init, 1826c577b8a1SJoseph Chan .free = via_free, 18271f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 18281f2e99feSLydia Wang .suspend = via_suspend, 18291f2e99feSLydia Wang #endif 1830cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1831cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1832cb53c626STakashi Iwai #endif 1833c577b8a1SJoseph Chan }; 1834c577b8a1SJoseph Chan 1835c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 1836c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 1837c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1838c577b8a1SJoseph Chan { 1839c577b8a1SJoseph Chan int i; 1840c577b8a1SJoseph Chan hda_nid_t nid; 1841c577b8a1SJoseph Chan 1842c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 1843c577b8a1SJoseph Chan 1844c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 1845c577b8a1SJoseph Chan 1846c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 1847c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1848c577b8a1SJoseph Chan if (nid) { 1849c577b8a1SJoseph Chan /* config dac list */ 1850c577b8a1SJoseph Chan switch (i) { 1851c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 1852dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 1853c577b8a1SJoseph Chan break; 1854c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 1855dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 1856c577b8a1SJoseph Chan break; 1857c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 1858dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 1859c577b8a1SJoseph Chan break; 1860c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 1861dda14410STakashi Iwai spec->private_dac_nids[i] = 0x13; 1862c577b8a1SJoseph Chan break; 1863c577b8a1SJoseph Chan } 1864c577b8a1SJoseph Chan } 1865c577b8a1SJoseph Chan } 1866c577b8a1SJoseph Chan 1867c577b8a1SJoseph Chan return 0; 1868c577b8a1SJoseph Chan } 1869c577b8a1SJoseph Chan 1870c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 1871c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 1872c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 1873c577b8a1SJoseph Chan { 1874c577b8a1SJoseph Chan char name[32]; 1875ea734963STakashi Iwai static const char * const chname[4] = { 1876ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 1877ea734963STakashi Iwai }; 18789645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 1879c577b8a1SJoseph Chan int i, err; 1880c577b8a1SJoseph Chan 1881c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 1882c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 1883c577b8a1SJoseph Chan 1884c577b8a1SJoseph Chan if (!nid) 1885c577b8a1SJoseph Chan continue; 1886c577b8a1SJoseph Chan 18879645c203SLydia Wang nid_vol = nid_vols[i]; 1888c577b8a1SJoseph Chan 1889c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 1890c577b8a1SJoseph Chan /* Center/LFE */ 1891c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1892c577b8a1SJoseph Chan "Center Playback Volume", 1893f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1894f7278fd0SJosepch Chan HDA_OUTPUT)); 1895c577b8a1SJoseph Chan if (err < 0) 1896c577b8a1SJoseph Chan return err; 1897c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1898c577b8a1SJoseph Chan "LFE Playback Volume", 1899f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1900f7278fd0SJosepch Chan HDA_OUTPUT)); 1901c577b8a1SJoseph Chan if (err < 0) 1902c577b8a1SJoseph Chan return err; 1903c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1904c577b8a1SJoseph Chan "Center Playback Switch", 1905f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 1906f7278fd0SJosepch Chan HDA_OUTPUT)); 1907c577b8a1SJoseph Chan if (err < 0) 1908c577b8a1SJoseph Chan return err; 1909c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1910c577b8a1SJoseph Chan "LFE Playback Switch", 1911f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 1912f7278fd0SJosepch Chan HDA_OUTPUT)); 1913c577b8a1SJoseph Chan if (err < 0) 1914c577b8a1SJoseph Chan return err; 1915c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 1916c577b8a1SJoseph Chan /* add control to mixer index 0 */ 1917c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1918c577b8a1SJoseph Chan "Master Front Playback Volume", 19199645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1920f7278fd0SJosepch Chan HDA_INPUT)); 1921c577b8a1SJoseph Chan if (err < 0) 1922c577b8a1SJoseph Chan return err; 1923c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1924c577b8a1SJoseph Chan "Master Front Playback Switch", 19259645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1926f7278fd0SJosepch Chan HDA_INPUT)); 1927c577b8a1SJoseph Chan if (err < 0) 1928c577b8a1SJoseph Chan return err; 1929c577b8a1SJoseph Chan 1930c577b8a1SJoseph Chan /* add control to PW3 */ 1931c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1932c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1933f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1934f7278fd0SJosepch Chan HDA_OUTPUT)); 1935c577b8a1SJoseph Chan if (err < 0) 1936c577b8a1SJoseph Chan return err; 1937c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1938c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1939f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 1940f7278fd0SJosepch Chan HDA_OUTPUT)); 1941c577b8a1SJoseph Chan if (err < 0) 1942c577b8a1SJoseph Chan return err; 1943c577b8a1SJoseph Chan } else { 1944c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 1945c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1946f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1947f7278fd0SJosepch Chan HDA_OUTPUT)); 1948c577b8a1SJoseph Chan if (err < 0) 1949c577b8a1SJoseph Chan return err; 1950c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 1951c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 1952f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 1953f7278fd0SJosepch Chan HDA_OUTPUT)); 1954c577b8a1SJoseph Chan if (err < 0) 1955c577b8a1SJoseph Chan return err; 1956c577b8a1SJoseph Chan } 1957c577b8a1SJoseph Chan } 1958c577b8a1SJoseph Chan 1959c577b8a1SJoseph Chan return 0; 1960c577b8a1SJoseph Chan } 1961c577b8a1SJoseph Chan 19620aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 19630aa62aefSHarald Welte { 19640aa62aefSHarald Welte int i; 19650aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1966ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 19670aa62aefSHarald Welte 19680aa62aefSHarald Welte /* for hp mode select */ 196910a20af7STakashi Iwai for (i = 0; texts[i]; i++) 197010a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 19710aa62aefSHarald Welte 19720aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 19730aa62aefSHarald Welte } 19740aa62aefSHarald Welte 1975c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 1976c577b8a1SJoseph Chan { 1977c577b8a1SJoseph Chan int err; 1978c577b8a1SJoseph Chan 1979c577b8a1SJoseph Chan if (!pin) 1980c577b8a1SJoseph Chan return 0; 1981c577b8a1SJoseph Chan 1982c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 1983cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 1984c577b8a1SJoseph Chan 1985c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 1986c577b8a1SJoseph Chan "Headphone Playback Volume", 1987c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1988c577b8a1SJoseph Chan if (err < 0) 1989c577b8a1SJoseph Chan return err; 1990c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1991c577b8a1SJoseph Chan "Headphone Playback Switch", 1992c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 1993c577b8a1SJoseph Chan if (err < 0) 1994c577b8a1SJoseph Chan return err; 1995c577b8a1SJoseph Chan 19960aa62aefSHarald Welte create_hp_imux(spec); 19970aa62aefSHarald Welte 1998c577b8a1SJoseph Chan return 0; 1999c577b8a1SJoseph Chan } 2000c577b8a1SJoseph Chan 2001a766d0d7STakashi Iwai static int get_connection_index(struct hda_codec *codec, hda_nid_t mux, 2002a766d0d7STakashi Iwai hda_nid_t nid) 2003a766d0d7STakashi Iwai { 2004a766d0d7STakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 2005a766d0d7STakashi Iwai int i, nums; 2006a766d0d7STakashi Iwai 2007a766d0d7STakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 2008a766d0d7STakashi Iwai for (i = 0; i < nums; i++) 2009a766d0d7STakashi Iwai if (conn[i] == nid) 2010a766d0d7STakashi Iwai return i; 2011a766d0d7STakashi Iwai return -1; 2012a766d0d7STakashi Iwai } 2013a766d0d7STakashi Iwai 2014a766d0d7STakashi Iwai /* look for ADCs */ 2015a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2016a766d0d7STakashi Iwai { 2017a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2018a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2019a766d0d7STakashi Iwai int i; 2020a766d0d7STakashi Iwai 2021a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2022a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2023a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2024a766d0d7STakashi Iwai continue; 2025a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2026a766d0d7STakashi Iwai continue; 2027a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2028a766d0d7STakashi Iwai continue; 2029a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2030a766d0d7STakashi Iwai return -ENOMEM; 2031a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2032a766d0d7STakashi Iwai } 2033a766d0d7STakashi Iwai return 0; 2034a766d0d7STakashi Iwai } 2035a766d0d7STakashi Iwai 2036a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2037a766d0d7STakashi Iwai 2038c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 203910a20af7STakashi Iwai static int vt_auto_create_analog_input_ctls(struct hda_codec *codec, 2040f3268512STakashi Iwai const struct auto_pin_cfg *cfg, 2041a766d0d7STakashi Iwai hda_nid_t mix_nid) 2042c577b8a1SJoseph Chan { 204310a20af7STakashi Iwai struct via_spec *spec = codec->spec; 20440aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2045a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 2046a766d0d7STakashi Iwai hda_nid_t cap_nid; 2047a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 2048a766d0d7STakashi Iwai int num_idxs; 2049a766d0d7STakashi Iwai 2050a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2051a766d0d7STakashi Iwai if (err < 0) 2052a766d0d7STakashi Iwai return err; 2053a766d0d7STakashi Iwai err = get_mux_nids(codec); 2054a766d0d7STakashi Iwai if (err < 0) 2055a766d0d7STakashi Iwai return err; 2056a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 2057a766d0d7STakashi Iwai 2058a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 2059a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 2060a766d0d7STakashi Iwai if (num_idxs <= 0) 2061a766d0d7STakashi Iwai return 0; 2062c577b8a1SJoseph Chan 2063c577b8a1SJoseph Chan /* for internal loopback recording select */ 2064f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 2065a766d0d7STakashi Iwai if (pin_idxs[idx] == mix_nid) { 206610a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 2067f3268512STakashi Iwai break; 2068f3268512STakashi Iwai } 2069f3268512STakashi Iwai } 2070c577b8a1SJoseph Chan 20717b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 207210a20af7STakashi Iwai const char *label; 20737b315bb4STakashi Iwai type = cfg->inputs[i].type; 2074f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 20757b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 2076c577b8a1SJoseph Chan break; 2077f3268512STakashi Iwai if (idx >= num_idxs) 2078f3268512STakashi Iwai continue; 20797b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 20807b315bb4STakashi Iwai type_idx++; 20817b315bb4STakashi Iwai else 20827b315bb4STakashi Iwai type_idx = 0; 208310a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2084a766d0d7STakashi Iwai idx2 = get_connection_index(codec, mix_nid, pin_idxs[idx]); 2085a766d0d7STakashi Iwai if (idx2 >= 0) 208616922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2087a766d0d7STakashi Iwai idx2, mix_nid); 2088c577b8a1SJoseph Chan if (err < 0) 2089c577b8a1SJoseph Chan return err; 209010a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 2091c577b8a1SJoseph Chan } 2092c577b8a1SJoseph Chan return 0; 2093c577b8a1SJoseph Chan } 2094c577b8a1SJoseph Chan 2095f3268512STakashi Iwai /* create playback/capture controls for input pins */ 209610a20af7STakashi Iwai static int vt1708_auto_create_analog_input_ctls(struct hda_codec *codec, 2097f3268512STakashi Iwai const struct auto_pin_cfg *cfg) 2098f3268512STakashi Iwai { 2099a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x17); 2100f3268512STakashi Iwai } 2101f3268512STakashi Iwai 2102cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 210390dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2104cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2105cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2106cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2107cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2108cb53c626STakashi Iwai { } /* end */ 2109cb53c626STakashi Iwai }; 2110cb53c626STakashi Iwai #endif 2111cb53c626STakashi Iwai 211276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 211376d9b0ddSHarald Welte { 211476d9b0ddSHarald Welte unsigned int def_conf; 211576d9b0ddSHarald Welte unsigned char seqassoc; 211676d9b0ddSHarald Welte 21172f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 211876d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 211976d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 212082ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 212182ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 212276d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 21232f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 212476d9b0ddSHarald Welte } 212576d9b0ddSHarald Welte 212676d9b0ddSHarald Welte return; 212776d9b0ddSHarald Welte } 212876d9b0ddSHarald Welte 2129e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 21301f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21311f2e99feSLydia Wang { 21321f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21331f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21341f2e99feSLydia Wang 21351f2e99feSLydia Wang if (spec->codec_type != VT1708) 21361f2e99feSLydia Wang return 0; 2137e06e5a29STakashi Iwai spec->vt1708_jack_detect = 21381f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2139e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 21401f2e99feSLydia Wang return 0; 21411f2e99feSLydia Wang } 21421f2e99feSLydia Wang 2143e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 21441f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 21451f2e99feSLydia Wang { 21461f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 21471f2e99feSLydia Wang struct via_spec *spec = codec->spec; 21481f2e99feSLydia Wang int change; 21491f2e99feSLydia Wang 21501f2e99feSLydia Wang if (spec->codec_type != VT1708) 21511f2e99feSLydia Wang return 0; 2152e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 21531f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2154e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2155e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 21561f2e99feSLydia Wang mute_aa_path(codec, 1); 21571f2e99feSLydia Wang notify_aa_path_ctls(codec); 21581f2e99feSLydia Wang } 21591f2e99feSLydia Wang return change; 21601f2e99feSLydia Wang } 21611f2e99feSLydia Wang 2162e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 21631f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 21641f2e99feSLydia Wang .name = "Jack Detect", 21651f2e99feSLydia Wang .count = 1, 21661f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2167e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2168e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 21691f2e99feSLydia Wang }; 21701f2e99feSLydia Wang 2171c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2172c577b8a1SJoseph Chan { 2173c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2174c577b8a1SJoseph Chan int err; 2175c577b8a1SJoseph Chan 217676d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 217776d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 217876d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 217976d9b0ddSHarald Welte 2180c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2181c577b8a1SJoseph Chan if (err < 0) 2182c577b8a1SJoseph Chan return err; 2183c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2184c577b8a1SJoseph Chan if (err < 0) 2185c577b8a1SJoseph Chan return err; 2186c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2187c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2188c577b8a1SJoseph Chan 2189c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2190c577b8a1SJoseph Chan if (err < 0) 2191c577b8a1SJoseph Chan return err; 2192c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2193c577b8a1SJoseph Chan if (err < 0) 2194c577b8a1SJoseph Chan return err; 219510a20af7STakashi Iwai err = vt1708_auto_create_analog_input_ctls(codec, &spec->autocfg); 2196c577b8a1SJoseph Chan if (err < 0) 2197c577b8a1SJoseph Chan return err; 21981f2e99feSLydia Wang /* add jack detect on/off control */ 2199e06e5a29STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 2200e06e5a29STakashi Iwai return -ENOMEM; 2201c577b8a1SJoseph Chan 2202c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2203c577b8a1SJoseph Chan 22040852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2205c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 220655d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2207c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2208c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2209c577b8a1SJoseph Chan 2210603c4019STakashi Iwai if (spec->kctls.list) 2211603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2212c577b8a1SJoseph Chan 221369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2214c577b8a1SJoseph Chan 22150aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 22160aa62aefSHarald Welte 2217f8fdd495SHarald Welte if (spec->hp_mux) 22183d83e577STakashi Iwai via_hp_build(codec); 2219c577b8a1SJoseph Chan 22205b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2221c577b8a1SJoseph Chan return 1; 2222c577b8a1SJoseph Chan } 2223c577b8a1SJoseph Chan 2224c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2225c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2226c577b8a1SJoseph Chan { 222725eaba2fSLydia Wang struct via_spec *spec = codec->spec; 222825eaba2fSLydia Wang 2229c577b8a1SJoseph Chan via_init(codec); 2230c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2231c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2232c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 223311890956SLydia Wang 223411890956SLydia Wang if (VT2002P_COMPATIBLE(spec)) { 223525eaba2fSLydia Wang via_hp_bind_automute(codec); 223625eaba2fSLydia Wang } else { 223725eaba2fSLydia Wang via_hp_automute(codec); 223825eaba2fSLydia Wang via_speaker_automute(codec); 223925eaba2fSLydia Wang } 224025eaba2fSLydia Wang 2241c577b8a1SJoseph Chan return 0; 2242c577b8a1SJoseph Chan } 2243c577b8a1SJoseph Chan 22441f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 22451f2e99feSLydia Wang { 22461f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 22471f2e99feSLydia Wang vt1708_hp_work.work); 22481f2e99feSLydia Wang if (spec->codec_type != VT1708) 22491f2e99feSLydia Wang return; 22501f2e99feSLydia Wang /* if jack state toggled */ 22511f2e99feSLydia Wang if (spec->vt1708_hp_present 2252d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 22531f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 22541f2e99feSLydia Wang via_hp_automute(spec->codec); 22551f2e99feSLydia Wang } 22561f2e99feSLydia Wang vt1708_start_hp_work(spec); 22571f2e99feSLydia Wang } 22581f2e99feSLydia Wang 2259337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2260337b9d02STakashi Iwai { 2261337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2262337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2263337b9d02STakashi Iwai unsigned int type; 2264337b9d02STakashi Iwai int i, n; 2265337b9d02STakashi Iwai 2266337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2267337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2268337b9d02STakashi Iwai while (nid) { 2269a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22701c55d521STakashi Iwai if (type == AC_WID_PIN) 22711c55d521STakashi Iwai break; 2272337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2273337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2274337b9d02STakashi Iwai if (n <= 0) 2275337b9d02STakashi Iwai break; 2276337b9d02STakashi Iwai if (n > 1) { 2277337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2278337b9d02STakashi Iwai break; 2279337b9d02STakashi Iwai } 2280337b9d02STakashi Iwai nid = conn[0]; 2281337b9d02STakashi Iwai } 2282337b9d02STakashi Iwai } 22831c55d521STakashi Iwai return 0; 2284337b9d02STakashi Iwai } 2285337b9d02STakashi Iwai 2286c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2287c577b8a1SJoseph Chan { 2288c577b8a1SJoseph Chan struct via_spec *spec; 2289c577b8a1SJoseph Chan int err; 2290c577b8a1SJoseph Chan 2291c577b8a1SJoseph Chan /* create a codec specific record */ 22925b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2293c577b8a1SJoseph Chan if (spec == NULL) 2294c577b8a1SJoseph Chan return -ENOMEM; 2295c577b8a1SJoseph Chan 2296c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2297c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2298c577b8a1SJoseph Chan if (err < 0) { 2299c577b8a1SJoseph Chan via_free(codec); 2300c577b8a1SJoseph Chan return err; 2301c577b8a1SJoseph Chan } else if (!err) { 2302c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2303c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2304c577b8a1SJoseph Chan } 2305c577b8a1SJoseph Chan 2306c577b8a1SJoseph Chan 2307c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2308bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2309bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2310bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2311c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2312c577b8a1SJoseph Chan 2313c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2314c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2315c577b8a1SJoseph Chan 2316a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2317c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2318c577b8a1SJoseph Chan spec->num_mixers++; 2319c577b8a1SJoseph Chan } 2320c577b8a1SJoseph Chan 2321c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2322c577b8a1SJoseph Chan 2323c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2324cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2325cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2326cb53c626STakashi Iwai #endif 23271f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2328c577b8a1SJoseph Chan return 0; 2329c577b8a1SJoseph Chan } 2330c577b8a1SJoseph Chan 2331c577b8a1SJoseph Chan /* capture mixer elements */ 233290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1709_capture_mixer[] = { 2333c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2334c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2335c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2336c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2337c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2338c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2339c577b8a1SJoseph Chan { 2340c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2341c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2342c577b8a1SJoseph Chan * So call somewhat different.. 2343c577b8a1SJoseph Chan */ 2344c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2345c577b8a1SJoseph Chan .name = "Input Source", 2346c577b8a1SJoseph Chan .count = 1, 2347c577b8a1SJoseph Chan .info = via_mux_enum_info, 2348c577b8a1SJoseph Chan .get = via_mux_enum_get, 2349c577b8a1SJoseph Chan .put = via_mux_enum_put, 2350c577b8a1SJoseph Chan }, 2351c577b8a1SJoseph Chan { } /* end */ 2352c577b8a1SJoseph Chan }; 2353c577b8a1SJoseph Chan 235490dd48a1STakashi Iwai static const struct hda_verb vt1709_uniwill_init_verbs[] = { 2355a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2356a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 235769e52a80SHarald Welte { } 235869e52a80SHarald Welte }; 235969e52a80SHarald Welte 2360c577b8a1SJoseph Chan /* 2361c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2362c577b8a1SJoseph Chan */ 236390dd48a1STakashi Iwai static const struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2364c577b8a1SJoseph Chan /* 2365c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2366c577b8a1SJoseph Chan */ 2367c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2368c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2369c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2370c577b8a1SJoseph Chan 2371c577b8a1SJoseph Chan 2372f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2373c577b8a1SJoseph Chan * mixer widget 2374c577b8a1SJoseph Chan */ 2375c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2376f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2377f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2378f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2379f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2380f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2381c577b8a1SJoseph Chan 2382c577b8a1SJoseph Chan /* 2383c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2384c577b8a1SJoseph Chan */ 2385c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2386c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2387c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2388c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2389c577b8a1SJoseph Chan 2390c577b8a1SJoseph Chan /* 2391c577b8a1SJoseph Chan * Unmute PW3 and PW4 2392c577b8a1SJoseph Chan */ 2393c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2394c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2395c577b8a1SJoseph Chan 2396bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2397bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2398c577b8a1SJoseph Chan /* PW9 Output enable */ 2399c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2400c577b8a1SJoseph Chan { } 2401c577b8a1SJoseph Chan }; 2402c577b8a1SJoseph Chan 240390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2404c577b8a1SJoseph Chan .substreams = 1, 2405c577b8a1SJoseph Chan .channels_min = 2, 2406c577b8a1SJoseph Chan .channels_max = 10, 2407c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2408c577b8a1SJoseph Chan .ops = { 2409c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2410c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2411c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2412c577b8a1SJoseph Chan }, 2413c577b8a1SJoseph Chan }; 2414c577b8a1SJoseph Chan 241590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2416c577b8a1SJoseph Chan .substreams = 1, 2417c577b8a1SJoseph Chan .channels_min = 2, 2418c577b8a1SJoseph Chan .channels_max = 6, 2419c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2420c577b8a1SJoseph Chan .ops = { 2421c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2422c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2423c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2424c577b8a1SJoseph Chan }, 2425c577b8a1SJoseph Chan }; 2426c577b8a1SJoseph Chan 242790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_analog_capture = { 2428c577b8a1SJoseph Chan .substreams = 2, 2429c577b8a1SJoseph Chan .channels_min = 2, 2430c577b8a1SJoseph Chan .channels_max = 2, 2431c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2432c577b8a1SJoseph Chan .ops = { 2433c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2434c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2435c577b8a1SJoseph Chan }, 2436c577b8a1SJoseph Chan }; 2437c577b8a1SJoseph Chan 243890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_playback = { 2439c577b8a1SJoseph Chan .substreams = 1, 2440c577b8a1SJoseph Chan .channels_min = 2, 2441c577b8a1SJoseph Chan .channels_max = 2, 2442c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2443c577b8a1SJoseph Chan .ops = { 2444c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2445c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2446c577b8a1SJoseph Chan }, 2447c577b8a1SJoseph Chan }; 2448c577b8a1SJoseph Chan 244990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1709_pcm_digital_capture = { 2450c577b8a1SJoseph Chan .substreams = 1, 2451c577b8a1SJoseph Chan .channels_min = 2, 2452c577b8a1SJoseph Chan .channels_max = 2, 2453c577b8a1SJoseph Chan }; 2454c577b8a1SJoseph Chan 2455c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2456c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2457c577b8a1SJoseph Chan { 2458c577b8a1SJoseph Chan int i; 2459c577b8a1SJoseph Chan hda_nid_t nid; 2460c577b8a1SJoseph Chan 2461c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2462c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2463c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2464c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2465c577b8a1SJoseph Chan 2466c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2467c577b8a1SJoseph Chan 2468c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2469c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2470c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2471c577b8a1SJoseph Chan if (nid) { 2472c577b8a1SJoseph Chan /* config dac list */ 2473c577b8a1SJoseph Chan switch (i) { 2474c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2475c577b8a1SJoseph Chan /* AOW0 */ 2476dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 2477c577b8a1SJoseph Chan break; 2478c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2479c577b8a1SJoseph Chan /* AOW2 */ 2480dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 2481c577b8a1SJoseph Chan break; 2482c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2483c577b8a1SJoseph Chan /* AOW3 */ 2484dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 2485c577b8a1SJoseph Chan break; 2486c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2487c577b8a1SJoseph Chan /* AOW1 */ 2488dda14410STakashi Iwai spec->private_dac_nids[i] = 0x27; 2489c577b8a1SJoseph Chan break; 2490c577b8a1SJoseph Chan default: 2491c577b8a1SJoseph Chan break; 2492c577b8a1SJoseph Chan } 2493c577b8a1SJoseph Chan } 2494c577b8a1SJoseph Chan } 2495dda14410STakashi Iwai spec->private_dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2496c577b8a1SJoseph Chan 2497c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2498c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2499c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2500c577b8a1SJoseph Chan if (nid) { 2501c577b8a1SJoseph Chan /* config dac list */ 2502c577b8a1SJoseph Chan switch (i) { 2503c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2504c577b8a1SJoseph Chan /* AOW0 */ 2505dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 2506c577b8a1SJoseph Chan break; 2507c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2508c577b8a1SJoseph Chan /* AOW2 */ 2509dda14410STakashi Iwai spec->private_dac_nids[i] = 0x12; 2510c577b8a1SJoseph Chan break; 2511c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2512c577b8a1SJoseph Chan /* AOW1 */ 2513dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 2514c577b8a1SJoseph Chan break; 2515c577b8a1SJoseph Chan default: 2516c577b8a1SJoseph Chan break; 2517c577b8a1SJoseph Chan } 2518c577b8a1SJoseph Chan } 2519c577b8a1SJoseph Chan } 2520c577b8a1SJoseph Chan } 2521c577b8a1SJoseph Chan 2522c577b8a1SJoseph Chan return 0; 2523c577b8a1SJoseph Chan } 2524c577b8a1SJoseph Chan 2525c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2526c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2527c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2528c577b8a1SJoseph Chan { 2529c577b8a1SJoseph Chan char name[32]; 2530ea734963STakashi Iwai static const char * const chname[4] = { 2531ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 2532ea734963STakashi Iwai }; 25334483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2534c577b8a1SJoseph Chan int i, err; 2535c577b8a1SJoseph Chan 2536c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2537c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2538c577b8a1SJoseph Chan 2539c577b8a1SJoseph Chan if (!nid) 2540c577b8a1SJoseph Chan continue; 2541c577b8a1SJoseph Chan 25424483a2f5SLydia Wang nid_vol = nid_vols[i]; 25434483a2f5SLydia Wang 2544c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2545c577b8a1SJoseph Chan /* Center/LFE */ 2546c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2547c577b8a1SJoseph Chan "Center Playback Volume", 25484483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2549f7278fd0SJosepch Chan HDA_OUTPUT)); 2550c577b8a1SJoseph Chan if (err < 0) 2551c577b8a1SJoseph Chan return err; 2552c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2553c577b8a1SJoseph Chan "LFE Playback Volume", 25544483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2555f7278fd0SJosepch Chan HDA_OUTPUT)); 2556c577b8a1SJoseph Chan if (err < 0) 2557c577b8a1SJoseph Chan return err; 2558c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2559c577b8a1SJoseph Chan "Center Playback Switch", 25604483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2561f7278fd0SJosepch Chan HDA_OUTPUT)); 2562c577b8a1SJoseph Chan if (err < 0) 2563c577b8a1SJoseph Chan return err; 2564c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2565c577b8a1SJoseph Chan "LFE Playback Switch", 25664483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2567f7278fd0SJosepch Chan HDA_OUTPUT)); 2568c577b8a1SJoseph Chan if (err < 0) 2569c577b8a1SJoseph Chan return err; 2570c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 25714483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2572c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2573c577b8a1SJoseph Chan "Master Front Playback Volume", 25744483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2575f7278fd0SJosepch Chan HDA_INPUT)); 2576c577b8a1SJoseph Chan if (err < 0) 2577c577b8a1SJoseph Chan return err; 2578c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2579c577b8a1SJoseph Chan "Master Front Playback Switch", 25804483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2581f7278fd0SJosepch Chan HDA_INPUT)); 2582c577b8a1SJoseph Chan if (err < 0) 2583c577b8a1SJoseph Chan return err; 2584c577b8a1SJoseph Chan 2585c577b8a1SJoseph Chan /* add control to PW3 */ 2586c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2587c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2588f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2589f7278fd0SJosepch Chan HDA_OUTPUT)); 2590c577b8a1SJoseph Chan if (err < 0) 2591c577b8a1SJoseph Chan return err; 2592c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2593c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2594f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2595f7278fd0SJosepch Chan HDA_OUTPUT)); 2596c577b8a1SJoseph Chan if (err < 0) 2597c577b8a1SJoseph Chan return err; 2598c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2599c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2600c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 26014483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2602f7278fd0SJosepch Chan HDA_OUTPUT)); 2603c577b8a1SJoseph Chan if (err < 0) 2604c577b8a1SJoseph Chan return err; 2605c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2606c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 26074483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2608f7278fd0SJosepch Chan HDA_OUTPUT)); 2609c577b8a1SJoseph Chan if (err < 0) 2610c577b8a1SJoseph Chan return err; 2611c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2612c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2613c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 26144483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2615f7278fd0SJosepch Chan HDA_OUTPUT)); 2616c577b8a1SJoseph Chan if (err < 0) 2617c577b8a1SJoseph Chan return err; 2618c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2619c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 26204483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2621f7278fd0SJosepch Chan HDA_OUTPUT)); 2622c577b8a1SJoseph Chan if (err < 0) 2623c577b8a1SJoseph Chan return err; 2624c577b8a1SJoseph Chan } 2625c577b8a1SJoseph Chan } 2626c577b8a1SJoseph Chan 2627c577b8a1SJoseph Chan return 0; 2628c577b8a1SJoseph Chan } 2629c577b8a1SJoseph Chan 2630c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2631c577b8a1SJoseph Chan { 2632c577b8a1SJoseph Chan int err; 2633c577b8a1SJoseph Chan 2634c577b8a1SJoseph Chan if (!pin) 2635c577b8a1SJoseph Chan return 0; 2636c577b8a1SJoseph Chan 2637c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2638c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2639c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2640c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2641cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2642c577b8a1SJoseph Chan 2643c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2644c577b8a1SJoseph Chan "Headphone Playback Volume", 2645c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2646c577b8a1SJoseph Chan if (err < 0) 2647c577b8a1SJoseph Chan return err; 2648c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2649c577b8a1SJoseph Chan "Headphone Playback Switch", 2650c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2651c577b8a1SJoseph Chan if (err < 0) 2652c577b8a1SJoseph Chan return err; 2653c577b8a1SJoseph Chan 2654c577b8a1SJoseph Chan return 0; 2655c577b8a1SJoseph Chan } 2656c577b8a1SJoseph Chan 2657c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 265810a20af7STakashi Iwai static int vt1709_auto_create_analog_input_ctls(struct hda_codec *codec, 2659c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2660c577b8a1SJoseph Chan { 2661a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x18); 2662c577b8a1SJoseph Chan } 2663c577b8a1SJoseph Chan 2664c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 2665c577b8a1SJoseph Chan { 2666c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2667c577b8a1SJoseph Chan int err; 2668c577b8a1SJoseph Chan 2669c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2670c577b8a1SJoseph Chan if (err < 0) 2671c577b8a1SJoseph Chan return err; 2672c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 2673c577b8a1SJoseph Chan if (err < 0) 2674c577b8a1SJoseph Chan return err; 2675c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2676c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2677c577b8a1SJoseph Chan 2678c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 2679c577b8a1SJoseph Chan if (err < 0) 2680c577b8a1SJoseph Chan return err; 2681c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2682c577b8a1SJoseph Chan if (err < 0) 2683c577b8a1SJoseph Chan return err; 268410a20af7STakashi Iwai err = vt1709_auto_create_analog_input_ctls(codec, &spec->autocfg); 2685c577b8a1SJoseph Chan if (err < 0) 2686c577b8a1SJoseph Chan return err; 2687c577b8a1SJoseph Chan 2688c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2689c577b8a1SJoseph Chan 26900852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2691c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 269255d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 2693c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2694c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 2695c577b8a1SJoseph Chan 2696603c4019STakashi Iwai if (spec->kctls.list) 2697603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2698c577b8a1SJoseph Chan 26990aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 2700c577b8a1SJoseph Chan 2701f8fdd495SHarald Welte if (spec->hp_mux) 27023d83e577STakashi Iwai via_hp_build(codec); 2703f8fdd495SHarald Welte 27045b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2705c577b8a1SJoseph Chan return 1; 2706c577b8a1SJoseph Chan } 2707c577b8a1SJoseph Chan 2708cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 270990dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2710cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2711cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2712cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2713cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2714cb53c626STakashi Iwai { } /* end */ 2715cb53c626STakashi Iwai }; 2716cb53c626STakashi Iwai #endif 2717cb53c626STakashi Iwai 2718c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2719c577b8a1SJoseph Chan { 2720c577b8a1SJoseph Chan struct via_spec *spec; 2721c577b8a1SJoseph Chan int err; 2722c577b8a1SJoseph Chan 2723c577b8a1SJoseph Chan /* create a codec specific record */ 27245b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2725c577b8a1SJoseph Chan if (spec == NULL) 2726c577b8a1SJoseph Chan return -ENOMEM; 2727c577b8a1SJoseph Chan 2728c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2729c577b8a1SJoseph Chan if (err < 0) { 2730c577b8a1SJoseph Chan via_free(codec); 2731c577b8a1SJoseph Chan return err; 2732c577b8a1SJoseph Chan } else if (!err) { 2733c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2734c577b8a1SJoseph Chan "Using genenic mode...\n"); 2735c577b8a1SJoseph Chan } 2736c577b8a1SJoseph Chan 273769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 273869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2739c577b8a1SJoseph Chan 2740c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 2741c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2742c577b8a1SJoseph Chan 2743c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2744c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2745c577b8a1SJoseph Chan 2746a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2747c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2748c577b8a1SJoseph Chan spec->num_mixers++; 2749c577b8a1SJoseph Chan } 2750c577b8a1SJoseph Chan 2751c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2752c577b8a1SJoseph Chan 2753c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 275469e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2755cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2756cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2757cb53c626STakashi Iwai #endif 2758c577b8a1SJoseph Chan 2759c577b8a1SJoseph Chan return 0; 2760c577b8a1SJoseph Chan } 2761c577b8a1SJoseph Chan /* 2762c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2763c577b8a1SJoseph Chan */ 276490dd48a1STakashi Iwai static const struct hda_verb vt1709_6ch_volume_init_verbs[] = { 2765c577b8a1SJoseph Chan /* 2766c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2767c577b8a1SJoseph Chan */ 2768c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2769c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2770c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2771c577b8a1SJoseph Chan 2772c577b8a1SJoseph Chan 2773c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2774c577b8a1SJoseph Chan * mixer widget 2775c577b8a1SJoseph Chan */ 2776c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2777c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2778c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2779c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2780c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2781c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2782c577b8a1SJoseph Chan 2783c577b8a1SJoseph Chan /* 2784c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2785c577b8a1SJoseph Chan */ 2786c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2787c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2788c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2789c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2790c577b8a1SJoseph Chan 2791c577b8a1SJoseph Chan /* 2792c577b8a1SJoseph Chan * Unmute PW3 and PW4 2793c577b8a1SJoseph Chan */ 2794c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2795c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2796c577b8a1SJoseph Chan 2797c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 2798c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2799c577b8a1SJoseph Chan /* PW9 Output enable */ 2800c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2801c577b8a1SJoseph Chan { } 2802c577b8a1SJoseph Chan }; 2803c577b8a1SJoseph Chan 2804c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2805c577b8a1SJoseph Chan { 2806c577b8a1SJoseph Chan struct via_spec *spec; 2807c577b8a1SJoseph Chan int err; 2808c577b8a1SJoseph Chan 2809c577b8a1SJoseph Chan /* create a codec specific record */ 28105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2811c577b8a1SJoseph Chan if (spec == NULL) 2812c577b8a1SJoseph Chan return -ENOMEM; 2813c577b8a1SJoseph Chan 2814c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 2815c577b8a1SJoseph Chan if (err < 0) { 2816c577b8a1SJoseph Chan via_free(codec); 2817c577b8a1SJoseph Chan return err; 2818c577b8a1SJoseph Chan } else if (!err) { 2819c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 2820c577b8a1SJoseph Chan "Using genenic mode...\n"); 2821c577b8a1SJoseph Chan } 2822c577b8a1SJoseph Chan 282369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 282469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 2825c577b8a1SJoseph Chan 2826c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 2827c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 2828c577b8a1SJoseph Chan 2829c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 2830c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 2831c577b8a1SJoseph Chan 2832a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 2833c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 2834c577b8a1SJoseph Chan spec->num_mixers++; 2835c577b8a1SJoseph Chan } 2836c577b8a1SJoseph Chan 2837c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2838c577b8a1SJoseph Chan 2839c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 284069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 2841cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2842cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2843cb53c626STakashi Iwai #endif 2844f7278fd0SJosepch Chan return 0; 2845f7278fd0SJosepch Chan } 2846f7278fd0SJosepch Chan 2847f7278fd0SJosepch Chan /* capture mixer elements */ 284890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708B_capture_mixer[] = { 2849f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 2850f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 2851f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 2852f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 2853f7278fd0SJosepch Chan { 2854f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2855f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 2856f7278fd0SJosepch Chan * So call somewhat different.. 2857f7278fd0SJosepch Chan */ 2858f7278fd0SJosepch Chan /* .name = "Capture Source", */ 2859f7278fd0SJosepch Chan .name = "Input Source", 2860f7278fd0SJosepch Chan .count = 1, 2861f7278fd0SJosepch Chan .info = via_mux_enum_info, 2862f7278fd0SJosepch Chan .get = via_mux_enum_get, 2863f7278fd0SJosepch Chan .put = via_mux_enum_put, 2864f7278fd0SJosepch Chan }, 2865f7278fd0SJosepch Chan { } /* end */ 2866f7278fd0SJosepch Chan }; 2867f7278fd0SJosepch Chan /* 2868f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2869f7278fd0SJosepch Chan */ 287090dd48a1STakashi Iwai static const struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 2871f7278fd0SJosepch Chan /* 2872f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2873f7278fd0SJosepch Chan */ 2874f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2875f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2876f7278fd0SJosepch Chan 2877f7278fd0SJosepch Chan 2878f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2879f7278fd0SJosepch Chan * mixer widget 2880f7278fd0SJosepch Chan */ 2881f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2882f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2883f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2884f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2885f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2886f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2887f7278fd0SJosepch Chan 2888f7278fd0SJosepch Chan /* 2889f7278fd0SJosepch Chan * Set up output mixers 2890f7278fd0SJosepch Chan */ 2891f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2892f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2893f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2894f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2895f7278fd0SJosepch Chan 2896f7278fd0SJosepch Chan /* Setup default input to PW4 */ 2897bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 2898f7278fd0SJosepch Chan /* PW9 Output enable */ 2899f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2900f7278fd0SJosepch Chan /* PW10 Input enable */ 2901f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2902f7278fd0SJosepch Chan { } 2903f7278fd0SJosepch Chan }; 2904f7278fd0SJosepch Chan 290590dd48a1STakashi Iwai static const struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 2906f7278fd0SJosepch Chan /* 2907f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 2908f7278fd0SJosepch Chan */ 2909f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2910f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2911f7278fd0SJosepch Chan 2912f7278fd0SJosepch Chan 2913f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2914f7278fd0SJosepch Chan * mixer widget 2915f7278fd0SJosepch Chan */ 2916f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2917f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2918f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2919f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2920f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2921f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2922f7278fd0SJosepch Chan 2923f7278fd0SJosepch Chan /* 2924f7278fd0SJosepch Chan * Set up output mixers 2925f7278fd0SJosepch Chan */ 2926f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 2927f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2928f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2929f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2930f7278fd0SJosepch Chan 2931f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 2932f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 2933f7278fd0SJosepch Chan /* PW9 Output enable */ 2934f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2935f7278fd0SJosepch Chan /* PW10 Input enable */ 2936f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 2937f7278fd0SJosepch Chan { } 2938f7278fd0SJosepch Chan }; 2939f7278fd0SJosepch Chan 294090dd48a1STakashi Iwai static const struct hda_verb vt1708B_uniwill_init_verbs[] = { 2941a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 2942a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 2943a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2944a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2945a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2946a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2947a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2948a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 2949a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 295069e52a80SHarald Welte { } 295169e52a80SHarald Welte }; 295269e52a80SHarald Welte 295317314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 295417314379SLydia Wang struct hda_codec *codec, 295517314379SLydia Wang struct snd_pcm_substream *substream) 295617314379SLydia Wang { 295717314379SLydia Wang int idle = substream->pstr->substream_opened == 1 295817314379SLydia Wang && substream->ref_count == 0; 295917314379SLydia Wang 296017314379SLydia Wang analog_low_current_mode(codec, idle); 296117314379SLydia Wang return 0; 296217314379SLydia Wang } 296317314379SLydia Wang 296490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 29650aa62aefSHarald Welte .substreams = 2, 2966f7278fd0SJosepch Chan .channels_min = 2, 2967f7278fd0SJosepch Chan .channels_max = 8, 2968f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2969f7278fd0SJosepch Chan .ops = { 2970f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29710aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 297217314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 297317314379SLydia Wang .close = via_pcm_open_close 2974f7278fd0SJosepch Chan }, 2975f7278fd0SJosepch Chan }; 2976f7278fd0SJosepch Chan 297790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 29780aa62aefSHarald Welte .substreams = 2, 2979f7278fd0SJosepch Chan .channels_min = 2, 2980f7278fd0SJosepch Chan .channels_max = 4, 2981f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 2982f7278fd0SJosepch Chan .ops = { 2983f7278fd0SJosepch Chan .open = via_playback_pcm_open, 29840aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 29850aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 2986f7278fd0SJosepch Chan }, 2987f7278fd0SJosepch Chan }; 2988f7278fd0SJosepch Chan 298990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_analog_capture = { 2990f7278fd0SJosepch Chan .substreams = 2, 2991f7278fd0SJosepch Chan .channels_min = 2, 2992f7278fd0SJosepch Chan .channels_max = 2, 2993f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 2994f7278fd0SJosepch Chan .ops = { 299517314379SLydia Wang .open = via_pcm_open_close, 2996f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 299717314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 299817314379SLydia Wang .close = via_pcm_open_close 2999f7278fd0SJosepch Chan }, 3000f7278fd0SJosepch Chan }; 3001f7278fd0SJosepch Chan 300290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3003f7278fd0SJosepch Chan .substreams = 1, 3004f7278fd0SJosepch Chan .channels_min = 2, 3005f7278fd0SJosepch Chan .channels_max = 2, 3006f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3007f7278fd0SJosepch Chan .ops = { 3008f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3009f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 30109da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 30119da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3012f7278fd0SJosepch Chan }, 3013f7278fd0SJosepch Chan }; 3014f7278fd0SJosepch Chan 301590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3016f7278fd0SJosepch Chan .substreams = 1, 3017f7278fd0SJosepch Chan .channels_min = 2, 3018f7278fd0SJosepch Chan .channels_max = 2, 3019f7278fd0SJosepch Chan }; 3020f7278fd0SJosepch Chan 3021f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3022f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3023f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3024f7278fd0SJosepch Chan { 3025f7278fd0SJosepch Chan int i; 3026f7278fd0SJosepch Chan hda_nid_t nid; 3027f7278fd0SJosepch Chan 3028f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3029f7278fd0SJosepch Chan 3030f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3031f7278fd0SJosepch Chan 3032f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3033f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3034f7278fd0SJosepch Chan if (nid) { 3035f7278fd0SJosepch Chan /* config dac list */ 3036f7278fd0SJosepch Chan switch (i) { 3037f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3038dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 3039f7278fd0SJosepch Chan break; 3040f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3041dda14410STakashi Iwai spec->private_dac_nids[i] = 0x24; 3042f7278fd0SJosepch Chan break; 3043f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3044dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 3045f7278fd0SJosepch Chan break; 3046f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3047dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3048f7278fd0SJosepch Chan break; 3049f7278fd0SJosepch Chan } 3050f7278fd0SJosepch Chan } 3051f7278fd0SJosepch Chan } 3052f7278fd0SJosepch Chan 3053f7278fd0SJosepch Chan return 0; 3054f7278fd0SJosepch Chan } 3055f7278fd0SJosepch Chan 3056f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3057f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3058f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3059f7278fd0SJosepch Chan { 3060f7278fd0SJosepch Chan char name[32]; 3061ea734963STakashi Iwai static const char * const chname[4] = { 3062ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3063ea734963STakashi Iwai }; 3064fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3065f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3066f7278fd0SJosepch Chan int i, err; 3067f7278fd0SJosepch Chan 3068f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3069f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3070f7278fd0SJosepch Chan 3071f7278fd0SJosepch Chan if (!nid) 3072f7278fd0SJosepch Chan continue; 3073f7278fd0SJosepch Chan 3074f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3075f7278fd0SJosepch Chan 3076f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3077f7278fd0SJosepch Chan /* Center/LFE */ 3078f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3079f7278fd0SJosepch Chan "Center Playback Volume", 3080f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3081f7278fd0SJosepch Chan HDA_OUTPUT)); 3082f7278fd0SJosepch Chan if (err < 0) 3083f7278fd0SJosepch Chan return err; 3084f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3085f7278fd0SJosepch Chan "LFE Playback Volume", 3086f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3087f7278fd0SJosepch Chan HDA_OUTPUT)); 3088f7278fd0SJosepch Chan if (err < 0) 3089f7278fd0SJosepch Chan return err; 3090f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3091f7278fd0SJosepch Chan "Center Playback Switch", 3092f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3093f7278fd0SJosepch Chan HDA_OUTPUT)); 3094f7278fd0SJosepch Chan if (err < 0) 3095f7278fd0SJosepch Chan return err; 3096f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3097f7278fd0SJosepch Chan "LFE Playback Switch", 3098f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3099f7278fd0SJosepch Chan HDA_OUTPUT)); 3100f7278fd0SJosepch Chan if (err < 0) 3101f7278fd0SJosepch Chan return err; 3102f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3103f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3104f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3105f7278fd0SJosepch Chan "Master Front Playback Volume", 3106f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3107f7278fd0SJosepch Chan HDA_INPUT)); 3108f7278fd0SJosepch Chan if (err < 0) 3109f7278fd0SJosepch Chan return err; 3110f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3111f7278fd0SJosepch Chan "Master Front Playback Switch", 3112f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3113f7278fd0SJosepch Chan HDA_INPUT)); 3114f7278fd0SJosepch Chan if (err < 0) 3115f7278fd0SJosepch Chan return err; 3116f7278fd0SJosepch Chan 3117f7278fd0SJosepch Chan /* add control to PW3 */ 3118f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3119f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3120f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3121f7278fd0SJosepch Chan HDA_OUTPUT)); 3122f7278fd0SJosepch Chan if (err < 0) 3123f7278fd0SJosepch Chan return err; 3124f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3125f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3126f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3127f7278fd0SJosepch Chan HDA_OUTPUT)); 3128f7278fd0SJosepch Chan if (err < 0) 3129f7278fd0SJosepch Chan return err; 3130f7278fd0SJosepch Chan } else { 3131f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3132f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3133f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3134f7278fd0SJosepch Chan HDA_OUTPUT)); 3135f7278fd0SJosepch Chan if (err < 0) 3136f7278fd0SJosepch Chan return err; 3137f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3138f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3139f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3140f7278fd0SJosepch Chan HDA_OUTPUT)); 3141f7278fd0SJosepch Chan if (err < 0) 3142f7278fd0SJosepch Chan return err; 3143f7278fd0SJosepch Chan } 3144f7278fd0SJosepch Chan } 3145f7278fd0SJosepch Chan 3146f7278fd0SJosepch Chan return 0; 3147f7278fd0SJosepch Chan } 3148f7278fd0SJosepch Chan 3149f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3150f7278fd0SJosepch Chan { 3151f7278fd0SJosepch Chan int err; 3152f7278fd0SJosepch Chan 3153f7278fd0SJosepch Chan if (!pin) 3154f7278fd0SJosepch Chan return 0; 3155f7278fd0SJosepch Chan 3156f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3157cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3158f7278fd0SJosepch Chan 3159f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3160f7278fd0SJosepch Chan "Headphone Playback Volume", 3161f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3162f7278fd0SJosepch Chan if (err < 0) 3163f7278fd0SJosepch Chan return err; 3164f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3165f7278fd0SJosepch Chan "Headphone Playback Switch", 3166f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3167f7278fd0SJosepch Chan if (err < 0) 3168f7278fd0SJosepch Chan return err; 3169f7278fd0SJosepch Chan 31700aa62aefSHarald Welte create_hp_imux(spec); 31710aa62aefSHarald Welte 3172f7278fd0SJosepch Chan return 0; 3173f7278fd0SJosepch Chan } 3174f7278fd0SJosepch Chan 3175f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 317610a20af7STakashi Iwai static int vt1708B_auto_create_analog_input_ctls(struct hda_codec *codec, 3177f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3178f7278fd0SJosepch Chan { 3179a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); 3180f7278fd0SJosepch Chan } 3181f7278fd0SJosepch Chan 3182f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3183f7278fd0SJosepch Chan { 3184f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3185f7278fd0SJosepch Chan int err; 3186f7278fd0SJosepch Chan 3187f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3188f7278fd0SJosepch Chan if (err < 0) 3189f7278fd0SJosepch Chan return err; 3190f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3191f7278fd0SJosepch Chan if (err < 0) 3192f7278fd0SJosepch Chan return err; 3193f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3194f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3195f7278fd0SJosepch Chan 3196f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3197f7278fd0SJosepch Chan if (err < 0) 3198f7278fd0SJosepch Chan return err; 3199f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3200f7278fd0SJosepch Chan if (err < 0) 3201f7278fd0SJosepch Chan return err; 320210a20af7STakashi Iwai err = vt1708B_auto_create_analog_input_ctls(codec, &spec->autocfg); 3203f7278fd0SJosepch Chan if (err < 0) 3204f7278fd0SJosepch Chan return err; 3205f7278fd0SJosepch Chan 3206f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3207f7278fd0SJosepch Chan 32080852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3209f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 321055d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3211f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3212f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3213f7278fd0SJosepch Chan 3214603c4019STakashi Iwai if (spec->kctls.list) 3215603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3216f7278fd0SJosepch Chan 32170aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 32180aa62aefSHarald Welte 3219f8fdd495SHarald Welte if (spec->hp_mux) 32203d83e577STakashi Iwai via_hp_build(codec); 3221f7278fd0SJosepch Chan 32225b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3223f7278fd0SJosepch Chan return 1; 3224f7278fd0SJosepch Chan } 3225f7278fd0SJosepch Chan 3226f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 322790dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 3228f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3229f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3230f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3231f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3232f7278fd0SJosepch Chan { } /* end */ 3233f7278fd0SJosepch Chan }; 3234f7278fd0SJosepch Chan #endif 32353e95b9abSLydia Wang 32363e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 32373e95b9abSLydia Wang { 32383e95b9abSLydia Wang struct via_spec *spec = codec->spec; 32393e95b9abSLydia Wang int imux_is_smixer; 32403e95b9abSLydia Wang unsigned int parm; 32413e95b9abSLydia Wang int is_8ch = 0; 3242bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 3243bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 32443e95b9abSLydia Wang is_8ch = 1; 32453e95b9abSLydia Wang 32463e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 32473e95b9abSLydia Wang imux_is_smixer = 32483e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 32493e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 32503e95b9abSLydia Wang /* inputs */ 32513e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 32523e95b9abSLydia Wang parm = AC_PWRST_D3; 32533e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32543e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32553e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 32563e95b9abSLydia Wang if (imux_is_smixer) 32573e95b9abSLydia Wang parm = AC_PWRST_D0; 32583e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 32593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 32603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 32613e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 32623e95b9abSLydia Wang 32633e95b9abSLydia Wang /* outputs */ 32643e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 32653e95b9abSLydia Wang parm = AC_PWRST_D3; 32663e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 32673e95b9abSLydia Wang if (spec->smart51_enabled) 32683e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 32693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 32703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 32713e95b9abSLydia Wang 32723e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 32733e95b9abSLydia Wang if (is_8ch) { 32743e95b9abSLydia Wang parm = AC_PWRST_D3; 32753e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 32763e95b9abSLydia Wang if (spec->smart51_enabled) 32773e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 32793e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 32813e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3282bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 3283bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 3284bc92df7fSLydia Wang parm = AC_PWRST_D3; 3285bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 3286bc92df7fSLydia Wang if (spec->smart51_enabled) 3287bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 3288bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 3289bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3290bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3291bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32923e95b9abSLydia Wang } 32933e95b9abSLydia Wang 32943e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 32953e95b9abSLydia Wang parm = AC_PWRST_D3; 32963e95b9abSLydia Wang /* force to D0 for internal Speaker */ 32973e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 32983e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 32993e95b9abSLydia Wang if (is_8ch) 33003e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 33013e95b9abSLydia Wang 33023e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 33033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 33043e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 33053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33063e95b9abSLydia Wang if (is_8ch) { 33073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 33083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 33103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 3311bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 3312bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 3313bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33143e95b9abSLydia Wang } 33153e95b9abSLydia Wang 3316518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3317f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3318f7278fd0SJosepch Chan { 3319f7278fd0SJosepch Chan struct via_spec *spec; 3320f7278fd0SJosepch Chan int err; 3321f7278fd0SJosepch Chan 3322518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3323518bf3baSLydia Wang return patch_vt1708S(codec); 3324f7278fd0SJosepch Chan /* create a codec specific record */ 33255b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3326f7278fd0SJosepch Chan if (spec == NULL) 3327f7278fd0SJosepch Chan return -ENOMEM; 3328f7278fd0SJosepch Chan 3329f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3330f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3331f7278fd0SJosepch Chan if (err < 0) { 3332f7278fd0SJosepch Chan via_free(codec); 3333f7278fd0SJosepch Chan return err; 3334f7278fd0SJosepch Chan } else if (!err) { 3335f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3336f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3337f7278fd0SJosepch Chan } 3338f7278fd0SJosepch Chan 333969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 334069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3341f7278fd0SJosepch Chan 3342f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3343f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3344f7278fd0SJosepch Chan 3345f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3346f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3347f7278fd0SJosepch Chan 3348a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3349f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3350f7278fd0SJosepch Chan spec->num_mixers++; 3351f7278fd0SJosepch Chan } 3352f7278fd0SJosepch Chan 3353f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3354f7278fd0SJosepch Chan 3355f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 335669e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3357f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3358f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3359f7278fd0SJosepch Chan #endif 3360f7278fd0SJosepch Chan 33613e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 33623e95b9abSLydia Wang 3363f7278fd0SJosepch Chan return 0; 3364f7278fd0SJosepch Chan } 3365f7278fd0SJosepch Chan 3366f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3367f7278fd0SJosepch Chan { 3368f7278fd0SJosepch Chan struct via_spec *spec; 3369f7278fd0SJosepch Chan int err; 3370f7278fd0SJosepch Chan 3371f7278fd0SJosepch Chan /* create a codec specific record */ 33725b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3373f7278fd0SJosepch Chan if (spec == NULL) 3374f7278fd0SJosepch Chan return -ENOMEM; 3375f7278fd0SJosepch Chan 3376f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3377f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3378f7278fd0SJosepch Chan if (err < 0) { 3379f7278fd0SJosepch Chan via_free(codec); 3380f7278fd0SJosepch Chan return err; 3381f7278fd0SJosepch Chan } else if (!err) { 3382f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3383f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3384f7278fd0SJosepch Chan } 3385f7278fd0SJosepch Chan 338669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 338769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3388f7278fd0SJosepch Chan 3389f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3390f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3391f7278fd0SJosepch Chan 3392f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3393f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3394f7278fd0SJosepch Chan 3395a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 3396f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3397f7278fd0SJosepch Chan spec->num_mixers++; 3398f7278fd0SJosepch Chan } 3399f7278fd0SJosepch Chan 3400f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3401f7278fd0SJosepch Chan 3402f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 340369e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3404f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3405f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3406f7278fd0SJosepch Chan #endif 3407c577b8a1SJoseph Chan 34083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 34093e95b9abSLydia Wang 3410c577b8a1SJoseph Chan return 0; 3411c577b8a1SJoseph Chan } 3412c577b8a1SJoseph Chan 3413d949cac1SHarald Welte /* Patch for VT1708S */ 3414d949cac1SHarald Welte 3415d949cac1SHarald Welte /* capture mixer elements */ 341690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3417d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3418d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3419d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3420d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 34216369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 34226369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 34236369bcfcSLydia Wang HDA_INPUT), 3424d949cac1SHarald Welte { 3425d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3426d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3427d949cac1SHarald Welte * So call somewhat different.. 3428d949cac1SHarald Welte */ 3429d949cac1SHarald Welte /* .name = "Capture Source", */ 3430d949cac1SHarald Welte .name = "Input Source", 3431d949cac1SHarald Welte .count = 1, 3432d949cac1SHarald Welte .info = via_mux_enum_info, 3433d949cac1SHarald Welte .get = via_mux_enum_get, 3434d949cac1SHarald Welte .put = via_mux_enum_put, 3435d949cac1SHarald Welte }, 3436d949cac1SHarald Welte { } /* end */ 3437d949cac1SHarald Welte }; 3438d949cac1SHarald Welte 343990dd48a1STakashi Iwai static const struct hda_verb vt1708S_volume_init_verbs[] = { 3440d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3441d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3442d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3443d949cac1SHarald Welte 3444d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3445d949cac1SHarald Welte * analog-loopback mixer widget */ 3446d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3447d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3448d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3449d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3450d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3451d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3452d949cac1SHarald Welte 3453d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3454d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 34555691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3456d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 34575691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3458d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3459d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3460bc7e7e5cSLydia Wang /* don't bybass mixer */ 3461bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3462d949cac1SHarald Welte { } 3463d949cac1SHarald Welte }; 3464d949cac1SHarald Welte 346590dd48a1STakashi Iwai static const struct hda_verb vt1708S_uniwill_init_verbs[] = { 3466a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3467a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3468a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3469a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3470a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3471a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3472a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3473a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3474a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 347569e52a80SHarald Welte { } 347669e52a80SHarald Welte }; 347769e52a80SHarald Welte 347890dd48a1STakashi Iwai static const struct hda_verb vt1705_uniwill_init_verbs[] = { 3479bc92df7fSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3480bc92df7fSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3481bc92df7fSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3482bc92df7fSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3483bc92df7fSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3484bc92df7fSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3485bc92df7fSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3486bc92df7fSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3487bc92df7fSLydia Wang { } 3488bc92df7fSLydia Wang }; 3489bc92df7fSLydia Wang 349090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3491d949cac1SHarald Welte .substreams = 2, 3492d949cac1SHarald Welte .channels_min = 2, 3493d949cac1SHarald Welte .channels_max = 8, 3494d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3495d949cac1SHarald Welte .ops = { 3496d949cac1SHarald Welte .open = via_playback_pcm_open, 3497c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3498c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 349917314379SLydia Wang .close = via_pcm_open_close 3500d949cac1SHarald Welte }, 3501d949cac1SHarald Welte }; 3502d949cac1SHarald Welte 350390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1705_pcm_analog_playback = { 3504bc92df7fSLydia Wang .substreams = 2, 3505bc92df7fSLydia Wang .channels_min = 2, 3506bc92df7fSLydia Wang .channels_max = 6, 3507bc92df7fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 3508bc92df7fSLydia Wang .ops = { 3509bc92df7fSLydia Wang .open = via_playback_pcm_open, 3510bc92df7fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 3511bc92df7fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 3512bc92df7fSLydia Wang .close = via_pcm_open_close 3513bc92df7fSLydia Wang }, 3514bc92df7fSLydia Wang }; 3515bc92df7fSLydia Wang 351690dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3517d949cac1SHarald Welte .substreams = 2, 3518d949cac1SHarald Welte .channels_min = 2, 3519d949cac1SHarald Welte .channels_max = 2, 3520d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3521d949cac1SHarald Welte .ops = { 352217314379SLydia Wang .open = via_pcm_open_close, 3523d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 352417314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 352517314379SLydia Wang .close = via_pcm_open_close 3526d949cac1SHarald Welte }, 3527d949cac1SHarald Welte }; 3528d949cac1SHarald Welte 352990dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708S_pcm_digital_playback = { 35309da29271STakashi Iwai .substreams = 1, 3531d949cac1SHarald Welte .channels_min = 2, 3532d949cac1SHarald Welte .channels_max = 2, 3533d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3534d949cac1SHarald Welte .ops = { 3535d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3536d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 35379da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 35389da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3539d949cac1SHarald Welte }, 3540d949cac1SHarald Welte }; 3541d949cac1SHarald Welte 3542d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3543d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3544d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3545d949cac1SHarald Welte { 3546d949cac1SHarald Welte int i; 3547d949cac1SHarald Welte hda_nid_t nid; 3548d949cac1SHarald Welte 3549d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3550d949cac1SHarald Welte 3551d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3552d949cac1SHarald Welte 3553d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3554d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3555d949cac1SHarald Welte if (nid) { 3556d949cac1SHarald Welte /* config dac list */ 3557d949cac1SHarald Welte switch (i) { 3558d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3559dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 3560d949cac1SHarald Welte break; 3561d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3562bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3563dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3564bc92df7fSLydia Wang else 3565dda14410STakashi Iwai spec->private_dac_nids[i] = 0x24; 3566d949cac1SHarald Welte break; 3567d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3568dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 3569d949cac1SHarald Welte break; 3570d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3571dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 3572d949cac1SHarald Welte break; 3573d949cac1SHarald Welte } 3574d949cac1SHarald Welte } 3575d949cac1SHarald Welte } 3576d949cac1SHarald Welte 357732e0191dSClemens Ladisch /* for Smart 5.1, line/mic inputs double as output pins */ 357832e0191dSClemens Ladisch if (cfg->line_outs == 1) { 357932e0191dSClemens Ladisch spec->multiout.num_dacs = 3; 3580dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_SURROUND] = 0x11; 3581bc92df7fSLydia Wang if (spec->codec->vendor_id == 0x11064397) 3582dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x25; 3583bc92df7fSLydia Wang else 3584dda14410STakashi Iwai spec->private_dac_nids[AUTO_SEQ_CENLFE] = 0x24; 358532e0191dSClemens Ladisch } 358632e0191dSClemens Ladisch 3587d949cac1SHarald Welte return 0; 3588d949cac1SHarald Welte } 3589d949cac1SHarald Welte 3590d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3591bc92df7fSLydia Wang static int vt1708S_auto_create_multi_out_ctls(struct hda_codec *codec, 3592d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3593d949cac1SHarald Welte { 3594bc92df7fSLydia Wang struct via_spec *spec = codec->spec; 3595d949cac1SHarald Welte char name[32]; 3596ea734963STakashi Iwai static const char * const chname[4] = { 3597ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 3598ea734963STakashi Iwai }; 3599bc92df7fSLydia Wang hda_nid_t nid_vols[2][4] = { {0x10, 0x11, 0x24, 0x25}, 3600bc92df7fSLydia Wang {0x10, 0x11, 0x25, 0} }; 3601bc92df7fSLydia Wang hda_nid_t nid_mutes[2][4] = { {0x1C, 0x18, 0x26, 0x27}, 3602bc92df7fSLydia Wang {0x1C, 0x18, 0x27, 0} }; 3603d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3604d949cac1SHarald Welte int i, err; 3605d949cac1SHarald Welte 3606d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3607d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3608d949cac1SHarald Welte 360932e0191dSClemens Ladisch /* for Smart 5.1, there are always at least six channels */ 361032e0191dSClemens Ladisch if (!nid && i > AUTO_SEQ_CENLFE) 3611d949cac1SHarald Welte continue; 3612d949cac1SHarald Welte 3613bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3614bc92df7fSLydia Wang nid_vol = nid_vols[1][i]; 3615bc92df7fSLydia Wang nid_mute = nid_mutes[1][i]; 3616bc92df7fSLydia Wang } else { 3617bc92df7fSLydia Wang nid_vol = nid_vols[0][i]; 3618bc92df7fSLydia Wang nid_mute = nid_mutes[0][i]; 3619bc92df7fSLydia Wang } 3620bc92df7fSLydia Wang if (!nid_vol && !nid_mute) 3621bc92df7fSLydia Wang continue; 3622d949cac1SHarald Welte 3623d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3624d949cac1SHarald Welte /* Center/LFE */ 3625d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3626d949cac1SHarald Welte "Center Playback Volume", 3627d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3628d949cac1SHarald Welte HDA_OUTPUT)); 3629d949cac1SHarald Welte if (err < 0) 3630d949cac1SHarald Welte return err; 3631d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3632d949cac1SHarald Welte "LFE Playback Volume", 3633d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3634d949cac1SHarald Welte HDA_OUTPUT)); 3635d949cac1SHarald Welte if (err < 0) 3636d949cac1SHarald Welte return err; 3637d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3638d949cac1SHarald Welte "Center Playback Switch", 3639d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3640d949cac1SHarald Welte 1, 0, 3641d949cac1SHarald Welte HDA_OUTPUT)); 3642d949cac1SHarald Welte if (err < 0) 3643d949cac1SHarald Welte return err; 3644d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3645d949cac1SHarald Welte "LFE Playback Switch", 3646d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3647d949cac1SHarald Welte 2, 0, 3648d949cac1SHarald Welte HDA_OUTPUT)); 3649d949cac1SHarald Welte if (err < 0) 3650d949cac1SHarald Welte return err; 3651d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3652d949cac1SHarald Welte /* add control to mixer index 0 */ 3653d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3654d949cac1SHarald Welte "Master Front Playback Volume", 3655d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3656d949cac1SHarald Welte HDA_INPUT)); 3657d949cac1SHarald Welte if (err < 0) 3658d949cac1SHarald Welte return err; 3659d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3660d949cac1SHarald Welte "Master Front Playback Switch", 3661d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3662d949cac1SHarald Welte HDA_INPUT)); 3663d949cac1SHarald Welte if (err < 0) 3664d949cac1SHarald Welte return err; 3665d949cac1SHarald Welte 3666d949cac1SHarald Welte /* Front */ 3667d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3668d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3669d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3670d949cac1SHarald Welte HDA_OUTPUT)); 3671d949cac1SHarald Welte if (err < 0) 3672d949cac1SHarald Welte return err; 3673d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3674d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3675d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3676d949cac1SHarald Welte 3, 0, 3677d949cac1SHarald Welte HDA_OUTPUT)); 3678d949cac1SHarald Welte if (err < 0) 3679d949cac1SHarald Welte return err; 3680d949cac1SHarald Welte } else { 3681d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3682d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3683d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3684d949cac1SHarald Welte HDA_OUTPUT)); 3685d949cac1SHarald Welte if (err < 0) 3686d949cac1SHarald Welte return err; 3687d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3688d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3689d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3690d949cac1SHarald Welte 3, 0, 3691d949cac1SHarald Welte HDA_OUTPUT)); 3692d949cac1SHarald Welte if (err < 0) 3693d949cac1SHarald Welte return err; 3694d949cac1SHarald Welte } 3695d949cac1SHarald Welte } 3696d949cac1SHarald Welte 3697d949cac1SHarald Welte return 0; 3698d949cac1SHarald Welte } 3699d949cac1SHarald Welte 3700d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3701d949cac1SHarald Welte { 3702d949cac1SHarald Welte int err; 3703d949cac1SHarald Welte 3704d949cac1SHarald Welte if (!pin) 3705d949cac1SHarald Welte return 0; 3706d949cac1SHarald Welte 3707d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 3708cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3709d949cac1SHarald Welte 3710d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3711d949cac1SHarald Welte "Headphone Playback Volume", 3712d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 3713d949cac1SHarald Welte if (err < 0) 3714d949cac1SHarald Welte return err; 3715d949cac1SHarald Welte 3716d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3717d949cac1SHarald Welte "Headphone Playback Switch", 3718d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3719d949cac1SHarald Welte if (err < 0) 3720d949cac1SHarald Welte return err; 3721d949cac1SHarald Welte 37220aa62aefSHarald Welte create_hp_imux(spec); 37230aa62aefSHarald Welte 3724d949cac1SHarald Welte return 0; 3725d949cac1SHarald Welte } 3726d949cac1SHarald Welte 3727d949cac1SHarald Welte /* create playback/capture controls for input pins */ 372810a20af7STakashi Iwai static int vt1708S_auto_create_analog_input_ctls(struct hda_codec *codec, 3729d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3730d949cac1SHarald Welte { 3731a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); 3732d949cac1SHarald Welte } 3733d949cac1SHarald Welte 37349da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 37359da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 37369da29271STakashi Iwai { 37379da29271STakashi Iwai struct via_spec *spec = codec->spec; 37389da29271STakashi Iwai int i; 37399da29271STakashi Iwai 37409da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 37419da29271STakashi Iwai hda_nid_t nid; 37429da29271STakashi Iwai int conn; 37439da29271STakashi Iwai 37449da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 37459da29271STakashi Iwai if (!nid) 37469da29271STakashi Iwai continue; 37479da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 37489da29271STakashi Iwai if (conn < 1) 37499da29271STakashi Iwai continue; 37509da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 37519da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 37529da29271STakashi Iwai else { 37539da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 37549da29271STakashi Iwai break; /* at most two dig outs */ 37559da29271STakashi Iwai } 37569da29271STakashi Iwai } 37579da29271STakashi Iwai } 37589da29271STakashi Iwai 3759d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 3760d949cac1SHarald Welte { 3761d949cac1SHarald Welte struct via_spec *spec = codec->spec; 3762d949cac1SHarald Welte int err; 3763d949cac1SHarald Welte 37649da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3765d949cac1SHarald Welte if (err < 0) 3766d949cac1SHarald Welte return err; 3767d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 3768d949cac1SHarald Welte if (err < 0) 3769d949cac1SHarald Welte return err; 3770d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3771d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 3772d949cac1SHarald Welte 3773bc92df7fSLydia Wang err = vt1708S_auto_create_multi_out_ctls(codec, &spec->autocfg); 3774d949cac1SHarald Welte if (err < 0) 3775d949cac1SHarald Welte return err; 3776d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3777d949cac1SHarald Welte if (err < 0) 3778d949cac1SHarald Welte return err; 377910a20af7STakashi Iwai err = vt1708S_auto_create_analog_input_ctls(codec, &spec->autocfg); 3780d949cac1SHarald Welte if (err < 0) 3781d949cac1SHarald Welte return err; 3782d949cac1SHarald Welte 3783d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3784d949cac1SHarald Welte 37859da29271STakashi Iwai fill_dig_outs(codec); 378698aa34c0SHarald Welte 3787603c4019STakashi Iwai if (spec->kctls.list) 3788603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3789d949cac1SHarald Welte 37900aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 37910aa62aefSHarald Welte 3792f8fdd495SHarald Welte if (spec->hp_mux) 37933d83e577STakashi Iwai via_hp_build(codec); 3794d949cac1SHarald Welte 37955b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3796d949cac1SHarald Welte return 1; 3797d949cac1SHarald Welte } 3798d949cac1SHarald Welte 3799d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 380090dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 3801d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 3802d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 3803d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 3804d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 3805d949cac1SHarald Welte { } /* end */ 3806d949cac1SHarald Welte }; 3807d949cac1SHarald Welte #endif 3808d949cac1SHarald Welte 38096369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 38106369bcfcSLydia Wang int offset, int num_steps, int step_size) 38116369bcfcSLydia Wang { 38126369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 38136369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 38146369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 38156369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 38166369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 38176369bcfcSLydia Wang } 38186369bcfcSLydia Wang 3819d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 3820d949cac1SHarald Welte { 3821d949cac1SHarald Welte struct via_spec *spec; 3822d949cac1SHarald Welte int err; 3823d949cac1SHarald Welte 3824d949cac1SHarald Welte /* create a codec specific record */ 38255b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3826d949cac1SHarald Welte if (spec == NULL) 3827d949cac1SHarald Welte return -ENOMEM; 3828d949cac1SHarald Welte 3829d949cac1SHarald Welte /* automatic parse from the BIOS config */ 3830d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 3831d949cac1SHarald Welte if (err < 0) { 3832d949cac1SHarald Welte via_free(codec); 3833d949cac1SHarald Welte return err; 3834d949cac1SHarald Welte } else if (!err) { 3835d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 3836d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 3837d949cac1SHarald Welte } 3838d949cac1SHarald Welte 383969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 3840bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3841bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3842bc92df7fSLydia Wang vt1705_uniwill_init_verbs; 3843bc92df7fSLydia Wang else 3844bc92df7fSLydia Wang spec->init_verbs[spec->num_iverbs++] = 3845bc92df7fSLydia Wang vt1708S_uniwill_init_verbs; 3846d949cac1SHarald Welte 3847bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) 3848bc92df7fSLydia Wang spec->stream_analog_playback = &vt1705_pcm_analog_playback; 3849bc92df7fSLydia Wang else 3850d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 3851d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 3852d949cac1SHarald Welte 3853d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 3854d949cac1SHarald Welte 3855a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 38566369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 38576369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 3858d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 3859d949cac1SHarald Welte spec->num_mixers++; 3860d949cac1SHarald Welte } 3861d949cac1SHarald Welte 3862d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3863d949cac1SHarald Welte 3864d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 386569e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3866d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 3867d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 3868d949cac1SHarald Welte #endif 3869d949cac1SHarald Welte 3870518bf3baSLydia Wang /* correct names for VT1708BCE */ 3871518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 3872518bf3baSLydia Wang kfree(codec->chip_name); 3873518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 3874518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 3875518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 3876518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3877970f630fSLydia Wang } 3878bc92df7fSLydia Wang /* correct names for VT1705 */ 3879bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 3880bc92df7fSLydia Wang kfree(codec->chip_name); 3881bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 3882bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 3883bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 3884bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 3885bc92df7fSLydia Wang } 38863e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 3887d949cac1SHarald Welte return 0; 3888d949cac1SHarald Welte } 3889d949cac1SHarald Welte 3890d949cac1SHarald Welte /* Patch for VT1702 */ 3891d949cac1SHarald Welte 3892d949cac1SHarald Welte /* capture mixer elements */ 389390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1702_capture_mixer[] = { 3894d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 3895d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 3896d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 3897d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 3898d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 3899d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 3900d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 3901d949cac1SHarald Welte HDA_INPUT), 3902d949cac1SHarald Welte { 3903d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3904d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3905d949cac1SHarald Welte * So call somewhat different.. 3906d949cac1SHarald Welte */ 3907d949cac1SHarald Welte /* .name = "Capture Source", */ 3908d949cac1SHarald Welte .name = "Input Source", 3909d949cac1SHarald Welte .count = 1, 3910d949cac1SHarald Welte .info = via_mux_enum_info, 3911d949cac1SHarald Welte .get = via_mux_enum_get, 3912d949cac1SHarald Welte .put = via_mux_enum_put, 3913d949cac1SHarald Welte }, 3914d949cac1SHarald Welte { } /* end */ 3915d949cac1SHarald Welte }; 3916d949cac1SHarald Welte 391790dd48a1STakashi Iwai static const struct hda_verb vt1702_volume_init_verbs[] = { 3918d949cac1SHarald Welte /* 3919d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 3920d949cac1SHarald Welte */ 3921d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3922d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3923d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3924d949cac1SHarald Welte 3925d949cac1SHarald Welte 3926d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3927d949cac1SHarald Welte * mixer widget 3928d949cac1SHarald Welte */ 3929d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 3930d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3931d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3932d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3933d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3934d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 3935d949cac1SHarald Welte 3936d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3937d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 3938d949cac1SHarald Welte /* PW6 PW7 Output enable */ 3939d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3940d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3941bc7e7e5cSLydia Wang /* mixer enable */ 3942bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3943bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3944bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3945d949cac1SHarald Welte { } 3946d949cac1SHarald Welte }; 3947d949cac1SHarald Welte 394890dd48a1STakashi Iwai static const struct hda_verb vt1702_uniwill_init_verbs[] = { 3949a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 3950a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3951a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3952a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3953a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3954a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 395569e52a80SHarald Welte { } 395669e52a80SHarald Welte }; 395769e52a80SHarald Welte 395890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_playback = { 39590aa62aefSHarald Welte .substreams = 2, 3960d949cac1SHarald Welte .channels_min = 2, 3961d949cac1SHarald Welte .channels_max = 2, 3962d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3963d949cac1SHarald Welte .ops = { 3964d949cac1SHarald Welte .open = via_playback_pcm_open, 39650aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 396617314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 396717314379SLydia Wang .close = via_pcm_open_close 3968d949cac1SHarald Welte }, 3969d949cac1SHarald Welte }; 3970d949cac1SHarald Welte 397190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_analog_capture = { 3972d949cac1SHarald Welte .substreams = 3, 3973d949cac1SHarald Welte .channels_min = 2, 3974d949cac1SHarald Welte .channels_max = 2, 3975d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 3976d949cac1SHarald Welte .ops = { 397717314379SLydia Wang .open = via_pcm_open_close, 3978d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 397917314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 398017314379SLydia Wang .close = via_pcm_open_close 3981d949cac1SHarald Welte }, 3982d949cac1SHarald Welte }; 3983d949cac1SHarald Welte 398490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1702_pcm_digital_playback = { 39855691ec7fSHarald Welte .substreams = 2, 3986d949cac1SHarald Welte .channels_min = 2, 3987d949cac1SHarald Welte .channels_max = 2, 3988d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3989d949cac1SHarald Welte .ops = { 3990d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3991d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 39929da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 39939da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3994d949cac1SHarald Welte }, 3995d949cac1SHarald Welte }; 3996d949cac1SHarald Welte 3997d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3998d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 3999d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4000d949cac1SHarald Welte { 4001d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4002d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4003d949cac1SHarald Welte 4004d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4005d949cac1SHarald Welte /* config dac list */ 4006dda14410STakashi Iwai spec->private_dac_nids[0] = 0x10; 4007d949cac1SHarald Welte } 4008d949cac1SHarald Welte 4009d949cac1SHarald Welte return 0; 4010d949cac1SHarald Welte } 4011d949cac1SHarald Welte 4012d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4013d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4014d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4015d949cac1SHarald Welte { 4016d949cac1SHarald Welte int err; 4017d949cac1SHarald Welte 4018d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4019d949cac1SHarald Welte return -1; 4020d949cac1SHarald Welte 4021d949cac1SHarald Welte /* add control to mixer index 0 */ 4022d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4023d949cac1SHarald Welte "Master Front Playback Volume", 4024d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4025d949cac1SHarald Welte if (err < 0) 4026d949cac1SHarald Welte return err; 4027d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4028d949cac1SHarald Welte "Master Front Playback Switch", 4029d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4030d949cac1SHarald Welte if (err < 0) 4031d949cac1SHarald Welte return err; 4032d949cac1SHarald Welte 4033d949cac1SHarald Welte /* Front */ 4034d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4035d949cac1SHarald Welte "Front Playback Volume", 4036d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4037d949cac1SHarald Welte if (err < 0) 4038d949cac1SHarald Welte return err; 4039d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4040d949cac1SHarald Welte "Front Playback Switch", 4041d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4042d949cac1SHarald Welte if (err < 0) 4043d949cac1SHarald Welte return err; 4044d949cac1SHarald Welte 4045d949cac1SHarald Welte return 0; 4046d949cac1SHarald Welte } 4047d949cac1SHarald Welte 4048d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4049d949cac1SHarald Welte { 40500713efebSLydia Wang int err, i; 40510713efebSLydia Wang struct hda_input_mux *imux; 4052ea734963STakashi Iwai static const char * const texts[] = { "ON", "OFF", NULL}; 4053d949cac1SHarald Welte if (!pin) 4054d949cac1SHarald Welte return 0; 4055d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4056cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4057d949cac1SHarald Welte 4058d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4059d949cac1SHarald Welte "Headphone Playback Volume", 4060d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4061d949cac1SHarald Welte if (err < 0) 4062d949cac1SHarald Welte return err; 4063d949cac1SHarald Welte 4064d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4065d949cac1SHarald Welte "Headphone Playback Switch", 4066d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4067d949cac1SHarald Welte if (err < 0) 4068d949cac1SHarald Welte return err; 4069d949cac1SHarald Welte 40700713efebSLydia Wang imux = &spec->private_imux[1]; 40710aa62aefSHarald Welte 40720713efebSLydia Wang /* for hp mode select */ 407310a20af7STakashi Iwai for (i = 0; texts[i]; i++) 407410a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 40750713efebSLydia Wang 40760713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4077d949cac1SHarald Welte return 0; 4078d949cac1SHarald Welte } 4079d949cac1SHarald Welte 4080d949cac1SHarald Welte /* create playback/capture controls for input pins */ 408110a20af7STakashi Iwai static int vt1702_auto_create_analog_input_ctls(struct hda_codec *codec, 4082d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4083d949cac1SHarald Welte { 4084a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x1a); 4085d949cac1SHarald Welte } 4086d949cac1SHarald Welte 4087d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4088d949cac1SHarald Welte { 4089d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4090d949cac1SHarald Welte int err; 4091d949cac1SHarald Welte 40929da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4093d949cac1SHarald Welte if (err < 0) 4094d949cac1SHarald Welte return err; 4095d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4096d949cac1SHarald Welte if (err < 0) 4097d949cac1SHarald Welte return err; 4098d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4099d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4100d949cac1SHarald Welte 4101d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4102d949cac1SHarald Welte if (err < 0) 4103d949cac1SHarald Welte return err; 4104d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4105d949cac1SHarald Welte if (err < 0) 4106d949cac1SHarald Welte return err; 4107c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4108c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4109c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4110c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4111c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4112c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 411310a20af7STakashi Iwai err = vt1702_auto_create_analog_input_ctls(codec, &spec->autocfg); 4114d949cac1SHarald Welte if (err < 0) 4115d949cac1SHarald Welte return err; 4116d949cac1SHarald Welte 4117d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4118d949cac1SHarald Welte 41199da29271STakashi Iwai fill_dig_outs(codec); 412098aa34c0SHarald Welte 4121603c4019STakashi Iwai if (spec->kctls.list) 4122603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4123d949cac1SHarald Welte 41240aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 41250aa62aefSHarald Welte 4126f8fdd495SHarald Welte if (spec->hp_mux) 41273d83e577STakashi Iwai via_hp_build(codec); 4128d949cac1SHarald Welte 4129d949cac1SHarald Welte return 1; 4130d949cac1SHarald Welte } 4131d949cac1SHarald Welte 4132d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 413390dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 4134d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4135d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4136d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4137d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4138d949cac1SHarald Welte { } /* end */ 4139d949cac1SHarald Welte }; 4140d949cac1SHarald Welte #endif 4141d949cac1SHarald Welte 41423e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 41433e95b9abSLydia Wang { 41443e95b9abSLydia Wang int imux_is_smixer = 41453e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 41463e95b9abSLydia Wang unsigned int parm; 41473e95b9abSLydia Wang /* inputs */ 41483e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 41493e95b9abSLydia Wang parm = AC_PWRST_D3; 41503e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 41513e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 41523e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 41533e95b9abSLydia Wang if (imux_is_smixer) 41543e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 41553e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 41563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 41573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 41583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 41593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 41603e95b9abSLydia Wang 41613e95b9abSLydia Wang /* outputs */ 41623e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 41633e95b9abSLydia Wang parm = AC_PWRST_D3; 41643e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 41653e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 41663e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 41673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 41683e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 41693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 41703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 41713e95b9abSLydia Wang } 41723e95b9abSLydia Wang 4173d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4174d949cac1SHarald Welte { 4175d949cac1SHarald Welte struct via_spec *spec; 4176d949cac1SHarald Welte int err; 4177d949cac1SHarald Welte 4178d949cac1SHarald Welte /* create a codec specific record */ 41795b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4180d949cac1SHarald Welte if (spec == NULL) 4181d949cac1SHarald Welte return -ENOMEM; 4182d949cac1SHarald Welte 4183d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4184d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4185d949cac1SHarald Welte if (err < 0) { 4186d949cac1SHarald Welte via_free(codec); 4187d949cac1SHarald Welte return err; 4188d949cac1SHarald Welte } else if (!err) { 4189d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4190d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4191d949cac1SHarald Welte } 4192d949cac1SHarald Welte 419369e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 419469e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4195d949cac1SHarald Welte 4196d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4197d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4198d949cac1SHarald Welte 4199d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4200d949cac1SHarald Welte 4201a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4202d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4203d949cac1SHarald Welte spec->num_mixers++; 4204d949cac1SHarald Welte } 4205d949cac1SHarald Welte 4206d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4207d949cac1SHarald Welte 4208d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 420969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4210d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4211d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4212d949cac1SHarald Welte #endif 4213d949cac1SHarald Welte 42143e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 4215d949cac1SHarald Welte return 0; 4216d949cac1SHarald Welte } 4217d949cac1SHarald Welte 4218eb7188caSLydia Wang /* Patch for VT1718S */ 4219eb7188caSLydia Wang 4220eb7188caSLydia Wang /* capture mixer elements */ 422190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4222eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4223eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4224eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4225eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4226eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4227eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4228eb7188caSLydia Wang HDA_INPUT), 4229eb7188caSLydia Wang { 4230eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4231eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4232eb7188caSLydia Wang * So call somewhat different.. 4233eb7188caSLydia Wang */ 4234eb7188caSLydia Wang .name = "Input Source", 4235eb7188caSLydia Wang .count = 2, 4236eb7188caSLydia Wang .info = via_mux_enum_info, 4237eb7188caSLydia Wang .get = via_mux_enum_get, 4238eb7188caSLydia Wang .put = via_mux_enum_put, 4239eb7188caSLydia Wang }, 4240eb7188caSLydia Wang { } /* end */ 4241eb7188caSLydia Wang }; 4242eb7188caSLydia Wang 424390dd48a1STakashi Iwai static const struct hda_verb vt1718S_volume_init_verbs[] = { 4244eb7188caSLydia Wang /* 4245eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4246eb7188caSLydia Wang */ 4247eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4248eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4249eb7188caSLydia Wang 42504ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 42514ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 4252eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4253eb7188caSLydia Wang * mixer widget 4254eb7188caSLydia Wang */ 4255eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4256eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4257eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4258eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4259eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 42604ab2d53aSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5)}, 4261eb7188caSLydia Wang 4262eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4263eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4264eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4265eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4266eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4267eb7188caSLydia Wang /* PW11 Input enable */ 4268eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4269eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4270eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4271eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4272eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4273eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4274eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4275eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4276eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4277eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4278eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4279eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4280eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4281eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4282eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4283eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4284eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4285eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4286eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4287eb7188caSLydia Wang { } 4288eb7188caSLydia Wang }; 4289eb7188caSLydia Wang 4290eb7188caSLydia Wang 429190dd48a1STakashi Iwai static const struct hda_verb vt1718S_uniwill_init_verbs[] = { 4292eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4293eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4294eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4295eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4296eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4297eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4298eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4299eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4300eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4301eb7188caSLydia Wang { } 4302eb7188caSLydia Wang }; 4303eb7188caSLydia Wang 430490dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4305eb7188caSLydia Wang .substreams = 2, 4306eb7188caSLydia Wang .channels_min = 2, 4307eb7188caSLydia Wang .channels_max = 10, 4308eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4309eb7188caSLydia Wang .ops = { 4310eb7188caSLydia Wang .open = via_playback_pcm_open, 4311eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4312eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4313eb7188caSLydia Wang .close = via_pcm_open_close, 4314eb7188caSLydia Wang }, 4315eb7188caSLydia Wang }; 4316eb7188caSLydia Wang 431790dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4318eb7188caSLydia Wang .substreams = 2, 4319eb7188caSLydia Wang .channels_min = 2, 4320eb7188caSLydia Wang .channels_max = 2, 4321eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4322eb7188caSLydia Wang .ops = { 4323eb7188caSLydia Wang .open = via_pcm_open_close, 4324eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4325eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4326eb7188caSLydia Wang .close = via_pcm_open_close, 4327eb7188caSLydia Wang }, 4328eb7188caSLydia Wang }; 4329eb7188caSLydia Wang 433090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4331eb7188caSLydia Wang .substreams = 2, 4332eb7188caSLydia Wang .channels_min = 2, 4333eb7188caSLydia Wang .channels_max = 2, 4334eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4335eb7188caSLydia Wang .ops = { 4336eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4337eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4338eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4339eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4340eb7188caSLydia Wang }, 4341eb7188caSLydia Wang }; 4342eb7188caSLydia Wang 434390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4344eb7188caSLydia Wang .substreams = 1, 4345eb7188caSLydia Wang .channels_min = 2, 4346eb7188caSLydia Wang .channels_max = 2, 4347eb7188caSLydia Wang }; 4348eb7188caSLydia Wang 4349eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4350eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4351eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4352eb7188caSLydia Wang { 4353eb7188caSLydia Wang int i; 4354eb7188caSLydia Wang hda_nid_t nid; 4355eb7188caSLydia Wang 4356eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4357eb7188caSLydia Wang 4358eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4359eb7188caSLydia Wang 4360eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4361eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4362eb7188caSLydia Wang if (nid) { 4363eb7188caSLydia Wang /* config dac list */ 4364eb7188caSLydia Wang switch (i) { 4365eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4366dda14410STakashi Iwai spec->private_dac_nids[i] = 0x8; 4367eb7188caSLydia Wang break; 4368eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4369dda14410STakashi Iwai spec->private_dac_nids[i] = 0xa; 4370eb7188caSLydia Wang break; 4371eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4372dda14410STakashi Iwai spec->private_dac_nids[i] = 0x9; 4373eb7188caSLydia Wang break; 4374eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4375dda14410STakashi Iwai spec->private_dac_nids[i] = 0xb; 4376eb7188caSLydia Wang break; 4377eb7188caSLydia Wang } 4378eb7188caSLydia Wang } 4379eb7188caSLydia Wang } 4380eb7188caSLydia Wang 4381eb7188caSLydia Wang return 0; 4382eb7188caSLydia Wang } 4383eb7188caSLydia Wang 4384eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4385eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4386eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4387eb7188caSLydia Wang { 4388eb7188caSLydia Wang char name[32]; 4389ea734963STakashi Iwai static const char * const chname[4] = { 4390ea734963STakashi Iwai "Front", "Surround", "C/LFE", "Side" 4391ea734963STakashi Iwai }; 4392eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4393eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4394eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4395eb7188caSLydia Wang int i, err; 4396eb7188caSLydia Wang 4397eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4398eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4399eb7188caSLydia Wang 4400eb7188caSLydia Wang if (!nid) 4401eb7188caSLydia Wang continue; 4402eb7188caSLydia Wang nid_vol = nid_vols[i]; 4403eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4404eb7188caSLydia Wang 4405eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4406eb7188caSLydia Wang /* Center/LFE */ 4407eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4408eb7188caSLydia Wang "Center Playback Volume", 4409eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4410eb7188caSLydia Wang HDA_OUTPUT)); 4411eb7188caSLydia Wang if (err < 0) 4412eb7188caSLydia Wang return err; 4413eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4414eb7188caSLydia Wang "LFE Playback Volume", 4415eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4416eb7188caSLydia Wang HDA_OUTPUT)); 4417eb7188caSLydia Wang if (err < 0) 4418eb7188caSLydia Wang return err; 4419eb7188caSLydia Wang err = via_add_control( 4420eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4421eb7188caSLydia Wang "Center Playback Switch", 4422eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4423eb7188caSLydia Wang HDA_OUTPUT)); 4424eb7188caSLydia Wang if (err < 0) 4425eb7188caSLydia Wang return err; 4426eb7188caSLydia Wang err = via_add_control( 4427eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4428eb7188caSLydia Wang "LFE Playback Switch", 4429eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4430eb7188caSLydia Wang HDA_OUTPUT)); 4431eb7188caSLydia Wang if (err < 0) 4432eb7188caSLydia Wang return err; 4433eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4434eb7188caSLydia Wang /* Front */ 4435eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4436eb7188caSLydia Wang err = via_add_control( 4437eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4438eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4439eb7188caSLydia Wang if (err < 0) 4440eb7188caSLydia Wang return err; 4441eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4442eb7188caSLydia Wang err = via_add_control( 4443eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4444eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4445eb7188caSLydia Wang HDA_OUTPUT)); 4446eb7188caSLydia Wang if (err < 0) 4447eb7188caSLydia Wang return err; 4448eb7188caSLydia Wang } else { 4449eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4450eb7188caSLydia Wang err = via_add_control( 4451eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4452eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4453eb7188caSLydia Wang if (err < 0) 4454eb7188caSLydia Wang return err; 4455eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4456eb7188caSLydia Wang err = via_add_control( 4457eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4458eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4459eb7188caSLydia Wang HDA_OUTPUT)); 4460eb7188caSLydia Wang if (err < 0) 4461eb7188caSLydia Wang return err; 4462eb7188caSLydia Wang } 4463eb7188caSLydia Wang } 4464eb7188caSLydia Wang return 0; 4465eb7188caSLydia Wang } 4466eb7188caSLydia Wang 4467eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4468eb7188caSLydia Wang { 4469eb7188caSLydia Wang int err; 4470eb7188caSLydia Wang 4471eb7188caSLydia Wang if (!pin) 4472eb7188caSLydia Wang return 0; 4473eb7188caSLydia Wang 4474eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4475eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4476eb7188caSLydia Wang 4477eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4478eb7188caSLydia Wang "Headphone Playback Volume", 4479eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4480eb7188caSLydia Wang if (err < 0) 4481eb7188caSLydia Wang return err; 4482eb7188caSLydia Wang 4483eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4484eb7188caSLydia Wang "Headphone Playback Switch", 4485eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4486eb7188caSLydia Wang if (err < 0) 4487eb7188caSLydia Wang return err; 4488eb7188caSLydia Wang 4489eb7188caSLydia Wang create_hp_imux(spec); 4490eb7188caSLydia Wang return 0; 4491eb7188caSLydia Wang } 4492eb7188caSLydia Wang 4493eb7188caSLydia Wang /* create playback/capture controls for input pins */ 449410a20af7STakashi Iwai static int vt1718S_auto_create_analog_input_ctls(struct hda_codec *codec, 4495eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4496eb7188caSLydia Wang { 4497a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); 4498eb7188caSLydia Wang } 4499eb7188caSLydia Wang 4500eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4501eb7188caSLydia Wang { 4502eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4503eb7188caSLydia Wang int err; 4504eb7188caSLydia Wang 4505eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4506eb7188caSLydia Wang 4507eb7188caSLydia Wang if (err < 0) 4508eb7188caSLydia Wang return err; 4509eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4510eb7188caSLydia Wang if (err < 0) 4511eb7188caSLydia Wang return err; 4512eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4513eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4514eb7188caSLydia Wang 4515eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4516eb7188caSLydia Wang if (err < 0) 4517eb7188caSLydia Wang return err; 4518eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4519eb7188caSLydia Wang if (err < 0) 4520eb7188caSLydia Wang return err; 452110a20af7STakashi Iwai err = vt1718S_auto_create_analog_input_ctls(codec, &spec->autocfg); 4522eb7188caSLydia Wang if (err < 0) 4523eb7188caSLydia Wang return err; 4524eb7188caSLydia Wang 4525eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4526eb7188caSLydia Wang 4527eb7188caSLydia Wang fill_dig_outs(codec); 4528eb7188caSLydia Wang 4529eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4530eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4531eb7188caSLydia Wang 4532eb7188caSLydia Wang if (spec->kctls.list) 4533eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4534eb7188caSLydia Wang 4535eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4536eb7188caSLydia Wang 4537eb7188caSLydia Wang if (spec->hp_mux) 45383d83e577STakashi Iwai via_hp_build(codec); 4539eb7188caSLydia Wang 45405b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4541eb7188caSLydia Wang 4542eb7188caSLydia Wang return 1; 4543eb7188caSLydia Wang } 4544eb7188caSLydia Wang 4545eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 454690dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 4547eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4548eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4549eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4550eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4551eb7188caSLydia Wang { } /* end */ 4552eb7188caSLydia Wang }; 4553eb7188caSLydia Wang #endif 4554eb7188caSLydia Wang 45553e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 45563e95b9abSLydia Wang { 45573e95b9abSLydia Wang struct via_spec *spec = codec->spec; 45583e95b9abSLydia Wang int imux_is_smixer; 45593e95b9abSLydia Wang unsigned int parm; 45603e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 45613e95b9abSLydia Wang imux_is_smixer = 45623e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 45633e95b9abSLydia Wang /* inputs */ 45643e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 45653e95b9abSLydia Wang parm = AC_PWRST_D3; 45663e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 45673e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 45683e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45693e95b9abSLydia Wang if (imux_is_smixer) 45703e95b9abSLydia Wang parm = AC_PWRST_D0; 45713e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 45723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 45733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 45743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 45753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 45763e95b9abSLydia Wang 45773e95b9abSLydia Wang /* outputs */ 45783e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 45793e95b9abSLydia Wang parm = AC_PWRST_D3; 45803e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 45813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 45823e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 45833e95b9abSLydia Wang 45843e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 45853e95b9abSLydia Wang parm = AC_PWRST_D3; 45863e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 45873e95b9abSLydia Wang if (spec->smart51_enabled) 45883e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 45893e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 45903e95b9abSLydia Wang 45913e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 45923e95b9abSLydia Wang parm = AC_PWRST_D3; 45933e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 45943e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 45953e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 45963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 45973e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 45983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 45993e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 46003e95b9abSLydia Wang 46013e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 46023e95b9abSLydia Wang parm = AC_PWRST_D3; 46033e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 46043e95b9abSLydia Wang if (spec->smart51_enabled) 46053e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 46063e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 46073e95b9abSLydia Wang 46083e95b9abSLydia Wang if (spec->hp_independent_mode) { 46093e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 46103e95b9abSLydia Wang parm = AC_PWRST_D3; 46113e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 46123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 46133e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 46153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46163e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 46173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 46183e95b9abSLydia Wang } 46193e95b9abSLydia Wang } 46203e95b9abSLydia Wang 4621eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4622eb7188caSLydia Wang { 4623eb7188caSLydia Wang struct via_spec *spec; 4624eb7188caSLydia Wang int err; 4625eb7188caSLydia Wang 4626eb7188caSLydia Wang /* create a codec specific record */ 46275b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4628eb7188caSLydia Wang if (spec == NULL) 4629eb7188caSLydia Wang return -ENOMEM; 4630eb7188caSLydia Wang 4631eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4632eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4633eb7188caSLydia Wang if (err < 0) { 4634eb7188caSLydia Wang via_free(codec); 4635eb7188caSLydia Wang return err; 4636eb7188caSLydia Wang } else if (!err) { 4637eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4638eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4639eb7188caSLydia Wang } 4640eb7188caSLydia Wang 4641eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4642eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4643eb7188caSLydia Wang 4644eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4645eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4646eb7188caSLydia Wang 4647eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4648bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4649eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4650eb7188caSLydia Wang 4651a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 4652bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4653bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4654eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 4655eb7188caSLydia Wang spec->num_mixers++; 4656eb7188caSLydia Wang } 4657eb7188caSLydia Wang 4658eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 4659eb7188caSLydia Wang 4660eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 46610f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 4662eb7188caSLydia Wang 4663eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4664eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 4665eb7188caSLydia Wang #endif 4666eb7188caSLydia Wang 46673e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 46683e95b9abSLydia Wang 4669eb7188caSLydia Wang return 0; 4670eb7188caSLydia Wang } 4671f3db423dSLydia Wang 4672f3db423dSLydia Wang /* Patch for VT1716S */ 4673f3db423dSLydia Wang 4674f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 4675f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 4676f3db423dSLydia Wang { 4677f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 4678f3db423dSLydia Wang uinfo->count = 1; 4679f3db423dSLydia Wang uinfo->value.integer.min = 0; 4680f3db423dSLydia Wang uinfo->value.integer.max = 1; 4681f3db423dSLydia Wang return 0; 4682f3db423dSLydia Wang } 4683f3db423dSLydia Wang 4684f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 4685f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4686f3db423dSLydia Wang { 4687f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4688f3db423dSLydia Wang int index = 0; 4689f3db423dSLydia Wang 4690f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 4691f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 4692f3db423dSLydia Wang if (index != -1) 4693f3db423dSLydia Wang *ucontrol->value.integer.value = index; 4694f3db423dSLydia Wang 4695f3db423dSLydia Wang return 0; 4696f3db423dSLydia Wang } 4697f3db423dSLydia Wang 4698f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 4699f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 4700f3db423dSLydia Wang { 4701f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 4702f3db423dSLydia Wang struct via_spec *spec = codec->spec; 4703f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 4704f3db423dSLydia Wang 4705f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 4706f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 4707f3db423dSLydia Wang spec->dmic_enabled = index; 47083e95b9abSLydia Wang set_widgets_power_state(codec); 4709f3db423dSLydia Wang return 1; 4710f3db423dSLydia Wang } 4711f3db423dSLydia Wang 4712f3db423dSLydia Wang /* capture mixer elements */ 471390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_capture_mixer[] = { 4714f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 4715f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 4716f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 4717f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 4718f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 4719f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 4720f3db423dSLydia Wang HDA_INPUT), 4721f3db423dSLydia Wang { 4722f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4723f3db423dSLydia Wang .name = "Input Source", 4724f3db423dSLydia Wang .count = 1, 4725f3db423dSLydia Wang .info = via_mux_enum_info, 4726f3db423dSLydia Wang .get = via_mux_enum_get, 4727f3db423dSLydia Wang .put = via_mux_enum_put, 4728f3db423dSLydia Wang }, 4729f3db423dSLydia Wang { } /* end */ 4730f3db423dSLydia Wang }; 4731f3db423dSLydia Wang 473290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 4733f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 4734f3db423dSLydia Wang { 4735f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4736f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 47375b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 4738f3db423dSLydia Wang .count = 1, 4739f3db423dSLydia Wang .info = vt1716s_dmic_info, 4740f3db423dSLydia Wang .get = vt1716s_dmic_get, 4741f3db423dSLydia Wang .put = vt1716s_dmic_put, 4742f3db423dSLydia Wang }, 4743f3db423dSLydia Wang {} /* end */ 4744f3db423dSLydia Wang }; 4745f3db423dSLydia Wang 4746f3db423dSLydia Wang 4747f3db423dSLydia Wang /* mono-out mixer elements */ 474890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 4749f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 4750f3db423dSLydia Wang { } /* end */ 4751f3db423dSLydia Wang }; 4752f3db423dSLydia Wang 475390dd48a1STakashi Iwai static const struct hda_verb vt1716S_volume_init_verbs[] = { 4754f3db423dSLydia Wang /* 4755f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4756f3db423dSLydia Wang */ 4757f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4758f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4759f3db423dSLydia Wang 4760f3db423dSLydia Wang 4761f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4762f3db423dSLydia Wang * mixer widget 4763f3db423dSLydia Wang */ 4764f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4765f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4766f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4767f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4768f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4769f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4770f3db423dSLydia Wang 4771f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 4772f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 4773f3db423dSLydia Wang 4774f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 4775f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 4776f3db423dSLydia Wang 4777f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 4778f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 4779f3db423dSLydia Wang 4780f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 4781f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4782f3db423dSLydia Wang 4783f3db423dSLydia Wang /* PW9 PW10 Output enable */ 4784f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4785f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4786f3db423dSLydia Wang 4787f3db423dSLydia Wang /* Unmute SW1, PW12 */ 4788f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4789f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 4790f3db423dSLydia Wang /* PW12 Output enable */ 4791f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4792f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 4793f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 4794f3db423dSLydia Wang /* don't bybass mixer */ 4795f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 4796f3db423dSLydia Wang /* Enable mono output */ 4797f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 4798f3db423dSLydia Wang { } 4799f3db423dSLydia Wang }; 4800f3db423dSLydia Wang 4801f3db423dSLydia Wang 480290dd48a1STakashi Iwai static const struct hda_verb vt1716S_uniwill_init_verbs[] = { 4803f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 4804f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4805f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4806f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4807f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4808f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 4809f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 4810f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4811f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4812f3db423dSLydia Wang { } 4813f3db423dSLydia Wang }; 4814f3db423dSLydia Wang 481590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_playback = { 4816f3db423dSLydia Wang .substreams = 2, 4817f3db423dSLydia Wang .channels_min = 2, 4818f3db423dSLydia Wang .channels_max = 6, 4819f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4820f3db423dSLydia Wang .ops = { 4821f3db423dSLydia Wang .open = via_playback_pcm_open, 4822f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4823f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4824f3db423dSLydia Wang .close = via_pcm_open_close, 4825f3db423dSLydia Wang }, 4826f3db423dSLydia Wang }; 4827f3db423dSLydia Wang 482890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_analog_capture = { 4829f3db423dSLydia Wang .substreams = 2, 4830f3db423dSLydia Wang .channels_min = 2, 4831f3db423dSLydia Wang .channels_max = 2, 4832f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 4833f3db423dSLydia Wang .ops = { 4834f3db423dSLydia Wang .open = via_pcm_open_close, 4835f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 4836f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 4837f3db423dSLydia Wang .close = via_pcm_open_close, 4838f3db423dSLydia Wang }, 4839f3db423dSLydia Wang }; 4840f3db423dSLydia Wang 484190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1716S_pcm_digital_playback = { 4842f3db423dSLydia Wang .substreams = 2, 4843f3db423dSLydia Wang .channels_min = 2, 4844f3db423dSLydia Wang .channels_max = 2, 4845f3db423dSLydia Wang /* NID is set in via_build_pcms */ 4846f3db423dSLydia Wang .ops = { 4847f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 4848f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 4849f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4850f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4851f3db423dSLydia Wang }, 4852f3db423dSLydia Wang }; 4853f3db423dSLydia Wang 4854f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4855f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 4856f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4857f3db423dSLydia Wang { int i; 4858f3db423dSLydia Wang hda_nid_t nid; 4859f3db423dSLydia Wang 4860f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4861f3db423dSLydia Wang 4862f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4863f3db423dSLydia Wang 4864f3db423dSLydia Wang for (i = 0; i < 3; i++) { 4865f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4866f3db423dSLydia Wang if (nid) { 4867f3db423dSLydia Wang /* config dac list */ 4868f3db423dSLydia Wang switch (i) { 4869f3db423dSLydia Wang case AUTO_SEQ_FRONT: 4870dda14410STakashi Iwai spec->private_dac_nids[i] = 0x10; 4871f3db423dSLydia Wang break; 4872f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 4873dda14410STakashi Iwai spec->private_dac_nids[i] = 0x25; 4874f3db423dSLydia Wang break; 4875f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 4876dda14410STakashi Iwai spec->private_dac_nids[i] = 0x11; 4877f3db423dSLydia Wang break; 4878f3db423dSLydia Wang } 4879f3db423dSLydia Wang } 4880f3db423dSLydia Wang } 4881f3db423dSLydia Wang 4882f3db423dSLydia Wang return 0; 4883f3db423dSLydia Wang } 4884f3db423dSLydia Wang 4885f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 4886f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 4887f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 4888f3db423dSLydia Wang { 4889f3db423dSLydia Wang char name[32]; 4890ea734963STakashi Iwai static const char * const chname[3] = { 4891ea734963STakashi Iwai "Front", "Surround", "C/LFE" 4892ea734963STakashi Iwai }; 4893f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 4894f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 4895f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 4896f3db423dSLydia Wang int i, err; 4897f3db423dSLydia Wang 4898f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 4899f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 4900f3db423dSLydia Wang 4901f3db423dSLydia Wang if (!nid) 4902f3db423dSLydia Wang continue; 4903f3db423dSLydia Wang 4904f3db423dSLydia Wang nid_vol = nid_vols[i]; 4905f3db423dSLydia Wang nid_mute = nid_mutes[i]; 4906f3db423dSLydia Wang 4907f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4908f3db423dSLydia Wang err = via_add_control( 4909f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4910f3db423dSLydia Wang "Center Playback Volume", 4911f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 4912f3db423dSLydia Wang if (err < 0) 4913f3db423dSLydia Wang return err; 4914f3db423dSLydia Wang err = via_add_control( 4915f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4916f3db423dSLydia Wang "LFE Playback Volume", 4917f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 4918f3db423dSLydia Wang if (err < 0) 4919f3db423dSLydia Wang return err; 4920f3db423dSLydia Wang err = via_add_control( 4921f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4922f3db423dSLydia Wang "Center Playback Switch", 4923f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4924f3db423dSLydia Wang HDA_OUTPUT)); 4925f3db423dSLydia Wang if (err < 0) 4926f3db423dSLydia Wang return err; 4927f3db423dSLydia Wang err = via_add_control( 4928f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4929f3db423dSLydia Wang "LFE Playback Switch", 4930f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4931f3db423dSLydia Wang HDA_OUTPUT)); 4932f3db423dSLydia Wang if (err < 0) 4933f3db423dSLydia Wang return err; 4934f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4935f3db423dSLydia Wang 4936f3db423dSLydia Wang err = via_add_control( 4937f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 4938f3db423dSLydia Wang "Master Front Playback Volume", 4939f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4940f3db423dSLydia Wang if (err < 0) 4941f3db423dSLydia Wang return err; 4942f3db423dSLydia Wang err = via_add_control( 4943f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4944f3db423dSLydia Wang "Master Front Playback Switch", 4945f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 4946f3db423dSLydia Wang if (err < 0) 4947f3db423dSLydia Wang return err; 4948f3db423dSLydia Wang 4949f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4950f3db423dSLydia Wang err = via_add_control( 4951f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4952f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4953f3db423dSLydia Wang if (err < 0) 4954f3db423dSLydia Wang return err; 4955f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4956f3db423dSLydia Wang err = via_add_control( 4957f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4958f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4959f3db423dSLydia Wang HDA_OUTPUT)); 4960f3db423dSLydia Wang if (err < 0) 4961f3db423dSLydia Wang return err; 4962f3db423dSLydia Wang } else { 4963f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4964f3db423dSLydia Wang err = via_add_control( 4965f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4966f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4967f3db423dSLydia Wang if (err < 0) 4968f3db423dSLydia Wang return err; 4969f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4970f3db423dSLydia Wang err = via_add_control( 4971f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4972f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4973f3db423dSLydia Wang HDA_OUTPUT)); 4974f3db423dSLydia Wang if (err < 0) 4975f3db423dSLydia Wang return err; 4976f3db423dSLydia Wang } 4977f3db423dSLydia Wang } 4978f3db423dSLydia Wang return 0; 4979f3db423dSLydia Wang } 4980f3db423dSLydia Wang 4981f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4982f3db423dSLydia Wang { 4983f3db423dSLydia Wang int err; 4984f3db423dSLydia Wang 4985f3db423dSLydia Wang if (!pin) 4986f3db423dSLydia Wang return 0; 4987f3db423dSLydia Wang 4988f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 4989f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 4990f3db423dSLydia Wang 4991f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4992f3db423dSLydia Wang "Headphone Playback Volume", 4993f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 4994f3db423dSLydia Wang if (err < 0) 4995f3db423dSLydia Wang return err; 4996f3db423dSLydia Wang 4997f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4998f3db423dSLydia Wang "Headphone Playback Switch", 4999f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5000f3db423dSLydia Wang if (err < 0) 5001f3db423dSLydia Wang return err; 5002f3db423dSLydia Wang 5003f3db423dSLydia Wang create_hp_imux(spec); 5004f3db423dSLydia Wang return 0; 5005f3db423dSLydia Wang } 5006f3db423dSLydia Wang 5007f3db423dSLydia Wang /* create playback/capture controls for input pins */ 500810a20af7STakashi Iwai static int vt1716S_auto_create_analog_input_ctls(struct hda_codec *codec, 5009f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5010f3db423dSLydia Wang { 5011a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x16); 5012f3db423dSLydia Wang } 5013f3db423dSLydia Wang 5014f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5015f3db423dSLydia Wang { 5016f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5017f3db423dSLydia Wang int err; 5018f3db423dSLydia Wang 5019f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5020f3db423dSLydia Wang if (err < 0) 5021f3db423dSLydia Wang return err; 5022f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5023f3db423dSLydia Wang if (err < 0) 5024f3db423dSLydia Wang return err; 5025f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5026f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5027f3db423dSLydia Wang 5028f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5029f3db423dSLydia Wang if (err < 0) 5030f3db423dSLydia Wang return err; 5031f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5032f3db423dSLydia Wang if (err < 0) 5033f3db423dSLydia Wang return err; 503410a20af7STakashi Iwai err = vt1716S_auto_create_analog_input_ctls(codec, &spec->autocfg); 5035f3db423dSLydia Wang if (err < 0) 5036f3db423dSLydia Wang return err; 5037f3db423dSLydia Wang 5038f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5039f3db423dSLydia Wang 5040f3db423dSLydia Wang fill_dig_outs(codec); 5041f3db423dSLydia Wang 5042f3db423dSLydia Wang if (spec->kctls.list) 5043f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5044f3db423dSLydia Wang 5045f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5046f3db423dSLydia Wang 5047f3db423dSLydia Wang if (spec->hp_mux) 50483d83e577STakashi Iwai via_hp_build(codec); 5049f3db423dSLydia Wang 50505b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5051f3db423dSLydia Wang 5052f3db423dSLydia Wang return 1; 5053f3db423dSLydia Wang } 5054f3db423dSLydia Wang 5055f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 505690dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 5057f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5058f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5059f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5060f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5061f3db423dSLydia Wang { } /* end */ 5062f3db423dSLydia Wang }; 5063f3db423dSLydia Wang #endif 5064f3db423dSLydia Wang 50653e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 50663e95b9abSLydia Wang { 50673e95b9abSLydia Wang struct via_spec *spec = codec->spec; 50683e95b9abSLydia Wang int imux_is_smixer; 50693e95b9abSLydia Wang unsigned int parm; 50703e95b9abSLydia Wang unsigned int mono_out, present; 50713e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 50723e95b9abSLydia Wang imux_is_smixer = 50733e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 50743e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 50753e95b9abSLydia Wang /* inputs */ 50763e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 50773e95b9abSLydia Wang parm = AC_PWRST_D3; 50783e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 50793e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 50803e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 50813e95b9abSLydia Wang if (imux_is_smixer) 50823e95b9abSLydia Wang parm = AC_PWRST_D0; 50833e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 50843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 50853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 50863e95b9abSLydia Wang 50873e95b9abSLydia Wang parm = AC_PWRST_D3; 50883e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 50893e95b9abSLydia Wang /* PW11 (22h) */ 50903e95b9abSLydia Wang if (spec->dmic_enabled) 50913e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 50923e95b9abSLydia Wang else 50933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 50943e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 50953e95b9abSLydia Wang 50963e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 50973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 50983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 50993e95b9abSLydia Wang 51003e95b9abSLydia Wang /* outputs */ 51013e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 51023e95b9abSLydia Wang parm = AC_PWRST_D3; 51033e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 51043e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 51053e95b9abSLydia Wang if (spec->smart51_enabled) 51063e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 51073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 51083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 51093e95b9abSLydia Wang 51103e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 51113e95b9abSLydia Wang parm = AC_PWRST_D3; 51123e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 51133e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 51143e95b9abSLydia Wang if (spec->smart51_enabled) 51153e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 51163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 51173e95b9abSLydia Wang 51183e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 51193e95b9abSLydia Wang if (spec->smart51_enabled) 51203e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 51213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 51223e95b9abSLydia Wang 51233e95b9abSLydia Wang /* Mono out */ 51243e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 51253e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 51263e95b9abSLydia Wang 51273e95b9abSLydia Wang if (present) 51283e95b9abSLydia Wang mono_out = 0; 51293e95b9abSLydia Wang else { 51303e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 51313e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 51323e95b9abSLydia Wang mono_out = 0; 51333e95b9abSLydia Wang else 51343e95b9abSLydia Wang mono_out = 1; 51353e95b9abSLydia Wang } 51363e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 51373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 51383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 51393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 51403e95b9abSLydia Wang 51413e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 51423e95b9abSLydia Wang parm = AC_PWRST_D3; 51433e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 51443e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 51453e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 51463e95b9abSLydia Wang if (spec->hp_independent_mode) 51473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 51483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 51493e95b9abSLydia Wang 51503e95b9abSLydia Wang /* force to D0 for internal Speaker */ 51513e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 51523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 51533e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 51543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 51553e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 51563e95b9abSLydia Wang } 51573e95b9abSLydia Wang 5158f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5159f3db423dSLydia Wang { 5160f3db423dSLydia Wang struct via_spec *spec; 5161f3db423dSLydia Wang int err; 5162f3db423dSLydia Wang 5163f3db423dSLydia Wang /* create a codec specific record */ 51645b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5165f3db423dSLydia Wang if (spec == NULL) 5166f3db423dSLydia Wang return -ENOMEM; 5167f3db423dSLydia Wang 5168f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5169f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5170f3db423dSLydia Wang if (err < 0) { 5171f3db423dSLydia Wang via_free(codec); 5172f3db423dSLydia Wang return err; 5173f3db423dSLydia Wang } else if (!err) { 5174f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5175f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5176f3db423dSLydia Wang } 5177f3db423dSLydia Wang 5178f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5179f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5180f3db423dSLydia Wang 5181f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5182f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5183f3db423dSLydia Wang 5184f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5185f3db423dSLydia Wang 5186a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 5187f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5188f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5189f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5190f3db423dSLydia Wang spec->num_mixers++; 5191f3db423dSLydia Wang } 5192f3db423dSLydia Wang 5193f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5194f3db423dSLydia Wang spec->num_mixers++; 5195f3db423dSLydia Wang 5196f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5197f3db423dSLydia Wang 5198f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5199f3db423dSLydia Wang 5200f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 52010f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5202f3db423dSLydia Wang 5203f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5204f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5205f3db423dSLydia Wang #endif 5206f3db423dSLydia Wang 52073e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 5208f3db423dSLydia Wang return 0; 5209f3db423dSLydia Wang } 521025eaba2fSLydia Wang 521125eaba2fSLydia Wang /* for vt2002P */ 521225eaba2fSLydia Wang 521325eaba2fSLydia Wang /* capture mixer elements */ 521490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt2002P_capture_mixer[] = { 521525eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 521625eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 521725eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 521825eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 521925eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 522025eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 522125eaba2fSLydia Wang HDA_INPUT), 522225eaba2fSLydia Wang { 522325eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 522425eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 522525eaba2fSLydia Wang * So call somewhat different.. 522625eaba2fSLydia Wang */ 522725eaba2fSLydia Wang /* .name = "Capture Source", */ 522825eaba2fSLydia Wang .name = "Input Source", 522925eaba2fSLydia Wang .count = 2, 523025eaba2fSLydia Wang .info = via_mux_enum_info, 523125eaba2fSLydia Wang .get = via_mux_enum_get, 523225eaba2fSLydia Wang .put = via_mux_enum_put, 523325eaba2fSLydia Wang }, 523425eaba2fSLydia Wang { } /* end */ 523525eaba2fSLydia Wang }; 523625eaba2fSLydia Wang 523790dd48a1STakashi Iwai static const struct hda_verb vt2002P_volume_init_verbs[] = { 5238eadb9a80SLydia Wang /* Class-D speaker related verbs */ 5239eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 5240eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 5241eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 524225eaba2fSLydia Wang /* 524325eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 524425eaba2fSLydia Wang */ 524525eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 524625eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 524725eaba2fSLydia Wang 524825eaba2fSLydia Wang 524925eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 525025eaba2fSLydia Wang * mixer widget 525125eaba2fSLydia Wang */ 525225eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 525325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 525425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 525525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 525625eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 525725eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 525825eaba2fSLydia Wang 525925eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 526025eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 526125eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 526225eaba2fSLydia Wang 526325eaba2fSLydia Wang /* PW9 Output enable */ 526425eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 526525eaba2fSLydia Wang 526625eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 526725eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 526825eaba2fSLydia Wang 526925eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 527025eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527125eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527225eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527325eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 527425eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527525eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527625eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527725eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 527825eaba2fSLydia Wang 527925eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 528025eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 528125eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 528225eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 528325eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 528425eaba2fSLydia Wang 528525eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 528625eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 528725eaba2fSLydia Wang 528825eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 528925eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 529025eaba2fSLydia Wang { } 529125eaba2fSLydia Wang }; 529290dd48a1STakashi Iwai static const struct hda_verb vt1802_volume_init_verbs[] = { 529311890956SLydia Wang /* 529411890956SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 529511890956SLydia Wang */ 529611890956SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 529711890956SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 529811890956SLydia Wang 529911890956SLydia Wang 530011890956SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 530111890956SLydia Wang * mixer widget 530211890956SLydia Wang */ 530311890956SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 530411890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 530511890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 530611890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 530711890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 530811890956SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 530911890956SLydia Wang 531011890956SLydia Wang /* MUX Indices: Mic = 0 */ 531111890956SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 531211890956SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 531311890956SLydia Wang 531411890956SLydia Wang /* PW9 Output enable */ 531511890956SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 531611890956SLydia Wang 531711890956SLydia Wang /* Enable Boost Volume backdoor */ 531811890956SLydia Wang {0x1, 0xfb9, 0x24}, 531911890956SLydia Wang 532011890956SLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 532111890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 532211890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 532311890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 532411890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 532511890956SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 532611890956SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 532711890956SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 532811890956SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 532911890956SLydia Wang 533011890956SLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 533111890956SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 533211890956SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 533311890956SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 533411890956SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 533511890956SLydia Wang 533611890956SLydia Wang /* set PW0 index=0 (MW0) */ 533711890956SLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 533811890956SLydia Wang 533911890956SLydia Wang /* Enable AOW0 to MW9 */ 534011890956SLydia Wang {0x1, 0xfb8, 0x88}, 534111890956SLydia Wang { } 534211890956SLydia Wang }; 534325eaba2fSLydia Wang 534425eaba2fSLydia Wang 534590dd48a1STakashi Iwai static const struct hda_verb vt2002P_uniwill_init_verbs[] = { 534625eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 534725eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 534825eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 534925eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 535025eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 535125eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 535225eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 535325eaba2fSLydia Wang { } 535425eaba2fSLydia Wang }; 535590dd48a1STakashi Iwai static const struct hda_verb vt1802_uniwill_init_verbs[] = { 535611890956SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 535711890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 535811890956SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 535911890956SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 536011890956SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536111890956SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536211890956SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 536311890956SLydia Wang { } 536411890956SLydia Wang }; 536525eaba2fSLydia Wang 536690dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_playback = { 536725eaba2fSLydia Wang .substreams = 2, 536825eaba2fSLydia Wang .channels_min = 2, 536925eaba2fSLydia Wang .channels_max = 2, 537025eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 537125eaba2fSLydia Wang .ops = { 537225eaba2fSLydia Wang .open = via_playback_pcm_open, 537325eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 537425eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 537525eaba2fSLydia Wang .close = via_pcm_open_close, 537625eaba2fSLydia Wang }, 537725eaba2fSLydia Wang }; 537825eaba2fSLydia Wang 537990dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_analog_capture = { 538025eaba2fSLydia Wang .substreams = 2, 538125eaba2fSLydia Wang .channels_min = 2, 538225eaba2fSLydia Wang .channels_max = 2, 538325eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 538425eaba2fSLydia Wang .ops = { 538525eaba2fSLydia Wang .open = via_pcm_open_close, 538625eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 538725eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 538825eaba2fSLydia Wang .close = via_pcm_open_close, 538925eaba2fSLydia Wang }, 539025eaba2fSLydia Wang }; 539125eaba2fSLydia Wang 539290dd48a1STakashi Iwai static const struct hda_pcm_stream vt2002P_pcm_digital_playback = { 539325eaba2fSLydia Wang .substreams = 1, 539425eaba2fSLydia Wang .channels_min = 2, 539525eaba2fSLydia Wang .channels_max = 2, 539625eaba2fSLydia Wang /* NID is set in via_build_pcms */ 539725eaba2fSLydia Wang .ops = { 539825eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 539925eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 540025eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 540125eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 540225eaba2fSLydia Wang }, 540325eaba2fSLydia Wang }; 540425eaba2fSLydia Wang 540525eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 540625eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 540725eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 540825eaba2fSLydia Wang { 540925eaba2fSLydia Wang spec->multiout.num_dacs = 1; 541025eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 541125eaba2fSLydia Wang if (cfg->line_out_pins[0]) 5412dda14410STakashi Iwai spec->private_dac_nids[0] = 0x8; 541325eaba2fSLydia Wang return 0; 541425eaba2fSLydia Wang } 541525eaba2fSLydia Wang 541625eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 541725eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 541825eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 541925eaba2fSLydia Wang { 542025eaba2fSLydia Wang int err; 542111890956SLydia Wang hda_nid_t sw_nid; 542225eaba2fSLydia Wang 542325eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 542425eaba2fSLydia Wang return -1; 542525eaba2fSLydia Wang 542611890956SLydia Wang if (spec->codec_type == VT1802) 542711890956SLydia Wang sw_nid = 0x28; 542811890956SLydia Wang else 542911890956SLydia Wang sw_nid = 0x26; 543025eaba2fSLydia Wang 543125eaba2fSLydia Wang /* Line-Out: PortE */ 543225eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 543325eaba2fSLydia Wang "Master Front Playback Volume", 543425eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 543525eaba2fSLydia Wang if (err < 0) 543625eaba2fSLydia Wang return err; 543725eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 543825eaba2fSLydia Wang "Master Front Playback Switch", 543911890956SLydia Wang HDA_COMPOSE_AMP_VAL(sw_nid, 3, 0, HDA_OUTPUT)); 544025eaba2fSLydia Wang if (err < 0) 544125eaba2fSLydia Wang return err; 544225eaba2fSLydia Wang 544325eaba2fSLydia Wang return 0; 544425eaba2fSLydia Wang } 544525eaba2fSLydia Wang 544625eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 544725eaba2fSLydia Wang { 544825eaba2fSLydia Wang int err; 544925eaba2fSLydia Wang 545025eaba2fSLydia Wang if (!pin) 545125eaba2fSLydia Wang return 0; 545225eaba2fSLydia Wang 545325eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 545425eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 545525eaba2fSLydia Wang 545625eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 545725eaba2fSLydia Wang "Headphone Playback Volume", 545825eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 545925eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 546025eaba2fSLydia Wang if (err < 0) 546125eaba2fSLydia Wang return err; 546225eaba2fSLydia Wang 546325eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 546425eaba2fSLydia Wang "Headphone Playback Switch", 546525eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 546625eaba2fSLydia Wang if (err < 0) 546725eaba2fSLydia Wang return err; 546825eaba2fSLydia Wang 546925eaba2fSLydia Wang create_hp_imux(spec); 547025eaba2fSLydia Wang return 0; 547125eaba2fSLydia Wang } 547225eaba2fSLydia Wang 547325eaba2fSLydia Wang /* create playback/capture controls for input pins */ 547410a20af7STakashi Iwai static int vt2002P_auto_create_analog_input_ctls(struct hda_codec *codec, 547525eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 547625eaba2fSLydia Wang { 5477a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); 547825eaba2fSLydia Wang } 547925eaba2fSLydia Wang 548025eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 548125eaba2fSLydia Wang { 548225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 548325eaba2fSLydia Wang int err; 548425eaba2fSLydia Wang 548525eaba2fSLydia Wang 548625eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 548725eaba2fSLydia Wang if (err < 0) 548825eaba2fSLydia Wang return err; 548925eaba2fSLydia Wang 549025eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 549125eaba2fSLydia Wang if (err < 0) 549225eaba2fSLydia Wang return err; 549325eaba2fSLydia Wang 549425eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 549525eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 549625eaba2fSLydia Wang 549725eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 549825eaba2fSLydia Wang if (err < 0) 549925eaba2fSLydia Wang return err; 550025eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 550125eaba2fSLydia Wang if (err < 0) 550225eaba2fSLydia Wang return err; 550310a20af7STakashi Iwai err = vt2002P_auto_create_analog_input_ctls(codec, &spec->autocfg); 550425eaba2fSLydia Wang if (err < 0) 550525eaba2fSLydia Wang return err; 550625eaba2fSLydia Wang 550725eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 550825eaba2fSLydia Wang 550925eaba2fSLydia Wang fill_dig_outs(codec); 551025eaba2fSLydia Wang 551125eaba2fSLydia Wang if (spec->kctls.list) 551225eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 551325eaba2fSLydia Wang 551425eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 551525eaba2fSLydia Wang 551625eaba2fSLydia Wang if (spec->hp_mux) 55173d83e577STakashi Iwai via_hp_build(codec); 551825eaba2fSLydia Wang 551925eaba2fSLydia Wang return 1; 552025eaba2fSLydia Wang } 552125eaba2fSLydia Wang 552225eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 552390dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 552425eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 552525eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 552625eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 552725eaba2fSLydia Wang { } /* end */ 552825eaba2fSLydia Wang }; 552925eaba2fSLydia Wang #endif 553025eaba2fSLydia Wang 55313e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 55323e95b9abSLydia Wang { 55333e95b9abSLydia Wang struct via_spec *spec = codec->spec; 55343e95b9abSLydia Wang int imux_is_smixer; 55353e95b9abSLydia Wang unsigned int parm; 55363e95b9abSLydia Wang unsigned int present; 55373e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 55383e95b9abSLydia Wang imux_is_smixer = 55393e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 55403e95b9abSLydia Wang /* inputs */ 55413e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 55423e95b9abSLydia Wang parm = AC_PWRST_D3; 55433e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 55443e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 55453e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 55463e95b9abSLydia Wang parm = AC_PWRST_D0; 55473e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 55483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 55493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 55503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 55513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 55523e95b9abSLydia Wang 55533e95b9abSLydia Wang /* outputs */ 55543e95b9abSLydia Wang /* AOW0 (8h)*/ 55553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 55563e95b9abSLydia Wang 555711890956SLydia Wang if (spec->codec_type == VT1802) { 555811890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 555911890956SLydia Wang parm = AC_PWRST_D3; 556011890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 556111890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 556211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 556311890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 556411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 556511890956SLydia Wang } else { 55663e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 55673e95b9abSLydia Wang parm = AC_PWRST_D3; 55683e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 55693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 55703e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 55723e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 557311890956SLydia Wang } 55743e95b9abSLydia Wang 557511890956SLydia Wang if (spec->codec_type == VT1802) { 557611890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 557711890956SLydia Wang parm = AC_PWRST_D3; 557811890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 557911890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 558011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 558111890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 558211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 558311890956SLydia Wang } else { 55843e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 55853e95b9abSLydia Wang parm = AC_PWRST_D3; 55863e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 55873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 55883e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 55893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 55903e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 559111890956SLydia Wang } 55923e95b9abSLydia Wang 55933e95b9abSLydia Wang if (spec->hp_independent_mode) 55943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 55953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 55963e95b9abSLydia Wang 55973e95b9abSLydia Wang /* Class-D */ 55983e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 55993e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 56003e95b9abSLydia Wang 56013e95b9abSLydia Wang parm = AC_PWRST_D3; 56023e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 56033e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 560411890956SLydia Wang if (spec->codec_type == VT1802) 560511890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 560611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 560711890956SLydia Wang else 56083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 56093e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 56113e95b9abSLydia Wang 56123e95b9abSLydia Wang /* Mono Out */ 56133e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 56143e95b9abSLydia Wang 56153e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 561611890956SLydia Wang if (spec->codec_type == VT1802) { 561711890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 561811890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 561911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 562011890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 562111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 562211890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 562311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 562411890956SLydia Wang } else { 56253e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 56263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 56273e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 56293e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 56303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 56313e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 563211890956SLydia Wang } 56333e95b9abSLydia Wang /* MW9 (21h) */ 56343e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 56353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 56363e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 56373e95b9abSLydia Wang else 56383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 56393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 56403e95b9abSLydia Wang } 564125eaba2fSLydia Wang 564225eaba2fSLydia Wang /* patch for vt2002P */ 564325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 564425eaba2fSLydia Wang { 564525eaba2fSLydia Wang struct via_spec *spec; 564625eaba2fSLydia Wang int err; 564725eaba2fSLydia Wang 564825eaba2fSLydia Wang /* create a codec specific record */ 56495b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 565025eaba2fSLydia Wang if (spec == NULL) 565125eaba2fSLydia Wang return -ENOMEM; 565225eaba2fSLydia Wang 565325eaba2fSLydia Wang /* automatic parse from the BIOS config */ 565425eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 565525eaba2fSLydia Wang if (err < 0) { 565625eaba2fSLydia Wang via_free(codec); 565725eaba2fSLydia Wang return err; 565825eaba2fSLydia Wang } else if (!err) { 565925eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 566025eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 566125eaba2fSLydia Wang } 566225eaba2fSLydia Wang 566311890956SLydia Wang if (spec->codec_type == VT1802) 566411890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 566511890956SLydia Wang vt1802_volume_init_verbs; 566611890956SLydia Wang else 566711890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 566811890956SLydia Wang vt2002P_volume_init_verbs; 566925eaba2fSLydia Wang 567011890956SLydia Wang if (spec->codec_type == VT1802) 567111890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 567211890956SLydia Wang vt1802_uniwill_init_verbs; 567311890956SLydia Wang else 567411890956SLydia Wang spec->init_verbs[spec->num_iverbs++] = 567511890956SLydia Wang vt2002P_uniwill_init_verbs; 567611890956SLydia Wang 567725eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 567825eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 567925eaba2fSLydia Wang 568025eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 568125eaba2fSLydia Wang 5682a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 568325eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 568425eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 568525eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 568625eaba2fSLydia Wang spec->num_mixers++; 568725eaba2fSLydia Wang } 568825eaba2fSLydia Wang 568925eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 569025eaba2fSLydia Wang 569125eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 56920f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 569325eaba2fSLydia Wang 569425eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 569525eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 569625eaba2fSLydia Wang #endif 569725eaba2fSLydia Wang 56983e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 569925eaba2fSLydia Wang return 0; 570025eaba2fSLydia Wang } 5701ab6734e7SLydia Wang 5702ab6734e7SLydia Wang /* for vt1812 */ 5703ab6734e7SLydia Wang 5704ab6734e7SLydia Wang /* capture mixer elements */ 570590dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1812_capture_mixer[] = { 5706ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5707ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5708ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5709ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5710ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5711ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5712ab6734e7SLydia Wang HDA_INPUT), 5713ab6734e7SLydia Wang { 5714ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5715ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5716ab6734e7SLydia Wang * So call somewhat different.. 5717ab6734e7SLydia Wang */ 5718ab6734e7SLydia Wang .name = "Input Source", 5719ab6734e7SLydia Wang .count = 2, 5720ab6734e7SLydia Wang .info = via_mux_enum_info, 5721ab6734e7SLydia Wang .get = via_mux_enum_get, 5722ab6734e7SLydia Wang .put = via_mux_enum_put, 5723ab6734e7SLydia Wang }, 5724ab6734e7SLydia Wang { } /* end */ 5725ab6734e7SLydia Wang }; 5726ab6734e7SLydia Wang 572790dd48a1STakashi Iwai static const struct hda_verb vt1812_volume_init_verbs[] = { 5728ab6734e7SLydia Wang /* 5729ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5730ab6734e7SLydia Wang */ 5731ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5732ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5733ab6734e7SLydia Wang 5734ab6734e7SLydia Wang 5735ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5736ab6734e7SLydia Wang * mixer widget 5737ab6734e7SLydia Wang */ 5738ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5739ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5740ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5741ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5742ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5743ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5744ab6734e7SLydia Wang 5745ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5746ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5747ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5748ab6734e7SLydia Wang 5749ab6734e7SLydia Wang /* PW9 Output enable */ 5750ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5751ab6734e7SLydia Wang 5752ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5753ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5754ab6734e7SLydia Wang 5755ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5756ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5757ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5758ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5759ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5760ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5761ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5762ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5763ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5764ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5765ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5766ab6734e7SLydia Wang 5767ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5768ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5769ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5770ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5771ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5772ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5773ab6734e7SLydia Wang 5774ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5775ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5776ab6734e7SLydia Wang { } 5777ab6734e7SLydia Wang }; 5778ab6734e7SLydia Wang 5779ab6734e7SLydia Wang 578090dd48a1STakashi Iwai static const struct hda_verb vt1812_uniwill_init_verbs[] = { 5781ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5782ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5783ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5784ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5785ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5786ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5787ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5788ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5789ab6734e7SLydia Wang { } 5790ab6734e7SLydia Wang }; 5791ab6734e7SLydia Wang 579290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_playback = { 5793ab6734e7SLydia Wang .substreams = 2, 5794ab6734e7SLydia Wang .channels_min = 2, 5795ab6734e7SLydia Wang .channels_max = 2, 5796ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5797ab6734e7SLydia Wang .ops = { 5798ab6734e7SLydia Wang .open = via_playback_pcm_open, 5799ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5800ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5801ab6734e7SLydia Wang .close = via_pcm_open_close, 5802ab6734e7SLydia Wang }, 5803ab6734e7SLydia Wang }; 5804ab6734e7SLydia Wang 580590dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_analog_capture = { 5806ab6734e7SLydia Wang .substreams = 2, 5807ab6734e7SLydia Wang .channels_min = 2, 5808ab6734e7SLydia Wang .channels_max = 2, 5809ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5810ab6734e7SLydia Wang .ops = { 5811ab6734e7SLydia Wang .open = via_pcm_open_close, 5812ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5813ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5814ab6734e7SLydia Wang .close = via_pcm_open_close, 5815ab6734e7SLydia Wang }, 5816ab6734e7SLydia Wang }; 5817ab6734e7SLydia Wang 581890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1812_pcm_digital_playback = { 5819ab6734e7SLydia Wang .substreams = 1, 5820ab6734e7SLydia Wang .channels_min = 2, 5821ab6734e7SLydia Wang .channels_max = 2, 5822ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5823ab6734e7SLydia Wang .ops = { 5824ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5825ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5826ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5827ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5828ab6734e7SLydia Wang }, 5829ab6734e7SLydia Wang }; 5830ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5831ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5832ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5833ab6734e7SLydia Wang { 5834ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5835ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5836ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5837dda14410STakashi Iwai spec->private_dac_nids[0] = 0x8; 5838ab6734e7SLydia Wang return 0; 5839ab6734e7SLydia Wang } 5840ab6734e7SLydia Wang 5841ab6734e7SLydia Wang 5842ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5843ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5844ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5845ab6734e7SLydia Wang { 5846ab6734e7SLydia Wang int err; 5847ab6734e7SLydia Wang 5848ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 5849ab6734e7SLydia Wang return -1; 5850ab6734e7SLydia Wang 5851ab6734e7SLydia Wang /* Line-Out: PortE */ 5852ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 58533d83e577STakashi Iwai "Front Playback Volume", 5854ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 5855ab6734e7SLydia Wang if (err < 0) 5856ab6734e7SLydia Wang return err; 5857ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 58583d83e577STakashi Iwai "Front Playback Switch", 5859ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 5860ab6734e7SLydia Wang if (err < 0) 5861ab6734e7SLydia Wang return err; 5862ab6734e7SLydia Wang 5863ab6734e7SLydia Wang return 0; 5864ab6734e7SLydia Wang } 5865ab6734e7SLydia Wang 5866ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5867ab6734e7SLydia Wang { 5868ab6734e7SLydia Wang int err; 5869ab6734e7SLydia Wang 5870ab6734e7SLydia Wang if (!pin) 5871ab6734e7SLydia Wang return 0; 5872ab6734e7SLydia Wang 5873ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 5874ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 5875ab6734e7SLydia Wang 5876ab6734e7SLydia Wang 5877ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5878ab6734e7SLydia Wang "Headphone Playback Volume", 5879ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 5880ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 5881ab6734e7SLydia Wang if (err < 0) 5882ab6734e7SLydia Wang return err; 5883ab6734e7SLydia Wang 5884ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5885ab6734e7SLydia Wang "Headphone Playback Switch", 5886ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5887ab6734e7SLydia Wang if (err < 0) 5888ab6734e7SLydia Wang return err; 5889ab6734e7SLydia Wang 5890ab6734e7SLydia Wang create_hp_imux(spec); 5891ab6734e7SLydia Wang return 0; 5892ab6734e7SLydia Wang } 5893ab6734e7SLydia Wang 5894ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 589510a20af7STakashi Iwai static int vt1812_auto_create_analog_input_ctls(struct hda_codec *codec, 5896ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5897ab6734e7SLydia Wang { 5898a766d0d7STakashi Iwai return vt_auto_create_analog_input_ctls(codec, cfg, 0x21); 5899ab6734e7SLydia Wang } 5900ab6734e7SLydia Wang 5901ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 5902ab6734e7SLydia Wang { 5903ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 5904ab6734e7SLydia Wang int err; 5905ab6734e7SLydia Wang 5906ab6734e7SLydia Wang 5907ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5908ab6734e7SLydia Wang if (err < 0) 5909ab6734e7SLydia Wang return err; 5910ab6734e7SLydia Wang fill_dig_outs(codec); 5911ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 5912ab6734e7SLydia Wang if (err < 0) 5913ab6734e7SLydia Wang return err; 5914ab6734e7SLydia Wang 5915ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 5916ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 5917ab6734e7SLydia Wang 5918ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 5919ab6734e7SLydia Wang if (err < 0) 5920ab6734e7SLydia Wang return err; 5921ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5922ab6734e7SLydia Wang if (err < 0) 5923ab6734e7SLydia Wang return err; 592410a20af7STakashi Iwai err = vt1812_auto_create_analog_input_ctls(codec, &spec->autocfg); 5925ab6734e7SLydia Wang if (err < 0) 5926ab6734e7SLydia Wang return err; 5927ab6734e7SLydia Wang 5928ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5929ab6734e7SLydia Wang 5930ab6734e7SLydia Wang fill_dig_outs(codec); 5931ab6734e7SLydia Wang 5932ab6734e7SLydia Wang if (spec->kctls.list) 5933ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5934ab6734e7SLydia Wang 5935ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 5936ab6734e7SLydia Wang 5937ab6734e7SLydia Wang if (spec->hp_mux) 59383d83e577STakashi Iwai via_hp_build(codec); 5939ab6734e7SLydia Wang 5940ab6734e7SLydia Wang return 1; 5941ab6734e7SLydia Wang } 5942ab6734e7SLydia Wang 5943ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 594490dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 5945ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 5946ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 5947ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 5948ab6734e7SLydia Wang { } /* end */ 5949ab6734e7SLydia Wang }; 5950ab6734e7SLydia Wang #endif 5951ab6734e7SLydia Wang 59523e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 59533e95b9abSLydia Wang { 59543e95b9abSLydia Wang struct via_spec *spec = codec->spec; 59553e95b9abSLydia Wang int imux_is_smixer = 59563e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 59573e95b9abSLydia Wang unsigned int parm; 59583e95b9abSLydia Wang unsigned int present; 59593e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 59603e95b9abSLydia Wang imux_is_smixer = 59613e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 59623e95b9abSLydia Wang /* inputs */ 59633e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 59643e95b9abSLydia Wang parm = AC_PWRST_D3; 59653e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 59663e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 59673e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 59683e95b9abSLydia Wang parm = AC_PWRST_D0; 59693e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 59703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 59713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 59723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 59733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 59743e95b9abSLydia Wang 59753e95b9abSLydia Wang /* outputs */ 59763e95b9abSLydia Wang /* AOW0 (8h)*/ 59773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 59783e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59793e95b9abSLydia Wang 59803e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 59813e95b9abSLydia Wang parm = AC_PWRST_D3; 59823e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 59833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 59843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 59853e95b9abSLydia Wang 59863e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 59873e95b9abSLydia Wang parm = AC_PWRST_D3; 59883e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 59893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 59903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 59913e95b9abSLydia Wang if (spec->hp_independent_mode) 59923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 59933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 59943e95b9abSLydia Wang 59953e95b9abSLydia Wang /* Internal Speaker */ 59963e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 59973e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 59983e95b9abSLydia Wang 59993e95b9abSLydia Wang parm = AC_PWRST_D3; 60003e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 60013e95b9abSLydia Wang if (present) { 60023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 60033e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 60053e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60063e95b9abSLydia Wang } else { 60073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 60083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 60103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60113e95b9abSLydia Wang } 60123e95b9abSLydia Wang 60133e95b9abSLydia Wang 60143e95b9abSLydia Wang /* Mono Out */ 60153e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 60163e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 60173e95b9abSLydia Wang 60183e95b9abSLydia Wang parm = AC_PWRST_D3; 60193e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 60203e95b9abSLydia Wang if (present) { 60213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 60223e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 60243e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 60263e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 60273e95b9abSLydia Wang } else { 60283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 60293e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 60313e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 60333e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 60343e95b9abSLydia Wang } 60353e95b9abSLydia Wang 60363e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 60373e95b9abSLydia Wang parm = AC_PWRST_D3; 60383e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 60393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 60403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 60413e95b9abSLydia Wang 60423e95b9abSLydia Wang } 6043ab6734e7SLydia Wang 6044ab6734e7SLydia Wang /* patch for vt1812 */ 6045ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 6046ab6734e7SLydia Wang { 6047ab6734e7SLydia Wang struct via_spec *spec; 6048ab6734e7SLydia Wang int err; 6049ab6734e7SLydia Wang 6050ab6734e7SLydia Wang /* create a codec specific record */ 60515b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 6052ab6734e7SLydia Wang if (spec == NULL) 6053ab6734e7SLydia Wang return -ENOMEM; 6054ab6734e7SLydia Wang 6055ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 6056ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 6057ab6734e7SLydia Wang if (err < 0) { 6058ab6734e7SLydia Wang via_free(codec); 6059ab6734e7SLydia Wang return err; 6060ab6734e7SLydia Wang } else if (!err) { 6061ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 6062ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 6063ab6734e7SLydia Wang } 6064ab6734e7SLydia Wang 6065ab6734e7SLydia Wang 6066ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 6067ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6068ab6734e7SLydia Wang 6069ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6070ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6071ab6734e7SLydia Wang 6072ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6073ab6734e7SLydia Wang 6074a766d0d7STakashi Iwai if (spec->adc_nids && spec->input_mux) { 6075ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6076ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6077ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6078ab6734e7SLydia Wang spec->num_mixers++; 6079ab6734e7SLydia Wang } 6080ab6734e7SLydia Wang 6081ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6082ab6734e7SLydia Wang 6083ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 60840f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 6085ab6734e7SLydia Wang 6086ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6087ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6088ab6734e7SLydia Wang #endif 6089ab6734e7SLydia Wang 60903e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 6091ab6734e7SLydia Wang return 0; 6092ab6734e7SLydia Wang } 6093ab6734e7SLydia Wang 6094c577b8a1SJoseph Chan /* 6095c577b8a1SJoseph Chan * patch entries 6096c577b8a1SJoseph Chan */ 609790dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 60983218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 60993218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 61003218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 61013218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 61023218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6103f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61043218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6105f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61063218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6107f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61083218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6109f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 61103218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6111f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61123218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6113f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61143218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6115f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61163218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6117f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 61183218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6119f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61203218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6121f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61223218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6123f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61243218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6125f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 61263218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6127f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61283218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6129f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61303218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6131f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61323218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6133f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 61343218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6135d949cac1SHarald Welte .patch = patch_vt1708S}, 61363218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6137d949cac1SHarald Welte .patch = patch_vt1708S}, 61383218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6139d949cac1SHarald Welte .patch = patch_vt1708S}, 61403218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6141d949cac1SHarald Welte .patch = patch_vt1708S}, 6142bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 6143d949cac1SHarald Welte .patch = patch_vt1708S}, 61443218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6145d949cac1SHarald Welte .patch = patch_vt1708S}, 61463218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6147d949cac1SHarald Welte .patch = patch_vt1708S}, 61483218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6149d949cac1SHarald Welte .patch = patch_vt1708S}, 61503218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6151d949cac1SHarald Welte .patch = patch_vt1702}, 61523218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6153d949cac1SHarald Welte .patch = patch_vt1702}, 61543218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6155d949cac1SHarald Welte .patch = patch_vt1702}, 61563218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6157d949cac1SHarald Welte .patch = patch_vt1702}, 61583218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6159d949cac1SHarald Welte .patch = patch_vt1702}, 61603218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6161d949cac1SHarald Welte .patch = patch_vt1702}, 61623218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6163d949cac1SHarald Welte .patch = patch_vt1702}, 61643218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6165d949cac1SHarald Welte .patch = patch_vt1702}, 6166eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6167eb7188caSLydia Wang .patch = patch_vt1718S}, 6168eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6169eb7188caSLydia Wang .patch = patch_vt1718S}, 6170bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6171bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6172bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6173bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6174f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6175f3db423dSLydia Wang .patch = patch_vt1716S}, 6176f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6177f3db423dSLydia Wang .patch = patch_vt1716S}, 617825eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 617925eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6180ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 618136dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 618236dd5c4aSLydia Wang .patch = patch_vt1708S}, 618311890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 618411890956SLydia Wang .patch = patch_vt2002P}, 618511890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 618611890956SLydia Wang .patch = patch_vt2002P}, 6187c577b8a1SJoseph Chan {} /* terminator */ 6188c577b8a1SJoseph Chan }; 61891289e9e8STakashi Iwai 61901289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 61911289e9e8STakashi Iwai 61921289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 61931289e9e8STakashi Iwai .preset = snd_hda_preset_via, 61941289e9e8STakashi Iwai .owner = THIS_MODULE, 61951289e9e8STakashi Iwai }; 61961289e9e8STakashi Iwai 61971289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 61981289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 61991289e9e8STakashi Iwai 62001289e9e8STakashi Iwai static int __init patch_via_init(void) 62011289e9e8STakashi Iwai { 62021289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 62031289e9e8STakashi Iwai } 62041289e9e8STakashi Iwai 62051289e9e8STakashi Iwai static void __exit patch_via_exit(void) 62061289e9e8STakashi Iwai { 62071289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 62081289e9e8STakashi Iwai } 62091289e9e8STakashi Iwai 62101289e9e8STakashi Iwai module_init(patch_via_init) 62111289e9e8STakashi Iwai module_exit(patch_via_exit) 6212