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; 168f3db423dSLydia Wang unsigned int dmic_enabled; 16924088a58STakashi Iwai unsigned int no_pin_power_ctl; 1701f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1711f2e99feSLydia Wang 172*e3d7a143STakashi Iwai /* smart51 setup */ 173*e3d7a143STakashi Iwai unsigned int smart51_nums; 174*e3d7a143STakashi Iwai hda_nid_t smart51_pins[2]; 175*e3d7a143STakashi Iwai int smart51_idxs[2]; 176*e3d7a143STakashi Iwai const char *smart51_labels[2]; 177*e3d7a143STakashi Iwai unsigned int smart51_enabled; 178*e3d7a143STakashi Iwai 1791f2e99feSLydia Wang /* work to check hp jack state */ 1801f2e99feSLydia Wang struct hda_codec *codec; 1811f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 182e06e5a29STakashi Iwai int vt1708_jack_detect; 1831f2e99feSLydia Wang int vt1708_hp_present; 1843e95b9abSLydia Wang 1853e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1863e95b9abSLydia Wang 1871f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1881f2e99feSLydia Wang struct hda_loopback_check loopback; 1891f2e99feSLydia Wang #endif 1901f2e99feSLydia Wang }; 1911f2e99feSLydia Wang 1920341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1935b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1945b0cb1d8SJaroslav Kysela { 1955b0cb1d8SJaroslav Kysela struct via_spec *spec; 1965b0cb1d8SJaroslav Kysela 1975b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1985b0cb1d8SJaroslav Kysela if (spec == NULL) 1995b0cb1d8SJaroslav Kysela return NULL; 2005b0cb1d8SJaroslav Kysela 2015b0cb1d8SJaroslav Kysela codec->spec = spec; 2025b0cb1d8SJaroslav Kysela spec->codec = codec; 2030341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 2040341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 2050341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 2060341ccd7SLydia Wang spec->codec_type = VT1708S; 2075b0cb1d8SJaroslav Kysela return spec; 2085b0cb1d8SJaroslav Kysela } 2095b0cb1d8SJaroslav Kysela 210744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 211d7426329SHarald Welte { 212744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 213d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 214d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 215d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 216d7426329SHarald Welte 217d7426329SHarald Welte /* get codec type */ 218d7426329SHarald Welte if (ven_id != 0x1106) 219d7426329SHarald Welte codec_type = UNKNOWN; 220d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 221d7426329SHarald Welte codec_type = VT1708; 222d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 223d7426329SHarald Welte codec_type = VT1709_10CH; 224d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 225d7426329SHarald Welte codec_type = VT1709_6CH; 226518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 227d7426329SHarald Welte codec_type = VT1708B_8CH; 228518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 229518bf3baSLydia Wang codec_type = VT1708BCE; 230518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 231d7426329SHarald Welte codec_type = VT1708B_4CH; 232d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 233d7426329SHarald Welte && (dev_id >> 12) < 8) 234d7426329SHarald Welte codec_type = VT1708S; 235d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 236d7426329SHarald Welte && (dev_id >> 12) < 8) 237d7426329SHarald Welte codec_type = VT1702; 238eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 239eb7188caSLydia Wang && (dev_id >> 12) < 8) 240eb7188caSLydia Wang codec_type = VT1718S; 241f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 242f3db423dSLydia Wang codec_type = VT1716S; 243bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 244bb3c6bfcSLydia Wang codec_type = VT1718S; 24525eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 24625eaba2fSLydia Wang codec_type = VT2002P; 247ab6734e7SLydia Wang else if (dev_id == 0x0448) 248ab6734e7SLydia Wang codec_type = VT1812; 24936dd5c4aSLydia Wang else if (dev_id == 0x0440) 25036dd5c4aSLydia Wang codec_type = VT1708S; 25111890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 25211890956SLydia Wang codec_type = VT1802; 253d7426329SHarald Welte else 254d7426329SHarald Welte codec_type = UNKNOWN; 255d7426329SHarald Welte return codec_type; 256d7426329SHarald Welte }; 257d7426329SHarald Welte 258ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 25969e52a80SHarald Welte #define VIA_HP_EVENT 0x01 26069e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 2614a918ffeSTakashi Iwai #define VIA_LINE_EVENT 0x03 26269e52a80SHarald Welte 263c577b8a1SJoseph Chan enum { 264c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 265c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 266f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 267c577b8a1SJoseph Chan }; 268c577b8a1SJoseph Chan 269f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 2701f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2711f2e99feSLydia Wang 2721f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2731f2e99feSLydia Wang { 2741f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2751f2e99feSLydia Wang return; 2761f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 277e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2781f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2791f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2801f2e99feSLydia Wang msecs_to_jiffies(100)); 2811f2e99feSLydia Wang } 2821f2e99feSLydia Wang 2831f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2841f2e99feSLydia Wang { 2851f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2861f2e99feSLydia Wang return; 2871f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2881f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2891f2e99feSLydia Wang return; 2901f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 291e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2925b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2931f2e99feSLydia Wang } 294f5271101SLydia Wang 2953e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2963e95b9abSLydia Wang { 2973e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2983e95b9abSLydia Wang if (spec->set_widgets_power_state) 2993e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3003e95b9abSLydia Wang } 30125eaba2fSLydia Wang 302f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 303f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 304f5271101SLydia Wang { 305f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 306f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 307f5271101SLydia Wang 3083e95b9abSLydia Wang set_widgets_power_state(codec); 309f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 3101f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3111f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3121f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3131f2e99feSLydia Wang else 3141f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3151f2e99feSLydia Wang } 316f5271101SLydia Wang return change; 317f5271101SLydia Wang } 318f5271101SLydia Wang 319f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 320f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 321f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 322f5271101SLydia Wang .name = NULL, \ 323f5271101SLydia Wang .index = 0, \ 324f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 325f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 326f5271101SLydia Wang .put = analog_input_switch_put, \ 327f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 328f5271101SLydia Wang 32990dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 330c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 331c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 332f5271101SLydia Wang ANALOG_INPUT_MUTE, 333c577b8a1SJoseph Chan }; 334c577b8a1SJoseph Chan 335ab6734e7SLydia Wang 336c577b8a1SJoseph Chan /* add dynamic controls */ 337291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 338291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 339291c9e33STakashi Iwai const char *name) 340c577b8a1SJoseph Chan { 341c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 342c577b8a1SJoseph Chan 343603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 344603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 345c577b8a1SJoseph Chan if (!knew) 346291c9e33STakashi Iwai return NULL; 347291c9e33STakashi Iwai *knew = *tmpl; 348291c9e33STakashi Iwai if (!name) 349291c9e33STakashi Iwai name = tmpl->name; 350291c9e33STakashi Iwai if (name) { 351c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 352c577b8a1SJoseph Chan if (!knew->name) 353291c9e33STakashi Iwai return NULL; 354291c9e33STakashi Iwai } 355291c9e33STakashi Iwai return knew; 356291c9e33STakashi Iwai } 357291c9e33STakashi Iwai 358291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 359291c9e33STakashi Iwai int idx, unsigned long val) 360291c9e33STakashi Iwai { 361291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 362291c9e33STakashi Iwai 363291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 364291c9e33STakashi Iwai if (!knew) 365c577b8a1SJoseph Chan return -ENOMEM; 366d7a99cceSTakashi Iwai knew->index = idx; 3674d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 3685e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 369c577b8a1SJoseph Chan knew->private_value = val; 370c577b8a1SJoseph Chan return 0; 371c577b8a1SJoseph Chan } 372c577b8a1SJoseph Chan 3737b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 3747b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 3757b315bb4STakashi Iwai 376291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 3775b0cb1d8SJaroslav Kysela 378603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 379603c4019STakashi Iwai { 380603c4019STakashi Iwai struct via_spec *spec = codec->spec; 381603c4019STakashi Iwai 382603c4019STakashi Iwai if (spec->kctls.list) { 383603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 384603c4019STakashi Iwai int i; 385603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 386603c4019STakashi Iwai kfree(kctl[i].name); 387603c4019STakashi Iwai } 388603c4019STakashi Iwai snd_array_free(&spec->kctls); 389603c4019STakashi Iwai } 390603c4019STakashi Iwai 391c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 3929510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 3937b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 394c577b8a1SJoseph Chan { 395c577b8a1SJoseph Chan char name[32]; 396c577b8a1SJoseph Chan int err; 397c577b8a1SJoseph Chan 398c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 3997b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 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 sprintf(name, "%s Playback Switch", ctlname); 4047b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 405c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 406c577b8a1SJoseph Chan if (err < 0) 407c577b8a1SJoseph Chan return err; 408c577b8a1SJoseph Chan return 0; 409c577b8a1SJoseph Chan } 410c577b8a1SJoseph Chan 4115d41762aSTakashi Iwai /* return the index of the given widget nid as the source of mux; 4125d41762aSTakashi Iwai * return -1 if not found; 4135d41762aSTakashi Iwai * if num_conns is non-NULL, set the total number of connections 4145d41762aSTakashi Iwai */ 4155d41762aSTakashi Iwai static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, 4165d41762aSTakashi Iwai hda_nid_t nid, int *num_conns) 417c577b8a1SJoseph Chan { 4185d41762aSTakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 4195d41762aSTakashi Iwai int i, nums; 4205d41762aSTakashi Iwai 4215d41762aSTakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 4225d41762aSTakashi Iwai if (num_conns) 4235d41762aSTakashi Iwai *num_conns = nums; 4245d41762aSTakashi Iwai for (i = 0; i < nums; i++) 4255d41762aSTakashi Iwai if (conn[i] == nid) 4265d41762aSTakashi Iwai return i; 4275d41762aSTakashi Iwai return -1; 4285d41762aSTakashi Iwai } 4295d41762aSTakashi Iwai 4305d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \ 4315d41762aSTakashi Iwai __get_connection_index(codec, mux, nid, NULL) 4325d41762aSTakashi Iwai 4335d41762aSTakashi Iwai /* unmute input amp and select the specificed source */ 4345d41762aSTakashi Iwai static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, 4355d41762aSTakashi Iwai hda_nid_t src, hda_nid_t mix) 4365d41762aSTakashi Iwai { 4375d41762aSTakashi Iwai int idx, num_conns; 4385d41762aSTakashi Iwai 4395d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, src, &num_conns); 4405d41762aSTakashi Iwai if (idx < 0) 4415d41762aSTakashi Iwai return; 4425d41762aSTakashi Iwai 4435d41762aSTakashi Iwai /* select the route explicitly when multiple connections exist */ 4445d41762aSTakashi Iwai if (num_conns > 1) 445d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 4465d41762aSTakashi Iwai AC_VERB_SET_CONNECT_SEL, idx); 4475d41762aSTakashi Iwai /* unmute if the input amp is present */ 4485d41762aSTakashi Iwai if (!(query_amp_caps(codec, nid, HDA_INPUT) & 4495d41762aSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))) 4505d41762aSTakashi Iwai return; 4515d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4525d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4535d41762aSTakashi Iwai 4545d41762aSTakashi Iwai /* unmute AA-path if present */ 4555d41762aSTakashi Iwai if (!mix) 4565d41762aSTakashi Iwai return; 4575d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, mix, NULL); 4585d41762aSTakashi Iwai if (idx >= 0) 4595d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, 4605d41762aSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 4615d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4625d41762aSTakashi Iwai } 4635d41762aSTakashi Iwai 4645d41762aSTakashi Iwai /* set the given pin as output */ 4655d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, 4665d41762aSTakashi Iwai int pin_type) 4675d41762aSTakashi Iwai { 4685d41762aSTakashi Iwai if (!pin) 4695d41762aSTakashi Iwai return; 4705d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 4715d41762aSTakashi Iwai pin_type); 4725d41762aSTakashi Iwai if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) 4735d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 474d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 475c577b8a1SJoseph Chan } 476c577b8a1SJoseph Chan 4775d41762aSTakashi Iwai static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, 4785d41762aSTakashi Iwai int pin_type, struct nid_path *path) 4795d41762aSTakashi Iwai { 4805d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 4815d41762aSTakashi Iwai unsigned int caps; 4825d41762aSTakashi Iwai hda_nid_t nid; 4835d41762aSTakashi Iwai int i; 4845d41762aSTakashi Iwai 4855d41762aSTakashi Iwai if (!pin) 4865d41762aSTakashi Iwai return; 4875d41762aSTakashi Iwai 4885d41762aSTakashi Iwai init_output_pin(codec, pin, pin_type); 4895d41762aSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_OUTPUT); 4905d41762aSTakashi Iwai if (caps & AC_AMPCAP_MUTE) { 4915d41762aSTakashi Iwai unsigned int val; 4925d41762aSTakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 4935d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4945d41762aSTakashi Iwai AMP_OUT_MUTE | val); 4955d41762aSTakashi Iwai } 4965d41762aSTakashi Iwai 4975d41762aSTakashi Iwai /* initialize the output path */ 4985d41762aSTakashi Iwai nid = pin; 4995d41762aSTakashi Iwai for (i = 0; i < path->depth; i++) { 5005d41762aSTakashi Iwai unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid); 5015d41762aSTakashi Iwai nid = path->path[i]; 5025d41762aSTakashi Iwai if (query_amp_caps(codec, nid, HDA_OUTPUT) & 5035d41762aSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) 5045d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, 5055d41762aSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 5065d41762aSTakashi Iwai AMP_OUT_UNMUTE); 5075d41762aSTakashi Iwai } 5085d41762aSTakashi Iwai } 5095d41762aSTakashi Iwai 510c577b8a1SJoseph Chan 511c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 512c577b8a1SJoseph Chan { 513c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 514c577b8a1SJoseph Chan int i; 515c577b8a1SJoseph Chan 516*e3d7a143STakashi Iwai for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) 5175d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.line_out_pins[i], 5185d41762aSTakashi Iwai PIN_OUT, &spec->out_path[i]); 519c577b8a1SJoseph Chan } 520c577b8a1SJoseph Chan 521c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 522c577b8a1SJoseph Chan { 523c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 524c577b8a1SJoseph Chan 5255d41762aSTakashi Iwai if (spec->hp_dac_nid) 5265d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5275d41762aSTakashi Iwai &spec->hp_path); 5285d41762aSTakashi Iwai else 5295d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5305d41762aSTakashi Iwai &spec->hp_dep_path); 53125eaba2fSLydia Wang } 532c577b8a1SJoseph Chan 5334a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec) 5344a918ffeSTakashi Iwai { 5354a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 5364a918ffeSTakashi Iwai 5374a918ffeSTakashi Iwai if (spec->autocfg.speaker_outs) 5384a918ffeSTakashi Iwai via_auto_init_output(codec, spec->autocfg.speaker_pins[0], 5394a918ffeSTakashi Iwai PIN_OUT, &spec->speaker_path); 5404a918ffeSTakashi Iwai } 5414a918ffeSTakashi Iwai 542f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 54332e0191dSClemens Ladisch 544c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 545c577b8a1SJoseph Chan { 546c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5477b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 548096a8854STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 54932e0191dSClemens Ladisch unsigned int ctl; 550096a8854STakashi Iwai int i, num_conns; 551c577b8a1SJoseph Chan 552096a8854STakashi Iwai /* init ADCs */ 553096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 554096a8854STakashi Iwai snd_hda_codec_write(codec, spec->adc_nids[i], 0, 555096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 556096a8854STakashi Iwai AMP_IN_UNMUTE(0)); 557096a8854STakashi Iwai } 558096a8854STakashi Iwai 559096a8854STakashi Iwai /* init pins */ 5607b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5617b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 562f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 56332e0191dSClemens Ladisch ctl = PIN_OUT; 56430649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 56532e0191dSClemens Ladisch ctl = PIN_VREF50; 56632e0191dSClemens Ladisch else 56732e0191dSClemens Ladisch ctl = PIN_IN; 568c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 56932e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 570c577b8a1SJoseph Chan } 571096a8854STakashi Iwai 572096a8854STakashi Iwai /* init input-src */ 573096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 574096a8854STakashi Iwai const struct hda_input_mux *imux = spec->input_mux; 575096a8854STakashi Iwai if (!imux || !spec->mux_nids[i]) 576096a8854STakashi Iwai continue; 577096a8854STakashi Iwai snd_hda_codec_write(codec, spec->mux_nids[i], 0, 578096a8854STakashi Iwai AC_VERB_SET_CONNECT_SEL, 579096a8854STakashi Iwai imux->items[spec->cur_mux[i]].index); 580096a8854STakashi Iwai } 581096a8854STakashi Iwai 582096a8854STakashi Iwai /* init aa-mixer */ 583096a8854STakashi Iwai if (!spec->aa_mix_nid) 584096a8854STakashi Iwai return; 585096a8854STakashi Iwai num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 586096a8854STakashi Iwai ARRAY_SIZE(conn)); 587096a8854STakashi Iwai for (i = 0; i < num_conns; i++) { 588096a8854STakashi Iwai unsigned int caps = get_wcaps(codec, conn[i]); 589096a8854STakashi Iwai if (get_wcaps_type(caps) == AC_WID_PIN) 590096a8854STakashi Iwai snd_hda_codec_write(codec, spec->aa_mix_nid, 0, 591096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 592096a8854STakashi Iwai AMP_IN_MUTE(i)); 593096a8854STakashi Iwai } 594c577b8a1SJoseph Chan } 595f5271101SLydia Wang 596f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 597f5271101SLydia Wang unsigned int *affected_parm) 598f5271101SLydia Wang { 599f5271101SLydia Wang unsigned parm; 600f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 601f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 602f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 603f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 6041564b287SLydia Wang struct via_spec *spec = codec->spec; 60524088a58STakashi Iwai unsigned present = 0; 60624088a58STakashi Iwai 60724088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 60824088a58STakashi Iwai if (!no_presence) 60924088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 610f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 6111564b287SLydia Wang || ((no_presence || present) 6121564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 613f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 614f5271101SLydia Wang parm = AC_PWRST_D0; 615f5271101SLydia Wang } else 616f5271101SLydia Wang parm = AC_PWRST_D3; 617f5271101SLydia Wang 618f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 619f5271101SLydia Wang } 620f5271101SLydia Wang 62124088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 62224088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 62324088a58STakashi Iwai { 62424088a58STakashi Iwai static const char * const texts[] = { 62524088a58STakashi Iwai "Disabled", "Enabled" 62624088a58STakashi Iwai }; 62724088a58STakashi Iwai 62824088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 62924088a58STakashi Iwai uinfo->count = 1; 63024088a58STakashi Iwai uinfo->value.enumerated.items = 2; 63124088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 63224088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 63324088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 63424088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 63524088a58STakashi Iwai return 0; 63624088a58STakashi Iwai } 63724088a58STakashi Iwai 63824088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 63924088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 64024088a58STakashi Iwai { 64124088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 64224088a58STakashi Iwai struct via_spec *spec = codec->spec; 64324088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 64424088a58STakashi Iwai return 0; 64524088a58STakashi Iwai } 64624088a58STakashi Iwai 64724088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 64824088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 64924088a58STakashi Iwai { 65024088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 65124088a58STakashi Iwai struct via_spec *spec = codec->spec; 65224088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 65324088a58STakashi Iwai 65424088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 65524088a58STakashi Iwai return 0; 65624088a58STakashi Iwai spec->no_pin_power_ctl = val; 65724088a58STakashi Iwai set_widgets_power_state(codec); 65824088a58STakashi Iwai return 1; 65924088a58STakashi Iwai } 66024088a58STakashi Iwai 66124088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 66224088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 66324088a58STakashi Iwai .name = "Dynamic Power-Control", 66424088a58STakashi Iwai .info = via_pin_power_ctl_info, 66524088a58STakashi Iwai .get = via_pin_power_ctl_get, 66624088a58STakashi Iwai .put = via_pin_power_ctl_put, 66724088a58STakashi Iwai }; 66824088a58STakashi Iwai 66924088a58STakashi Iwai 670c577b8a1SJoseph Chan /* 671c577b8a1SJoseph Chan * input MUX handling 672c577b8a1SJoseph Chan */ 673c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 674c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 675c577b8a1SJoseph Chan { 676c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 677c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 678c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 679c577b8a1SJoseph Chan } 680c577b8a1SJoseph Chan 681c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 682c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 683c577b8a1SJoseph Chan { 684c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 685c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 686c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 687c577b8a1SJoseph Chan 688c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 689c577b8a1SJoseph Chan return 0; 690c577b8a1SJoseph Chan } 691c577b8a1SJoseph Chan 692c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 693c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 694c577b8a1SJoseph Chan { 695c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 696c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 697c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 698bff5fbf5SLydia Wang int ret; 699c577b8a1SJoseph Chan 700337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 701337b9d02STakashi Iwai return -EINVAL; 702a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 703a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 704a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 705a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 706a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 707bff5fbf5SLydia Wang 708bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 709bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 710bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 711a80e6e3cSLydia Wang /* update jack power state */ 7123e95b9abSLydia Wang set_widgets_power_state(codec); 713a80e6e3cSLydia Wang 714bff5fbf5SLydia Wang return ret; 715c577b8a1SJoseph Chan } 716c577b8a1SJoseph Chan 7170aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 7180aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 7190aa62aefSHarald Welte { 7200aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7210aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7220aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 7230aa62aefSHarald Welte } 7240aa62aefSHarald Welte 7250aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7260aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7270aa62aefSHarald Welte { 7280aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 729cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 730cdc1784dSLydia Wang 731ece8d043STakashi Iwai ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; 732cdc1784dSLydia Wang return 0; 733cdc1784dSLydia Wang } 734cdc1784dSLydia Wang 7350aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7360aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7370aa62aefSHarald Welte { 7380aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7390aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7405b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7410aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 742cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 743cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 744cdc1784dSLydia Wang ? 1 : 0; 745ece8d043STakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 7460aa62aefSHarald Welte 747ce0e5a9eSLydia Wang /* update jack power state */ 7483e95b9abSLydia Wang set_widgets_power_state(codec); 7490aa62aefSHarald Welte return 0; 7500aa62aefSHarald Welte } 7510aa62aefSHarald Welte 752ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = { 7530aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7540aa62aefSHarald Welte .name = "Independent HP", 7550aa62aefSHarald Welte .info = via_independent_hp_info, 7560aa62aefSHarald Welte .get = via_independent_hp_get, 7570aa62aefSHarald Welte .put = via_independent_hp_put, 7580aa62aefSHarald Welte }; 7590aa62aefSHarald Welte 7603d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7615b0cb1d8SJaroslav Kysela { 7623d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7635b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7645b0cb1d8SJaroslav Kysela hda_nid_t nid; 7655b0cb1d8SJaroslav Kysela 7665b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 767ece8d043STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer); 7683d83e577STakashi Iwai if (knew == NULL) 7693d83e577STakashi Iwai return -ENOMEM; 7703d83e577STakashi Iwai 7715b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 7725b0cb1d8SJaroslav Kysela knew->private_value = nid; 7735b0cb1d8SJaroslav Kysela 7745b0cb1d8SJaroslav Kysela return 0; 7755b0cb1d8SJaroslav Kysela } 7765b0cb1d8SJaroslav Kysela 7771564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 7781564b287SLydia Wang { 779*e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 7801564b287SLydia Wang int i; 7811564b287SLydia Wang 782*e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 783*e3d7a143STakashi Iwai struct snd_kcontrol *ctl; 784*e3d7a143STakashi Iwai struct snd_ctl_elem_id id; 7851564b287SLydia Wang memset(&id, 0, sizeof(id)); 7861564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 787*e3d7a143STakashi Iwai sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); 788525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 789525566cbSLydia Wang if (ctl) 790525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 791525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 792525566cbSLydia Wang &ctl->id); 7931564b287SLydia Wang } 7941564b287SLydia Wang } 7951564b287SLydia Wang 7961564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 7971564b287SLydia Wang { 7981564b287SLydia Wang struct via_spec *spec = codec->spec; 7991564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 800*e3d7a143STakashi Iwai int i; 801*e3d7a143STakashi Iwai 802*e3d7a143STakashi Iwai /* check AA path's mute status */ 803*e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 804*e3d7a143STakashi Iwai if (spec->smart51_idxs[i] < 0) 805*e3d7a143STakashi Iwai continue; 806*e3d7a143STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, 807*e3d7a143STakashi Iwai HDA_INPUT, spec->smart51_idxs[i], 8081564b287SLydia Wang HDA_AMP_MUTE, val); 8091564b287SLydia Wang } 8101564b287SLydia Wang } 811f4a7828bSTakashi Iwai 812*e3d7a143STakashi Iwai static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin) 8131564b287SLydia Wang { 814f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 8157b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 8167b315bb4STakashi Iwai int i; 8177b315bb4STakashi Iwai 8187b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 819f4a7828bSTakashi Iwai unsigned int defcfg; 820f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 821f4a7828bSTakashi Iwai continue; 822f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 823f4a7828bSTakashi Iwai return false; 824f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 825f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 826f4a7828bSTakashi Iwai return false; 827f4a7828bSTakashi Iwai return true; 8281564b287SLydia Wang } 829f4a7828bSTakashi Iwai return false; 8301564b287SLydia Wang } 8311564b287SLydia Wang 832*e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 833*e3d7a143STakashi Iwai { 834*e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 835*e3d7a143STakashi Iwai int i; 836*e3d7a143STakashi Iwai 837*e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) 838*e3d7a143STakashi Iwai if (spec->smart51_pins[i] == pin) 839*e3d7a143STakashi Iwai return true; 840*e3d7a143STakashi Iwai return false; 841*e3d7a143STakashi Iwai } 842*e3d7a143STakashi Iwai 8431564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 8441564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 8451564b287SLydia Wang { 8461564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 8471564b287SLydia Wang uinfo->count = 1; 8481564b287SLydia Wang uinfo->value.integer.min = 0; 8491564b287SLydia Wang uinfo->value.integer.max = 1; 8501564b287SLydia Wang return 0; 8511564b287SLydia Wang } 8521564b287SLydia Wang 8531564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 8541564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8551564b287SLydia Wang { 8561564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8571564b287SLydia Wang struct via_spec *spec = codec->spec; 8581564b287SLydia Wang int on = 1; 8591564b287SLydia Wang int i; 8601564b287SLydia Wang 861*e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 862*e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 863f4a7828bSTakashi Iwai unsigned int ctl; 864f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 865f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8667b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 8671564b287SLydia Wang on = 0; 8681564b287SLydia Wang } 8691564b287SLydia Wang *ucontrol->value.integer.value = on; 8701564b287SLydia Wang return 0; 8711564b287SLydia Wang } 8721564b287SLydia Wang 8731564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 8741564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8751564b287SLydia Wang { 8761564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8771564b287SLydia Wang struct via_spec *spec = codec->spec; 8781564b287SLydia Wang int out_in = *ucontrol->value.integer.value 8791564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 8801564b287SLydia Wang int i; 8811564b287SLydia Wang 882*e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 883*e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 8847b315bb4STakashi Iwai unsigned int parm; 8857b315bb4STakashi Iwai 8867b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 8871564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8881564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 8891564b287SLydia Wang parm |= out_in; 8901564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 8911564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 8921564b287SLydia Wang parm); 8931564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 8941564b287SLydia Wang mute_aa_path(codec, 1); 8951564b287SLydia Wang notify_aa_path_ctls(codec); 8961564b287SLydia Wang } 8971564b287SLydia Wang } 8981564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 8993e95b9abSLydia Wang set_widgets_power_state(codec); 9001564b287SLydia Wang return 1; 9011564b287SLydia Wang } 9021564b287SLydia Wang 9035f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 9041564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9051564b287SLydia Wang .name = "Smart 5.1", 9061564b287SLydia Wang .count = 1, 9071564b287SLydia Wang .info = via_smart51_info, 9081564b287SLydia Wang .get = via_smart51_get, 9091564b287SLydia Wang .put = via_smart51_put, 9101564b287SLydia Wang }; 9111564b287SLydia Wang 912f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 9135b0cb1d8SJaroslav Kysela { 914f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9155b0cb1d8SJaroslav Kysela 916*e3d7a143STakashi Iwai if (!spec->smart51_nums) 917cb34c207SLydia Wang return 0; 918*e3d7a143STakashi Iwai if (!via_clone_control(spec, &via_smart51_mixer)) 9195b0cb1d8SJaroslav Kysela return -ENOMEM; 9205b0cb1d8SJaroslav Kysela return 0; 9215b0cb1d8SJaroslav Kysela } 9225b0cb1d8SJaroslav Kysela 923f5271101SLydia Wang /* check AA path's mute statue */ 924f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 925f5271101SLydia Wang { 926f5271101SLydia Wang int mute = 1; 927f5271101SLydia Wang int start_idx; 928f5271101SLydia Wang int end_idx; 929f5271101SLydia Wang int i; 930f5271101SLydia Wang struct via_spec *spec = codec->spec; 931f5271101SLydia Wang /* get nid of MW0 and start & end index */ 932f5271101SLydia Wang switch (spec->codec_type) { 933f5271101SLydia Wang case VT1708B_8CH: 934f5271101SLydia Wang case VT1708B_4CH: 935f5271101SLydia Wang case VT1708S: 936f3db423dSLydia Wang case VT1716S: 937f5271101SLydia Wang start_idx = 2; 938f5271101SLydia Wang end_idx = 4; 939f5271101SLydia Wang break; 940f5271101SLydia Wang case VT1702: 941f5271101SLydia Wang start_idx = 1; 942f5271101SLydia Wang end_idx = 3; 943f5271101SLydia Wang break; 944eb7188caSLydia Wang case VT1718S: 945eb7188caSLydia Wang start_idx = 1; 946eb7188caSLydia Wang end_idx = 3; 947eb7188caSLydia Wang break; 94825eaba2fSLydia Wang case VT2002P: 949ab6734e7SLydia Wang case VT1812: 95011890956SLydia Wang case VT1802: 95125eaba2fSLydia Wang start_idx = 0; 95225eaba2fSLydia Wang end_idx = 2; 95325eaba2fSLydia Wang break; 954f5271101SLydia Wang default: 955f5271101SLydia Wang return 0; 956f5271101SLydia Wang } 957f5271101SLydia Wang /* check AA path's mute status */ 958f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 959f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 960620e2b28STakashi Iwai codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 961f5271101SLydia Wang int shift = 8 * (i % 4); 962f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 963f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 964f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 965f5271101SLydia Wang /* check mute status while the pin is connected */ 966620e2b28STakashi Iwai int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0, 967f5271101SLydia Wang HDA_INPUT, i) >> 7; 968620e2b28STakashi Iwai int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1, 969f5271101SLydia Wang HDA_INPUT, i) >> 7; 970f5271101SLydia Wang if (!mute_l || !mute_r) { 971f5271101SLydia Wang mute = 0; 972f5271101SLydia Wang break; 973f5271101SLydia Wang } 974f5271101SLydia Wang } 975f5271101SLydia Wang } 976f5271101SLydia Wang return mute; 977f5271101SLydia Wang } 978f5271101SLydia Wang 979f5271101SLydia Wang /* enter/exit analog low-current mode */ 980f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 981f5271101SLydia Wang { 982f5271101SLydia Wang struct via_spec *spec = codec->spec; 983f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 984f5271101SLydia Wang int enable = is_aa_path_mute(codec); 985f5271101SLydia Wang unsigned int verb = 0; 986f5271101SLydia Wang unsigned int parm = 0; 987f5271101SLydia Wang 988f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 989f5271101SLydia Wang enable = enable && saved_stream_idle; 990f5271101SLydia Wang else { 991f5271101SLydia Wang enable = enable && stream_idle; 992f5271101SLydia Wang saved_stream_idle = stream_idle; 993f5271101SLydia Wang } 994f5271101SLydia Wang 995f5271101SLydia Wang /* decide low current mode's verb & parameter */ 996f5271101SLydia Wang switch (spec->codec_type) { 997f5271101SLydia Wang case VT1708B_8CH: 998f5271101SLydia Wang case VT1708B_4CH: 999f5271101SLydia Wang verb = 0xf70; 1000f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1001f5271101SLydia Wang break; 1002f5271101SLydia Wang case VT1708S: 1003eb7188caSLydia Wang case VT1718S: 1004f3db423dSLydia Wang case VT1716S: 1005f5271101SLydia Wang verb = 0xf73; 1006f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1007f5271101SLydia Wang break; 1008f5271101SLydia Wang case VT1702: 1009f5271101SLydia Wang verb = 0xf73; 1010f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1011f5271101SLydia Wang break; 101225eaba2fSLydia Wang case VT2002P: 1013ab6734e7SLydia Wang case VT1812: 101411890956SLydia Wang case VT1802: 101525eaba2fSLydia Wang verb = 0xf93; 101625eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 101725eaba2fSLydia Wang break; 1018f5271101SLydia Wang default: 1019f5271101SLydia Wang return; /* other codecs are not supported */ 1020f5271101SLydia Wang } 1021f5271101SLydia Wang /* send verb */ 1022f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1023f5271101SLydia Wang } 1024f5271101SLydia Wang 1025c577b8a1SJoseph Chan /* 1026c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1027c577b8a1SJoseph Chan */ 1028096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 1029aa266fccSLydia Wang /* power down jack detect function */ 1030aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 1031f7278fd0SJosepch Chan { } 1032c577b8a1SJoseph Chan }; 1033c577b8a1SJoseph Chan 10347eb56e84STakashi Iwai static void substream_set_idle(struct hda_codec *codec, 10357eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10367eb56e84STakashi Iwai { 10377eb56e84STakashi Iwai int idle = substream->pstr->substream_opened == 1 10387eb56e84STakashi Iwai && substream->ref_count == 0; 10397eb56e84STakashi Iwai analog_low_current_mode(codec, idle); 10407eb56e84STakashi Iwai } 10417eb56e84STakashi Iwai 1042ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, 1043c577b8a1SJoseph Chan struct hda_codec *codec, 1044c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1045c577b8a1SJoseph Chan { 1046c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1047ece8d043STakashi Iwai 1048ece8d043STakashi Iwai if (!spec->hp_independent_mode) 1049ece8d043STakashi Iwai spec->multiout.hp_nid = spec->hp_dac_nid; 10507eb56e84STakashi Iwai substream_set_idle(codec, substream); 10519a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 10529a08160bSTakashi Iwai hinfo); 1053c577b8a1SJoseph Chan } 1054c577b8a1SJoseph Chan 1055ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, 10569af74210STakashi Iwai struct hda_codec *codec, 10579af74210STakashi Iwai struct snd_pcm_substream *substream) 10589af74210STakashi Iwai { 1059ece8d043STakashi Iwai struct via_spec *spec = codec->spec; 1060ece8d043STakashi Iwai 1061ece8d043STakashi Iwai spec->multiout.hp_nid = 0; 10627eb56e84STakashi Iwai substream_set_idle(codec, substream); 10639af74210STakashi Iwai return 0; 10649af74210STakashi Iwai } 10659af74210STakashi Iwai 10667eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 10677eb56e84STakashi Iwai struct hda_codec *codec, 10687eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10697eb56e84STakashi Iwai { 10707eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 10717eb56e84STakashi Iwai 1072ece8d043STakashi Iwai if (snd_BUG_ON(!spec->hp_dac_nid)) 10737eb56e84STakashi Iwai return -EINVAL; 1074ece8d043STakashi Iwai if (!spec->hp_independent_mode || spec->multiout.hp_nid) 1075ece8d043STakashi Iwai return -EBUSY; 1076ece8d043STakashi Iwai substream_set_idle(codec, substream); 1077ece8d043STakashi Iwai return 0; 1078ece8d043STakashi Iwai } 1079ece8d043STakashi Iwai 1080ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, 1081ece8d043STakashi Iwai struct hda_codec *codec, 1082ece8d043STakashi Iwai struct snd_pcm_substream *substream) 1083ece8d043STakashi Iwai { 10847eb56e84STakashi Iwai substream_set_idle(codec, substream); 10857eb56e84STakashi Iwai return 0; 10867eb56e84STakashi Iwai } 10877eb56e84STakashi Iwai 10887eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 10897eb56e84STakashi Iwai struct hda_codec *codec, 10900aa62aefSHarald Welte unsigned int stream_tag, 10910aa62aefSHarald Welte unsigned int format, 10920aa62aefSHarald Welte struct snd_pcm_substream *substream) 10930aa62aefSHarald Welte { 10940aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10950aa62aefSHarald Welte 1096ece8d043STakashi Iwai snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1097ece8d043STakashi Iwai format, substream); 10987eb56e84STakashi Iwai vt1708_start_hp_work(spec); 10997eb56e84STakashi Iwai return 0; 11000aa62aefSHarald Welte } 11010aa62aefSHarald Welte 11027eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 11030aa62aefSHarald Welte struct hda_codec *codec, 11040aa62aefSHarald Welte unsigned int stream_tag, 11050aa62aefSHarald Welte unsigned int format, 11060aa62aefSHarald Welte struct snd_pcm_substream *substream) 11070aa62aefSHarald Welte { 11080aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11090aa62aefSHarald Welte 1110ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1111ece8d043STakashi Iwai stream_tag, 0, format); 11121f2e99feSLydia Wang vt1708_start_hp_work(spec); 11130aa62aefSHarald Welte return 0; 11140aa62aefSHarald Welte } 11150aa62aefSHarald Welte 11160aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 11170aa62aefSHarald Welte struct hda_codec *codec, 11180aa62aefSHarald Welte struct snd_pcm_substream *substream) 11190aa62aefSHarald Welte { 11200aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11210aa62aefSHarald Welte 1122ece8d043STakashi Iwai snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 11237eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 11247eb56e84STakashi Iwai return 0; 11250aa62aefSHarald Welte } 11267eb56e84STakashi Iwai 11277eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 11287eb56e84STakashi Iwai struct hda_codec *codec, 11297eb56e84STakashi Iwai struct snd_pcm_substream *substream) 11307eb56e84STakashi Iwai { 11317eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 11327eb56e84STakashi Iwai 1133ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 11341f2e99feSLydia Wang vt1708_stop_hp_work(spec); 11350aa62aefSHarald Welte return 0; 11360aa62aefSHarald Welte } 11370aa62aefSHarald Welte 1138c577b8a1SJoseph Chan /* 1139c577b8a1SJoseph Chan * Digital out 1140c577b8a1SJoseph Chan */ 1141c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1142c577b8a1SJoseph Chan struct hda_codec *codec, 1143c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1144c577b8a1SJoseph Chan { 1145c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1146c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1147c577b8a1SJoseph Chan } 1148c577b8a1SJoseph Chan 1149c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1150c577b8a1SJoseph Chan struct hda_codec *codec, 1151c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1152c577b8a1SJoseph Chan { 1153c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1154c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1155c577b8a1SJoseph Chan } 1156c577b8a1SJoseph Chan 11575691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 115898aa34c0SHarald Welte struct hda_codec *codec, 115998aa34c0SHarald Welte unsigned int stream_tag, 116098aa34c0SHarald Welte unsigned int format, 116198aa34c0SHarald Welte struct snd_pcm_substream *substream) 116298aa34c0SHarald Welte { 116398aa34c0SHarald Welte struct via_spec *spec = codec->spec; 11649da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 11659da29271STakashi Iwai stream_tag, format, substream); 11669da29271STakashi Iwai } 11675691ec7fSHarald Welte 11689da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 11699da29271STakashi Iwai struct hda_codec *codec, 11709da29271STakashi Iwai struct snd_pcm_substream *substream) 11719da29271STakashi Iwai { 11729da29271STakashi Iwai struct via_spec *spec = codec->spec; 11739da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 117498aa34c0SHarald Welte return 0; 117598aa34c0SHarald Welte } 117698aa34c0SHarald Welte 1177c577b8a1SJoseph Chan /* 1178c577b8a1SJoseph Chan * Analog capture 1179c577b8a1SJoseph Chan */ 1180c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1181c577b8a1SJoseph Chan struct hda_codec *codec, 1182c577b8a1SJoseph Chan unsigned int stream_tag, 1183c577b8a1SJoseph Chan unsigned int format, 1184c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1185c577b8a1SJoseph Chan { 1186c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1187c577b8a1SJoseph Chan 1188c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1189c577b8a1SJoseph Chan stream_tag, 0, format); 1190c577b8a1SJoseph Chan return 0; 1191c577b8a1SJoseph Chan } 1192c577b8a1SJoseph Chan 1193c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1194c577b8a1SJoseph Chan struct hda_codec *codec, 1195c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1196c577b8a1SJoseph Chan { 1197c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1198888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1199c577b8a1SJoseph Chan return 0; 1200c577b8a1SJoseph Chan } 1201c577b8a1SJoseph Chan 12029af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 12037eb56e84STakashi Iwai .substreams = 1, 1204c577b8a1SJoseph Chan .channels_min = 2, 1205c577b8a1SJoseph Chan .channels_max = 8, 12069af74210STakashi Iwai /* NID is set in via_build_pcms */ 1207c577b8a1SJoseph Chan .ops = { 1208ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1209ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 12100aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 12110aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1212c577b8a1SJoseph Chan }, 1213c577b8a1SJoseph Chan }; 1214c577b8a1SJoseph Chan 12157eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 12167eb56e84STakashi Iwai .substreams = 1, 12177eb56e84STakashi Iwai .channels_min = 2, 12187eb56e84STakashi Iwai .channels_max = 2, 12197eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 12207eb56e84STakashi Iwai .ops = { 12217eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1222ece8d043STakashi Iwai .close = via_playback_hp_pcm_close, 12237eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 12247eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 12257eb56e84STakashi Iwai }, 12267eb56e84STakashi Iwai }; 12277eb56e84STakashi Iwai 122890dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 12297eb56e84STakashi Iwai .substreams = 1, 1230bc9b5623STakashi Iwai .channels_min = 2, 1231bc9b5623STakashi Iwai .channels_max = 8, 12329af74210STakashi Iwai /* NID is set in via_build_pcms */ 1233bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1234bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1235bc9b5623STakashi Iwai * disable the 24bit format, so far. 1236bc9b5623STakashi Iwai */ 1237bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1238bc9b5623STakashi Iwai .ops = { 1239ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1240ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 1241c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1242c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1243bc9b5623STakashi Iwai }, 1244bc9b5623STakashi Iwai }; 1245bc9b5623STakashi Iwai 12469af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 12477eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1248c577b8a1SJoseph Chan .channels_min = 2, 1249c577b8a1SJoseph Chan .channels_max = 2, 12509af74210STakashi Iwai /* NID is set in via_build_pcms */ 1251c577b8a1SJoseph Chan .ops = { 1252c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1253c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1254c577b8a1SJoseph Chan }, 1255c577b8a1SJoseph Chan }; 1256c577b8a1SJoseph Chan 12579af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1258c577b8a1SJoseph Chan .substreams = 1, 1259c577b8a1SJoseph Chan .channels_min = 2, 1260c577b8a1SJoseph Chan .channels_max = 2, 1261c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1262c577b8a1SJoseph Chan .ops = { 1263c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 12646b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 12659da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 12669da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1267c577b8a1SJoseph Chan }, 1268c577b8a1SJoseph Chan }; 1269c577b8a1SJoseph Chan 12709af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1271c577b8a1SJoseph Chan .substreams = 1, 1272c577b8a1SJoseph Chan .channels_min = 2, 1273c577b8a1SJoseph Chan .channels_max = 2, 1274c577b8a1SJoseph Chan }; 1275c577b8a1SJoseph Chan 1276370bafbdSTakashi Iwai /* 1277370bafbdSTakashi Iwai * slave controls for virtual master 1278370bafbdSTakashi Iwai */ 1279370bafbdSTakashi Iwai static const char * const via_slave_vols[] = { 1280370bafbdSTakashi Iwai "Front Playback Volume", 1281370bafbdSTakashi Iwai "Surround Playback Volume", 1282370bafbdSTakashi Iwai "Center Playback Volume", 1283370bafbdSTakashi Iwai "LFE Playback Volume", 1284370bafbdSTakashi Iwai "Side Playback Volume", 1285370bafbdSTakashi Iwai "Headphone Playback Volume", 1286370bafbdSTakashi Iwai "Speaker Playback Volume", 1287370bafbdSTakashi Iwai NULL, 1288370bafbdSTakashi Iwai }; 1289370bafbdSTakashi Iwai 1290370bafbdSTakashi Iwai static const char * const via_slave_sws[] = { 1291370bafbdSTakashi Iwai "Front Playback Switch", 1292370bafbdSTakashi Iwai "Surround Playback Switch", 1293370bafbdSTakashi Iwai "Center Playback Switch", 1294370bafbdSTakashi Iwai "LFE Playback Switch", 1295370bafbdSTakashi Iwai "Side Playback Switch", 1296370bafbdSTakashi Iwai "Headphone Playback Switch", 1297370bafbdSTakashi Iwai "Speaker Playback Switch", 1298370bafbdSTakashi Iwai NULL, 1299370bafbdSTakashi Iwai }; 1300370bafbdSTakashi Iwai 1301c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1302c577b8a1SJoseph Chan { 1303c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 13045b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 130590dd48a1STakashi Iwai const struct snd_kcontrol_new *knew; 13065b0cb1d8SJaroslav Kysela int err, i; 1307c577b8a1SJoseph Chan 130824088a58STakashi Iwai if (spec->set_widgets_power_state) 130924088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 131024088a58STakashi Iwai return -ENOMEM; 131124088a58STakashi Iwai 1312c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1313c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1314c577b8a1SJoseph Chan if (err < 0) 1315c577b8a1SJoseph Chan return err; 1316c577b8a1SJoseph Chan } 1317c577b8a1SJoseph Chan 1318c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1319c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 132074b654c9SStephen Warren spec->multiout.dig_out_nid, 1321c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1322c577b8a1SJoseph Chan if (err < 0) 1323c577b8a1SJoseph Chan return err; 13249a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 13259a08160bSTakashi Iwai &spec->multiout); 13269a08160bSTakashi Iwai if (err < 0) 13279a08160bSTakashi Iwai return err; 13289a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1329c577b8a1SJoseph Chan } 1330c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1331c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1332c577b8a1SJoseph Chan if (err < 0) 1333c577b8a1SJoseph Chan return err; 1334c577b8a1SJoseph Chan } 133517314379SLydia Wang 1336370bafbdSTakashi Iwai /* if we have no master control, let's create it */ 1337370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 1338370bafbdSTakashi Iwai unsigned int vmaster_tlv[4]; 1339370bafbdSTakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], 1340370bafbdSTakashi Iwai HDA_OUTPUT, vmaster_tlv); 1341370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 1342370bafbdSTakashi Iwai vmaster_tlv, via_slave_vols); 1343370bafbdSTakashi Iwai if (err < 0) 1344370bafbdSTakashi Iwai return err; 1345370bafbdSTakashi Iwai } 1346370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 1347370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Switch", 1348370bafbdSTakashi Iwai NULL, via_slave_sws); 1349370bafbdSTakashi Iwai if (err < 0) 1350370bafbdSTakashi Iwai return err; 1351370bafbdSTakashi Iwai } 1352370bafbdSTakashi Iwai 13535b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 13545b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 13555b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 135621949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 13575b0cb1d8SJaroslav Kysela if (err < 0) 13585b0cb1d8SJaroslav Kysela return err; 13595b0cb1d8SJaroslav Kysela } 13605b0cb1d8SJaroslav Kysela 13615b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 13625b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 13635b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 13645b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 13655b0cb1d8SJaroslav Kysela continue; 13665b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 13675b0cb1d8SJaroslav Kysela if (kctl == NULL) 13685b0cb1d8SJaroslav Kysela continue; 13695b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 13705b0cb1d8SJaroslav Kysela knew->subdevice); 13715b0cb1d8SJaroslav Kysela } 13725b0cb1d8SJaroslav Kysela } 13735b0cb1d8SJaroslav Kysela 137417314379SLydia Wang /* init power states */ 13753e95b9abSLydia Wang set_widgets_power_state(codec); 137617314379SLydia Wang analog_low_current_mode(codec, 1); 137717314379SLydia Wang 1378603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1379c577b8a1SJoseph Chan return 0; 1380c577b8a1SJoseph Chan } 1381c577b8a1SJoseph Chan 1382c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1383c577b8a1SJoseph Chan { 1384c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1385c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1386c577b8a1SJoseph Chan 1387c577b8a1SJoseph Chan codec->num_pcms = 1; 1388c577b8a1SJoseph Chan codec->pcm_info = info; 1389c577b8a1SJoseph Chan 139082673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 139182673bc8STakashi Iwai "%s Analog", codec->chip_name); 1392c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 13939af74210STakashi Iwai 13949af74210STakashi Iwai if (!spec->stream_analog_playback) 13959af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1396377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 13979af74210STakashi Iwai *spec->stream_analog_playback; 1398377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1399377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1400c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1401c577b8a1SJoseph Chan spec->multiout.max_channels; 14029af74210STakashi Iwai 14039af74210STakashi Iwai if (!spec->stream_analog_capture) 14049af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 14059af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14069af74210STakashi Iwai *spec->stream_analog_capture; 14079af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 14089af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 14099af74210STakashi Iwai spec->num_adc_nids; 1410c577b8a1SJoseph Chan 1411c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1412c577b8a1SJoseph Chan codec->num_pcms++; 1413c577b8a1SJoseph Chan info++; 141482673bc8STakashi Iwai snprintf(spec->stream_name_digital, 141582673bc8STakashi Iwai sizeof(spec->stream_name_digital), 141682673bc8STakashi Iwai "%s Digital", codec->chip_name); 1417c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 14187ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1419c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 14209af74210STakashi Iwai if (!spec->stream_digital_playback) 14219af74210STakashi Iwai spec->stream_digital_playback = 14229af74210STakashi Iwai &via_pcm_digital_playback; 1423c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 14249af74210STakashi Iwai *spec->stream_digital_playback; 1425c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1426c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1427c577b8a1SJoseph Chan } 1428c577b8a1SJoseph Chan if (spec->dig_in_nid) { 14299af74210STakashi Iwai if (!spec->stream_digital_capture) 14309af74210STakashi Iwai spec->stream_digital_capture = 14319af74210STakashi Iwai &via_pcm_digital_capture; 1432c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14339af74210STakashi Iwai *spec->stream_digital_capture; 1434c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1435c577b8a1SJoseph Chan spec->dig_in_nid; 1436c577b8a1SJoseph Chan } 1437c577b8a1SJoseph Chan } 1438c577b8a1SJoseph Chan 1439ece8d043STakashi Iwai if (spec->hp_dac_nid) { 14407eb56e84STakashi Iwai codec->num_pcms++; 14417eb56e84STakashi Iwai info++; 14427eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 14437eb56e84STakashi Iwai "%s HP", codec->chip_name); 14447eb56e84STakashi Iwai info->name = spec->stream_name_hp; 14457eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 14467eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1447ece8d043STakashi Iwai spec->hp_dac_nid; 14487eb56e84STakashi Iwai } 1449c577b8a1SJoseph Chan return 0; 1450c577b8a1SJoseph Chan } 1451c577b8a1SJoseph Chan 1452c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1453c577b8a1SJoseph Chan { 1454c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1455c577b8a1SJoseph Chan 1456c577b8a1SJoseph Chan if (!spec) 1457c577b8a1SJoseph Chan return; 1458c577b8a1SJoseph Chan 1459603c4019STakashi Iwai via_free_kctls(codec); 14601f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1461c577b8a1SJoseph Chan kfree(codec->spec); 1462c577b8a1SJoseph Chan } 1463c577b8a1SJoseph Chan 146464be285bSTakashi Iwai /* mute/unmute outputs */ 146564be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 146664be285bSTakashi Iwai hda_nid_t *pins, bool mute) 146764be285bSTakashi Iwai { 146864be285bSTakashi Iwai int i; 146964be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 147064be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 147164be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 147264be285bSTakashi Iwai mute ? 0 : PIN_OUT); 147364be285bSTakashi Iwai } 147464be285bSTakashi Iwai 14754a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */ 14764a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present) 14774a918ffeSTakashi Iwai { 14784a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 14794a918ffeSTakashi Iwai 14804a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs) 14814a918ffeSTakashi Iwai return; 14824a918ffeSTakashi Iwai if (!present) 14834a918ffeSTakashi Iwai present = snd_hda_jack_detect(codec, 14844a918ffeSTakashi Iwai spec->autocfg.line_out_pins[0]); 14854a918ffeSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 14864a918ffeSTakashi Iwai spec->autocfg.speaker_pins, 14874a918ffeSTakashi Iwai present); 14884a918ffeSTakashi Iwai } 14894a918ffeSTakashi Iwai 149069e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 149169e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 149269e52a80SHarald Welte { 14934a918ffeSTakashi Iwai int present = 0; 149469e52a80SHarald Welte struct via_spec *spec = codec->spec; 149569e52a80SHarald Welte 14964a918ffeSTakashi Iwai if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { 1497d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 149864be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 149964be285bSTakashi Iwai spec->autocfg.line_out_pins, 150064be285bSTakashi Iwai present); 150169e52a80SHarald Welte } 15024a918ffeSTakashi Iwai via_line_automute(codec, present); 1503f3db423dSLydia Wang } 1504f3db423dSLydia Wang 150569e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 150669e52a80SHarald Welte { 150769e52a80SHarald Welte unsigned int gpio_data; 150869e52a80SHarald Welte unsigned int vol_counter; 150969e52a80SHarald Welte unsigned int vol; 151069e52a80SHarald Welte unsigned int master_vol; 151169e52a80SHarald Welte 151269e52a80SHarald Welte struct via_spec *spec = codec->spec; 151369e52a80SHarald Welte 151469e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 151569e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 151669e52a80SHarald Welte 151769e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 151869e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 151969e52a80SHarald Welte 152069e52a80SHarald Welte vol = vol_counter & 0x1F; 152169e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 152269e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 152369e52a80SHarald Welte AC_AMP_GET_INPUT); 152469e52a80SHarald Welte 152569e52a80SHarald Welte if (gpio_data == 0x02) { 152669e52a80SHarald Welte /* unmute line out */ 15273e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15283e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15293e0693e2STakashi Iwai PIN_OUT); 153069e52a80SHarald Welte if (vol_counter & 0x20) { 153169e52a80SHarald Welte /* decrease volume */ 153269e52a80SHarald Welte if (vol > master_vol) 153369e52a80SHarald Welte vol = master_vol; 153469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 153569e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 153669e52a80SHarald Welte master_vol-vol); 153769e52a80SHarald Welte } else { 153869e52a80SHarald Welte /* increase volume */ 153969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 154069e52a80SHarald Welte HDA_AMP_VOLMASK, 154169e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 154269e52a80SHarald Welte (master_vol+vol)); 154369e52a80SHarald Welte } 154469e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 154569e52a80SHarald Welte /* mute line out */ 15463e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15473e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15483e0693e2STakashi Iwai 0); 154969e52a80SHarald Welte } 155069e52a80SHarald Welte } 155169e52a80SHarald Welte 155269e52a80SHarald Welte /* unsolicited event for jack sensing */ 155369e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 155469e52a80SHarald Welte unsigned int res) 155569e52a80SHarald Welte { 155669e52a80SHarald Welte res >>= 26; 1557ec7e7e42SLydia Wang 1558a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 15593e95b9abSLydia Wang set_widgets_power_state(codec); 1560ec7e7e42SLydia Wang 1561ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1562ec7e7e42SLydia Wang 1563ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1564ec7e7e42SLydia Wang via_hp_automute(codec); 1565ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1566ec7e7e42SLydia Wang via_gpio_control(codec); 15674a918ffeSTakashi Iwai else if (res == VIA_LINE_EVENT) 15684a918ffeSTakashi Iwai via_line_automute(codec, false); 156969e52a80SHarald Welte } 157069e52a80SHarald Welte 15711f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 15721f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 15731f2e99feSLydia Wang { 15741f2e99feSLydia Wang struct via_spec *spec = codec->spec; 15751f2e99feSLydia Wang vt1708_stop_hp_work(spec); 15761f2e99feSLydia Wang return 0; 15771f2e99feSLydia Wang } 15781f2e99feSLydia Wang #endif 15791f2e99feSLydia Wang 1580cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1581cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1582cb53c626STakashi Iwai { 1583cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1584cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1585cb53c626STakashi Iwai } 1586cb53c626STakashi Iwai #endif 1587cb53c626STakashi Iwai 1588c577b8a1SJoseph Chan /* 1589c577b8a1SJoseph Chan */ 15905d41762aSTakashi Iwai 15915d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 15925d41762aSTakashi Iwai 159390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1594c577b8a1SJoseph Chan .build_controls = via_build_controls, 1595c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1596c577b8a1SJoseph Chan .init = via_init, 1597c577b8a1SJoseph Chan .free = via_free, 15984a918ffeSTakashi Iwai .unsol_event = via_unsol_event, 15991f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 16001f2e99feSLydia Wang .suspend = via_suspend, 16011f2e99feSLydia Wang #endif 1602cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1603cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1604cb53c626STakashi Iwai #endif 1605c577b8a1SJoseph Chan }; 1606c577b8a1SJoseph Chan 16074a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1608c577b8a1SJoseph Chan { 16094a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16104a79616dSTakashi Iwai int i; 16114a79616dSTakashi Iwai 16124a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 16134a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 16144a79616dSTakashi Iwai return false; 16154a79616dSTakashi Iwai } 1616ece8d043STakashi Iwai if (spec->hp_dac_nid == dac) 16174a79616dSTakashi Iwai return false; 16184a79616dSTakashi Iwai return true; 16194a79616dSTakashi Iwai } 16204a79616dSTakashi Iwai 16214a79616dSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 16224a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 16234a79616dSTakashi Iwai int depth, int wid_type) 16244a79616dSTakashi Iwai { 16254a79616dSTakashi Iwai hda_nid_t conn[8]; 16264a79616dSTakashi Iwai int i, nums; 16274a79616dSTakashi Iwai 16284a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 16294a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16304a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 16314a79616dSTakashi Iwai continue; 16324a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 16334a79616dSTakashi Iwai path->path[depth] = conn[i]; 16344a79616dSTakashi Iwai path->idx[depth] = i; 16354a79616dSTakashi Iwai path->depth = ++depth; 16364a79616dSTakashi Iwai return true; 16374a79616dSTakashi Iwai } 16384a79616dSTakashi Iwai } 16394a79616dSTakashi Iwai if (depth > 4) 16404a79616dSTakashi Iwai return false; 16414a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16424a79616dSTakashi Iwai unsigned int type; 16434a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 16444a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 16454a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 16464a79616dSTakashi Iwai continue; 16474a79616dSTakashi Iwai if (parse_output_path(codec, conn[i], target_dac, 16484a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 16494a79616dSTakashi Iwai path->path[depth] = conn[i]; 16504a79616dSTakashi Iwai path->idx[depth] = i; 16514a79616dSTakashi Iwai return true; 16524a79616dSTakashi Iwai } 16534a79616dSTakashi Iwai } 16544a79616dSTakashi Iwai return false; 16554a79616dSTakashi Iwai } 16564a79616dSTakashi Iwai 16574a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 16584a79616dSTakashi Iwai { 16594a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16604a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1661c577b8a1SJoseph Chan int i; 1662c577b8a1SJoseph Chan hda_nid_t nid; 1663c577b8a1SJoseph Chan 1664c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 16654a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 16664a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1667c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 16684a79616dSTakashi Iwai if (!nid) 16694a79616dSTakashi Iwai continue; 16704a79616dSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1)) 16714a79616dSTakashi Iwai spec->private_dac_nids[i] = 16724a79616dSTakashi Iwai spec->out_path[i].path[spec->out_path[i].depth - 1]; 1673c577b8a1SJoseph Chan } 1674c577b8a1SJoseph Chan return 0; 1675c577b8a1SJoseph Chan } 1676c577b8a1SJoseph Chan 16774a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 16784a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1679c577b8a1SJoseph Chan { 16804a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1681c577b8a1SJoseph Chan char name[32]; 16824a79616dSTakashi Iwai hda_nid_t nid; 16834a79616dSTakashi Iwai int err; 1684c577b8a1SJoseph Chan 16854a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 16864a79616dSTakashi Iwai nid = dac; 16874a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 16884a79616dSTakashi Iwai nid = pin; 16894a79616dSTakashi Iwai else 16904a79616dSTakashi Iwai nid = 0; 16914a79616dSTakashi Iwai if (nid) { 16924a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1693c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 16944a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT)); 1695c577b8a1SJoseph Chan if (err < 0) 1696c577b8a1SJoseph Chan return err; 1697c577b8a1SJoseph Chan } 16984a79616dSTakashi Iwai 16994a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 17004a79616dSTakashi Iwai nid = dac; 17014a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 17024a79616dSTakashi Iwai nid = pin; 17034a79616dSTakashi Iwai else 17044a79616dSTakashi Iwai nid = 0; 17054a79616dSTakashi Iwai if (nid) { 17064a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 17074a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 17084a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 17094a79616dSTakashi Iwai if (err < 0) 17104a79616dSTakashi Iwai return err; 17114a79616dSTakashi Iwai } 17124a79616dSTakashi Iwai return 0; 17134a79616dSTakashi Iwai } 17144a79616dSTakashi Iwai 1715f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1716f4a7828bSTakashi Iwai { 1717f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1718f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1719*e3d7a143STakashi Iwai int i, nums = 0; 1720f4a7828bSTakashi Iwai 1721f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1722*e3d7a143STakashi Iwai if (is_smart51_candidate(codec, cfg->inputs[i].pin)) 1723*e3d7a143STakashi Iwai nums++; 1724*e3d7a143STakashi Iwai } 1725*e3d7a143STakashi Iwai if (cfg->line_outs + nums < 3) 1726*e3d7a143STakashi Iwai return; 1727*e3d7a143STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1728*e3d7a143STakashi Iwai if (!is_smart51_candidate(codec, cfg->inputs[i].pin)) 1729f4a7828bSTakashi Iwai continue; 1730*e3d7a143STakashi Iwai spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin; 1731f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 1732f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1733f4a7828bSTakashi Iwai break; 1734f4a7828bSTakashi Iwai } 1735f4a7828bSTakashi Iwai } 1736f4a7828bSTakashi Iwai 17374a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 17384a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 17394a79616dSTakashi Iwai { 17404a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1741f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 17424a79616dSTakashi Iwai static const char * const chname[4] = { 17434a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 17444a79616dSTakashi Iwai }; 17454a79616dSTakashi Iwai int i, idx, err; 1746f4a7828bSTakashi Iwai int old_line_outs; 1747f4a7828bSTakashi Iwai 1748f4a7828bSTakashi Iwai /* check smart51 */ 1749f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 1750f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 1751f4a7828bSTakashi Iwai mangle_smart51(codec); 17524a79616dSTakashi Iwai 1753*e3d7a143STakashi Iwai err = via_auto_fill_dac_nids(codec); 1754*e3d7a143STakashi Iwai if (err < 0) 1755*e3d7a143STakashi Iwai return err; 1756*e3d7a143STakashi Iwai 17574a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 17584a79616dSTakashi Iwai hda_nid_t pin, dac; 17594a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 17604a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 17614a79616dSTakashi Iwai if (!pin || !dac) 17624a79616dSTakashi Iwai continue; 17630fe0adf8STakashi Iwai if (i == HDA_CLFE) { 17644a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 17654a79616dSTakashi Iwai if (err < 0) 17664a79616dSTakashi Iwai return err; 17674a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 17684a79616dSTakashi Iwai if (err < 0) 17694a79616dSTakashi Iwai return err; 17704a79616dSTakashi Iwai } else { 17714a79616dSTakashi Iwai err = create_ch_ctls(codec, chname[i], pin, dac, 3); 17724a79616dSTakashi Iwai if (err < 0) 17734a79616dSTakashi Iwai return err; 17744a79616dSTakashi Iwai } 17754a79616dSTakashi Iwai } 17764a79616dSTakashi Iwai 17774a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 17784a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 17794a79616dSTakashi Iwai if (idx >= 0) { 17804a79616dSTakashi Iwai /* add control to mixer */ 17814a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 17824a79616dSTakashi Iwai "PCM Playback Volume", 17834a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17844a79616dSTakashi Iwai idx, HDA_INPUT)); 17854a79616dSTakashi Iwai if (err < 0) 17864a79616dSTakashi Iwai return err; 17874a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 17884a79616dSTakashi Iwai "PCM Playback Switch", 17894a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17904a79616dSTakashi Iwai idx, HDA_INPUT)); 17914a79616dSTakashi Iwai if (err < 0) 17924a79616dSTakashi Iwai return err; 1793c577b8a1SJoseph Chan } 1794c577b8a1SJoseph Chan 1795f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 1796f4a7828bSTakashi Iwai 1797c577b8a1SJoseph Chan return 0; 1798c577b8a1SJoseph Chan } 1799c577b8a1SJoseph Chan 18000aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 18010aa62aefSHarald Welte { 18020aa62aefSHarald Welte int i; 18030aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1804ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 18050aa62aefSHarald Welte 18060aa62aefSHarald Welte /* for hp mode select */ 180710a20af7STakashi Iwai for (i = 0; texts[i]; i++) 180810a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 18090aa62aefSHarald Welte 18100aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 18110aa62aefSHarald Welte } 18120aa62aefSHarald Welte 18134a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 1814c577b8a1SJoseph Chan { 18154a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1816c577b8a1SJoseph Chan int err; 1817c577b8a1SJoseph Chan 1818c577b8a1SJoseph Chan if (!pin) 1819c577b8a1SJoseph Chan return 0; 1820c577b8a1SJoseph Chan 18214a79616dSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) { 1822ece8d043STakashi Iwai spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1]; 18234a79616dSTakashi Iwai spec->hp_independent_mode_index = 18244a79616dSTakashi Iwai spec->hp_path.idx[spec->hp_path.depth - 1]; 18250aa62aefSHarald Welte create_hp_imux(spec); 18264a79616dSTakashi Iwai } 18274a79616dSTakashi Iwai 1828ece8d043STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 1829ece8d043STakashi Iwai &spec->hp_dep_path, 0, -1) && 1830ece8d043STakashi Iwai !spec->hp_dac_nid) 1831ece8d043STakashi Iwai return 0; 1832ece8d043STakashi Iwai 1833ece8d043STakashi Iwai 1834ece8d043STakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); 18354a79616dSTakashi Iwai if (err < 0) 18364a79616dSTakashi Iwai return err; 18370aa62aefSHarald Welte 1838c577b8a1SJoseph Chan return 0; 1839c577b8a1SJoseph Chan } 1840c577b8a1SJoseph Chan 18414a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec) 18424a918ffeSTakashi Iwai { 18434a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 18444a918ffeSTakashi Iwai hda_nid_t pin, dac; 18454a918ffeSTakashi Iwai 18464a918ffeSTakashi Iwai pin = spec->autocfg.speaker_pins[0]; 18474a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs || !pin) 18484a918ffeSTakashi Iwai return 0; 18494a918ffeSTakashi Iwai 18504a918ffeSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) { 18514a918ffeSTakashi Iwai dac = spec->speaker_path.path[spec->speaker_path.depth - 1]; 18524a918ffeSTakashi Iwai spec->multiout.extra_out_nid[0] = dac; 18534a918ffeSTakashi Iwai return create_ch_ctls(codec, "Speaker", pin, dac, 3); 18544a918ffeSTakashi Iwai } 18554a918ffeSTakashi Iwai if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 18564a918ffeSTakashi Iwai &spec->speaker_path, 0, -1)) 18574a918ffeSTakashi Iwai return create_ch_ctls(codec, "Headphone", pin, 0, 3); 18584a918ffeSTakashi Iwai 18594a918ffeSTakashi Iwai return 0; 18604a918ffeSTakashi Iwai } 18614a918ffeSTakashi Iwai 1862a766d0d7STakashi Iwai /* look for ADCs */ 1863a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 1864a766d0d7STakashi Iwai { 1865a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 1866a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 1867a766d0d7STakashi Iwai int i; 1868a766d0d7STakashi Iwai 1869a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 1870a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 1871a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 1872a766d0d7STakashi Iwai continue; 1873a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 1874a766d0d7STakashi Iwai continue; 1875a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 1876a766d0d7STakashi Iwai continue; 1877a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 1878a766d0d7STakashi Iwai return -ENOMEM; 1879a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 1880a766d0d7STakashi Iwai } 1881a766d0d7STakashi Iwai return 0; 1882a766d0d7STakashi Iwai } 1883a766d0d7STakashi Iwai 1884a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 1885a766d0d7STakashi Iwai 1886d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 1887d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1888d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 1889d7a99cceSTakashi Iwai * So call somewhat different.. 1890d7a99cceSTakashi Iwai */ 1891d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 1892d7a99cceSTakashi Iwai .name = "Input Source", 1893d7a99cceSTakashi Iwai .info = via_mux_enum_info, 1894d7a99cceSTakashi Iwai .get = via_mux_enum_get, 1895d7a99cceSTakashi Iwai .put = via_mux_enum_put, 1896d7a99cceSTakashi Iwai }; 1897d7a99cceSTakashi Iwai 1898c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 1899620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 1900620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 1901c577b8a1SJoseph Chan { 190210a20af7STakashi Iwai struct via_spec *spec = codec->spec; 19030aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 1904*e3d7a143STakashi Iwai int i, j, err, idx, idx2, type, type_idx = 0; 1905a766d0d7STakashi Iwai hda_nid_t cap_nid; 1906a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 1907a766d0d7STakashi Iwai int num_idxs; 1908a766d0d7STakashi Iwai 1909a766d0d7STakashi Iwai err = via_fill_adcs(codec); 1910a766d0d7STakashi Iwai if (err < 0) 1911a766d0d7STakashi Iwai return err; 1912a766d0d7STakashi Iwai err = get_mux_nids(codec); 1913a766d0d7STakashi Iwai if (err < 0) 1914a766d0d7STakashi Iwai return err; 1915a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 1916a766d0d7STakashi Iwai 1917a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 1918a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 1919a766d0d7STakashi Iwai if (num_idxs <= 0) 1920a766d0d7STakashi Iwai return 0; 1921c577b8a1SJoseph Chan 1922c577b8a1SJoseph Chan /* for internal loopback recording select */ 1923f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 1924620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 192510a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 1926f3268512STakashi Iwai break; 1927f3268512STakashi Iwai } 1928f3268512STakashi Iwai } 1929c577b8a1SJoseph Chan 19307b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 193110a20af7STakashi Iwai const char *label; 19327b315bb4STakashi Iwai type = cfg->inputs[i].type; 1933f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 19347b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 1935c577b8a1SJoseph Chan break; 1936f3268512STakashi Iwai if (idx >= num_idxs) 1937f3268512STakashi Iwai continue; 19387b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 19397b315bb4STakashi Iwai type_idx++; 19407b315bb4STakashi Iwai else 19417b315bb4STakashi Iwai type_idx = 0; 194210a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 1943620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 1944620e2b28STakashi Iwai pin_idxs[idx]); 1945a766d0d7STakashi Iwai if (idx2 >= 0) 194616922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 1947620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 1948c577b8a1SJoseph Chan if (err < 0) 1949c577b8a1SJoseph Chan return err; 195010a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 1951*e3d7a143STakashi Iwai 1952*e3d7a143STakashi Iwai /* remember the label for smart51 control */ 1953*e3d7a143STakashi Iwai for (j = 0; j < spec->smart51_nums; j++) { 1954*e3d7a143STakashi Iwai if (spec->smart51_pins[j] == cfg->inputs[i].pin) { 1955*e3d7a143STakashi Iwai spec->smart51_idxs[j] = idx; 1956*e3d7a143STakashi Iwai spec->smart51_labels[j] = label; 1957*e3d7a143STakashi Iwai break; 1958*e3d7a143STakashi Iwai } 1959*e3d7a143STakashi Iwai } 1960c577b8a1SJoseph Chan } 1961d7a99cceSTakashi Iwai 1962d7a99cceSTakashi Iwai /* create capture mixer elements */ 1963d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 1964d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 1965d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 1966d7a99cceSTakashi Iwai "Capture Volume", i, 1967d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1968d7a99cceSTakashi Iwai HDA_INPUT)); 1969d7a99cceSTakashi Iwai if (err < 0) 1970d7a99cceSTakashi Iwai return err; 1971d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1972d7a99cceSTakashi Iwai "Capture Switch", i, 1973d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1974d7a99cceSTakashi Iwai HDA_INPUT)); 1975d7a99cceSTakashi Iwai if (err < 0) 1976d7a99cceSTakashi Iwai return err; 1977d7a99cceSTakashi Iwai } 1978d7a99cceSTakashi Iwai 1979d7a99cceSTakashi Iwai /* input-source control */ 1980d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 1981d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 1982d7a99cceSTakashi Iwai break; 1983d7a99cceSTakashi Iwai if (i) { 1984d7a99cceSTakashi Iwai struct snd_kcontrol_new *knew; 1985d7a99cceSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 1986d7a99cceSTakashi Iwai if (!knew) 1987d7a99cceSTakashi Iwai return -ENOMEM; 1988d7a99cceSTakashi Iwai knew->count = i; 1989d7a99cceSTakashi Iwai } 1990d7a99cceSTakashi Iwai 1991d7a99cceSTakashi Iwai /* mic-boosts */ 1992d7a99cceSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1993d7a99cceSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 1994d7a99cceSTakashi Iwai unsigned int caps; 1995d7a99cceSTakashi Iwai const char *label; 1996d7a99cceSTakashi Iwai char name[32]; 1997d7a99cceSTakashi Iwai 1998d7a99cceSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 1999d7a99cceSTakashi Iwai continue; 2000d7a99cceSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 2001d7a99cceSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 2002d7a99cceSTakashi Iwai continue; 2003d7a99cceSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2004d7a99cceSTakashi Iwai snprintf(name, sizeof(name), "%s Boost Capture Volume", label); 2005d7a99cceSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2006d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 2007d7a99cceSTakashi Iwai if (err < 0) 2008d7a99cceSTakashi Iwai return err; 2009d7a99cceSTakashi Iwai } 2010d7a99cceSTakashi Iwai 2011c577b8a1SJoseph Chan return 0; 2012c577b8a1SJoseph Chan } 2013c577b8a1SJoseph Chan 2014cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 201590dd48a1STakashi Iwai static const struct hda_amp_list vt1708_loopbacks[] = { 2016cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2017cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2018cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2019cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2020cb53c626STakashi Iwai { } /* end */ 2021cb53c626STakashi Iwai }; 2022cb53c626STakashi Iwai #endif 2023cb53c626STakashi Iwai 202476d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 202576d9b0ddSHarald Welte { 202676d9b0ddSHarald Welte unsigned int def_conf; 202776d9b0ddSHarald Welte unsigned char seqassoc; 202876d9b0ddSHarald Welte 20292f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 203076d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 203176d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 203282ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 203382ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 203476d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 20352f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 203676d9b0ddSHarald Welte } 203776d9b0ddSHarald Welte 203876d9b0ddSHarald Welte return; 203976d9b0ddSHarald Welte } 204076d9b0ddSHarald Welte 2041e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 20421f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20431f2e99feSLydia Wang { 20441f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20451f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20461f2e99feSLydia Wang 20471f2e99feSLydia Wang if (spec->codec_type != VT1708) 20481f2e99feSLydia Wang return 0; 2049e06e5a29STakashi Iwai spec->vt1708_jack_detect = 20501f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2051e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 20521f2e99feSLydia Wang return 0; 20531f2e99feSLydia Wang } 20541f2e99feSLydia Wang 2055e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 20561f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20571f2e99feSLydia Wang { 20581f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20591f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20601f2e99feSLydia Wang int change; 20611f2e99feSLydia Wang 20621f2e99feSLydia Wang if (spec->codec_type != VT1708) 20631f2e99feSLydia Wang return 0; 2064e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 20651f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2066e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2067e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 20681f2e99feSLydia Wang mute_aa_path(codec, 1); 20691f2e99feSLydia Wang notify_aa_path_ctls(codec); 20701f2e99feSLydia Wang } 20711f2e99feSLydia Wang return change; 20721f2e99feSLydia Wang } 20731f2e99feSLydia Wang 2074e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 20751f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20761f2e99feSLydia Wang .name = "Jack Detect", 20771f2e99feSLydia Wang .count = 1, 20781f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2079e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2080e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 20811f2e99feSLydia Wang }; 20821f2e99feSLydia Wang 208312daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec); 208412daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec); 208512daef65STakashi Iwai 208612daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 2087c577b8a1SJoseph Chan { 2088c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2089c577b8a1SJoseph Chan int err; 2090c577b8a1SJoseph Chan 2091c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2092c577b8a1SJoseph Chan if (err < 0) 2093c577b8a1SJoseph Chan return err; 2094c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 20957f0df88cSTakashi Iwai return -EINVAL; 2096c577b8a1SJoseph Chan 20974a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2098c577b8a1SJoseph Chan if (err < 0) 2099c577b8a1SJoseph Chan return err; 21004a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2101c577b8a1SJoseph Chan if (err < 0) 2102c577b8a1SJoseph Chan return err; 21034a918ffeSTakashi Iwai err = via_auto_create_speaker_ctls(codec); 21044a918ffeSTakashi Iwai if (err < 0) 21054a918ffeSTakashi Iwai return err; 2106620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2107c577b8a1SJoseph Chan if (err < 0) 2108c577b8a1SJoseph Chan return err; 2109c577b8a1SJoseph Chan 2110c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2111c577b8a1SJoseph Chan 211212daef65STakashi Iwai fill_dig_outs(codec); 211312daef65STakashi Iwai fill_dig_in(codec); 2114c577b8a1SJoseph Chan 2115603c4019STakashi Iwai if (spec->kctls.list) 2116603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2117c577b8a1SJoseph Chan 2118096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 2119c577b8a1SJoseph Chan 21200aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 21210aa62aefSHarald Welte 2122ece8d043STakashi Iwai if (spec->hp_mux) { 2123ece8d043STakashi Iwai err = via_hp_build(codec); 2124ece8d043STakashi Iwai if (err < 0) 2125ece8d043STakashi Iwai return err; 2126ece8d043STakashi Iwai } 2127c577b8a1SJoseph Chan 2128f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2129f4a7828bSTakashi Iwai if (err < 0) 2130f4a7828bSTakashi Iwai return err; 2131f4a7828bSTakashi Iwai 21325d41762aSTakashi Iwai /* assign slave outs */ 21335d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 21345d41762aSTakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 21355d41762aSTakashi Iwai 2136c577b8a1SJoseph Chan return 1; 2137c577b8a1SJoseph Chan } 2138c577b8a1SJoseph Chan 21395d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec) 2140c577b8a1SJoseph Chan { 214125eaba2fSLydia Wang struct via_spec *spec = codec->spec; 21425d41762aSTakashi Iwai if (spec->multiout.dig_out_nid) 21435d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); 21445d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 21455d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); 21465d41762aSTakashi Iwai } 214725eaba2fSLydia Wang 21485d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec) 21495d41762aSTakashi Iwai { 21505d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 21515d41762aSTakashi Iwai if (!spec->dig_in_nid) 21525d41762aSTakashi Iwai return; 21535d41762aSTakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 21545d41762aSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 21555d41762aSTakashi Iwai } 21565d41762aSTakashi Iwai 21574a918ffeSTakashi Iwai /* initialize the unsolicited events */ 21584a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec) 21594a918ffeSTakashi Iwai { 21604a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 21614a918ffeSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 21624a918ffeSTakashi Iwai unsigned int ev; 21634a918ffeSTakashi Iwai int i; 21644a918ffeSTakashi Iwai 21654a918ffeSTakashi Iwai if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) 21664a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->hp_pins[0], 0, 21674a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21684a918ffeSTakashi Iwai AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); 21694a918ffeSTakashi Iwai 21704a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 21714a918ffeSTakashi Iwai ev = VIA_LINE_EVENT; 21724a918ffeSTakashi Iwai else 21734a918ffeSTakashi Iwai ev = 0; 21744a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 21754a918ffeSTakashi Iwai if (cfg->line_out_pins[i] && 21764a918ffeSTakashi Iwai is_jack_detectable(codec, cfg->line_out_pins[i])) 21774a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->line_out_pins[0], 0, 21784a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21794a918ffeSTakashi Iwai AC_USRSP_EN | ev | VIA_JACK_EVENT); 21804a918ffeSTakashi Iwai } 21814a918ffeSTakashi Iwai 21824a918ffeSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 21834a918ffeSTakashi Iwai if (is_jack_detectable(codec, cfg->inputs[i].pin)) 21844a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, 21854a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21864a918ffeSTakashi Iwai AC_USRSP_EN | VIA_JACK_EVENT); 21874a918ffeSTakashi Iwai } 21884a918ffeSTakashi Iwai } 21894a918ffeSTakashi Iwai 21905d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 21915d41762aSTakashi Iwai { 21925d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 21935d41762aSTakashi Iwai int i; 21945d41762aSTakashi Iwai 21955d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 21965d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 21975d41762aSTakashi Iwai 2198c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2199c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 22004a918ffeSTakashi Iwai via_auto_init_speaker_out(codec); 2201c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 22025d41762aSTakashi Iwai via_auto_init_dig_outs(codec); 22035d41762aSTakashi Iwai via_auto_init_dig_in(codec); 220411890956SLydia Wang 22054a918ffeSTakashi Iwai via_auto_init_unsol_event(codec); 22064a918ffeSTakashi Iwai 220725eaba2fSLydia Wang via_hp_automute(codec); 22084a918ffeSTakashi Iwai via_line_automute(codec, false); 220925eaba2fSLydia Wang 2210c577b8a1SJoseph Chan return 0; 2211c577b8a1SJoseph Chan } 2212c577b8a1SJoseph Chan 22131f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 22141f2e99feSLydia Wang { 22151f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 22161f2e99feSLydia Wang vt1708_hp_work.work); 22171f2e99feSLydia Wang if (spec->codec_type != VT1708) 22181f2e99feSLydia Wang return; 22191f2e99feSLydia Wang /* if jack state toggled */ 22201f2e99feSLydia Wang if (spec->vt1708_hp_present 2221d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 22221f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 22231f2e99feSLydia Wang via_hp_automute(spec->codec); 22241f2e99feSLydia Wang } 22251f2e99feSLydia Wang vt1708_start_hp_work(spec); 22261f2e99feSLydia Wang } 22271f2e99feSLydia Wang 2228337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2229337b9d02STakashi Iwai { 2230337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2231337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2232337b9d02STakashi Iwai unsigned int type; 2233337b9d02STakashi Iwai int i, n; 2234337b9d02STakashi Iwai 2235337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2236337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2237337b9d02STakashi Iwai while (nid) { 2238a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 22391c55d521STakashi Iwai if (type == AC_WID_PIN) 22401c55d521STakashi Iwai break; 2241337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2242337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2243337b9d02STakashi Iwai if (n <= 0) 2244337b9d02STakashi Iwai break; 2245337b9d02STakashi Iwai if (n > 1) { 2246337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2247337b9d02STakashi Iwai break; 2248337b9d02STakashi Iwai } 2249337b9d02STakashi Iwai nid = conn[0]; 2250337b9d02STakashi Iwai } 2251337b9d02STakashi Iwai } 22521c55d521STakashi Iwai return 0; 2253337b9d02STakashi Iwai } 2254337b9d02STakashi Iwai 2255c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2256c577b8a1SJoseph Chan { 2257c577b8a1SJoseph Chan struct via_spec *spec; 2258c577b8a1SJoseph Chan int err; 2259c577b8a1SJoseph Chan 2260c577b8a1SJoseph Chan /* create a codec specific record */ 22615b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2262c577b8a1SJoseph Chan if (spec == NULL) 2263c577b8a1SJoseph Chan return -ENOMEM; 2264c577b8a1SJoseph Chan 2265620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2266620e2b28STakashi Iwai 226712daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 226812daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 226912daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 227012daef65STakashi Iwai 2271c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 227212daef65STakashi Iwai err = via_parse_auto_config(codec); 2273c577b8a1SJoseph Chan if (err < 0) { 2274c577b8a1SJoseph Chan via_free(codec); 2275c577b8a1SJoseph Chan return err; 2276c577b8a1SJoseph Chan } 2277c577b8a1SJoseph Chan 227812daef65STakashi Iwai /* add jack detect on/off control */ 227912daef65STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 228012daef65STakashi Iwai return -ENOMEM; 228112daef65STakashi Iwai 2282bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2283bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2284bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2285c577b8a1SJoseph Chan 2286c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2287c577b8a1SJoseph Chan 2288cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2289cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2290cb53c626STakashi Iwai #endif 22911f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2292c577b8a1SJoseph Chan return 0; 2293c577b8a1SJoseph Chan } 2294c577b8a1SJoseph Chan 2295c577b8a1SJoseph Chan /* 2296c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2297c577b8a1SJoseph Chan */ 2298cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 229990dd48a1STakashi Iwai static const struct hda_amp_list vt1709_loopbacks[] = { 2300cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 2301cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 2302cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 2303cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 2304cb53c626STakashi Iwai { } /* end */ 2305cb53c626STakashi Iwai }; 2306cb53c626STakashi Iwai #endif 2307cb53c626STakashi Iwai 2308c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2309c577b8a1SJoseph Chan { 2310c577b8a1SJoseph Chan struct via_spec *spec; 2311c577b8a1SJoseph Chan int err; 2312c577b8a1SJoseph Chan 2313c577b8a1SJoseph Chan /* create a codec specific record */ 23145b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2315c577b8a1SJoseph Chan if (spec == NULL) 2316c577b8a1SJoseph Chan return -ENOMEM; 2317c577b8a1SJoseph Chan 2318620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2319620e2b28STakashi Iwai 232012daef65STakashi Iwai err = via_parse_auto_config(codec); 2321c577b8a1SJoseph Chan if (err < 0) { 2322c577b8a1SJoseph Chan via_free(codec); 2323c577b8a1SJoseph Chan return err; 2324c577b8a1SJoseph Chan } 2325c577b8a1SJoseph Chan 2326c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2327c577b8a1SJoseph Chan 2328cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2329cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2330cb53c626STakashi Iwai #endif 2331c577b8a1SJoseph Chan 2332c577b8a1SJoseph Chan return 0; 2333c577b8a1SJoseph Chan } 2334c577b8a1SJoseph Chan /* 2335c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2336c577b8a1SJoseph Chan */ 2337c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2338c577b8a1SJoseph Chan { 2339c577b8a1SJoseph Chan struct via_spec *spec; 2340c577b8a1SJoseph Chan int err; 2341c577b8a1SJoseph Chan 2342c577b8a1SJoseph Chan /* create a codec specific record */ 23435b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2344c577b8a1SJoseph Chan if (spec == NULL) 2345c577b8a1SJoseph Chan return -ENOMEM; 2346c577b8a1SJoseph Chan 2347620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2348620e2b28STakashi Iwai 234912daef65STakashi Iwai err = via_parse_auto_config(codec); 2350c577b8a1SJoseph Chan if (err < 0) { 2351c577b8a1SJoseph Chan via_free(codec); 2352c577b8a1SJoseph Chan return err; 2353c577b8a1SJoseph Chan } 2354c577b8a1SJoseph Chan 2355c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2356c577b8a1SJoseph Chan 2357cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2358cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 2359cb53c626STakashi Iwai #endif 2360f7278fd0SJosepch Chan return 0; 2361f7278fd0SJosepch Chan } 2362f7278fd0SJosepch Chan 2363f7278fd0SJosepch Chan /* 2364f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 2365f7278fd0SJosepch Chan */ 2366f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 236790dd48a1STakashi Iwai static const struct hda_amp_list vt1708B_loopbacks[] = { 2368f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 2369f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 2370f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 2371f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 2372f7278fd0SJosepch Chan { } /* end */ 2373f7278fd0SJosepch Chan }; 2374f7278fd0SJosepch Chan #endif 23753e95b9abSLydia Wang 23763e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 23773e95b9abSLydia Wang { 23783e95b9abSLydia Wang struct via_spec *spec = codec->spec; 23793e95b9abSLydia Wang int imux_is_smixer; 23803e95b9abSLydia Wang unsigned int parm; 23813e95b9abSLydia Wang int is_8ch = 0; 2382bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2383bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 23843e95b9abSLydia Wang is_8ch = 1; 23853e95b9abSLydia Wang 23863e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 23873e95b9abSLydia Wang imux_is_smixer = 23883e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 23893e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 23903e95b9abSLydia Wang /* inputs */ 23913e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 23923e95b9abSLydia Wang parm = AC_PWRST_D3; 23933e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 23943e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 23953e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 23963e95b9abSLydia Wang if (imux_is_smixer) 23973e95b9abSLydia Wang parm = AC_PWRST_D0; 23983e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 23993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 24003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 24013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 24023e95b9abSLydia Wang 24033e95b9abSLydia Wang /* outputs */ 24043e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 24053e95b9abSLydia Wang parm = AC_PWRST_D3; 24063e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 24073e95b9abSLydia Wang if (spec->smart51_enabled) 24083e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 24093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 24103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 24113e95b9abSLydia Wang 24123e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 24133e95b9abSLydia Wang if (is_8ch) { 24143e95b9abSLydia Wang parm = AC_PWRST_D3; 24153e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 24163e95b9abSLydia Wang if (spec->smart51_enabled) 24173e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 24183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 24193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 24213e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2422bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2423bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2424bc92df7fSLydia Wang parm = AC_PWRST_D3; 2425bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2426bc92df7fSLydia Wang if (spec->smart51_enabled) 2427bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2428bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2429bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2430bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2431bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24323e95b9abSLydia Wang } 24333e95b9abSLydia Wang 24343e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 24353e95b9abSLydia Wang parm = AC_PWRST_D3; 24363e95b9abSLydia Wang /* force to D0 for internal Speaker */ 24373e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 24383e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 24393e95b9abSLydia Wang if (is_8ch) 24403e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 24413e95b9abSLydia Wang 24423e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 24433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 24443e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 24453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 24463e95b9abSLydia Wang if (is_8ch) { 24473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 24483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 24503e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2451bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2452bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2453bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 24543e95b9abSLydia Wang } 24553e95b9abSLydia Wang 2456518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2457f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2458f7278fd0SJosepch Chan { 2459f7278fd0SJosepch Chan struct via_spec *spec; 2460f7278fd0SJosepch Chan int err; 2461f7278fd0SJosepch Chan 2462518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2463518bf3baSLydia Wang return patch_vt1708S(codec); 2464f7278fd0SJosepch Chan /* create a codec specific record */ 24655b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2466f7278fd0SJosepch Chan if (spec == NULL) 2467f7278fd0SJosepch Chan return -ENOMEM; 2468f7278fd0SJosepch Chan 2469620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2470620e2b28STakashi Iwai 2471f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 247212daef65STakashi Iwai err = via_parse_auto_config(codec); 2473f7278fd0SJosepch Chan if (err < 0) { 2474f7278fd0SJosepch Chan via_free(codec); 2475f7278fd0SJosepch Chan return err; 2476f7278fd0SJosepch Chan } 2477f7278fd0SJosepch Chan 2478f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2479f7278fd0SJosepch Chan 2480f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2481f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2482f7278fd0SJosepch Chan #endif 2483f7278fd0SJosepch Chan 24843e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 24853e95b9abSLydia Wang 2486f7278fd0SJosepch Chan return 0; 2487f7278fd0SJosepch Chan } 2488f7278fd0SJosepch Chan 2489f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2490f7278fd0SJosepch Chan { 2491f7278fd0SJosepch Chan struct via_spec *spec; 2492f7278fd0SJosepch Chan int err; 2493f7278fd0SJosepch Chan 2494f7278fd0SJosepch Chan /* create a codec specific record */ 24955b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2496f7278fd0SJosepch Chan if (spec == NULL) 2497f7278fd0SJosepch Chan return -ENOMEM; 2498f7278fd0SJosepch Chan 2499f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 250012daef65STakashi Iwai err = via_parse_auto_config(codec); 2501f7278fd0SJosepch Chan if (err < 0) { 2502f7278fd0SJosepch Chan via_free(codec); 2503f7278fd0SJosepch Chan return err; 2504f7278fd0SJosepch Chan } 2505f7278fd0SJosepch Chan 2506f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2507f7278fd0SJosepch Chan 2508f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 2509f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 2510f7278fd0SJosepch Chan #endif 2511c577b8a1SJoseph Chan 25123e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 25133e95b9abSLydia Wang 2514c577b8a1SJoseph Chan return 0; 2515c577b8a1SJoseph Chan } 2516c577b8a1SJoseph Chan 2517d949cac1SHarald Welte /* Patch for VT1708S */ 2518096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 2519d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2520d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2521bc7e7e5cSLydia Wang /* don't bybass mixer */ 2522bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 2523d949cac1SHarald Welte { } 2524d949cac1SHarald Welte }; 2525d949cac1SHarald Welte 25269da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 25279da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 25289da29271STakashi Iwai { 25299da29271STakashi Iwai struct via_spec *spec = codec->spec; 25309da29271STakashi Iwai int i; 25319da29271STakashi Iwai 25329da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 25339da29271STakashi Iwai hda_nid_t nid; 25349da29271STakashi Iwai int conn; 25359da29271STakashi Iwai 25369da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 25379da29271STakashi Iwai if (!nid) 25389da29271STakashi Iwai continue; 25399da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 25409da29271STakashi Iwai if (conn < 1) 25419da29271STakashi Iwai continue; 25429da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 25439da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 25449da29271STakashi Iwai else { 25459da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 25469da29271STakashi Iwai break; /* at most two dig outs */ 25479da29271STakashi Iwai } 25489da29271STakashi Iwai } 25499da29271STakashi Iwai } 25509da29271STakashi Iwai 255112daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec) 2552d949cac1SHarald Welte { 2553d949cac1SHarald Welte struct via_spec *spec = codec->spec; 255412daef65STakashi Iwai hda_nid_t dig_nid; 255512daef65STakashi Iwai int i, err; 2556d949cac1SHarald Welte 255712daef65STakashi Iwai if (!spec->autocfg.dig_in_pin) 255812daef65STakashi Iwai return; 2559d949cac1SHarald Welte 256012daef65STakashi Iwai dig_nid = codec->start_nid; 256112daef65STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 256212daef65STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 256312daef65STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 256412daef65STakashi Iwai continue; 256512daef65STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 256612daef65STakashi Iwai continue; 256712daef65STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 256812daef65STakashi Iwai continue; 256912daef65STakashi Iwai err = get_connection_index(codec, dig_nid, 257012daef65STakashi Iwai spec->autocfg.dig_in_pin); 257112daef65STakashi Iwai if (err >= 0) { 257212daef65STakashi Iwai spec->dig_in_nid = dig_nid; 257312daef65STakashi Iwai break; 257412daef65STakashi Iwai } 257512daef65STakashi Iwai } 2576d949cac1SHarald Welte } 2577d949cac1SHarald Welte 2578d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 257990dd48a1STakashi Iwai static const struct hda_amp_list vt1708S_loopbacks[] = { 2580d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 2581d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 2582d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 2583d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 2584d949cac1SHarald Welte { } /* end */ 2585d949cac1SHarald Welte }; 2586d949cac1SHarald Welte #endif 2587d949cac1SHarald Welte 25886369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 25896369bcfcSLydia Wang int offset, int num_steps, int step_size) 25906369bcfcSLydia Wang { 25916369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 25926369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 25936369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 25946369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 25956369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 25966369bcfcSLydia Wang } 25976369bcfcSLydia Wang 2598d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2599d949cac1SHarald Welte { 2600d949cac1SHarald Welte struct via_spec *spec; 2601d949cac1SHarald Welte int err; 2602d949cac1SHarald Welte 2603d949cac1SHarald Welte /* create a codec specific record */ 26045b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2605d949cac1SHarald Welte if (spec == NULL) 2606d949cac1SHarald Welte return -ENOMEM; 2607d949cac1SHarald Welte 2608620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2609d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2610d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2611620e2b28STakashi Iwai 2612d949cac1SHarald Welte /* automatic parse from the BIOS config */ 261312daef65STakashi Iwai err = via_parse_auto_config(codec); 2614d949cac1SHarald Welte if (err < 0) { 2615d949cac1SHarald Welte via_free(codec); 2616d949cac1SHarald Welte return err; 2617d949cac1SHarald Welte } 2618d949cac1SHarald Welte 2619096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 2620d949cac1SHarald Welte 2621d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2622d949cac1SHarald Welte 2623d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2624d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 2625d949cac1SHarald Welte #endif 2626d949cac1SHarald Welte 2627518bf3baSLydia Wang /* correct names for VT1708BCE */ 2628518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 2629518bf3baSLydia Wang kfree(codec->chip_name); 2630518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 2631518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 2632518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 2633518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2634970f630fSLydia Wang } 2635bc92df7fSLydia Wang /* correct names for VT1705 */ 2636bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 2637bc92df7fSLydia Wang kfree(codec->chip_name); 2638bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 2639bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 2640bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 2641bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2642bc92df7fSLydia Wang } 26433e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 2644d949cac1SHarald Welte return 0; 2645d949cac1SHarald Welte } 2646d949cac1SHarald Welte 2647d949cac1SHarald Welte /* Patch for VT1702 */ 2648d949cac1SHarald Welte 2649096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 2650bc7e7e5cSLydia Wang /* mixer enable */ 2651bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 2652bc7e7e5cSLydia Wang /* GPIO 0~2 */ 2653bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 2654d949cac1SHarald Welte { } 2655d949cac1SHarald Welte }; 2656d949cac1SHarald Welte 2657d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 265890dd48a1STakashi Iwai static const struct hda_amp_list vt1702_loopbacks[] = { 2659d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 2660d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 2661d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 2662d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 2663d949cac1SHarald Welte { } /* end */ 2664d949cac1SHarald Welte }; 2665d949cac1SHarald Welte #endif 2666d949cac1SHarald Welte 26673e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 26683e95b9abSLydia Wang { 26693e95b9abSLydia Wang int imux_is_smixer = 26703e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 26713e95b9abSLydia Wang unsigned int parm; 26723e95b9abSLydia Wang /* inputs */ 26733e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 26743e95b9abSLydia Wang parm = AC_PWRST_D3; 26753e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 26763e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 26773e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 26783e95b9abSLydia Wang if (imux_is_smixer) 26793e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 26803e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 26813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 26823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 26833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 26843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 26853e95b9abSLydia Wang 26863e95b9abSLydia Wang /* outputs */ 26873e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 26883e95b9abSLydia Wang parm = AC_PWRST_D3; 26893e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 26903e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 26913e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 26923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 26933e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 26943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 26953e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 26963e95b9abSLydia Wang } 26973e95b9abSLydia Wang 2698d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 2699d949cac1SHarald Welte { 2700d949cac1SHarald Welte struct via_spec *spec; 2701d949cac1SHarald Welte int err; 2702d949cac1SHarald Welte 2703d949cac1SHarald Welte /* create a codec specific record */ 27045b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2705d949cac1SHarald Welte if (spec == NULL) 2706d949cac1SHarald Welte return -ENOMEM; 2707d949cac1SHarald Welte 2708620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 2709620e2b28STakashi Iwai 271012daef65STakashi Iwai /* limit AA path volume to 0 dB */ 271112daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 271212daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 271312daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 271412daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 271512daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 271612daef65STakashi Iwai 2717d949cac1SHarald Welte /* automatic parse from the BIOS config */ 271812daef65STakashi Iwai err = via_parse_auto_config(codec); 2719d949cac1SHarald Welte if (err < 0) { 2720d949cac1SHarald Welte via_free(codec); 2721d949cac1SHarald Welte return err; 2722d949cac1SHarald Welte } 2723d949cac1SHarald Welte 2724096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 2725d949cac1SHarald Welte 2726d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2727d949cac1SHarald Welte 2728d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 2729d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 2730d949cac1SHarald Welte #endif 2731d949cac1SHarald Welte 27323e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 2733d949cac1SHarald Welte return 0; 2734d949cac1SHarald Welte } 2735d949cac1SHarald Welte 2736eb7188caSLydia Wang /* Patch for VT1718S */ 2737eb7188caSLydia Wang 2738096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 27394ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 27404ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 2741eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 2742eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 27435d41762aSTakashi Iwai 2744eb7188caSLydia Wang { } 2745eb7188caSLydia Wang }; 2746eb7188caSLydia Wang 2747eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 274890dd48a1STakashi Iwai static const struct hda_amp_list vt1718S_loopbacks[] = { 2749eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 2750eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 2751eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 2752eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 2753eb7188caSLydia Wang { } /* end */ 2754eb7188caSLydia Wang }; 2755eb7188caSLydia Wang #endif 2756eb7188caSLydia Wang 27573e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 27583e95b9abSLydia Wang { 27593e95b9abSLydia Wang struct via_spec *spec = codec->spec; 27603e95b9abSLydia Wang int imux_is_smixer; 27613e95b9abSLydia Wang unsigned int parm; 27623e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 27633e95b9abSLydia Wang imux_is_smixer = 27643e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 27653e95b9abSLydia Wang /* inputs */ 27663e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 27673e95b9abSLydia Wang parm = AC_PWRST_D3; 27683e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 27693e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 27703e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 27713e95b9abSLydia Wang if (imux_is_smixer) 27723e95b9abSLydia Wang parm = AC_PWRST_D0; 27733e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 27743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 27753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 27763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 27773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 27783e95b9abSLydia Wang 27793e95b9abSLydia Wang /* outputs */ 27803e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 27813e95b9abSLydia Wang parm = AC_PWRST_D3; 27823e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 27833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 27843e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 27853e95b9abSLydia Wang 27863e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 27873e95b9abSLydia Wang parm = AC_PWRST_D3; 27883e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 27893e95b9abSLydia Wang if (spec->smart51_enabled) 27903e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 27913e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 27923e95b9abSLydia Wang 27933e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 27943e95b9abSLydia Wang parm = AC_PWRST_D3; 27953e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 27963e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 27973e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 27983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 27993e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 28003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 28013e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28023e95b9abSLydia Wang 28033e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 28043e95b9abSLydia Wang parm = AC_PWRST_D3; 28053e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 28063e95b9abSLydia Wang if (spec->smart51_enabled) 28073e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 28083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 28093e95b9abSLydia Wang 28103e95b9abSLydia Wang if (spec->hp_independent_mode) { 28113e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 28123e95b9abSLydia Wang parm = AC_PWRST_D3; 28133e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 28143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 28153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28163e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 28173e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28183e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 28193e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28203e95b9abSLydia Wang } 28213e95b9abSLydia Wang } 28223e95b9abSLydia Wang 2823eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 2824eb7188caSLydia Wang { 2825eb7188caSLydia Wang struct via_spec *spec; 2826eb7188caSLydia Wang int err; 2827eb7188caSLydia Wang 2828eb7188caSLydia Wang /* create a codec specific record */ 28295b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2830eb7188caSLydia Wang if (spec == NULL) 2831eb7188caSLydia Wang return -ENOMEM; 2832eb7188caSLydia Wang 2833620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 2834d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 2835d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 2836620e2b28STakashi Iwai 2837eb7188caSLydia Wang /* automatic parse from the BIOS config */ 283812daef65STakashi Iwai err = via_parse_auto_config(codec); 2839eb7188caSLydia Wang if (err < 0) { 2840eb7188caSLydia Wang via_free(codec); 2841eb7188caSLydia Wang return err; 2842eb7188caSLydia Wang } 2843eb7188caSLydia Wang 2844096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 2845eb7188caSLydia Wang 2846eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 2847eb7188caSLydia Wang 2848eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 2849eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 2850eb7188caSLydia Wang #endif 2851eb7188caSLydia Wang 28523e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 28533e95b9abSLydia Wang 2854eb7188caSLydia Wang return 0; 2855eb7188caSLydia Wang } 2856f3db423dSLydia Wang 2857f3db423dSLydia Wang /* Patch for VT1716S */ 2858f3db423dSLydia Wang 2859f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 2860f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 2861f3db423dSLydia Wang { 2862f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 2863f3db423dSLydia Wang uinfo->count = 1; 2864f3db423dSLydia Wang uinfo->value.integer.min = 0; 2865f3db423dSLydia Wang uinfo->value.integer.max = 1; 2866f3db423dSLydia Wang return 0; 2867f3db423dSLydia Wang } 2868f3db423dSLydia Wang 2869f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 2870f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2871f3db423dSLydia Wang { 2872f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2873f3db423dSLydia Wang int index = 0; 2874f3db423dSLydia Wang 2875f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 2876f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 2877f3db423dSLydia Wang if (index != -1) 2878f3db423dSLydia Wang *ucontrol->value.integer.value = index; 2879f3db423dSLydia Wang 2880f3db423dSLydia Wang return 0; 2881f3db423dSLydia Wang } 2882f3db423dSLydia Wang 2883f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 2884f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2885f3db423dSLydia Wang { 2886f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2887f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2888f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 2889f3db423dSLydia Wang 2890f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 2891f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 2892f3db423dSLydia Wang spec->dmic_enabled = index; 28933e95b9abSLydia Wang set_widgets_power_state(codec); 2894f3db423dSLydia Wang return 1; 2895f3db423dSLydia Wang } 2896f3db423dSLydia Wang 289790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 2898f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 2899f3db423dSLydia Wang { 2900f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2901f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 29025b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 2903f3db423dSLydia Wang .count = 1, 2904f3db423dSLydia Wang .info = vt1716s_dmic_info, 2905f3db423dSLydia Wang .get = vt1716s_dmic_get, 2906f3db423dSLydia Wang .put = vt1716s_dmic_put, 2907f3db423dSLydia Wang }, 2908f3db423dSLydia Wang {} /* end */ 2909f3db423dSLydia Wang }; 2910f3db423dSLydia Wang 2911f3db423dSLydia Wang 2912f3db423dSLydia Wang /* mono-out mixer elements */ 291390dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 2914f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 2915f3db423dSLydia Wang { } /* end */ 2916f3db423dSLydia Wang }; 2917f3db423dSLydia Wang 2918096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 2919f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 2920f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 2921f3db423dSLydia Wang /* don't bybass mixer */ 2922f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 2923f3db423dSLydia Wang /* Enable mono output */ 2924f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 2925f3db423dSLydia Wang { } 2926f3db423dSLydia Wang }; 2927f3db423dSLydia Wang 2928f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 292990dd48a1STakashi Iwai static const struct hda_amp_list vt1716S_loopbacks[] = { 2930f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 2931f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 2932f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 2933f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 2934f3db423dSLydia Wang { } /* end */ 2935f3db423dSLydia Wang }; 2936f3db423dSLydia Wang #endif 2937f3db423dSLydia Wang 29383e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 29393e95b9abSLydia Wang { 29403e95b9abSLydia Wang struct via_spec *spec = codec->spec; 29413e95b9abSLydia Wang int imux_is_smixer; 29423e95b9abSLydia Wang unsigned int parm; 29433e95b9abSLydia Wang unsigned int mono_out, present; 29443e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 29453e95b9abSLydia Wang imux_is_smixer = 29463e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 29473e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 29483e95b9abSLydia Wang /* inputs */ 29493e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 29503e95b9abSLydia Wang parm = AC_PWRST_D3; 29513e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29523e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29533e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29543e95b9abSLydia Wang if (imux_is_smixer) 29553e95b9abSLydia Wang parm = AC_PWRST_D0; 29563e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 29573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 29583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 29593e95b9abSLydia Wang 29603e95b9abSLydia Wang parm = AC_PWRST_D3; 29613e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29623e95b9abSLydia Wang /* PW11 (22h) */ 29633e95b9abSLydia Wang if (spec->dmic_enabled) 29643e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 29653e95b9abSLydia Wang else 29663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 29673e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 29683e95b9abSLydia Wang 29693e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 29703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 29713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 29723e95b9abSLydia Wang 29733e95b9abSLydia Wang /* outputs */ 29743e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 29753e95b9abSLydia Wang parm = AC_PWRST_D3; 29763e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 29773e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 29783e95b9abSLydia Wang if (spec->smart51_enabled) 29793e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 29803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 29813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 29823e95b9abSLydia Wang 29833e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 29843e95b9abSLydia Wang parm = AC_PWRST_D3; 29853e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 29863e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 29873e95b9abSLydia Wang if (spec->smart51_enabled) 29883e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 29893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 29903e95b9abSLydia Wang 29913e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 29923e95b9abSLydia Wang if (spec->smart51_enabled) 29933e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 29943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 29953e95b9abSLydia Wang 29963e95b9abSLydia Wang /* Mono out */ 29973e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 29983e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 29993e95b9abSLydia Wang 30003e95b9abSLydia Wang if (present) 30013e95b9abSLydia Wang mono_out = 0; 30023e95b9abSLydia Wang else { 30033e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 30043e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 30053e95b9abSLydia Wang mono_out = 0; 30063e95b9abSLydia Wang else 30073e95b9abSLydia Wang mono_out = 1; 30083e95b9abSLydia Wang } 30093e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 30103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 30113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 30123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 30133e95b9abSLydia Wang 30143e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 30153e95b9abSLydia Wang parm = AC_PWRST_D3; 30163e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 30173e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 30183e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 30193e95b9abSLydia Wang if (spec->hp_independent_mode) 30203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 30213e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30223e95b9abSLydia Wang 30233e95b9abSLydia Wang /* force to D0 for internal Speaker */ 30243e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 30253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 30263e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 30273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 30283e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 30293e95b9abSLydia Wang } 30303e95b9abSLydia Wang 3031f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3032f3db423dSLydia Wang { 3033f3db423dSLydia Wang struct via_spec *spec; 3034f3db423dSLydia Wang int err; 3035f3db423dSLydia Wang 3036f3db423dSLydia Wang /* create a codec specific record */ 30375b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3038f3db423dSLydia Wang if (spec == NULL) 3039f3db423dSLydia Wang return -ENOMEM; 3040f3db423dSLydia Wang 3041620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3042d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3043d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3044620e2b28STakashi Iwai 3045f3db423dSLydia Wang /* automatic parse from the BIOS config */ 304612daef65STakashi Iwai err = via_parse_auto_config(codec); 3047f3db423dSLydia Wang if (err < 0) { 3048f3db423dSLydia Wang via_free(codec); 3049f3db423dSLydia Wang return err; 3050f3db423dSLydia Wang } 3051f3db423dSLydia Wang 3052096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 3053f3db423dSLydia Wang 3054f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3055f3db423dSLydia Wang spec->num_mixers++; 3056f3db423dSLydia Wang 3057f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3058f3db423dSLydia Wang 3059f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3060f3db423dSLydia Wang 3061f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3062f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 3063f3db423dSLydia Wang #endif 3064f3db423dSLydia Wang 30653e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 3066f3db423dSLydia Wang return 0; 3067f3db423dSLydia Wang } 306825eaba2fSLydia Wang 306925eaba2fSLydia Wang /* for vt2002P */ 307025eaba2fSLydia Wang 3071096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 3072eadb9a80SLydia Wang /* Class-D speaker related verbs */ 3073eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 3074eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 3075eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 307625eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 307725eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 307825eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 307925eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 308025eaba2fSLydia Wang { } 308125eaba2fSLydia Wang }; 30824a918ffeSTakashi Iwai 3083096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 308411890956SLydia Wang /* Enable Boost Volume backdoor */ 308511890956SLydia Wang {0x1, 0xfb9, 0x24}, 308611890956SLydia Wang /* Enable AOW0 to MW9 */ 308711890956SLydia Wang {0x1, 0xfb8, 0x88}, 308811890956SLydia Wang { } 308911890956SLydia Wang }; 309025eaba2fSLydia Wang 309125eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 309290dd48a1STakashi Iwai static const struct hda_amp_list vt2002P_loopbacks[] = { 309325eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 309425eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 309525eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 309625eaba2fSLydia Wang { } /* end */ 309725eaba2fSLydia Wang }; 309825eaba2fSLydia Wang #endif 309925eaba2fSLydia Wang 31003e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 31013e95b9abSLydia Wang { 31023e95b9abSLydia Wang struct via_spec *spec = codec->spec; 31033e95b9abSLydia Wang int imux_is_smixer; 31043e95b9abSLydia Wang unsigned int parm; 31053e95b9abSLydia Wang unsigned int present; 31063e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 31073e95b9abSLydia Wang imux_is_smixer = 31083e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 31093e95b9abSLydia Wang /* inputs */ 31103e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 31113e95b9abSLydia Wang parm = AC_PWRST_D3; 31123e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 31133e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 31143e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 31153e95b9abSLydia Wang parm = AC_PWRST_D0; 31163e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 31173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 31183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 31193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 31203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 31213e95b9abSLydia Wang 31223e95b9abSLydia Wang /* outputs */ 31233e95b9abSLydia Wang /* AOW0 (8h)*/ 31243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 31253e95b9abSLydia Wang 312611890956SLydia Wang if (spec->codec_type == VT1802) { 312711890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 312811890956SLydia Wang parm = AC_PWRST_D3; 312911890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 313011890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 313111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 313211890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 313311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 313411890956SLydia Wang } else { 31353e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 31363e95b9abSLydia Wang parm = AC_PWRST_D3; 31373e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 31383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 31393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 31413e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 314211890956SLydia Wang } 31433e95b9abSLydia Wang 314411890956SLydia Wang if (spec->codec_type == VT1802) { 314511890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 314611890956SLydia Wang parm = AC_PWRST_D3; 314711890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 314811890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 314911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 315011890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 315111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 315211890956SLydia Wang } else { 31533e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 31543e95b9abSLydia Wang parm = AC_PWRST_D3; 31553e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 31563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 31573e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 31593e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 316011890956SLydia Wang } 31613e95b9abSLydia Wang 31623e95b9abSLydia Wang if (spec->hp_independent_mode) 31633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 31643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31653e95b9abSLydia Wang 31663e95b9abSLydia Wang /* Class-D */ 31673e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 31683e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 31693e95b9abSLydia Wang 31703e95b9abSLydia Wang parm = AC_PWRST_D3; 31713e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 31723e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 317311890956SLydia Wang if (spec->codec_type == VT1802) 317411890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 317511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 317611890956SLydia Wang else 31773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 31783e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31793e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 31803e95b9abSLydia Wang 31813e95b9abSLydia Wang /* Mono Out */ 31823e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 31833e95b9abSLydia Wang 31843e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 318511890956SLydia Wang if (spec->codec_type == VT1802) { 318611890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 318711890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 318811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 318911890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 319011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 319111890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 319211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 319311890956SLydia Wang } else { 31943e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 31953e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 31963e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 31983e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 32003e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 320111890956SLydia Wang } 32023e95b9abSLydia Wang /* MW9 (21h) */ 32033e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 32043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32053e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 32063e95b9abSLydia Wang else 32073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 32093e95b9abSLydia Wang } 321025eaba2fSLydia Wang 321125eaba2fSLydia Wang /* patch for vt2002P */ 321225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 321325eaba2fSLydia Wang { 321425eaba2fSLydia Wang struct via_spec *spec; 321525eaba2fSLydia Wang int err; 321625eaba2fSLydia Wang 321725eaba2fSLydia Wang /* create a codec specific record */ 32185b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 321925eaba2fSLydia Wang if (spec == NULL) 322025eaba2fSLydia Wang return -ENOMEM; 322125eaba2fSLydia Wang 3222620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3223d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3224d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3225620e2b28STakashi Iwai 322625eaba2fSLydia Wang /* automatic parse from the BIOS config */ 322712daef65STakashi Iwai err = via_parse_auto_config(codec); 322825eaba2fSLydia Wang if (err < 0) { 322925eaba2fSLydia Wang via_free(codec); 323025eaba2fSLydia Wang return err; 323125eaba2fSLydia Wang } 323225eaba2fSLydia Wang 323311890956SLydia Wang if (spec->codec_type == VT1802) 32344a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 323511890956SLydia Wang else 32364a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 323711890956SLydia Wang 323825eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 323925eaba2fSLydia Wang 324025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 324125eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 324225eaba2fSLydia Wang #endif 324325eaba2fSLydia Wang 32443e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 324525eaba2fSLydia Wang return 0; 324625eaba2fSLydia Wang } 3247ab6734e7SLydia Wang 3248ab6734e7SLydia Wang /* for vt1812 */ 3249ab6734e7SLydia Wang 3250096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 3251ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 3252ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 3253ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 3254ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 3255ab6734e7SLydia Wang { } 3256ab6734e7SLydia Wang }; 3257ab6734e7SLydia Wang 3258ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 325990dd48a1STakashi Iwai static const struct hda_amp_list vt1812_loopbacks[] = { 3260ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 3261ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 3262ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 3263ab6734e7SLydia Wang { } /* end */ 3264ab6734e7SLydia Wang }; 3265ab6734e7SLydia Wang #endif 3266ab6734e7SLydia Wang 32673e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 32683e95b9abSLydia Wang { 32693e95b9abSLydia Wang struct via_spec *spec = codec->spec; 32703e95b9abSLydia Wang int imux_is_smixer = 32713e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 32723e95b9abSLydia Wang unsigned int parm; 32733e95b9abSLydia Wang unsigned int present; 32743e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 32753e95b9abSLydia Wang imux_is_smixer = 32763e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 32773e95b9abSLydia Wang /* inputs */ 32783e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 32793e95b9abSLydia Wang parm = AC_PWRST_D3; 32803e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 32813e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 32823e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 32833e95b9abSLydia Wang parm = AC_PWRST_D0; 32843e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 32853e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 32863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 32873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 32883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 32893e95b9abSLydia Wang 32903e95b9abSLydia Wang /* outputs */ 32913e95b9abSLydia Wang /* AOW0 (8h)*/ 32923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 32933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 32943e95b9abSLydia Wang 32953e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 32963e95b9abSLydia Wang parm = AC_PWRST_D3; 32973e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 32983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 32993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 33003e95b9abSLydia Wang 33013e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 33023e95b9abSLydia Wang parm = AC_PWRST_D3; 33033e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 33043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 33053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 33063e95b9abSLydia Wang if (spec->hp_independent_mode) 33073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 33083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33093e95b9abSLydia Wang 33103e95b9abSLydia Wang /* Internal Speaker */ 33113e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 33123e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 33133e95b9abSLydia Wang 33143e95b9abSLydia Wang parm = AC_PWRST_D3; 33153e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 33163e95b9abSLydia Wang if (present) { 33173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33183e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33203e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33213e95b9abSLydia Wang } else { 33223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33233e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33253e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33263e95b9abSLydia Wang } 33273e95b9abSLydia Wang 33283e95b9abSLydia Wang 33293e95b9abSLydia Wang /* Mono Out */ 33303e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 33313e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 33323e95b9abSLydia Wang 33333e95b9abSLydia Wang parm = AC_PWRST_D3; 33343e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 33353e95b9abSLydia Wang if (present) { 33363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33373e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33403e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33413e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33423e95b9abSLydia Wang } else { 33433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33453e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33463e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33493e95b9abSLydia Wang } 33503e95b9abSLydia Wang 33513e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 33523e95b9abSLydia Wang parm = AC_PWRST_D3; 33533e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 33543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 33553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 33563e95b9abSLydia Wang 33573e95b9abSLydia Wang } 3358ab6734e7SLydia Wang 3359ab6734e7SLydia Wang /* patch for vt1812 */ 3360ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 3361ab6734e7SLydia Wang { 3362ab6734e7SLydia Wang struct via_spec *spec; 3363ab6734e7SLydia Wang int err; 3364ab6734e7SLydia Wang 3365ab6734e7SLydia Wang /* create a codec specific record */ 33665b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3367ab6734e7SLydia Wang if (spec == NULL) 3368ab6734e7SLydia Wang return -ENOMEM; 3369ab6734e7SLydia Wang 3370620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3371d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3372d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3373620e2b28STakashi Iwai 3374ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 337512daef65STakashi Iwai err = via_parse_auto_config(codec); 3376ab6734e7SLydia Wang if (err < 0) { 3377ab6734e7SLydia Wang via_free(codec); 3378ab6734e7SLydia Wang return err; 3379ab6734e7SLydia Wang } 3380ab6734e7SLydia Wang 3381096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 3382ab6734e7SLydia Wang 3383ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 3384ab6734e7SLydia Wang 3385ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 3386ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 3387ab6734e7SLydia Wang #endif 3388ab6734e7SLydia Wang 33893e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 3390ab6734e7SLydia Wang return 0; 3391ab6734e7SLydia Wang } 3392ab6734e7SLydia Wang 3393c577b8a1SJoseph Chan /* 3394c577b8a1SJoseph Chan * patch entries 3395c577b8a1SJoseph Chan */ 339690dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 33973218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 33983218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 33993218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 34003218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 34013218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3402f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34033218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3404f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34053218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3406f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34073218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3408f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 34093218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3410f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34113218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3412f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34133218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3414f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34153218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3416f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 34173218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3418f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34193218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3420f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34213218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3422f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34233218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3424f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 34253218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3426f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34273218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3428f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34293218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3430f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34313218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3432f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 34333218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3434d949cac1SHarald Welte .patch = patch_vt1708S}, 34353218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3436d949cac1SHarald Welte .patch = patch_vt1708S}, 34373218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3438d949cac1SHarald Welte .patch = patch_vt1708S}, 34393218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3440d949cac1SHarald Welte .patch = patch_vt1708S}, 3441bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 3442d949cac1SHarald Welte .patch = patch_vt1708S}, 34433218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3444d949cac1SHarald Welte .patch = patch_vt1708S}, 34453218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3446d949cac1SHarald Welte .patch = patch_vt1708S}, 34473218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3448d949cac1SHarald Welte .patch = patch_vt1708S}, 34493218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3450d949cac1SHarald Welte .patch = patch_vt1702}, 34513218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3452d949cac1SHarald Welte .patch = patch_vt1702}, 34533218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3454d949cac1SHarald Welte .patch = patch_vt1702}, 34553218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3456d949cac1SHarald Welte .patch = patch_vt1702}, 34573218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3458d949cac1SHarald Welte .patch = patch_vt1702}, 34593218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3460d949cac1SHarald Welte .patch = patch_vt1702}, 34613218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3462d949cac1SHarald Welte .patch = patch_vt1702}, 34633218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3464d949cac1SHarald Welte .patch = patch_vt1702}, 3465eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 3466eb7188caSLydia Wang .patch = patch_vt1718S}, 3467eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 3468eb7188caSLydia Wang .patch = patch_vt1718S}, 3469bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 3470bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3471bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 3472bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3473f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 3474f3db423dSLydia Wang .patch = patch_vt1716S}, 3475f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 3476f3db423dSLydia Wang .patch = patch_vt1716S}, 347725eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 347825eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 3479ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 348036dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 348136dd5c4aSLydia Wang .patch = patch_vt1708S}, 348211890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 348311890956SLydia Wang .patch = patch_vt2002P}, 348411890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 348511890956SLydia Wang .patch = patch_vt2002P}, 3486c577b8a1SJoseph Chan {} /* terminator */ 3487c577b8a1SJoseph Chan }; 34881289e9e8STakashi Iwai 34891289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 34901289e9e8STakashi Iwai 34911289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 34921289e9e8STakashi Iwai .preset = snd_hda_preset_via, 34931289e9e8STakashi Iwai .owner = THIS_MODULE, 34941289e9e8STakashi Iwai }; 34951289e9e8STakashi Iwai 34961289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 34971289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 34981289e9e8STakashi Iwai 34991289e9e8STakashi Iwai static int __init patch_via_init(void) 35001289e9e8STakashi Iwai { 35011289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 35021289e9e8STakashi Iwai } 35031289e9e8STakashi Iwai 35041289e9e8STakashi Iwai static void __exit patch_via_exit(void) 35051289e9e8STakashi Iwai { 35061289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 35071289e9e8STakashi Iwai } 35081289e9e8STakashi Iwai 35091289e9e8STakashi Iwai module_init(patch_via_init) 35101289e9e8STakashi Iwai module_exit(patch_via_exit) 3511