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 1104a79616dSTakashi Iwai struct nid_path { 1114a79616dSTakashi Iwai int depth; 1124a79616dSTakashi Iwai hda_nid_t path[5]; 1134a79616dSTakashi Iwai short idx[5]; 1144a79616dSTakashi Iwai }; 1154a79616dSTakashi Iwai 1161f2e99feSLydia Wang struct via_spec { 1171f2e99feSLydia Wang /* codec parameterization */ 11890dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1191f2e99feSLydia Wang unsigned int num_mixers; 1201f2e99feSLydia Wang 12190dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1221f2e99feSLydia Wang unsigned int num_iverbs; 1231f2e99feSLydia Wang 12482673bc8STakashi Iwai char stream_name_analog[32]; 1257eb56e84STakashi Iwai char stream_name_hp[32]; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12790dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1281f2e99feSLydia Wang 12982673bc8STakashi Iwai char stream_name_digital[32]; 13090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 13190dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1321f2e99feSLydia Wang 1331f2e99feSLydia Wang /* playback */ 1341f2e99feSLydia Wang struct hda_multi_out multiout; 1351f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 136ece8d043STakashi Iwai hda_nid_t hp_dac_nid; 1371f2e99feSLydia Wang 1384a79616dSTakashi Iwai struct nid_path out_path[4]; 1394a79616dSTakashi Iwai struct nid_path hp_path; 1404a79616dSTakashi Iwai struct nid_path hp_dep_path; 1414a918ffeSTakashi Iwai struct nid_path speaker_path; 1424a79616dSTakashi Iwai 1431f2e99feSLydia Wang /* capture */ 1441f2e99feSLydia Wang unsigned int num_adc_nids; 145a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1461f2e99feSLydia Wang hda_nid_t mux_nids[3]; 147620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1481f2e99feSLydia Wang hda_nid_t dig_in_nid; 1491f2e99feSLydia Wang hda_nid_t dig_in_pin; 1501f2e99feSLydia Wang 1511f2e99feSLydia Wang /* capture source */ 1521f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1531f2e99feSLydia Wang unsigned int cur_mux[3]; 1541f2e99feSLydia Wang 1551f2e99feSLydia Wang /* PCM information */ 1561f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1571f2e99feSLydia Wang 1581f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1591f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1601f2e99feSLydia Wang struct snd_array kctls; 1611f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1621f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1631f2e99feSLydia Wang 1641f2e99feSLydia Wang /* HP mode source */ 1651f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1661f2e99feSLydia Wang unsigned int hp_independent_mode; 1671f2e99feSLydia Wang unsigned int hp_independent_mode_index; 168f4a7828bSTakashi Iwai unsigned int can_smart51; 1691f2e99feSLydia Wang unsigned int smart51_enabled; 170f3db423dSLydia Wang unsigned int dmic_enabled; 17124088a58STakashi Iwai unsigned int no_pin_power_ctl; 1721f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1731f2e99feSLydia Wang 1741f2e99feSLydia Wang /* work to check hp jack state */ 1751f2e99feSLydia Wang struct hda_codec *codec; 1761f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 177e06e5a29STakashi Iwai int vt1708_jack_detect; 1781f2e99feSLydia Wang int vt1708_hp_present; 1793e95b9abSLydia Wang 1803e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1813e95b9abSLydia Wang 1821f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1831f2e99feSLydia Wang struct hda_loopback_check loopback; 1841f2e99feSLydia Wang #endif 1851f2e99feSLydia Wang }; 1861f2e99feSLydia Wang 1870341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1885b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1895b0cb1d8SJaroslav Kysela { 1905b0cb1d8SJaroslav Kysela struct via_spec *spec; 1915b0cb1d8SJaroslav Kysela 1925b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1935b0cb1d8SJaroslav Kysela if (spec == NULL) 1945b0cb1d8SJaroslav Kysela return NULL; 1955b0cb1d8SJaroslav Kysela 1965b0cb1d8SJaroslav Kysela codec->spec = spec; 1975b0cb1d8SJaroslav Kysela spec->codec = codec; 1980341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1990341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 2000341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 2010341ccd7SLydia Wang spec->codec_type = VT1708S; 2025b0cb1d8SJaroslav Kysela return spec; 2035b0cb1d8SJaroslav Kysela } 2045b0cb1d8SJaroslav Kysela 205744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 206d7426329SHarald Welte { 207744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 208d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 209d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 210d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 211d7426329SHarald Welte 212d7426329SHarald Welte /* get codec type */ 213d7426329SHarald Welte if (ven_id != 0x1106) 214d7426329SHarald Welte codec_type = UNKNOWN; 215d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 216d7426329SHarald Welte codec_type = VT1708; 217d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 218d7426329SHarald Welte codec_type = VT1709_10CH; 219d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 220d7426329SHarald Welte codec_type = VT1709_6CH; 221518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 222d7426329SHarald Welte codec_type = VT1708B_8CH; 223518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 224518bf3baSLydia Wang codec_type = VT1708BCE; 225518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 226d7426329SHarald Welte codec_type = VT1708B_4CH; 227d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 228d7426329SHarald Welte && (dev_id >> 12) < 8) 229d7426329SHarald Welte codec_type = VT1708S; 230d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 231d7426329SHarald Welte && (dev_id >> 12) < 8) 232d7426329SHarald Welte codec_type = VT1702; 233eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 234eb7188caSLydia Wang && (dev_id >> 12) < 8) 235eb7188caSLydia Wang codec_type = VT1718S; 236f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 237f3db423dSLydia Wang codec_type = VT1716S; 238bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 239bb3c6bfcSLydia Wang codec_type = VT1718S; 24025eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 24125eaba2fSLydia Wang codec_type = VT2002P; 242ab6734e7SLydia Wang else if (dev_id == 0x0448) 243ab6734e7SLydia Wang codec_type = VT1812; 24436dd5c4aSLydia Wang else if (dev_id == 0x0440) 24536dd5c4aSLydia Wang codec_type = VT1708S; 24611890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 24711890956SLydia Wang codec_type = VT1802; 248d7426329SHarald Welte else 249d7426329SHarald Welte codec_type = UNKNOWN; 250d7426329SHarald Welte return codec_type; 251d7426329SHarald Welte }; 252d7426329SHarald Welte 253ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25469e52a80SHarald Welte #define VIA_HP_EVENT 0x01 25569e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 2564a918ffeSTakashi Iwai #define VIA_LINE_EVENT 0x03 25769e52a80SHarald Welte 258c577b8a1SJoseph Chan enum { 259c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 260c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 261f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 262c577b8a1SJoseph Chan }; 263c577b8a1SJoseph Chan 264f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2651f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2661f2e99feSLydia Wang 2671f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2681f2e99feSLydia Wang { 2691f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2701f2e99feSLydia Wang return; 2711f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 272e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2731f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2741f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2751f2e99feSLydia Wang msecs_to_jiffies(100)); 2761f2e99feSLydia Wang } 2771f2e99feSLydia Wang 2781f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2791f2e99feSLydia Wang { 2801f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2811f2e99feSLydia Wang return; 2821f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2831f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2841f2e99feSLydia Wang return; 2851f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 286e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2875b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2881f2e99feSLydia Wang } 289f5271101SLydia Wang 2903e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2913e95b9abSLydia Wang { 2923e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2933e95b9abSLydia Wang if (spec->set_widgets_power_state) 2943e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2953e95b9abSLydia Wang } 29625eaba2fSLydia Wang 297f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 298f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 299f5271101SLydia Wang { 300f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 301f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 302f5271101SLydia Wang 3033e95b9abSLydia Wang set_widgets_power_state(codec); 304f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3051f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3061f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3071f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3081f2e99feSLydia Wang else 3091f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3101f2e99feSLydia Wang } 311f5271101SLydia Wang return change; 312f5271101SLydia Wang } 313f5271101SLydia Wang 314f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 315f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 316f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 317f5271101SLydia Wang .name = NULL, \ 318f5271101SLydia Wang .index = 0, \ 319f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 320f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 321f5271101SLydia Wang .put = analog_input_switch_put, \ 322f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 323f5271101SLydia Wang 32490dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 325c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 326c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 327f5271101SLydia Wang ANALOG_INPUT_MUTE, 328c577b8a1SJoseph Chan }; 329c577b8a1SJoseph Chan 330ab6734e7SLydia Wang 331c577b8a1SJoseph Chan /* add dynamic controls */ 332291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 333291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 334291c9e33STakashi Iwai const char *name) 335c577b8a1SJoseph Chan { 336c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 337c577b8a1SJoseph Chan 338603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 339603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 340c577b8a1SJoseph Chan if (!knew) 341291c9e33STakashi Iwai return NULL; 342291c9e33STakashi Iwai *knew = *tmpl; 343291c9e33STakashi Iwai if (!name) 344291c9e33STakashi Iwai name = tmpl->name; 345291c9e33STakashi Iwai if (name) { 346c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 347c577b8a1SJoseph Chan if (!knew->name) 348291c9e33STakashi Iwai return NULL; 349291c9e33STakashi Iwai } 350291c9e33STakashi Iwai return knew; 351291c9e33STakashi Iwai } 352291c9e33STakashi Iwai 353291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 354291c9e33STakashi Iwai int idx, unsigned long val) 355291c9e33STakashi Iwai { 356291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 357291c9e33STakashi Iwai 358291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 359291c9e33STakashi Iwai if (!knew) 360c577b8a1SJoseph Chan return -ENOMEM; 361d7a99cceSTakashi Iwai knew->index = idx; 3624d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 3635e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 364c577b8a1SJoseph Chan knew->private_value = val; 365c577b8a1SJoseph Chan return 0; 366c577b8a1SJoseph Chan } 367c577b8a1SJoseph Chan 3687b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 3697b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 3707b315bb4STakashi Iwai 371291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 3725b0cb1d8SJaroslav Kysela 373603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 374603c4019STakashi Iwai { 375603c4019STakashi Iwai struct via_spec *spec = codec->spec; 376603c4019STakashi Iwai 377603c4019STakashi Iwai if (spec->kctls.list) { 378603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 379603c4019STakashi Iwai int i; 380603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 381603c4019STakashi Iwai kfree(kctl[i].name); 382603c4019STakashi Iwai } 383603c4019STakashi Iwai snd_array_free(&spec->kctls); 384603c4019STakashi Iwai } 385603c4019STakashi Iwai 386c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 3879510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 3887b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 389c577b8a1SJoseph Chan { 390c577b8a1SJoseph Chan char name[32]; 391c577b8a1SJoseph Chan int err; 392c577b8a1SJoseph Chan 393c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 3947b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 395c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 396c577b8a1SJoseph Chan if (err < 0) 397c577b8a1SJoseph Chan return err; 398c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 3997b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 400c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 401c577b8a1SJoseph Chan if (err < 0) 402c577b8a1SJoseph Chan return err; 403c577b8a1SJoseph Chan return 0; 404c577b8a1SJoseph Chan } 405c577b8a1SJoseph Chan 4065d41762aSTakashi Iwai /* return the index of the given widget nid as the source of mux; 4075d41762aSTakashi Iwai * return -1 if not found; 4085d41762aSTakashi Iwai * if num_conns is non-NULL, set the total number of connections 4095d41762aSTakashi Iwai */ 4105d41762aSTakashi Iwai static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, 4115d41762aSTakashi Iwai hda_nid_t nid, int *num_conns) 412c577b8a1SJoseph Chan { 4135d41762aSTakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 4145d41762aSTakashi Iwai int i, nums; 4155d41762aSTakashi Iwai 4165d41762aSTakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 4175d41762aSTakashi Iwai if (num_conns) 4185d41762aSTakashi Iwai *num_conns = nums; 4195d41762aSTakashi Iwai for (i = 0; i < nums; i++) 4205d41762aSTakashi Iwai if (conn[i] == nid) 4215d41762aSTakashi Iwai return i; 4225d41762aSTakashi Iwai return -1; 4235d41762aSTakashi Iwai } 4245d41762aSTakashi Iwai 4255d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \ 4265d41762aSTakashi Iwai __get_connection_index(codec, mux, nid, NULL) 4275d41762aSTakashi Iwai 4285d41762aSTakashi Iwai /* unmute input amp and select the specificed source */ 4295d41762aSTakashi Iwai static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, 4305d41762aSTakashi Iwai hda_nid_t src, hda_nid_t mix) 4315d41762aSTakashi Iwai { 4325d41762aSTakashi Iwai int idx, num_conns; 4335d41762aSTakashi Iwai 4345d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, src, &num_conns); 4355d41762aSTakashi Iwai if (idx < 0) 4365d41762aSTakashi Iwai return; 4375d41762aSTakashi Iwai 4385d41762aSTakashi Iwai /* select the route explicitly when multiple connections exist */ 4395d41762aSTakashi Iwai if (num_conns > 1) 440d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 4415d41762aSTakashi Iwai AC_VERB_SET_CONNECT_SEL, idx); 4425d41762aSTakashi Iwai /* unmute if the input amp is present */ 4435d41762aSTakashi Iwai if (!(query_amp_caps(codec, nid, HDA_INPUT) & 4445d41762aSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))) 4455d41762aSTakashi Iwai return; 4465d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4475d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4485d41762aSTakashi Iwai 4495d41762aSTakashi Iwai /* unmute AA-path if present */ 4505d41762aSTakashi Iwai if (!mix) 4515d41762aSTakashi Iwai return; 4525d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, mix, NULL); 4535d41762aSTakashi Iwai if (idx >= 0) 4545d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, 4555d41762aSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 4565d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4575d41762aSTakashi Iwai } 4585d41762aSTakashi Iwai 4595d41762aSTakashi Iwai /* set the given pin as output */ 4605d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, 4615d41762aSTakashi Iwai int pin_type) 4625d41762aSTakashi Iwai { 4635d41762aSTakashi Iwai if (!pin) 4645d41762aSTakashi Iwai return; 4655d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 4665d41762aSTakashi Iwai pin_type); 4675d41762aSTakashi Iwai if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) 4685d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 469d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 470c577b8a1SJoseph Chan } 471c577b8a1SJoseph Chan 4725d41762aSTakashi Iwai static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, 4735d41762aSTakashi Iwai int pin_type, struct nid_path *path) 4745d41762aSTakashi Iwai { 4755d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 4765d41762aSTakashi Iwai unsigned int caps; 4775d41762aSTakashi Iwai hda_nid_t nid; 4785d41762aSTakashi Iwai int i; 4795d41762aSTakashi Iwai 4805d41762aSTakashi Iwai if (!pin) 4815d41762aSTakashi Iwai return; 4825d41762aSTakashi Iwai 4835d41762aSTakashi Iwai init_output_pin(codec, pin, pin_type); 4845d41762aSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_OUTPUT); 4855d41762aSTakashi Iwai if (caps & AC_AMPCAP_MUTE) { 4865d41762aSTakashi Iwai unsigned int val; 4875d41762aSTakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 4885d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4895d41762aSTakashi Iwai AMP_OUT_MUTE | val); 4905d41762aSTakashi Iwai } 4915d41762aSTakashi Iwai 4925d41762aSTakashi Iwai /* initialize the output path */ 4935d41762aSTakashi Iwai nid = pin; 4945d41762aSTakashi Iwai for (i = 0; i < path->depth; i++) { 4955d41762aSTakashi Iwai unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid); 4965d41762aSTakashi Iwai nid = path->path[i]; 4975d41762aSTakashi Iwai if (query_amp_caps(codec, nid, HDA_OUTPUT) & 4985d41762aSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) 4995d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, 5005d41762aSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 5015d41762aSTakashi Iwai AMP_OUT_UNMUTE); 5025d41762aSTakashi Iwai } 5035d41762aSTakashi Iwai } 5045d41762aSTakashi Iwai 505c577b8a1SJoseph Chan 506c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 507c577b8a1SJoseph Chan { 508c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 509c577b8a1SJoseph Chan int i; 510c577b8a1SJoseph Chan 5115d41762aSTakashi Iwai for (i = 0; i < spec->autocfg.line_outs; i++) 5125d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.line_out_pins[i], 5135d41762aSTakashi Iwai PIN_OUT, &spec->out_path[i]); 514c577b8a1SJoseph Chan } 515c577b8a1SJoseph Chan 516c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 517c577b8a1SJoseph Chan { 518c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 519c577b8a1SJoseph Chan 5205d41762aSTakashi Iwai if (spec->hp_dac_nid) 5215d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5225d41762aSTakashi Iwai &spec->hp_path); 5235d41762aSTakashi Iwai else 5245d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5255d41762aSTakashi Iwai &spec->hp_dep_path); 52625eaba2fSLydia Wang } 527c577b8a1SJoseph Chan 5284a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec) 5294a918ffeSTakashi Iwai { 5304a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 5314a918ffeSTakashi Iwai 5324a918ffeSTakashi Iwai if (spec->autocfg.speaker_outs) 5334a918ffeSTakashi Iwai via_auto_init_output(codec, spec->autocfg.speaker_pins[0], 5344a918ffeSTakashi Iwai PIN_OUT, &spec->speaker_path); 5354a918ffeSTakashi Iwai } 5364a918ffeSTakashi Iwai 537f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 53832e0191dSClemens Ladisch 539c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 540c577b8a1SJoseph Chan { 541c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5427b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 543096a8854STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 54432e0191dSClemens Ladisch unsigned int ctl; 545096a8854STakashi Iwai int i, num_conns; 546c577b8a1SJoseph Chan 547096a8854STakashi Iwai /* init ADCs */ 548096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 549096a8854STakashi Iwai snd_hda_codec_write(codec, spec->adc_nids[i], 0, 550096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 551096a8854STakashi Iwai AMP_IN_UNMUTE(0)); 552096a8854STakashi Iwai } 553096a8854STakashi Iwai 554096a8854STakashi Iwai /* init pins */ 5557b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5567b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 557f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 55832e0191dSClemens Ladisch ctl = PIN_OUT; 55930649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56032e0191dSClemens Ladisch ctl = PIN_VREF50; 56132e0191dSClemens Ladisch else 56232e0191dSClemens Ladisch ctl = PIN_IN; 563c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56432e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 565c577b8a1SJoseph Chan } 566096a8854STakashi Iwai 567096a8854STakashi Iwai /* init input-src */ 568096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 569096a8854STakashi Iwai const struct hda_input_mux *imux = spec->input_mux; 570096a8854STakashi Iwai if (!imux || !spec->mux_nids[i]) 571096a8854STakashi Iwai continue; 572096a8854STakashi Iwai snd_hda_codec_write(codec, spec->mux_nids[i], 0, 573096a8854STakashi Iwai AC_VERB_SET_CONNECT_SEL, 574096a8854STakashi Iwai imux->items[spec->cur_mux[i]].index); 575096a8854STakashi Iwai } 576096a8854STakashi Iwai 577096a8854STakashi Iwai /* init aa-mixer */ 578096a8854STakashi Iwai if (!spec->aa_mix_nid) 579096a8854STakashi Iwai return; 580096a8854STakashi Iwai num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 581096a8854STakashi Iwai ARRAY_SIZE(conn)); 582096a8854STakashi Iwai for (i = 0; i < num_conns; i++) { 583096a8854STakashi Iwai unsigned int caps = get_wcaps(codec, conn[i]); 584096a8854STakashi Iwai if (get_wcaps_type(caps) == AC_WID_PIN) 585096a8854STakashi Iwai snd_hda_codec_write(codec, spec->aa_mix_nid, 0, 586096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 587096a8854STakashi Iwai AMP_IN_MUTE(i)); 588096a8854STakashi Iwai } 589c577b8a1SJoseph Chan } 590f5271101SLydia Wang 591f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 592f5271101SLydia Wang unsigned int *affected_parm) 593f5271101SLydia Wang { 594f5271101SLydia Wang unsigned parm; 595f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 596f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 597f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 598f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5991564b287SLydia Wang struct via_spec *spec = codec->spec; 60024088a58STakashi Iwai unsigned present = 0; 60124088a58STakashi Iwai 60224088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 60324088a58STakashi Iwai if (!no_presence) 60424088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 605f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 6061564b287SLydia Wang || ((no_presence || present) 6071564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 608f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 609f5271101SLydia Wang parm = AC_PWRST_D0; 610f5271101SLydia Wang } else 611f5271101SLydia Wang parm = AC_PWRST_D3; 612f5271101SLydia Wang 613f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 614f5271101SLydia Wang } 615f5271101SLydia Wang 61624088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 61724088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 61824088a58STakashi Iwai { 61924088a58STakashi Iwai static const char * const texts[] = { 62024088a58STakashi Iwai "Disabled", "Enabled" 62124088a58STakashi Iwai }; 62224088a58STakashi Iwai 62324088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 62424088a58STakashi Iwai uinfo->count = 1; 62524088a58STakashi Iwai uinfo->value.enumerated.items = 2; 62624088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 62724088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 62824088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 62924088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 63024088a58STakashi Iwai return 0; 63124088a58STakashi Iwai } 63224088a58STakashi Iwai 63324088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 63424088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 63524088a58STakashi Iwai { 63624088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 63724088a58STakashi Iwai struct via_spec *spec = codec->spec; 63824088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 63924088a58STakashi Iwai return 0; 64024088a58STakashi Iwai } 64124088a58STakashi Iwai 64224088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 64324088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 64424088a58STakashi Iwai { 64524088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 64624088a58STakashi Iwai struct via_spec *spec = codec->spec; 64724088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 64824088a58STakashi Iwai 64924088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 65024088a58STakashi Iwai return 0; 65124088a58STakashi Iwai spec->no_pin_power_ctl = val; 65224088a58STakashi Iwai set_widgets_power_state(codec); 65324088a58STakashi Iwai return 1; 65424088a58STakashi Iwai } 65524088a58STakashi Iwai 65624088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 65724088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 65824088a58STakashi Iwai .name = "Dynamic Power-Control", 65924088a58STakashi Iwai .info = via_pin_power_ctl_info, 66024088a58STakashi Iwai .get = via_pin_power_ctl_get, 66124088a58STakashi Iwai .put = via_pin_power_ctl_put, 66224088a58STakashi Iwai }; 66324088a58STakashi Iwai 66424088a58STakashi Iwai 665c577b8a1SJoseph Chan /* 666c577b8a1SJoseph Chan * input MUX handling 667c577b8a1SJoseph Chan */ 668c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 669c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 670c577b8a1SJoseph Chan { 671c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 672c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 673c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 674c577b8a1SJoseph Chan } 675c577b8a1SJoseph Chan 676c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 677c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 678c577b8a1SJoseph Chan { 679c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 680c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 681c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 682c577b8a1SJoseph Chan 683c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 684c577b8a1SJoseph Chan return 0; 685c577b8a1SJoseph Chan } 686c577b8a1SJoseph Chan 687c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 688c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 689c577b8a1SJoseph Chan { 690c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 691c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 692c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 693bff5fbf5SLydia Wang int ret; 694c577b8a1SJoseph Chan 695337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 696337b9d02STakashi Iwai return -EINVAL; 697a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 698a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 699a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 700a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 701a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 702bff5fbf5SLydia Wang 703bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 704bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 705bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 706a80e6e3cSLydia Wang /* update jack power state */ 7073e95b9abSLydia Wang set_widgets_power_state(codec); 708a80e6e3cSLydia Wang 709bff5fbf5SLydia Wang return ret; 710c577b8a1SJoseph Chan } 711c577b8a1SJoseph Chan 7120aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 7130aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 7140aa62aefSHarald Welte { 7150aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7160aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7170aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 7180aa62aefSHarald Welte } 7190aa62aefSHarald Welte 7200aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7210aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7220aa62aefSHarald Welte { 7230aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 724cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 725cdc1784dSLydia Wang 726ece8d043STakashi Iwai ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; 727cdc1784dSLydia Wang return 0; 728cdc1784dSLydia Wang } 729cdc1784dSLydia Wang 7300aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7310aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7320aa62aefSHarald Welte { 7330aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7340aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7355b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7360aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 737cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 738cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 739cdc1784dSLydia Wang ? 1 : 0; 740ece8d043STakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 7410aa62aefSHarald Welte 742ce0e5a9eSLydia Wang /* update jack power state */ 7433e95b9abSLydia Wang set_widgets_power_state(codec); 7440aa62aefSHarald Welte return 0; 7450aa62aefSHarald Welte } 7460aa62aefSHarald Welte 747ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = { 7480aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7490aa62aefSHarald Welte .name = "Independent HP", 7500aa62aefSHarald Welte .info = via_independent_hp_info, 7510aa62aefSHarald Welte .get = via_independent_hp_get, 7520aa62aefSHarald Welte .put = via_independent_hp_put, 7530aa62aefSHarald Welte }; 7540aa62aefSHarald Welte 7553d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7565b0cb1d8SJaroslav Kysela { 7573d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7585b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7595b0cb1d8SJaroslav Kysela hda_nid_t nid; 7605b0cb1d8SJaroslav Kysela 7615b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 762ece8d043STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer); 7633d83e577STakashi Iwai if (knew == NULL) 7643d83e577STakashi Iwai return -ENOMEM; 7653d83e577STakashi Iwai 7665b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 7675b0cb1d8SJaroslav Kysela knew->private_value = nid; 7685b0cb1d8SJaroslav Kysela 7695b0cb1d8SJaroslav Kysela return 0; 7705b0cb1d8SJaroslav Kysela } 7715b0cb1d8SJaroslav Kysela 7721564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 7731564b287SLydia Wang { 7741564b287SLydia Wang int i; 7751564b287SLydia Wang struct snd_ctl_elem_id id; 776525566cbSLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"}; 777525566cbSLydia Wang struct snd_kcontrol *ctl; 7781564b287SLydia Wang 7791564b287SLydia Wang memset(&id, 0, sizeof(id)); 7801564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 7811564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 7821564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 783525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 784525566cbSLydia Wang if (ctl) 785525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 786525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 787525566cbSLydia Wang &ctl->id); 7881564b287SLydia Wang } 7891564b287SLydia Wang } 7901564b287SLydia Wang 7911564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 7921564b287SLydia Wang { 7931564b287SLydia Wang struct via_spec *spec = codec->spec; 7941564b287SLydia Wang int start_idx; 7951564b287SLydia Wang int end_idx; 7961564b287SLydia Wang int i; 7971564b287SLydia Wang /* get nid of MW0 and start & end index */ 7981564b287SLydia Wang switch (spec->codec_type) { 7991564b287SLydia Wang case VT1708: 8001564b287SLydia Wang start_idx = 2; 8011564b287SLydia Wang end_idx = 4; 8021564b287SLydia Wang break; 8031564b287SLydia Wang case VT1709_10CH: 8041564b287SLydia Wang case VT1709_6CH: 8051564b287SLydia Wang start_idx = 2; 8061564b287SLydia Wang end_idx = 4; 8071564b287SLydia Wang break; 8081564b287SLydia Wang case VT1708B_8CH: 8091564b287SLydia Wang case VT1708B_4CH: 8101564b287SLydia Wang case VT1708S: 811f3db423dSLydia Wang case VT1716S: 8121564b287SLydia Wang start_idx = 2; 8131564b287SLydia Wang end_idx = 4; 8141564b287SLydia Wang break; 815ab657e0cSLydia Wang case VT1718S: 816ab657e0cSLydia Wang start_idx = 1; 817ab657e0cSLydia Wang end_idx = 3; 818ab657e0cSLydia Wang break; 8191564b287SLydia Wang default: 8201564b287SLydia Wang return; 8211564b287SLydia Wang } 8221564b287SLydia Wang /* check AA path's mute status */ 8231564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 8241564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 825620e2b28STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i, 8261564b287SLydia Wang HDA_AMP_MUTE, val); 8271564b287SLydia Wang } 8281564b287SLydia Wang } 829f4a7828bSTakashi Iwai 830f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 8311564b287SLydia Wang { 832f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 8337b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8347b315bb4STakashi Iwai int i; 8357b315bb4STakashi Iwai 8367b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 837f4a7828bSTakashi Iwai unsigned int defcfg; 838f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 839f4a7828bSTakashi Iwai continue; 840f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 841f4a7828bSTakashi Iwai return false; 842f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 843f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 844f4a7828bSTakashi Iwai return false; 845f4a7828bSTakashi Iwai return true; 8461564b287SLydia Wang } 847f4a7828bSTakashi Iwai return false; 8481564b287SLydia Wang } 8491564b287SLydia Wang 8501564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 8511564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 8521564b287SLydia Wang { 8531564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 8541564b287SLydia Wang uinfo->count = 1; 8551564b287SLydia Wang uinfo->value.integer.min = 0; 8561564b287SLydia Wang uinfo->value.integer.max = 1; 8571564b287SLydia Wang return 0; 8581564b287SLydia Wang } 8591564b287SLydia Wang 8601564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 8611564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8621564b287SLydia Wang { 8631564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8641564b287SLydia Wang struct via_spec *spec = codec->spec; 8657b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8661564b287SLydia Wang int on = 1; 8671564b287SLydia Wang int i; 8681564b287SLydia Wang 8697b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 8707b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 871f4a7828bSTakashi Iwai unsigned int ctl; 87286e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 8737b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 8741564b287SLydia Wang continue; /* ignore FMic for independent HP */ 875f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 876f4a7828bSTakashi Iwai continue; 877f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 878f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8797b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 8801564b287SLydia Wang on = 0; 8811564b287SLydia Wang } 8821564b287SLydia Wang *ucontrol->value.integer.value = on; 8831564b287SLydia Wang return 0; 8841564b287SLydia Wang } 8851564b287SLydia Wang 8861564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 8871564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8881564b287SLydia Wang { 8891564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8901564b287SLydia Wang struct via_spec *spec = codec->spec; 8917b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8921564b287SLydia Wang int out_in = *ucontrol->value.integer.value 8931564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 8941564b287SLydia Wang int i; 8951564b287SLydia Wang 8967b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 8977b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 8987b315bb4STakashi Iwai unsigned int parm; 8997b315bb4STakashi Iwai 90086e2959aSTakashi Iwai if (cfg->inputs[i].type == AUTO_PIN_MIC && 9017b315bb4STakashi Iwai spec->hp_independent_mode && spec->codec_type != VT1718S) 9021564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 903f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, nid)) 904f4a7828bSTakashi Iwai continue; 9057b315bb4STakashi Iwai 9067b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 9071564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 9081564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 9091564b287SLydia Wang parm |= out_in; 9101564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 9111564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 9121564b287SLydia Wang parm); 9131564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 9141564b287SLydia Wang mute_aa_path(codec, 1); 9151564b287SLydia Wang notify_aa_path_ctls(codec); 9161564b287SLydia Wang } 9171564b287SLydia Wang } 9181564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 9193e95b9abSLydia Wang set_widgets_power_state(codec); 9201564b287SLydia Wang return 1; 9211564b287SLydia Wang } 9221564b287SLydia Wang 9235f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 9241564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9251564b287SLydia Wang .name = "Smart 5.1", 9261564b287SLydia Wang .count = 1, 9271564b287SLydia Wang .info = via_smart51_info, 9281564b287SLydia Wang .get = via_smart51_get, 9291564b287SLydia Wang .put = via_smart51_put, 9301564b287SLydia Wang }; 9311564b287SLydia Wang 932f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 9335b0cb1d8SJaroslav Kysela { 934f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9355b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 9367b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 9375b0cb1d8SJaroslav Kysela hda_nid_t nid; 9385b0cb1d8SJaroslav Kysela int i; 9395b0cb1d8SJaroslav Kysela 940f4a7828bSTakashi Iwai if (!spec->can_smart51) 941cb34c207SLydia Wang return 0; 942cb34c207SLydia Wang 9435f4b36d6STakashi Iwai knew = via_clone_control(spec, &via_smart51_mixer); 9445b0cb1d8SJaroslav Kysela if (knew == NULL) 9455b0cb1d8SJaroslav Kysela return -ENOMEM; 9465b0cb1d8SJaroslav Kysela 9477b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 9487b315bb4STakashi Iwai nid = cfg->inputs[i].pin; 949f4a7828bSTakashi Iwai if (is_smart51_pins(codec, nid)) { 9505f4b36d6STakashi Iwai knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 9517b315bb4STakashi Iwai break; 9525b0cb1d8SJaroslav Kysela } 9535b0cb1d8SJaroslav Kysela } 9545b0cb1d8SJaroslav Kysela 9555b0cb1d8SJaroslav Kysela return 0; 9565b0cb1d8SJaroslav Kysela } 9575b0cb1d8SJaroslav Kysela 958f5271101SLydia Wang /* check AA path's mute statue */ 959f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 960f5271101SLydia Wang { 961f5271101SLydia Wang int mute = 1; 962f5271101SLydia Wang int start_idx; 963f5271101SLydia Wang int end_idx; 964f5271101SLydia Wang int i; 965f5271101SLydia Wang struct via_spec *spec = codec->spec; 966f5271101SLydia Wang /* get nid of MW0 and start & end index */ 967f5271101SLydia Wang switch (spec->codec_type) { 968f5271101SLydia Wang case VT1708B_8CH: 969f5271101SLydia Wang case VT1708B_4CH: 970f5271101SLydia Wang case VT1708S: 971f3db423dSLydia Wang case VT1716S: 972f5271101SLydia Wang start_idx = 2; 973f5271101SLydia Wang end_idx = 4; 974f5271101SLydia Wang break; 975f5271101SLydia Wang case VT1702: 976f5271101SLydia Wang start_idx = 1; 977f5271101SLydia Wang end_idx = 3; 978f5271101SLydia Wang break; 979eb7188caSLydia Wang case VT1718S: 980eb7188caSLydia Wang start_idx = 1; 981eb7188caSLydia Wang end_idx = 3; 982eb7188caSLydia Wang break; 98325eaba2fSLydia Wang case VT2002P: 984ab6734e7SLydia Wang case VT1812: 98511890956SLydia Wang case VT1802: 98625eaba2fSLydia Wang start_idx = 0; 98725eaba2fSLydia Wang end_idx = 2; 98825eaba2fSLydia Wang break; 989f5271101SLydia Wang default: 990f5271101SLydia Wang return 0; 991f5271101SLydia Wang } 992f5271101SLydia Wang /* check AA path's mute status */ 993f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 994f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 995620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 996f5271101SLydia Wang int shift = 8 * (i % 4); 997f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 998f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 999f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1000f5271101SLydia Wang /* check mute status while the pin is connected */ 1001620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 1002f5271101SLydia Wang HDA_INPUT, i) >> 7; 1003620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 1004f5271101SLydia Wang HDA_INPUT, i) >> 7; 1005f5271101SLydia Wang if (!mute_l || !mute_r) { 1006f5271101SLydia Wang mute = 0; 1007f5271101SLydia Wang break; 1008f5271101SLydia Wang } 1009f5271101SLydia Wang } 1010f5271101SLydia Wang } 1011f5271101SLydia Wang return mute; 1012f5271101SLydia Wang } 1013f5271101SLydia Wang 1014f5271101SLydia Wang /* enter/exit analog low-current mode */ 1015f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1016f5271101SLydia Wang { 1017f5271101SLydia Wang struct via_spec *spec = codec->spec; 1018f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1019f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1020f5271101SLydia Wang unsigned int verb = 0; 1021f5271101SLydia Wang unsigned int parm = 0; 1022f5271101SLydia Wang 1023f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1024f5271101SLydia Wang enable = enable && saved_stream_idle; 1025f5271101SLydia Wang else { 1026f5271101SLydia Wang enable = enable && stream_idle; 1027f5271101SLydia Wang saved_stream_idle = stream_idle; 1028f5271101SLydia Wang } 1029f5271101SLydia Wang 1030f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1031f5271101SLydia Wang switch (spec->codec_type) { 1032f5271101SLydia Wang case VT1708B_8CH: 1033f5271101SLydia Wang case VT1708B_4CH: 1034f5271101SLydia Wang verb = 0xf70; 1035f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1036f5271101SLydia Wang break; 1037f5271101SLydia Wang case VT1708S: 1038eb7188caSLydia Wang case VT1718S: 1039f3db423dSLydia Wang case VT1716S: 1040f5271101SLydia Wang verb = 0xf73; 1041f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1042f5271101SLydia Wang break; 1043f5271101SLydia Wang case VT1702: 1044f5271101SLydia Wang verb = 0xf73; 1045f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1046f5271101SLydia Wang break; 104725eaba2fSLydia Wang case VT2002P: 1048ab6734e7SLydia Wang case VT1812: 104911890956SLydia Wang case VT1802: 105025eaba2fSLydia Wang verb = 0xf93; 105125eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 105225eaba2fSLydia Wang break; 1053f5271101SLydia Wang default: 1054f5271101SLydia Wang return; /* other codecs are not supported */ 1055f5271101SLydia Wang } 1056f5271101SLydia Wang /* send verb */ 1057f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1058f5271101SLydia Wang } 1059f5271101SLydia Wang 1060c577b8a1SJoseph Chan /* 1061c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1062c577b8a1SJoseph Chan */ 1063096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 1064aa266fccSLydia Wang /* power down jack detect function */ 1065aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1066f7278fd0SJosepch Chan { } 1067c577b8a1SJoseph Chan }; 1068c577b8a1SJoseph Chan 10697eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec, 10707eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10717eb56e84STakashi Iwai { 10727eb56e84STakashi Iwai int idle = substream->pstr->substream_opened == 1 10737eb56e84STakashi Iwai && substream->ref_count == 0; 10747eb56e84STakashi Iwai analog_low_current_mode(codec, idle); 10757eb56e84STakashi Iwai } 10767eb56e84STakashi Iwai 1077ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, 1078c577b8a1SJoseph Chan struct hda_codec *codec, 1079c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1080c577b8a1SJoseph Chan { 1081c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1082ece8d043STakashi Iwai 1083ece8d043STakashi Iwai if (!spec->hp_independent_mode) 1084ece8d043STakashi Iwai spec->multiout.hp_nid = spec->hp_dac_nid; 10857eb56e84STakashi Iwai substream_set_idle(codec, substream); 10869a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 10879a08160bSTakashi Iwai hinfo); 1088c577b8a1SJoseph Chan } 1089c577b8a1SJoseph Chan 1090ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, 10919af74210STakashi Iwai struct hda_codec *codec, 10929af74210STakashi Iwai struct snd_pcm_substream *substream) 10939af74210STakashi Iwai { 1094ece8d043STakashi Iwai struct via_spec *spec = codec->spec; 1095ece8d043STakashi Iwai 1096ece8d043STakashi Iwai spec->multiout.hp_nid = 0; 10977eb56e84STakashi Iwai substream_set_idle(codec, substream); 10989af74210STakashi Iwai return 0; 10999af74210STakashi Iwai } 11009af74210STakashi Iwai 11017eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 11027eb56e84STakashi Iwai struct hda_codec *codec, 11037eb56e84STakashi Iwai struct snd_pcm_substream *substream) 11047eb56e84STakashi Iwai { 11057eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 11067eb56e84STakashi Iwai 1107ece8d043STakashi Iwai if (snd_BUG_ON(!spec->hp_dac_nid)) 11087eb56e84STakashi Iwai return -EINVAL; 1109ece8d043STakashi Iwai if (!spec->hp_independent_mode || spec->multiout.hp_nid) 1110ece8d043STakashi Iwai return -EBUSY; 1111ece8d043STakashi Iwai substream_set_idle(codec, substream); 1112ece8d043STakashi Iwai return 0; 1113ece8d043STakashi Iwai } 1114ece8d043STakashi Iwai 1115ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, 1116ece8d043STakashi Iwai struct hda_codec *codec, 1117ece8d043STakashi Iwai struct snd_pcm_substream *substream) 1118ece8d043STakashi Iwai { 11197eb56e84STakashi Iwai substream_set_idle(codec, substream); 11207eb56e84STakashi Iwai return 0; 11217eb56e84STakashi Iwai } 11227eb56e84STakashi Iwai 11237eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 11247eb56e84STakashi Iwai struct hda_codec *codec, 11250aa62aefSHarald Welte unsigned int stream_tag, 11260aa62aefSHarald Welte unsigned int format, 11270aa62aefSHarald Welte struct snd_pcm_substream *substream) 11280aa62aefSHarald Welte { 11290aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11300aa62aefSHarald Welte 1131ece8d043STakashi Iwai snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1132ece8d043STakashi Iwai format, substream); 11337eb56e84STakashi Iwai vt1708_start_hp_work(spec); 11347eb56e84STakashi Iwai return 0; 11350aa62aefSHarald Welte } 11360aa62aefSHarald Welte 11377eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 11380aa62aefSHarald Welte struct hda_codec *codec, 11390aa62aefSHarald Welte unsigned int stream_tag, 11400aa62aefSHarald Welte unsigned int format, 11410aa62aefSHarald Welte struct snd_pcm_substream *substream) 11420aa62aefSHarald Welte { 11430aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11440aa62aefSHarald Welte 1145ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1146ece8d043STakashi Iwai stream_tag, 0, format); 11471f2e99feSLydia Wang vt1708_start_hp_work(spec); 11480aa62aefSHarald Welte return 0; 11490aa62aefSHarald Welte } 11500aa62aefSHarald Welte 11510aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 11520aa62aefSHarald Welte struct hda_codec *codec, 11530aa62aefSHarald Welte struct snd_pcm_substream *substream) 11540aa62aefSHarald Welte { 11550aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11560aa62aefSHarald Welte 1157ece8d043STakashi Iwai snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 11587eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 11597eb56e84STakashi Iwai return 0; 11600aa62aefSHarald Welte } 11617eb56e84STakashi Iwai 11627eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 11637eb56e84STakashi Iwai struct hda_codec *codec, 11647eb56e84STakashi Iwai struct snd_pcm_substream *substream) 11657eb56e84STakashi Iwai { 11667eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 11677eb56e84STakashi Iwai 1168ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 11691f2e99feSLydia Wang vt1708_stop_hp_work(spec); 11700aa62aefSHarald Welte return 0; 11710aa62aefSHarald Welte } 11720aa62aefSHarald Welte 1173c577b8a1SJoseph Chan /* 1174c577b8a1SJoseph Chan * Digital out 1175c577b8a1SJoseph Chan */ 1176c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1177c577b8a1SJoseph Chan struct hda_codec *codec, 1178c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1179c577b8a1SJoseph Chan { 1180c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1181c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1182c577b8a1SJoseph Chan } 1183c577b8a1SJoseph Chan 1184c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1185c577b8a1SJoseph Chan struct hda_codec *codec, 1186c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1187c577b8a1SJoseph Chan { 1188c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1189c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1190c577b8a1SJoseph Chan } 1191c577b8a1SJoseph Chan 11925691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 119398aa34c0SHarald Welte struct hda_codec *codec, 119498aa34c0SHarald Welte unsigned int stream_tag, 119598aa34c0SHarald Welte unsigned int format, 119698aa34c0SHarald Welte struct snd_pcm_substream *substream) 119798aa34c0SHarald Welte { 119898aa34c0SHarald Welte struct via_spec *spec = codec->spec; 11999da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 12009da29271STakashi Iwai stream_tag, format, substream); 12019da29271STakashi Iwai } 12025691ec7fSHarald Welte 12039da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 12049da29271STakashi Iwai struct hda_codec *codec, 12059da29271STakashi Iwai struct snd_pcm_substream *substream) 12069da29271STakashi Iwai { 12079da29271STakashi Iwai struct via_spec *spec = codec->spec; 12089da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 120998aa34c0SHarald Welte return 0; 121098aa34c0SHarald Welte } 121198aa34c0SHarald Welte 1212c577b8a1SJoseph Chan /* 1213c577b8a1SJoseph Chan * Analog capture 1214c577b8a1SJoseph Chan */ 1215c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1216c577b8a1SJoseph Chan struct hda_codec *codec, 1217c577b8a1SJoseph Chan unsigned int stream_tag, 1218c577b8a1SJoseph Chan unsigned int format, 1219c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1220c577b8a1SJoseph Chan { 1221c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1222c577b8a1SJoseph Chan 1223c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1224c577b8a1SJoseph Chan stream_tag, 0, format); 1225c577b8a1SJoseph Chan return 0; 1226c577b8a1SJoseph Chan } 1227c577b8a1SJoseph Chan 1228c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1229c577b8a1SJoseph Chan struct hda_codec *codec, 1230c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1231c577b8a1SJoseph Chan { 1232c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1233888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1234c577b8a1SJoseph Chan return 0; 1235c577b8a1SJoseph Chan } 1236c577b8a1SJoseph Chan 12379af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 12387eb56e84STakashi Iwai .substreams = 1, 1239c577b8a1SJoseph Chan .channels_min = 2, 1240c577b8a1SJoseph Chan .channels_max = 8, 12419af74210STakashi Iwai /* NID is set in via_build_pcms */ 1242c577b8a1SJoseph Chan .ops = { 1243ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1244ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 12450aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 12460aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1247c577b8a1SJoseph Chan }, 1248c577b8a1SJoseph Chan }; 1249c577b8a1SJoseph Chan 12507eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 12517eb56e84STakashi Iwai .substreams = 1, 12527eb56e84STakashi Iwai .channels_min = 2, 12537eb56e84STakashi Iwai .channels_max = 2, 12547eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 12557eb56e84STakashi Iwai .ops = { 12567eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1257ece8d043STakashi Iwai .close = via_playback_hp_pcm_close, 12587eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 12597eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 12607eb56e84STakashi Iwai }, 12617eb56e84STakashi Iwai }; 12627eb56e84STakashi Iwai 126390dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 12647eb56e84STakashi Iwai .substreams = 1, 1265bc9b5623STakashi Iwai .channels_min = 2, 1266bc9b5623STakashi Iwai .channels_max = 8, 12679af74210STakashi Iwai /* NID is set in via_build_pcms */ 1268bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1269bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1270bc9b5623STakashi Iwai * disable the 24bit format, so far. 1271bc9b5623STakashi Iwai */ 1272bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1273bc9b5623STakashi Iwai .ops = { 1274ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1275ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 1276c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1277c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1278bc9b5623STakashi Iwai }, 1279bc9b5623STakashi Iwai }; 1280bc9b5623STakashi Iwai 12819af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 12827eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1283c577b8a1SJoseph Chan .channels_min = 2, 1284c577b8a1SJoseph Chan .channels_max = 2, 12859af74210STakashi Iwai /* NID is set in via_build_pcms */ 1286c577b8a1SJoseph Chan .ops = { 1287c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1288c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1289c577b8a1SJoseph Chan }, 1290c577b8a1SJoseph Chan }; 1291c577b8a1SJoseph Chan 12929af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1293c577b8a1SJoseph Chan .substreams = 1, 1294c577b8a1SJoseph Chan .channels_min = 2, 1295c577b8a1SJoseph Chan .channels_max = 2, 1296c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1297c577b8a1SJoseph Chan .ops = { 1298c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 12996b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 13009da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 13019da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1302c577b8a1SJoseph Chan }, 1303c577b8a1SJoseph Chan }; 1304c577b8a1SJoseph Chan 13059af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1306c577b8a1SJoseph Chan .substreams = 1, 1307c577b8a1SJoseph Chan .channels_min = 2, 1308c577b8a1SJoseph Chan .channels_max = 2, 1309c577b8a1SJoseph Chan }; 1310c577b8a1SJoseph Chan 1311*370bafbdSTakashi Iwai /* 1312*370bafbdSTakashi Iwai * slave controls for virtual master 1313*370bafbdSTakashi Iwai */ 1314*370bafbdSTakashi Iwai static const char * const via_slave_vols[] = { 1315*370bafbdSTakashi Iwai "Front Playback Volume", 1316*370bafbdSTakashi Iwai "Surround Playback Volume", 1317*370bafbdSTakashi Iwai "Center Playback Volume", 1318*370bafbdSTakashi Iwai "LFE Playback Volume", 1319*370bafbdSTakashi Iwai "Side Playback Volume", 1320*370bafbdSTakashi Iwai "Headphone Playback Volume", 1321*370bafbdSTakashi Iwai "Speaker Playback Volume", 1322*370bafbdSTakashi Iwai NULL, 1323*370bafbdSTakashi Iwai }; 1324*370bafbdSTakashi Iwai 1325*370bafbdSTakashi Iwai static const char * const via_slave_sws[] = { 1326*370bafbdSTakashi Iwai "Front Playback Switch", 1327*370bafbdSTakashi Iwai "Surround Playback Switch", 1328*370bafbdSTakashi Iwai "Center Playback Switch", 1329*370bafbdSTakashi Iwai "LFE Playback Switch", 1330*370bafbdSTakashi Iwai "Side Playback Switch", 1331*370bafbdSTakashi Iwai "Headphone Playback Switch", 1332*370bafbdSTakashi Iwai "Speaker Playback Switch", 1333*370bafbdSTakashi Iwai NULL, 1334*370bafbdSTakashi Iwai }; 1335*370bafbdSTakashi Iwai 1336c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1337c577b8a1SJoseph Chan { 1338c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 13395b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 134090dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 13415b0cb1d8SJaroslav Kysela int err, i; 1342c577b8a1SJoseph Chan 134324088a58STakashi Iwai if (spec->set_widgets_power_state) 134424088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 134524088a58STakashi Iwai return -ENOMEM; 134624088a58STakashi Iwai 1347c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1348c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1349c577b8a1SJoseph Chan if (err < 0) 1350c577b8a1SJoseph Chan return err; 1351c577b8a1SJoseph Chan } 1352c577b8a1SJoseph Chan 1353c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1354c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 135574b654c9SStephen Warren spec->multiout.dig_out_nid, 1356c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1357c577b8a1SJoseph Chan if (err < 0) 1358c577b8a1SJoseph Chan return err; 13599a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 13609a08160bSTakashi Iwai &spec->multiout); 13619a08160bSTakashi Iwai if (err < 0) 13629a08160bSTakashi Iwai return err; 13639a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1364c577b8a1SJoseph Chan } 1365c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1366c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1367c577b8a1SJoseph Chan if (err < 0) 1368c577b8a1SJoseph Chan return err; 1369c577b8a1SJoseph Chan } 137017314379SLydia Wang 1371*370bafbdSTakashi Iwai /* if we have no master control, let's create it */ 1372*370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 1373*370bafbdSTakashi Iwai unsigned int vmaster_tlv[4]; 1374*370bafbdSTakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], 1375*370bafbdSTakashi Iwai HDA_OUTPUT, vmaster_tlv); 1376*370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 1377*370bafbdSTakashi Iwai vmaster_tlv, via_slave_vols); 1378*370bafbdSTakashi Iwai if (err < 0) 1379*370bafbdSTakashi Iwai return err; 1380*370bafbdSTakashi Iwai } 1381*370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 1382*370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Switch", 1383*370bafbdSTakashi Iwai NULL, via_slave_sws); 1384*370bafbdSTakashi Iwai if (err < 0) 1385*370bafbdSTakashi Iwai return err; 1386*370bafbdSTakashi Iwai } 1387*370bafbdSTakashi Iwai 13885b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 13895b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 13905b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 139121949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 13925b0cb1d8SJaroslav Kysela if (err < 0) 13935b0cb1d8SJaroslav Kysela return err; 13945b0cb1d8SJaroslav Kysela } 13955b0cb1d8SJaroslav Kysela 13965b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 13975b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 13985b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 13995b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 14005b0cb1d8SJaroslav Kysela continue; 14015b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 14025b0cb1d8SJaroslav Kysela if (kctl == NULL) 14035b0cb1d8SJaroslav Kysela continue; 14045b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 14055b0cb1d8SJaroslav Kysela knew->subdevice); 14065b0cb1d8SJaroslav Kysela } 14075b0cb1d8SJaroslav Kysela } 14085b0cb1d8SJaroslav Kysela 140917314379SLydia Wang /* init power states */ 14103e95b9abSLydia Wang set_widgets_power_state(codec); 141117314379SLydia Wang analog_low_current_mode(codec, 1); 141217314379SLydia Wang 1413603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1414c577b8a1SJoseph Chan return 0; 1415c577b8a1SJoseph Chan } 1416c577b8a1SJoseph Chan 1417c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1418c577b8a1SJoseph Chan { 1419c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1420c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1421c577b8a1SJoseph Chan 1422c577b8a1SJoseph Chan codec->num_pcms = 1; 1423c577b8a1SJoseph Chan codec->pcm_info = info; 1424c577b8a1SJoseph Chan 142582673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 142682673bc8STakashi Iwai "%s Analog", codec->chip_name); 1427c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 14289af74210STakashi Iwai 14299af74210STakashi Iwai if (!spec->stream_analog_playback) 14309af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1431377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 14329af74210STakashi Iwai *spec->stream_analog_playback; 1433377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1434377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1435c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1436c577b8a1SJoseph Chan spec->multiout.max_channels; 14379af74210STakashi Iwai 14389af74210STakashi Iwai if (!spec->stream_analog_capture) 14399af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 14409af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14419af74210STakashi Iwai *spec->stream_analog_capture; 14429af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 14439af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 14449af74210STakashi Iwai spec->num_adc_nids; 1445c577b8a1SJoseph Chan 1446c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1447c577b8a1SJoseph Chan codec->num_pcms++; 1448c577b8a1SJoseph Chan info++; 144982673bc8STakashi Iwai snprintf(spec->stream_name_digital, 145082673bc8STakashi Iwai sizeof(spec->stream_name_digital), 145182673bc8STakashi Iwai "%s Digital", codec->chip_name); 1452c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 14537ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1454c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 14559af74210STakashi Iwai if (!spec->stream_digital_playback) 14569af74210STakashi Iwai spec->stream_digital_playback = 14579af74210STakashi Iwai &via_pcm_digital_playback; 1458c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 14599af74210STakashi Iwai *spec->stream_digital_playback; 1460c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1461c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1462c577b8a1SJoseph Chan } 1463c577b8a1SJoseph Chan if (spec->dig_in_nid) { 14649af74210STakashi Iwai if (!spec->stream_digital_capture) 14659af74210STakashi Iwai spec->stream_digital_capture = 14669af74210STakashi Iwai &via_pcm_digital_capture; 1467c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14689af74210STakashi Iwai *spec->stream_digital_capture; 1469c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1470c577b8a1SJoseph Chan spec->dig_in_nid; 1471c577b8a1SJoseph Chan } 1472c577b8a1SJoseph Chan } 1473c577b8a1SJoseph Chan 1474ece8d043STakashi Iwai if (spec->hp_dac_nid) { 14757eb56e84STakashi Iwai codec->num_pcms++; 14767eb56e84STakashi Iwai info++; 14777eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 14787eb56e84STakashi Iwai "%s HP", codec->chip_name); 14797eb56e84STakashi Iwai info->name = spec->stream_name_hp; 14807eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 14817eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1482ece8d043STakashi Iwai spec->hp_dac_nid; 14837eb56e84STakashi Iwai } 1484c577b8a1SJoseph Chan return 0; 1485c577b8a1SJoseph Chan } 1486c577b8a1SJoseph Chan 1487c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1488c577b8a1SJoseph Chan { 1489c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1490c577b8a1SJoseph Chan 1491c577b8a1SJoseph Chan if (!spec) 1492c577b8a1SJoseph Chan return; 1493c577b8a1SJoseph Chan 1494603c4019STakashi Iwai via_free_kctls(codec); 14951f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1496c577b8a1SJoseph Chan kfree(codec->spec); 1497c577b8a1SJoseph Chan } 1498c577b8a1SJoseph Chan 149964be285bSTakashi Iwai /* mute/unmute outputs */ 150064be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 150164be285bSTakashi Iwai hda_nid_t *pins, bool mute) 150264be285bSTakashi Iwai { 150364be285bSTakashi Iwai int i; 150464be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 150564be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 150664be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 150764be285bSTakashi Iwai mute ? 0 : PIN_OUT); 150864be285bSTakashi Iwai } 150964be285bSTakashi Iwai 15104a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */ 15114a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present) 15124a918ffeSTakashi Iwai { 15134a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 15144a918ffeSTakashi Iwai 15154a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs) 15164a918ffeSTakashi Iwai return; 15174a918ffeSTakashi Iwai if (!present) 15184a918ffeSTakashi Iwai present = snd_hda_jack_detect(codec, 15194a918ffeSTakashi Iwai spec->autocfg.line_out_pins[0]); 15204a918ffeSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 15214a918ffeSTakashi Iwai spec->autocfg.speaker_pins, 15224a918ffeSTakashi Iwai present); 15234a918ffeSTakashi Iwai } 15244a918ffeSTakashi Iwai 152569e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 152669e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 152769e52a80SHarald Welte { 15284a918ffeSTakashi Iwai int present = 0; 152969e52a80SHarald Welte struct via_spec *spec = codec->spec; 153069e52a80SHarald Welte 15314a918ffeSTakashi Iwai if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { 1532d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 153364be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 153464be285bSTakashi Iwai spec->autocfg.line_out_pins, 153564be285bSTakashi Iwai present); 153669e52a80SHarald Welte } 15374a918ffeSTakashi Iwai via_line_automute(codec, present); 1538f3db423dSLydia Wang } 1539f3db423dSLydia Wang 154069e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 154169e52a80SHarald Welte { 154269e52a80SHarald Welte unsigned int gpio_data; 154369e52a80SHarald Welte unsigned int vol_counter; 154469e52a80SHarald Welte unsigned int vol; 154569e52a80SHarald Welte unsigned int master_vol; 154669e52a80SHarald Welte 154769e52a80SHarald Welte struct via_spec *spec = codec->spec; 154869e52a80SHarald Welte 154969e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 155069e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 155169e52a80SHarald Welte 155269e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 155369e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 155469e52a80SHarald Welte 155569e52a80SHarald Welte vol = vol_counter & 0x1F; 155669e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 155769e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 155869e52a80SHarald Welte AC_AMP_GET_INPUT); 155969e52a80SHarald Welte 156069e52a80SHarald Welte if (gpio_data == 0x02) { 156169e52a80SHarald Welte /* unmute line out */ 15623e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15633e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15643e0693e2STakashi Iwai PIN_OUT); 156569e52a80SHarald Welte if (vol_counter & 0x20) { 156669e52a80SHarald Welte /* decrease volume */ 156769e52a80SHarald Welte if (vol > master_vol) 156869e52a80SHarald Welte vol = master_vol; 156969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 157069e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 157169e52a80SHarald Welte master_vol-vol); 157269e52a80SHarald Welte } else { 157369e52a80SHarald Welte /* increase volume */ 157469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 157569e52a80SHarald Welte HDA_AMP_VOLMASK, 157669e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 157769e52a80SHarald Welte (master_vol+vol)); 157869e52a80SHarald Welte } 157969e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 158069e52a80SHarald Welte /* mute line out */ 15813e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15823e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15833e0693e2STakashi Iwai 0); 158469e52a80SHarald Welte } 158569e52a80SHarald Welte } 158669e52a80SHarald Welte 158769e52a80SHarald Welte /* unsolicited event for jack sensing */ 158869e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 158969e52a80SHarald Welte unsigned int res) 159069e52a80SHarald Welte { 159169e52a80SHarald Welte res >>= 26; 1592ec7e7e42SLydia Wang 1593a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 15943e95b9abSLydia Wang set_widgets_power_state(codec); 1595ec7e7e42SLydia Wang 1596ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1597ec7e7e42SLydia Wang 1598ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1599ec7e7e42SLydia Wang via_hp_automute(codec); 1600ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1601ec7e7e42SLydia Wang via_gpio_control(codec); 16024a918ffeSTakashi Iwai else if (res == VIA_LINE_EVENT) 16034a918ffeSTakashi Iwai via_line_automute(codec, false); 160469e52a80SHarald Welte } 160569e52a80SHarald Welte 16061f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 16071f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 16081f2e99feSLydia Wang { 16091f2e99feSLydia Wang struct via_spec *spec = codec->spec; 16101f2e99feSLydia Wang vt1708_stop_hp_work(spec); 16111f2e99feSLydia Wang return 0; 16121f2e99feSLydia Wang } 16131f2e99feSLydia Wang #endif 16141f2e99feSLydia Wang 1615cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1616cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1617cb53c626STakashi Iwai { 1618cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1619cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1620cb53c626STakashi Iwai } 1621cb53c626STakashi Iwai #endif 1622cb53c626STakashi Iwai 1623c577b8a1SJoseph Chan /* 1624c577b8a1SJoseph Chan */ 16255d41762aSTakashi Iwai 16265d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 16275d41762aSTakashi Iwai 162890dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1629c577b8a1SJoseph Chan .build_controls = via_build_controls, 1630c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1631c577b8a1SJoseph Chan .init = via_init, 1632c577b8a1SJoseph Chan .free = via_free, 16334a918ffeSTakashi Iwai .unsol_event = via_unsol_event, 16341f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 16351f2e99feSLydia Wang .suspend = via_suspend, 16361f2e99feSLydia Wang #endif 1637cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1638cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1639cb53c626STakashi Iwai #endif 1640c577b8a1SJoseph Chan }; 1641c577b8a1SJoseph Chan 16424a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1643c577b8a1SJoseph Chan { 16444a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16454a79616dSTakashi Iwai int i; 16464a79616dSTakashi Iwai 16474a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 16484a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 16494a79616dSTakashi Iwai return false; 16504a79616dSTakashi Iwai } 1651ece8d043STakashi Iwai if (spec->hp_dac_nid == dac) 16524a79616dSTakashi Iwai return false; 16534a79616dSTakashi Iwai return true; 16544a79616dSTakashi Iwai } 16554a79616dSTakashi Iwai 16564a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 16574a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 16584a79616dSTakashi Iwai int depth, int wid_type) 16594a79616dSTakashi Iwai { 16604a79616dSTakashi Iwai hda_nid_t conn[8]; 16614a79616dSTakashi Iwai int i, nums; 16624a79616dSTakashi Iwai 16634a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 16644a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16654a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 16664a79616dSTakashi Iwai continue; 16674a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 16684a79616dSTakashi Iwai path->path[depth] = conn[i]; 16694a79616dSTakashi Iwai path->idx[depth] = i; 16704a79616dSTakashi Iwai path->depth = ++depth; 16714a79616dSTakashi Iwai return true; 16724a79616dSTakashi Iwai } 16734a79616dSTakashi Iwai } 16744a79616dSTakashi Iwai if (depth > 4) 16754a79616dSTakashi Iwai return false; 16764a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16774a79616dSTakashi Iwai unsigned int type; 16784a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 16794a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 16804a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 16814a79616dSTakashi Iwai continue; 16824a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 16834a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 16844a79616dSTakashi Iwai path->path[depth] = conn[i]; 16854a79616dSTakashi Iwai path->idx[depth] = i; 16864a79616dSTakashi Iwai return true; 16874a79616dSTakashi Iwai } 16884a79616dSTakashi Iwai } 16894a79616dSTakashi Iwai return false; 16904a79616dSTakashi Iwai } 16914a79616dSTakashi Iwai 16924a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 16934a79616dSTakashi Iwai { 16944a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16954a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1696c577b8a1SJoseph Chan int i; 1697c577b8a1SJoseph Chan hda_nid_t nid; 1698c577b8a1SJoseph Chan 1699c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 17004a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 17014a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1702c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 17034a79616dSTakashi Iwai if (!nid) 17044a79616dSTakashi Iwai continue; 17054a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 17064a79616dSTakashi Iwai spec->private_dac_nids[i] = 17074a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1708c577b8a1SJoseph Chan } 1709c577b8a1SJoseph Chan return 0; 1710c577b8a1SJoseph Chan } 1711c577b8a1SJoseph Chan 17124a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 17134a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1714c577b8a1SJoseph Chan { 17154a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1716c577b8a1SJoseph Chan char name[32]; 17174a79616dSTakashi Iwai hda_nid_t nid; 17184a79616dSTakashi Iwai int err; 1719c577b8a1SJoseph Chan 17204a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 17214a79616dSTakashi Iwai nid = dac; 17224a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 17234a79616dSTakashi Iwai nid = pin; 17244a79616dSTakashi Iwai else 17254a79616dSTakashi Iwai nid = 0; 17264a79616dSTakashi Iwai if (nid) { 17274a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1728c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 17294a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1730c577b8a1SJoseph Chan if (err < 0) 1731c577b8a1SJoseph Chan return err; 1732c577b8a1SJoseph Chan } 17334a79616dSTakashi Iwai 17344a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 17354a79616dSTakashi Iwai nid = dac; 17364a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 17374a79616dSTakashi Iwai nid = pin; 17384a79616dSTakashi Iwai else 17394a79616dSTakashi Iwai nid = 0; 17404a79616dSTakashi Iwai if (nid) { 17414a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 17424a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 17434a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 17444a79616dSTakashi Iwai if (err < 0) 17454a79616dSTakashi Iwai return err; 17464a79616dSTakashi Iwai } 17474a79616dSTakashi Iwai return 0; 17484a79616dSTakashi Iwai } 17494a79616dSTakashi Iwai 1750f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1751f4a7828bSTakashi Iwai { 1752f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1753f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1754f4a7828bSTakashi Iwai int i; 1755f4a7828bSTakashi Iwai 1756f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1757f4a7828bSTakashi Iwai if (!is_smart51_pins(codec, cfg->inputs[i].pin)) 1758f4a7828bSTakashi Iwai continue; 1759f4a7828bSTakashi Iwai spec->can_smart51 = 1; 1760f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 1761f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1762f4a7828bSTakashi Iwai break; 1763f4a7828bSTakashi Iwai } 1764f4a7828bSTakashi Iwai } 1765f4a7828bSTakashi Iwai 17664a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 17674a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 17684a79616dSTakashi Iwai { 17694a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1770f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 17714a79616dSTakashi Iwai static const char * const chname[4] = { 17724a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 17734a79616dSTakashi Iwai }; 17744a79616dSTakashi Iwai int i, idx, err; 1775f4a7828bSTakashi Iwai int old_line_outs; 1776f4a7828bSTakashi Iwai 1777f4a7828bSTakashi Iwai /* check smart51 */ 1778f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 1779f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 1780f4a7828bSTakashi Iwai mangle_smart51(codec); 17814a79616dSTakashi Iwai 17824a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 17834a79616dSTakashi Iwai hda_nid_t pin, dac; 17844a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 17854a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 17864a79616dSTakashi Iwai if (!pin || !dac) 17874a79616dSTakashi Iwai continue; 17880fe0adf8STakashi Iwai if (i == HDA_CLFE) { 17894a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 17904a79616dSTakashi Iwai if (err < 0) 17914a79616dSTakashi Iwai return err; 17924a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 17934a79616dSTakashi Iwai if (err < 0) 17944a79616dSTakashi Iwai return err; 17954a79616dSTakashi Iwai } else { 17964a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 17974a79616dSTakashi Iwai if (err < 0) 17984a79616dSTakashi Iwai return err; 17994a79616dSTakashi Iwai } 18004a79616dSTakashi Iwai } 18014a79616dSTakashi Iwai 18024a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 18034a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 18044a79616dSTakashi Iwai if (idx >= 0) { 18054a79616dSTakashi Iwai /* add control to mixer */ 18064a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 18074a79616dSTakashi Iwai "PCM Playback Volume", 18084a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 18094a79616dSTakashi Iwai idx, HDA_INPUT)); 18104a79616dSTakashi Iwai if (err < 0) 18114a79616dSTakashi Iwai return err; 18124a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 18134a79616dSTakashi Iwai "PCM Playback Switch", 18144a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 18154a79616dSTakashi Iwai idx, HDA_INPUT)); 18164a79616dSTakashi Iwai if (err < 0) 18174a79616dSTakashi Iwai return err; 1818c577b8a1SJoseph Chan } 1819c577b8a1SJoseph Chan 1820f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 1821f4a7828bSTakashi Iwai 1822c577b8a1SJoseph Chan return 0; 1823c577b8a1SJoseph Chan } 1824c577b8a1SJoseph Chan 18250aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 18260aa62aefSHarald Welte { 18270aa62aefSHarald Welte int i; 18280aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1829ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 18300aa62aefSHarald Welte 18310aa62aefSHarald Welte /* for hp mode select */ 183210a20af7STakashi Iwai for (i = 0; texts[i]; i++) 183310a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 18340aa62aefSHarald Welte 18350aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 18360aa62aefSHarald Welte } 18370aa62aefSHarald Welte 18384a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 1839c577b8a1SJoseph Chan { 18404a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1841c577b8a1SJoseph Chan int err; 1842c577b8a1SJoseph Chan 1843c577b8a1SJoseph Chan if (!pin) 1844c577b8a1SJoseph Chan return 0; 1845c577b8a1SJoseph Chan 18464a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 1847ece8d043STakashi Iwai spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1]; 18484a79616dSTakashi Iwai spec->hp_independent_mode_index = 18494a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 18500aa62aefSHarald Welte create_hp_imux(spec); 18514a79616dSTakashi Iwai } 18524a79616dSTakashi Iwai 1853ece8d043STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 1854ece8d043STakashi Iwai &spec->hp_dep_path, 0, -1) && 1855ece8d043STakashi Iwai !spec->hp_dac_nid) 1856ece8d043STakashi Iwai return 0; 1857ece8d043STakashi Iwai 1858ece8d043STakashi Iwai 1859ece8d043STakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); 18604a79616dSTakashi Iwai if (err < 0) 18614a79616dSTakashi Iwai return err; 18620aa62aefSHarald Welte 1863c577b8a1SJoseph Chan return 0; 1864c577b8a1SJoseph Chan } 1865c577b8a1SJoseph Chan 18664a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec) 18674a918ffeSTakashi Iwai { 18684a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 18694a918ffeSTakashi Iwai hda_nid_t pin, dac; 18704a918ffeSTakashi Iwai 18714a918ffeSTakashi Iwai pin = spec->autocfg.speaker_pins[0]; 18724a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs || !pin) 18734a918ffeSTakashi Iwai return 0; 18744a918ffeSTakashi Iwai 18754a918ffeSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) { 18764a918ffeSTakashi Iwai dac = spec->speaker_path.path[spec->speaker_path.depth - 1]; 18774a918ffeSTakashi Iwai spec->multiout.extra_out_nid[0] = dac; 18784a918ffeSTakashi Iwai return create_ch_ctls(codec, "Speaker", pin, dac, 3); 18794a918ffeSTakashi Iwai } 18804a918ffeSTakashi Iwai if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 18814a918ffeSTakashi Iwai &spec->speaker_path, 0, -1)) 18824a918ffeSTakashi Iwai return create_ch_ctls(codec, "Headphone", pin, 0, 3); 18834a918ffeSTakashi Iwai 18844a918ffeSTakashi Iwai return 0; 18854a918ffeSTakashi Iwai } 18864a918ffeSTakashi Iwai 1887a766d0d7STakashi Iwai /* look for ADCs */ 1888a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 1889a766d0d7STakashi Iwai { 1890a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 1891a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 1892a766d0d7STakashi Iwai int i; 1893a766d0d7STakashi Iwai 1894a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 1895a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 1896a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 1897a766d0d7STakashi Iwai continue; 1898a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 1899a766d0d7STakashi Iwai continue; 1900a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 1901a766d0d7STakashi Iwai continue; 1902a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 1903a766d0d7STakashi Iwai return -ENOMEM; 1904a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 1905a766d0d7STakashi Iwai } 1906a766d0d7STakashi Iwai return 0; 1907a766d0d7STakashi Iwai } 1908a766d0d7STakashi Iwai 1909a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 1910a766d0d7STakashi Iwai 1911d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 1912d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1913d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 1914d7a99cceSTakashi Iwai * So call somewhat different.. 1915d7a99cceSTakashi Iwai */ 1916d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 1917d7a99cceSTakashi Iwai .name = "Input Source", 1918d7a99cceSTakashi Iwai .info = via_mux_enum_info, 1919d7a99cceSTakashi Iwai .get = via_mux_enum_get, 1920d7a99cceSTakashi Iwai .put = via_mux_enum_put, 1921d7a99cceSTakashi Iwai }; 1922d7a99cceSTakashi Iwai 1923c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 1924620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 1925620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 1926c577b8a1SJoseph Chan { 192710a20af7STakashi Iwai struct via_spec *spec = codec->spec; 19280aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 1929a766d0d7STakashi Iwai int i, err, idx, idx2, type, type_idx = 0; 1930a766d0d7STakashi Iwai hda_nid_t cap_nid; 1931a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 1932a766d0d7STakashi Iwai int num_idxs; 1933a766d0d7STakashi Iwai 1934a766d0d7STakashi Iwai err = via_fill_adcs(codec); 1935a766d0d7STakashi Iwai if (err < 0) 1936a766d0d7STakashi Iwai return err; 1937a766d0d7STakashi Iwai err = get_mux_nids(codec); 1938a766d0d7STakashi Iwai if (err < 0) 1939a766d0d7STakashi Iwai return err; 1940a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 1941a766d0d7STakashi Iwai 1942a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 1943a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 1944a766d0d7STakashi Iwai if (num_idxs <= 0) 1945a766d0d7STakashi Iwai return 0; 1946c577b8a1SJoseph Chan 1947c577b8a1SJoseph Chan /* for internal loopback recording select */ 1948f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 1949620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 195010a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 1951f3268512STakashi Iwai break; 1952f3268512STakashi Iwai } 1953f3268512STakashi Iwai } 1954c577b8a1SJoseph Chan 19557b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 195610a20af7STakashi Iwai const char *label; 19577b315bb4STakashi Iwai type = cfg->inputs[i].type; 1958f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 19597b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 1960c577b8a1SJoseph Chan break; 1961f3268512STakashi Iwai if (idx >= num_idxs) 1962f3268512STakashi Iwai continue; 19637b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 19647b315bb4STakashi Iwai type_idx++; 19657b315bb4STakashi Iwai else 19667b315bb4STakashi Iwai type_idx = 0; 196710a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 1968620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 1969620e2b28STakashi Iwai pin_idxs[idx]); 1970a766d0d7STakashi Iwai if (idx2 >= 0) 197116922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 1972620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 1973c577b8a1SJoseph Chan if (err < 0) 1974c577b8a1SJoseph Chan return err; 197510a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 1976c577b8a1SJoseph Chan } 1977d7a99cceSTakashi Iwai 1978d7a99cceSTakashi Iwai /* create capture mixer elements */ 1979d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 1980d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 1981d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 1982d7a99cceSTakashi Iwai "Capture Volume", i, 1983d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1984d7a99cceSTakashi Iwai HDA_INPUT)); 1985d7a99cceSTakashi Iwai if (err < 0) 1986d7a99cceSTakashi Iwai return err; 1987d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1988d7a99cceSTakashi Iwai "Capture Switch", i, 1989d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1990d7a99cceSTakashi Iwai HDA_INPUT)); 1991d7a99cceSTakashi Iwai if (err < 0) 1992d7a99cceSTakashi Iwai return err; 1993d7a99cceSTakashi Iwai } 1994d7a99cceSTakashi Iwai 1995d7a99cceSTakashi Iwai /* input-source control */ 1996d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 1997d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 1998d7a99cceSTakashi Iwai break; 1999d7a99cceSTakashi Iwai if (i) { 2000d7a99cceSTakashi Iwai struct snd_kcontrol_new *knew; 2001d7a99cceSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 2002d7a99cceSTakashi Iwai if (!knew) 2003d7a99cceSTakashi Iwai return -ENOMEM; 2004d7a99cceSTakashi Iwai knew->count = i; 2005d7a99cceSTakashi Iwai } 2006d7a99cceSTakashi Iwai 2007d7a99cceSTakashi Iwai /* mic-boosts */ 2008d7a99cceSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2009d7a99cceSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2010d7a99cceSTakashi Iwai unsigned int caps; 2011d7a99cceSTakashi Iwai const char *label; 2012d7a99cceSTakashi Iwai char name[32]; 2013d7a99cceSTakashi Iwai 2014d7a99cceSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 2015d7a99cceSTakashi Iwai continue; 2016d7a99cceSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 2017d7a99cceSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 2018d7a99cceSTakashi Iwai continue; 2019d7a99cceSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2020d7a99cceSTakashi Iwai snprintf(name, sizeof(name), "%s Boost Capture Volume", label); 2021d7a99cceSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2022d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 2023d7a99cceSTakashi Iwai if (err < 0) 2024d7a99cceSTakashi Iwai return err; 2025d7a99cceSTakashi Iwai } 2026d7a99cceSTakashi Iwai 2027c577b8a1SJoseph Chan return 0; 2028c577b8a1SJoseph Chan } 2029c577b8a1SJoseph Chan 2030cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 203190dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2032cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2033cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2034cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2035cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2036cb53c626STakashi Iwai { } /* end */ 2037cb53c626STakashi Iwai }; 2038cb53c626STakashi Iwai #endif 2039cb53c626STakashi Iwai 204076d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 204176d9b0ddSHarald Welte { 204276d9b0ddSHarald Welte unsigned int def_conf; 204376d9b0ddSHarald Welte unsigned char seqassoc; 204476d9b0ddSHarald Welte 20452f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 204676d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 204776d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 204882ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 204982ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 205076d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 20512f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 205276d9b0ddSHarald Welte } 205376d9b0ddSHarald Welte 205476d9b0ddSHarald Welte return; 205576d9b0ddSHarald Welte } 205676d9b0ddSHarald Welte 2057e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 20581f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20591f2e99feSLydia Wang { 20601f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20611f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20621f2e99feSLydia Wang 20631f2e99feSLydia Wang if (spec->codec_type != VT1708) 20641f2e99feSLydia Wang return 0; 2065e06e5a29STakashi Iwai spec->vt1708_jack_detect = 20661f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2067e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 20681f2e99feSLydia Wang return 0; 20691f2e99feSLydia Wang } 20701f2e99feSLydia Wang 2071e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 20721f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20731f2e99feSLydia Wang { 20741f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20751f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20761f2e99feSLydia Wang int change; 20771f2e99feSLydia Wang 20781f2e99feSLydia Wang if (spec->codec_type != VT1708) 20791f2e99feSLydia Wang return 0; 2080e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 20811f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2082e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2083e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 20841f2e99feSLydia Wang mute_aa_path(codec, 1); 20851f2e99feSLydia Wang notify_aa_path_ctls(codec); 20861f2e99feSLydia Wang } 20871f2e99feSLydia Wang return change; 20881f2e99feSLydia Wang } 20891f2e99feSLydia Wang 2090e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 20911f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20921f2e99feSLydia Wang .name = "Jack Detect", 20931f2e99feSLydia Wang .count = 1, 20941f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2095e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2096e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 20971f2e99feSLydia Wang }; 20981f2e99feSLydia Wang 209912daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec); 210012daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec); 210112daef65STakashi Iwai 210212daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 2103c577b8a1SJoseph Chan { 2104c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2105c577b8a1SJoseph Chan int err; 2106c577b8a1SJoseph Chan 2107c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2108c577b8a1SJoseph Chan if (err < 0) 2109c577b8a1SJoseph Chan return err; 21104a79616dSTakashi Iwai err = via_auto_fill_dac_nids(codec); 2111c577b8a1SJoseph Chan if (err < 0) 2112c577b8a1SJoseph Chan return err; 2113c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 21147f0df88cSTakashi Iwai return -EINVAL; 2115c577b8a1SJoseph Chan 21164a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2117c577b8a1SJoseph Chan if (err < 0) 2118c577b8a1SJoseph Chan return err; 21194a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2120c577b8a1SJoseph Chan if (err < 0) 2121c577b8a1SJoseph Chan return err; 21224a918ffeSTakashi Iwai err = via_auto_create_speaker_ctls(codec); 21234a918ffeSTakashi Iwai if (err < 0) 21244a918ffeSTakashi Iwai return err; 2125620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2126c577b8a1SJoseph Chan if (err < 0) 2127c577b8a1SJoseph Chan return err; 2128c577b8a1SJoseph Chan 2129c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2130c577b8a1SJoseph Chan 213112daef65STakashi Iwai fill_dig_outs(codec); 213212daef65STakashi Iwai fill_dig_in(codec); 2133c577b8a1SJoseph Chan 2134603c4019STakashi Iwai if (spec->kctls.list) 2135603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2136c577b8a1SJoseph Chan 2137096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 2138c577b8a1SJoseph Chan 21390aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 21400aa62aefSHarald Welte 2141ece8d043STakashi Iwai if (spec->hp_mux) { 2142ece8d043STakashi Iwai err = via_hp_build(codec); 2143ece8d043STakashi Iwai if (err < 0) 2144ece8d043STakashi Iwai return err; 2145ece8d043STakashi Iwai } 2146c577b8a1SJoseph Chan 2147f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2148f4a7828bSTakashi Iwai if (err < 0) 2149f4a7828bSTakashi Iwai return err; 2150f4a7828bSTakashi Iwai 21515d41762aSTakashi Iwai /* assign slave outs */ 21525d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 21535d41762aSTakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 21545d41762aSTakashi Iwai 2155c577b8a1SJoseph Chan return 1; 2156c577b8a1SJoseph Chan } 2157c577b8a1SJoseph Chan 21585d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec) 2159c577b8a1SJoseph Chan { 216025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 21615d41762aSTakashi Iwai if (spec->multiout.dig_out_nid) 21625d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); 21635d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 21645d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); 21655d41762aSTakashi Iwai } 216625eaba2fSLydia Wang 21675d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec) 21685d41762aSTakashi Iwai { 21695d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 21705d41762aSTakashi Iwai if (!spec->dig_in_nid) 21715d41762aSTakashi Iwai return; 21725d41762aSTakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 21735d41762aSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 21745d41762aSTakashi Iwai } 21755d41762aSTakashi Iwai 21764a918ffeSTakashi Iwai /* initialize the unsolicited events */ 21774a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec) 21784a918ffeSTakashi Iwai { 21794a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 21804a918ffeSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 21814a918ffeSTakashi Iwai unsigned int ev; 21824a918ffeSTakashi Iwai int i; 21834a918ffeSTakashi Iwai 21844a918ffeSTakashi Iwai if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) 21854a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->hp_pins[0], 0, 21864a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21874a918ffeSTakashi Iwai AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); 21884a918ffeSTakashi Iwai 21894a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 21904a918ffeSTakashi Iwai ev = VIA_LINE_EVENT; 21914a918ffeSTakashi Iwai else 21924a918ffeSTakashi Iwai ev = 0; 21934a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 21944a918ffeSTakashi Iwai if (cfg->line_out_pins[i] && 21954a918ffeSTakashi Iwai is_jack_detectable(codec, cfg->line_out_pins[i])) 21964a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->line_out_pins[0], 0, 21974a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21984a918ffeSTakashi Iwai AC_USRSP_EN | ev | VIA_JACK_EVENT); 21994a918ffeSTakashi Iwai } 22004a918ffeSTakashi Iwai 22014a918ffeSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 22024a918ffeSTakashi Iwai if (is_jack_detectable(codec, cfg->inputs[i].pin)) 22034a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, 22044a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 22054a918ffeSTakashi Iwai AC_USRSP_EN | VIA_JACK_EVENT); 22064a918ffeSTakashi Iwai } 22074a918ffeSTakashi Iwai } 22084a918ffeSTakashi Iwai 22095d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 22105d41762aSTakashi Iwai { 22115d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 22125d41762aSTakashi Iwai int i; 22135d41762aSTakashi Iwai 22145d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 22155d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 22165d41762aSTakashi Iwai 2217c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2218c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 22194a918ffeSTakashi Iwai via_auto_init_speaker_out(codec); 2220c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 22215d41762aSTakashi Iwai via_auto_init_dig_outs(codec); 22225d41762aSTakashi Iwai via_auto_init_dig_in(codec); 222311890956SLydia Wang 22244a918ffeSTakashi Iwai via_auto_init_unsol_event(codec); 22254a918ffeSTakashi Iwai 222625eaba2fSLydia Wang via_hp_automute(codec); 22274a918ffeSTakashi Iwai via_line_automute(codec, false); 222825eaba2fSLydia Wang 2229c577b8a1SJoseph Chan return 0; 2230c577b8a1SJoseph Chan } 2231c577b8a1SJoseph Chan 22321f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 22331f2e99feSLydia Wang { 22341f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 22351f2e99feSLydia Wang vt1708_hp_work.work); 22361f2e99feSLydia Wang if (spec->codec_type != VT1708) 22371f2e99feSLydia Wang return; 22381f2e99feSLydia Wang /* if jack state toggled */ 22391f2e99feSLydia Wang if (spec->vt1708_hp_present 2240d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 22411f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 22421f2e99feSLydia Wang via_hp_automute(spec->codec); 22431f2e99feSLydia Wang } 22441f2e99feSLydia Wang vt1708_start_hp_work(spec); 22451f2e99feSLydia Wang } 22461f2e99feSLydia Wang 2247337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2248337b9d02STakashi Iwai { 2249337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2250337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2251337b9d02STakashi Iwai unsigned int type; 2252337b9d02STakashi Iwai int i, n; 2253337b9d02STakashi Iwai 2254337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2255337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2256337b9d02STakashi Iwai while (nid) { 2257a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22581c55d521STakashi Iwai if (type == AC_WID_PIN) 22591c55d521STakashi Iwai break; 2260337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2261337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2262337b9d02STakashi Iwai if (n <= 0) 2263337b9d02STakashi Iwai break; 2264337b9d02STakashi Iwai if (n > 1) { 2265337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2266337b9d02STakashi Iwai break; 2267337b9d02STakashi Iwai } 2268337b9d02STakashi Iwai nid = conn[0]; 2269337b9d02STakashi Iwai } 2270337b9d02STakashi Iwai } 22711c55d521STakashi Iwai return 0; 2272337b9d02STakashi Iwai } 2273337b9d02STakashi Iwai 2274c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2275c577b8a1SJoseph Chan { 2276c577b8a1SJoseph Chan struct via_spec *spec; 2277c577b8a1SJoseph Chan int err; 2278c577b8a1SJoseph Chan 2279c577b8a1SJoseph Chan /* create a codec specific record */ 22805b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2281c577b8a1SJoseph Chan if (spec == NULL) 2282c577b8a1SJoseph Chan return -ENOMEM; 2283c577b8a1SJoseph Chan 2284620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2285620e2b28STakashi Iwai 228612daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 228712daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 228812daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 228912daef65STakashi Iwai 2290c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 229112daef65STakashi Iwai err = via_parse_auto_config(codec); 2292c577b8a1SJoseph Chan if (err < 0) { 2293c577b8a1SJoseph Chan via_free(codec); 2294c577b8a1SJoseph Chan return err; 2295c577b8a1SJoseph Chan } 2296c577b8a1SJoseph Chan 229712daef65STakashi Iwai /* add jack detect on/off control */ 229812daef65STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 229912daef65STakashi Iwai return -ENOMEM; 230012daef65STakashi Iwai 2301bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2302bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2303bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2304c577b8a1SJoseph Chan 2305c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2306c577b8a1SJoseph Chan 2307cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2308cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2309cb53c626STakashi Iwai #endif 23101f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2311c577b8a1SJoseph Chan return 0; 2312c577b8a1SJoseph Chan } 2313c577b8a1SJoseph Chan 2314c577b8a1SJoseph Chan /* 2315c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2316c577b8a1SJoseph Chan */ 2317cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 231890dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2319cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2320cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2321cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2322cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2323cb53c626STakashi Iwai { } /* end */ 2324cb53c626STakashi Iwai }; 2325cb53c626STakashi Iwai #endif 2326cb53c626STakashi Iwai 2327c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2328c577b8a1SJoseph Chan { 2329c577b8a1SJoseph Chan struct via_spec *spec; 2330c577b8a1SJoseph Chan int err; 2331c577b8a1SJoseph Chan 2332c577b8a1SJoseph Chan /* create a codec specific record */ 23335b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2334c577b8a1SJoseph Chan if (spec == NULL) 2335c577b8a1SJoseph Chan return -ENOMEM; 2336c577b8a1SJoseph Chan 2337620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2338620e2b28STakashi Iwai 233912daef65STakashi Iwai err = via_parse_auto_config(codec); 2340c577b8a1SJoseph Chan if (err < 0) { 2341c577b8a1SJoseph Chan via_free(codec); 2342c577b8a1SJoseph Chan return err; 2343c577b8a1SJoseph Chan } 2344c577b8a1SJoseph Chan 2345c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2346c577b8a1SJoseph Chan 2347cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2348cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2349cb53c626STakashi Iwai #endif 2350c577b8a1SJoseph Chan 2351c577b8a1SJoseph Chan return 0; 2352c577b8a1SJoseph Chan } 2353c577b8a1SJoseph Chan /* 2354c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2355c577b8a1SJoseph Chan */ 2356c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2357c577b8a1SJoseph Chan { 2358c577b8a1SJoseph Chan struct via_spec *spec; 2359c577b8a1SJoseph Chan int err; 2360c577b8a1SJoseph Chan 2361c577b8a1SJoseph Chan /* create a codec specific record */ 23625b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2363c577b8a1SJoseph Chan if (spec == NULL) 2364c577b8a1SJoseph Chan return -ENOMEM; 2365c577b8a1SJoseph Chan 2366620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2367620e2b28STakashi Iwai 236812daef65STakashi Iwai err = via_parse_auto_config(codec); 2369c577b8a1SJoseph Chan if (err < 0) { 2370c577b8a1SJoseph Chan via_free(codec); 2371c577b8a1SJoseph Chan return err; 2372c577b8a1SJoseph Chan } 2373c577b8a1SJoseph Chan 2374c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2375c577b8a1SJoseph Chan 2376cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2377cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2378cb53c626STakashi Iwai #endif 2379f7278fd0SJosepch Chan return 0; 2380f7278fd0SJosepch Chan } 2381f7278fd0SJosepch Chan 2382f7278fd0SJosepch Chan /* 2383f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2384f7278fd0SJosepch Chan */ 2385f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 238690dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2387f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2388f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2389f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2390f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2391f7278fd0SJosepch Chan { } /* end */ 2392f7278fd0SJosepch Chan }; 2393f7278fd0SJosepch Chan #endif 23943e95b9abSLydia Wang 23953e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 23963e95b9abSLydia Wang { 23973e95b9abSLydia Wang struct via_spec *spec = codec->spec; 23983e95b9abSLydia Wang int imux_is_smixer; 23993e95b9abSLydia Wang unsigned int parm; 24003e95b9abSLydia Wang int is_8ch = 0; 2401bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2402bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 24033e95b9abSLydia Wang is_8ch = 1; 24043e95b9abSLydia Wang 24053e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 24063e95b9abSLydia Wang imux_is_smixer = 24073e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 24083e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 24093e95b9abSLydia Wang /* inputs */ 24103e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 24113e95b9abSLydia Wang parm = AC_PWRST_D3; 24123e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 24133e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 24143e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 24153e95b9abSLydia Wang if (imux_is_smixer) 24163e95b9abSLydia Wang parm = AC_PWRST_D0; 24173e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 24183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 24193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 24203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 24213e95b9abSLydia Wang 24223e95b9abSLydia Wang /* outputs */ 24233e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 24243e95b9abSLydia Wang parm = AC_PWRST_D3; 24253e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 24263e95b9abSLydia Wang if (spec->smart51_enabled) 24273e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 24283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 24293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 24303e95b9abSLydia Wang 24313e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 24323e95b9abSLydia Wang if (is_8ch) { 24333e95b9abSLydia Wang parm = AC_PWRST_D3; 24343e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 24353e95b9abSLydia Wang if (spec->smart51_enabled) 24363e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 24373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 24383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 24403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2441bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2442bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2443bc92df7fSLydia Wang parm = AC_PWRST_D3; 2444bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2445bc92df7fSLydia Wang if (spec->smart51_enabled) 2446bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2447bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2448bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2449bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2450bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24513e95b9abSLydia Wang } 24523e95b9abSLydia Wang 24533e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 24543e95b9abSLydia Wang parm = AC_PWRST_D3; 24553e95b9abSLydia Wang /* force to D0 for internal Speaker */ 24563e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 24573e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 24583e95b9abSLydia Wang if (is_8ch) 24593e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 24603e95b9abSLydia Wang 24613e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 24623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 24633e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 24643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 24653e95b9abSLydia Wang if (is_8ch) { 24663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 24673e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 24693e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2470bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2471bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2472bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24733e95b9abSLydia Wang } 24743e95b9abSLydia Wang 2475518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2476f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2477f7278fd0SJosepch Chan { 2478f7278fd0SJosepch Chan struct via_spec *spec; 2479f7278fd0SJosepch Chan int err; 2480f7278fd0SJosepch Chan 2481518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2482518bf3baSLydia Wang return patch_vt1708S(codec); 2483f7278fd0SJosepch Chan /* create a codec specific record */ 24845b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2485f7278fd0SJosepch Chan if (spec == NULL) 2486f7278fd0SJosepch Chan return -ENOMEM; 2487f7278fd0SJosepch Chan 2488620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2489620e2b28STakashi Iwai 2490f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 249112daef65STakashi Iwai err = via_parse_auto_config(codec); 2492f7278fd0SJosepch Chan if (err < 0) { 2493f7278fd0SJosepch Chan via_free(codec); 2494f7278fd0SJosepch Chan return err; 2495f7278fd0SJosepch Chan } 2496f7278fd0SJosepch Chan 2497f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2498f7278fd0SJosepch Chan 2499f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2500f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2501f7278fd0SJosepch Chan #endif 2502f7278fd0SJosepch Chan 25033e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 25043e95b9abSLydia Wang 2505f7278fd0SJosepch Chan return 0; 2506f7278fd0SJosepch Chan } 2507f7278fd0SJosepch Chan 2508f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2509f7278fd0SJosepch Chan { 2510f7278fd0SJosepch Chan struct via_spec *spec; 2511f7278fd0SJosepch Chan int err; 2512f7278fd0SJosepch Chan 2513f7278fd0SJosepch Chan /* create a codec specific record */ 25145b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2515f7278fd0SJosepch Chan if (spec == NULL) 2516f7278fd0SJosepch Chan return -ENOMEM; 2517f7278fd0SJosepch Chan 2518f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 251912daef65STakashi Iwai err = via_parse_auto_config(codec); 2520f7278fd0SJosepch Chan if (err < 0) { 2521f7278fd0SJosepch Chan via_free(codec); 2522f7278fd0SJosepch Chan return err; 2523f7278fd0SJosepch Chan } 2524f7278fd0SJosepch Chan 2525f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2526f7278fd0SJosepch Chan 2527f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2528f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2529f7278fd0SJosepch Chan #endif 2530c577b8a1SJoseph Chan 25313e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 25323e95b9abSLydia Wang 2533c577b8a1SJoseph Chan return 0; 2534c577b8a1SJoseph Chan } 2535c577b8a1SJoseph Chan 2536d949cac1SHarald Welte /* Patch for VT1708S */ 2537096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 2538d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2539d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2540bc7e7e5cSLydia Wang /* don't bybass mixer */ 2541bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 2542d949cac1SHarald Welte { } 2543d949cac1SHarald Welte }; 2544d949cac1SHarald Welte 25459da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 25469da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 25479da29271STakashi Iwai { 25489da29271STakashi Iwai struct via_spec *spec = codec->spec; 25499da29271STakashi Iwai int i; 25509da29271STakashi Iwai 25519da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 25529da29271STakashi Iwai hda_nid_t nid; 25539da29271STakashi Iwai int conn; 25549da29271STakashi Iwai 25559da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 25569da29271STakashi Iwai if (!nid) 25579da29271STakashi Iwai continue; 25589da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 25599da29271STakashi Iwai if (conn < 1) 25609da29271STakashi Iwai continue; 25619da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 25629da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 25639da29271STakashi Iwai else { 25649da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 25659da29271STakashi Iwai break; /* at most two dig outs */ 25669da29271STakashi Iwai } 25679da29271STakashi Iwai } 25689da29271STakashi Iwai } 25699da29271STakashi Iwai 257012daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec) 2571d949cac1SHarald Welte { 2572d949cac1SHarald Welte struct via_spec *spec = codec->spec; 257312daef65STakashi Iwai hda_nid_t dig_nid; 257412daef65STakashi Iwai int i, err; 2575d949cac1SHarald Welte 257612daef65STakashi Iwai if (!spec->autocfg.dig_in_pin) 257712daef65STakashi Iwai return; 2578d949cac1SHarald Welte 257912daef65STakashi Iwai dig_nid = codec->start_nid; 258012daef65STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 258112daef65STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 258212daef65STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 258312daef65STakashi Iwai continue; 258412daef65STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 258512daef65STakashi Iwai continue; 258612daef65STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 258712daef65STakashi Iwai continue; 258812daef65STakashi Iwai err = get_connection_index(codec, dig_nid, 258912daef65STakashi Iwai spec->autocfg.dig_in_pin); 259012daef65STakashi Iwai if (err >= 0) { 259112daef65STakashi Iwai spec->dig_in_nid = dig_nid; 259212daef65STakashi Iwai break; 259312daef65STakashi Iwai } 259412daef65STakashi Iwai } 2595d949cac1SHarald Welte } 2596d949cac1SHarald Welte 2597d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 259890dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 2599d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 2600d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 2601d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 2602d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 2603d949cac1SHarald Welte { } /* end */ 2604d949cac1SHarald Welte }; 2605d949cac1SHarald Welte #endif 2606d949cac1SHarald Welte 26076369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 26086369bcfcSLydia Wang int offset, int num_steps, int step_size) 26096369bcfcSLydia Wang { 26106369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 26116369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 26126369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 26136369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 26146369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 26156369bcfcSLydia Wang } 26166369bcfcSLydia Wang 2617d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2618d949cac1SHarald Welte { 2619d949cac1SHarald Welte struct via_spec *spec; 2620d949cac1SHarald Welte int err; 2621d949cac1SHarald Welte 2622d949cac1SHarald Welte /* create a codec specific record */ 26235b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2624d949cac1SHarald Welte if (spec == NULL) 2625d949cac1SHarald Welte return -ENOMEM; 2626d949cac1SHarald Welte 2627620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2628d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2629d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2630620e2b28STakashi Iwai 2631d949cac1SHarald Welte /* automatic parse from the BIOS config */ 263212daef65STakashi Iwai err = via_parse_auto_config(codec); 2633d949cac1SHarald Welte if (err < 0) { 2634d949cac1SHarald Welte via_free(codec); 2635d949cac1SHarald Welte return err; 2636d949cac1SHarald Welte } 2637d949cac1SHarald Welte 2638096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 2639d949cac1SHarald Welte 2640d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2641d949cac1SHarald Welte 2642d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2643d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 2644d949cac1SHarald Welte #endif 2645d949cac1SHarald Welte 2646518bf3baSLydia Wang /* correct names for VT1708BCE */ 2647518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 2648518bf3baSLydia Wang kfree(codec->chip_name); 2649518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 2650518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 2651518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 2652518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2653970f630fSLydia Wang } 2654bc92df7fSLydia Wang /* correct names for VT1705 */ 2655bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 2656bc92df7fSLydia Wang kfree(codec->chip_name); 2657bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 2658bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 2659bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 2660bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2661bc92df7fSLydia Wang } 26623e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 2663d949cac1SHarald Welte return 0; 2664d949cac1SHarald Welte } 2665d949cac1SHarald Welte 2666d949cac1SHarald Welte /* Patch for VT1702 */ 2667d949cac1SHarald Welte 2668096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 2669bc7e7e5cSLydia Wang /* mixer enable */ 2670bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 2671bc7e7e5cSLydia Wang /* GPIO 0~2 */ 2672bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 2673d949cac1SHarald Welte { } 2674d949cac1SHarald Welte }; 2675d949cac1SHarald Welte 2676d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 267790dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 2678d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 2679d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 2680d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 2681d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 2682d949cac1SHarald Welte { } /* end */ 2683d949cac1SHarald Welte }; 2684d949cac1SHarald Welte #endif 2685d949cac1SHarald Welte 26863e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 26873e95b9abSLydia Wang { 26883e95b9abSLydia Wang int imux_is_smixer = 26893e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 26903e95b9abSLydia Wang unsigned int parm; 26913e95b9abSLydia Wang /* inputs */ 26923e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 26933e95b9abSLydia Wang parm = AC_PWRST_D3; 26943e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 26953e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 26963e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 26973e95b9abSLydia Wang if (imux_is_smixer) 26983e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 26993e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 27003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 27013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 27023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 27033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 27043e95b9abSLydia Wang 27053e95b9abSLydia Wang /* outputs */ 27063e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 27073e95b9abSLydia Wang parm = AC_PWRST_D3; 27083e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 27093e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 27103e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 27113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 27123e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 27133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 27143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 27153e95b9abSLydia Wang } 27163e95b9abSLydia Wang 2717d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 2718d949cac1SHarald Welte { 2719d949cac1SHarald Welte struct via_spec *spec; 2720d949cac1SHarald Welte int err; 2721d949cac1SHarald Welte 2722d949cac1SHarald Welte /* create a codec specific record */ 27235b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2724d949cac1SHarald Welte if (spec == NULL) 2725d949cac1SHarald Welte return -ENOMEM; 2726d949cac1SHarald Welte 2727620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 2728620e2b28STakashi Iwai 272912daef65STakashi Iwai /* limit AA path volume to 0 dB */ 273012daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 273112daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 273212daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 273312daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 273412daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 273512daef65STakashi Iwai 2736d949cac1SHarald Welte /* automatic parse from the BIOS config */ 273712daef65STakashi Iwai err = via_parse_auto_config(codec); 2738d949cac1SHarald Welte if (err < 0) { 2739d949cac1SHarald Welte via_free(codec); 2740d949cac1SHarald Welte return err; 2741d949cac1SHarald Welte } 2742d949cac1SHarald Welte 2743096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 2744d949cac1SHarald Welte 2745d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2746d949cac1SHarald Welte 2747d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2748d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 2749d949cac1SHarald Welte #endif 2750d949cac1SHarald Welte 27513e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 2752d949cac1SHarald Welte return 0; 2753d949cac1SHarald Welte } 2754d949cac1SHarald Welte 2755eb7188caSLydia Wang /* Patch for VT1718S */ 2756eb7188caSLydia Wang 2757096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 27584ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 27594ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 2760eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 2761eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 27625d41762aSTakashi Iwai 2763eb7188caSLydia Wang { } 2764eb7188caSLydia Wang }; 2765eb7188caSLydia Wang 2766eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 276790dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 2768eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 2769eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 2770eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 2771eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 2772eb7188caSLydia Wang { } /* end */ 2773eb7188caSLydia Wang }; 2774eb7188caSLydia Wang #endif 2775eb7188caSLydia Wang 27763e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 27773e95b9abSLydia Wang { 27783e95b9abSLydia Wang struct via_spec *spec = codec->spec; 27793e95b9abSLydia Wang int imux_is_smixer; 27803e95b9abSLydia Wang unsigned int parm; 27813e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 27823e95b9abSLydia Wang imux_is_smixer = 27833e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 27843e95b9abSLydia Wang /* inputs */ 27853e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 27863e95b9abSLydia Wang parm = AC_PWRST_D3; 27873e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 27883e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 27893e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 27903e95b9abSLydia Wang if (imux_is_smixer) 27913e95b9abSLydia Wang parm = AC_PWRST_D0; 27923e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 27933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 27943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 27953e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 27963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 27973e95b9abSLydia Wang 27983e95b9abSLydia Wang /* outputs */ 27993e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 28003e95b9abSLydia Wang parm = AC_PWRST_D3; 28013e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 28023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 28033e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 28043e95b9abSLydia Wang 28053e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 28063e95b9abSLydia Wang parm = AC_PWRST_D3; 28073e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 28083e95b9abSLydia Wang if (spec->smart51_enabled) 28093e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 28103e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 28113e95b9abSLydia Wang 28123e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 28133e95b9abSLydia Wang parm = AC_PWRST_D3; 28143e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 28153e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 28163e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 28173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 28183e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 28193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 28203e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28213e95b9abSLydia Wang 28223e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 28233e95b9abSLydia Wang parm = AC_PWRST_D3; 28243e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 28253e95b9abSLydia Wang if (spec->smart51_enabled) 28263e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 28273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 28283e95b9abSLydia Wang 28293e95b9abSLydia Wang if (spec->hp_independent_mode) { 28303e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 28313e95b9abSLydia Wang parm = AC_PWRST_D3; 28323e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 28333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 28343e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 28363e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28373e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 28383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28393e95b9abSLydia Wang } 28403e95b9abSLydia Wang } 28413e95b9abSLydia Wang 2842eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 2843eb7188caSLydia Wang { 2844eb7188caSLydia Wang struct via_spec *spec; 2845eb7188caSLydia Wang int err; 2846eb7188caSLydia Wang 2847eb7188caSLydia Wang /* create a codec specific record */ 28485b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2849eb7188caSLydia Wang if (spec == NULL) 2850eb7188caSLydia Wang return -ENOMEM; 2851eb7188caSLydia Wang 2852620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 2853d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 2854d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 2855620e2b28STakashi Iwai 2856eb7188caSLydia Wang /* automatic parse from the BIOS config */ 285712daef65STakashi Iwai err = via_parse_auto_config(codec); 2858eb7188caSLydia Wang if (err < 0) { 2859eb7188caSLydia Wang via_free(codec); 2860eb7188caSLydia Wang return err; 2861eb7188caSLydia Wang } 2862eb7188caSLydia Wang 2863096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 2864eb7188caSLydia Wang 2865eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 2866eb7188caSLydia Wang 2867eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 2868eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 2869eb7188caSLydia Wang #endif 2870eb7188caSLydia Wang 28713e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 28723e95b9abSLydia Wang 2873eb7188caSLydia Wang return 0; 2874eb7188caSLydia Wang } 2875f3db423dSLydia Wang 2876f3db423dSLydia Wang /* Patch for VT1716S */ 2877f3db423dSLydia Wang 2878f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 2879f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 2880f3db423dSLydia Wang { 2881f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 2882f3db423dSLydia Wang uinfo->count = 1; 2883f3db423dSLydia Wang uinfo->value.integer.min = 0; 2884f3db423dSLydia Wang uinfo->value.integer.max = 1; 2885f3db423dSLydia Wang return 0; 2886f3db423dSLydia Wang } 2887f3db423dSLydia Wang 2888f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 2889f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2890f3db423dSLydia Wang { 2891f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2892f3db423dSLydia Wang int index = 0; 2893f3db423dSLydia Wang 2894f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 2895f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 2896f3db423dSLydia Wang if (index != -1) 2897f3db423dSLydia Wang *ucontrol->value.integer.value = index; 2898f3db423dSLydia Wang 2899f3db423dSLydia Wang return 0; 2900f3db423dSLydia Wang } 2901f3db423dSLydia Wang 2902f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 2903f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2904f3db423dSLydia Wang { 2905f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2906f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2907f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 2908f3db423dSLydia Wang 2909f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 2910f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 2911f3db423dSLydia Wang spec->dmic_enabled = index; 29123e95b9abSLydia Wang set_widgets_power_state(codec); 2913f3db423dSLydia Wang return 1; 2914f3db423dSLydia Wang } 2915f3db423dSLydia Wang 291690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 2917f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 2918f3db423dSLydia Wang { 2919f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2920f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 29215b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 2922f3db423dSLydia Wang .count = 1, 2923f3db423dSLydia Wang .info = vt1716s_dmic_info, 2924f3db423dSLydia Wang .get = vt1716s_dmic_get, 2925f3db423dSLydia Wang .put = vt1716s_dmic_put, 2926f3db423dSLydia Wang }, 2927f3db423dSLydia Wang {} /* end */ 2928f3db423dSLydia Wang }; 2929f3db423dSLydia Wang 2930f3db423dSLydia Wang 2931f3db423dSLydia Wang /* mono-out mixer elements */ 293290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 2933f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 2934f3db423dSLydia Wang { } /* end */ 2935f3db423dSLydia Wang }; 2936f3db423dSLydia Wang 2937096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 2938f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 2939f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 2940f3db423dSLydia Wang /* don't bybass mixer */ 2941f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 2942f3db423dSLydia Wang /* Enable mono output */ 2943f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 2944f3db423dSLydia Wang { } 2945f3db423dSLydia Wang }; 2946f3db423dSLydia Wang 2947f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 294890dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 2949f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 2950f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 2951f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 2952f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 2953f3db423dSLydia Wang { } /* end */ 2954f3db423dSLydia Wang }; 2955f3db423dSLydia Wang #endif 2956f3db423dSLydia Wang 29573e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 29583e95b9abSLydia Wang { 29593e95b9abSLydia Wang struct via_spec *spec = codec->spec; 29603e95b9abSLydia Wang int imux_is_smixer; 29613e95b9abSLydia Wang unsigned int parm; 29623e95b9abSLydia Wang unsigned int mono_out, present; 29633e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 29643e95b9abSLydia Wang imux_is_smixer = 29653e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 29663e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 29673e95b9abSLydia Wang /* inputs */ 29683e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 29693e95b9abSLydia Wang parm = AC_PWRST_D3; 29703e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29713e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29723e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29733e95b9abSLydia Wang if (imux_is_smixer) 29743e95b9abSLydia Wang parm = AC_PWRST_D0; 29753e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 29763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 29773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 29783e95b9abSLydia Wang 29793e95b9abSLydia Wang parm = AC_PWRST_D3; 29803e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29813e95b9abSLydia Wang /* PW11 (22h) */ 29823e95b9abSLydia Wang if (spec->dmic_enabled) 29833e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 29843e95b9abSLydia Wang else 29853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 29863e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 29873e95b9abSLydia Wang 29883e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 29893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 29903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 29913e95b9abSLydia Wang 29923e95b9abSLydia Wang /* outputs */ 29933e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 29943e95b9abSLydia Wang parm = AC_PWRST_D3; 29953e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 29963e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 29973e95b9abSLydia Wang if (spec->smart51_enabled) 29983e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 30003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 30013e95b9abSLydia Wang 30023e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 30033e95b9abSLydia Wang parm = AC_PWRST_D3; 30043e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 30053e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 30063e95b9abSLydia Wang if (spec->smart51_enabled) 30073e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 30083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 30093e95b9abSLydia Wang 30103e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 30113e95b9abSLydia Wang if (spec->smart51_enabled) 30123e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 30133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 30143e95b9abSLydia Wang 30153e95b9abSLydia Wang /* Mono out */ 30163e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 30173e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 30183e95b9abSLydia Wang 30193e95b9abSLydia Wang if (present) 30203e95b9abSLydia Wang mono_out = 0; 30213e95b9abSLydia Wang else { 30223e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 30233e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 30243e95b9abSLydia Wang mono_out = 0; 30253e95b9abSLydia Wang else 30263e95b9abSLydia Wang mono_out = 1; 30273e95b9abSLydia Wang } 30283e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 30293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 30303e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 30313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 30323e95b9abSLydia Wang 30333e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 30343e95b9abSLydia Wang parm = AC_PWRST_D3; 30353e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 30363e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 30373e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 30383e95b9abSLydia Wang if (spec->hp_independent_mode) 30393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 30403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30413e95b9abSLydia Wang 30423e95b9abSLydia Wang /* force to D0 for internal Speaker */ 30433e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 30443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 30453e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 30463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 30473e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 30483e95b9abSLydia Wang } 30493e95b9abSLydia Wang 3050f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3051f3db423dSLydia Wang { 3052f3db423dSLydia Wang struct via_spec *spec; 3053f3db423dSLydia Wang int err; 3054f3db423dSLydia Wang 3055f3db423dSLydia Wang /* create a codec specific record */ 30565b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3057f3db423dSLydia Wang if (spec == NULL) 3058f3db423dSLydia Wang return -ENOMEM; 3059f3db423dSLydia Wang 3060620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3061d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3062d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3063620e2b28STakashi Iwai 3064f3db423dSLydia Wang /* automatic parse from the BIOS config */ 306512daef65STakashi Iwai err = via_parse_auto_config(codec); 3066f3db423dSLydia Wang if (err < 0) { 3067f3db423dSLydia Wang via_free(codec); 3068f3db423dSLydia Wang return err; 3069f3db423dSLydia Wang } 3070f3db423dSLydia Wang 3071096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 3072f3db423dSLydia Wang 3073f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3074f3db423dSLydia Wang spec->num_mixers++; 3075f3db423dSLydia Wang 3076f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3077f3db423dSLydia Wang 3078f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3079f3db423dSLydia Wang 3080f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3081f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 3082f3db423dSLydia Wang #endif 3083f3db423dSLydia Wang 30843e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 3085f3db423dSLydia Wang return 0; 3086f3db423dSLydia Wang } 308725eaba2fSLydia Wang 308825eaba2fSLydia Wang /* for vt2002P */ 308925eaba2fSLydia Wang 3090096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 3091eadb9a80SLydia Wang /* Class-D speaker related verbs */ 3092eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 3093eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 3094eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 309525eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 309625eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 309725eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 309825eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 309925eaba2fSLydia Wang { } 310025eaba2fSLydia Wang }; 31014a918ffeSTakashi Iwai 3102096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 310311890956SLydia Wang /* Enable Boost Volume backdoor */ 310411890956SLydia Wang {0x1, 0xfb9, 0x24}, 310511890956SLydia Wang /* Enable AOW0 to MW9 */ 310611890956SLydia Wang {0x1, 0xfb8, 0x88}, 310711890956SLydia Wang { } 310811890956SLydia Wang }; 310925eaba2fSLydia Wang 311025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 311190dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 311225eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 311325eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 311425eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 311525eaba2fSLydia Wang { } /* end */ 311625eaba2fSLydia Wang }; 311725eaba2fSLydia Wang #endif 311825eaba2fSLydia Wang 31193e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 31203e95b9abSLydia Wang { 31213e95b9abSLydia Wang struct via_spec *spec = codec->spec; 31223e95b9abSLydia Wang int imux_is_smixer; 31233e95b9abSLydia Wang unsigned int parm; 31243e95b9abSLydia Wang unsigned int present; 31253e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 31263e95b9abSLydia Wang imux_is_smixer = 31273e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 31283e95b9abSLydia Wang /* inputs */ 31293e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 31303e95b9abSLydia Wang parm = AC_PWRST_D3; 31313e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 31323e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 31333e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 31343e95b9abSLydia Wang parm = AC_PWRST_D0; 31353e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 31363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 31373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 31383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 31393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 31403e95b9abSLydia Wang 31413e95b9abSLydia Wang /* outputs */ 31423e95b9abSLydia Wang /* AOW0 (8h)*/ 31433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 31443e95b9abSLydia Wang 314511890956SLydia Wang if (spec->codec_type == VT1802) { 314611890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 314711890956SLydia Wang parm = AC_PWRST_D3; 314811890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 314911890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 315011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 315111890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 315211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 315311890956SLydia Wang } else { 31543e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 31553e95b9abSLydia Wang parm = AC_PWRST_D3; 31563e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 31573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 31583e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 31603e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 316111890956SLydia Wang } 31623e95b9abSLydia Wang 316311890956SLydia Wang if (spec->codec_type == VT1802) { 316411890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 316511890956SLydia Wang parm = AC_PWRST_D3; 316611890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 316711890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 316811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 316911890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 317011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 317111890956SLydia Wang } else { 31723e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 31733e95b9abSLydia Wang parm = AC_PWRST_D3; 31743e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 31753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 31763e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 31783e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 317911890956SLydia Wang } 31803e95b9abSLydia Wang 31813e95b9abSLydia Wang if (spec->hp_independent_mode) 31823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 31833e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31843e95b9abSLydia Wang 31853e95b9abSLydia Wang /* Class-D */ 31863e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 31873e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 31883e95b9abSLydia Wang 31893e95b9abSLydia Wang parm = AC_PWRST_D3; 31903e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 31913e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 319211890956SLydia Wang if (spec->codec_type == VT1802) 319311890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 319411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 319511890956SLydia Wang else 31963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 31973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 31993e95b9abSLydia Wang 32003e95b9abSLydia Wang /* Mono Out */ 32013e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 32023e95b9abSLydia Wang 32033e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 320411890956SLydia Wang if (spec->codec_type == VT1802) { 320511890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 320611890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 320711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 320811890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 320911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 321011890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 321111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 321211890956SLydia Wang } else { 32133e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 32143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 32153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 32173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 32193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 322011890956SLydia Wang } 32213e95b9abSLydia Wang /* MW9 (21h) */ 32223e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 32233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32243e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 32253e95b9abSLydia Wang else 32263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32273e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 32283e95b9abSLydia Wang } 322925eaba2fSLydia Wang 323025eaba2fSLydia Wang /* patch for vt2002P */ 323125eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 323225eaba2fSLydia Wang { 323325eaba2fSLydia Wang struct via_spec *spec; 323425eaba2fSLydia Wang int err; 323525eaba2fSLydia Wang 323625eaba2fSLydia Wang /* create a codec specific record */ 32375b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 323825eaba2fSLydia Wang if (spec == NULL) 323925eaba2fSLydia Wang return -ENOMEM; 324025eaba2fSLydia Wang 3241620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3242d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3243d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3244620e2b28STakashi Iwai 324525eaba2fSLydia Wang /* automatic parse from the BIOS config */ 324612daef65STakashi Iwai err = via_parse_auto_config(codec); 324725eaba2fSLydia Wang if (err < 0) { 324825eaba2fSLydia Wang via_free(codec); 324925eaba2fSLydia Wang return err; 325025eaba2fSLydia Wang } 325125eaba2fSLydia Wang 325211890956SLydia Wang if (spec->codec_type == VT1802) 32534a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 325411890956SLydia Wang else 32554a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 325611890956SLydia Wang 325725eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 325825eaba2fSLydia Wang 325925eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 326025eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 326125eaba2fSLydia Wang #endif 326225eaba2fSLydia Wang 32633e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 326425eaba2fSLydia Wang return 0; 326525eaba2fSLydia Wang } 3266ab6734e7SLydia Wang 3267ab6734e7SLydia Wang /* for vt1812 */ 3268ab6734e7SLydia Wang 3269096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 3270ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 3271ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 3272ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 3273ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 3274ab6734e7SLydia Wang { } 3275ab6734e7SLydia Wang }; 3276ab6734e7SLydia Wang 3277ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 327890dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 3279ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 3280ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 3281ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 3282ab6734e7SLydia Wang { } /* end */ 3283ab6734e7SLydia Wang }; 3284ab6734e7SLydia Wang #endif 3285ab6734e7SLydia Wang 32863e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 32873e95b9abSLydia Wang { 32883e95b9abSLydia Wang struct via_spec *spec = codec->spec; 32893e95b9abSLydia Wang int imux_is_smixer = 32903e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 32913e95b9abSLydia Wang unsigned int parm; 32923e95b9abSLydia Wang unsigned int present; 32933e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 32943e95b9abSLydia Wang imux_is_smixer = 32953e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 32963e95b9abSLydia Wang /* inputs */ 32973e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 32983e95b9abSLydia Wang parm = AC_PWRST_D3; 32993e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 33003e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 33013e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 33023e95b9abSLydia Wang parm = AC_PWRST_D0; 33033e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 33043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 33053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 33063e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 33083e95b9abSLydia Wang 33093e95b9abSLydia Wang /* outputs */ 33103e95b9abSLydia Wang /* AOW0 (8h)*/ 33113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 33123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33133e95b9abSLydia Wang 33143e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 33153e95b9abSLydia Wang parm = AC_PWRST_D3; 33163e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 33173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 33183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 33193e95b9abSLydia Wang 33203e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 33213e95b9abSLydia Wang parm = AC_PWRST_D3; 33223e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 33233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 33243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 33253e95b9abSLydia Wang if (spec->hp_independent_mode) 33263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 33273e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33283e95b9abSLydia Wang 33293e95b9abSLydia Wang /* Internal Speaker */ 33303e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 33313e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 33323e95b9abSLydia Wang 33333e95b9abSLydia Wang parm = AC_PWRST_D3; 33343e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 33353e95b9abSLydia Wang if (present) { 33363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33373e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33403e95b9abSLydia Wang } else { 33413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33453e95b9abSLydia Wang } 33463e95b9abSLydia Wang 33473e95b9abSLydia Wang 33483e95b9abSLydia Wang /* Mono Out */ 33493e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 33503e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 33513e95b9abSLydia Wang 33523e95b9abSLydia Wang parm = AC_PWRST_D3; 33533e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 33543e95b9abSLydia Wang if (present) { 33553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33563e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33583e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33603e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33613e95b9abSLydia Wang } else { 33623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33653e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33673e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33683e95b9abSLydia Wang } 33693e95b9abSLydia Wang 33703e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 33713e95b9abSLydia Wang parm = AC_PWRST_D3; 33723e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 33733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 33743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 33753e95b9abSLydia Wang 33763e95b9abSLydia Wang } 3377ab6734e7SLydia Wang 3378ab6734e7SLydia Wang /* patch for vt1812 */ 3379ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 3380ab6734e7SLydia Wang { 3381ab6734e7SLydia Wang struct via_spec *spec; 3382ab6734e7SLydia Wang int err; 3383ab6734e7SLydia Wang 3384ab6734e7SLydia Wang /* create a codec specific record */ 33855b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3386ab6734e7SLydia Wang if (spec == NULL) 3387ab6734e7SLydia Wang return -ENOMEM; 3388ab6734e7SLydia Wang 3389620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3390d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3391d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3392620e2b28STakashi Iwai 3393ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 339412daef65STakashi Iwai err = via_parse_auto_config(codec); 3395ab6734e7SLydia Wang if (err < 0) { 3396ab6734e7SLydia Wang via_free(codec); 3397ab6734e7SLydia Wang return err; 3398ab6734e7SLydia Wang } 3399ab6734e7SLydia Wang 3400096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 3401ab6734e7SLydia Wang 3402ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 3403ab6734e7SLydia Wang 3404ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3405ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 3406ab6734e7SLydia Wang #endif 3407ab6734e7SLydia Wang 34083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 3409ab6734e7SLydia Wang return 0; 3410ab6734e7SLydia Wang } 3411ab6734e7SLydia Wang 3412c577b8a1SJoseph Chan /* 3413c577b8a1SJoseph Chan * patch entries 3414c577b8a1SJoseph Chan */ 341590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 34163218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 34173218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 34183218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 34193218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 34203218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3421f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34223218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3423f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34243218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3425f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34263218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3427f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34283218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3429f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34303218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3431f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34323218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3433f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34343218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3435f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34363218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3437f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34383218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3439f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34403218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3441f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34423218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3443f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34443218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3445f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34463218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3447f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34483218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3449f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34503218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3451f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34523218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3453d949cac1SHarald Welte .patch = patch_vt1708S}, 34543218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3455d949cac1SHarald Welte .patch = patch_vt1708S}, 34563218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3457d949cac1SHarald Welte .patch = patch_vt1708S}, 34583218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3459d949cac1SHarald Welte .patch = patch_vt1708S}, 3460bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 3461d949cac1SHarald Welte .patch = patch_vt1708S}, 34623218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3463d949cac1SHarald Welte .patch = patch_vt1708S}, 34643218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3465d949cac1SHarald Welte .patch = patch_vt1708S}, 34663218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3467d949cac1SHarald Welte .patch = patch_vt1708S}, 34683218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3469d949cac1SHarald Welte .patch = patch_vt1702}, 34703218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3471d949cac1SHarald Welte .patch = patch_vt1702}, 34723218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3473d949cac1SHarald Welte .patch = patch_vt1702}, 34743218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3475d949cac1SHarald Welte .patch = patch_vt1702}, 34763218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3477d949cac1SHarald Welte .patch = patch_vt1702}, 34783218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3479d949cac1SHarald Welte .patch = patch_vt1702}, 34803218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3481d949cac1SHarald Welte .patch = patch_vt1702}, 34823218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3483d949cac1SHarald Welte .patch = patch_vt1702}, 3484eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 3485eb7188caSLydia Wang .patch = patch_vt1718S}, 3486eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 3487eb7188caSLydia Wang .patch = patch_vt1718S}, 3488bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 3489bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3490bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 3491bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3492f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 3493f3db423dSLydia Wang .patch = patch_vt1716S}, 3494f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 3495f3db423dSLydia Wang .patch = patch_vt1716S}, 349625eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 349725eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 3498ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 349936dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 350036dd5c4aSLydia Wang .patch = patch_vt1708S}, 350111890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 350211890956SLydia Wang .patch = patch_vt2002P}, 350311890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 350411890956SLydia Wang .patch = patch_vt2002P}, 3505c577b8a1SJoseph Chan {} /* terminator */ 3506c577b8a1SJoseph Chan }; 35071289e9e8STakashi Iwai 35081289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 35091289e9e8STakashi Iwai 35101289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 35111289e9e8STakashi Iwai .preset = snd_hda_preset_via, 35121289e9e8STakashi Iwai .owner = THIS_MODULE, 35131289e9e8STakashi Iwai }; 35141289e9e8STakashi Iwai 35151289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 35161289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 35171289e9e8STakashi Iwai 35181289e9e8STakashi Iwai static int __init patch_via_init(void) 35191289e9e8STakashi Iwai { 35201289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 35211289e9e8STakashi Iwai } 35221289e9e8STakashi Iwai 35231289e9e8STakashi Iwai static void __exit patch_via_exit(void) 35241289e9e8STakashi Iwai { 35251289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 35261289e9e8STakashi Iwai } 35271289e9e8STakashi Iwai 35281289e9e8STakashi Iwai module_init(patch_via_init) 35291289e9e8STakashi Iwai module_exit(patch_via_exit) 3530