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 57c577b8a1SJoseph Chan /* Pin Widget NID */ 5876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 5976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 60c577b8a1SJoseph Chan 61d7426329SHarald Welte enum VIA_HDA_CODEC { 62d7426329SHarald Welte UNKNOWN = -1, 63d7426329SHarald Welte VT1708, 64d7426329SHarald Welte VT1709_10CH, 65d7426329SHarald Welte VT1709_6CH, 66d7426329SHarald Welte VT1708B_8CH, 67d7426329SHarald Welte VT1708B_4CH, 68d7426329SHarald Welte VT1708S, 69518bf3baSLydia Wang VT1708BCE, 70d7426329SHarald Welte VT1702, 71eb7188caSLydia Wang VT1718S, 72f3db423dSLydia Wang VT1716S, 7325eaba2fSLydia Wang VT2002P, 74ab6734e7SLydia Wang VT1812, 7511890956SLydia Wang VT1802, 76d7426329SHarald Welte CODEC_TYPES, 77d7426329SHarald Welte }; 78d7426329SHarald Welte 7911890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \ 8011890956SLydia Wang ((spec)->codec_type == VT2002P ||\ 8111890956SLydia Wang (spec)->codec_type == VT1812 ||\ 8211890956SLydia Wang (spec)->codec_type == VT1802) 8311890956SLydia Wang 848e3679dcSTakashi Iwai #define MAX_NID_PATH_DEPTH 5 858e3679dcSTakashi Iwai 8609a9ad69STakashi Iwai /* output-path: DAC -> ... -> pin 8709a9ad69STakashi Iwai * idx[] contains the source index number of the next widget; 8809a9ad69STakashi Iwai * e.g. idx[0] is the index of the DAC selected by path[1] widget 8909a9ad69STakashi Iwai * multi[] indicates whether it's a selector widget with multi-connectors 9009a9ad69STakashi Iwai * (i.e. the connection selection is mandatory) 9109a9ad69STakashi Iwai * vol_ctl and mute_ctl contains the NIDs for the assigned mixers 9209a9ad69STakashi Iwai */ 934a79616dSTakashi Iwai struct nid_path { 944a79616dSTakashi Iwai int depth; 958e3679dcSTakashi Iwai hda_nid_t path[MAX_NID_PATH_DEPTH]; 9609a9ad69STakashi Iwai unsigned char idx[MAX_NID_PATH_DEPTH]; 9709a9ad69STakashi Iwai unsigned char multi[MAX_NID_PATH_DEPTH]; 9809a9ad69STakashi Iwai unsigned int vol_ctl; 9909a9ad69STakashi Iwai unsigned int mute_ctl; 1004a79616dSTakashi Iwai }; 1014a79616dSTakashi Iwai 102a86a88eaSTakashi Iwai /* input-path */ 103a86a88eaSTakashi Iwai struct via_input { 104a86a88eaSTakashi Iwai hda_nid_t pin; /* input-pin or aa-mix */ 105a86a88eaSTakashi Iwai int adc_idx; /* ADC index to be used */ 106a86a88eaSTakashi Iwai int mux_idx; /* MUX index (if any) */ 107a86a88eaSTakashi Iwai const char *label; /* input-source label */ 108a86a88eaSTakashi Iwai }; 109a86a88eaSTakashi Iwai 1101f2e99feSLydia Wang struct via_spec { 1111f2e99feSLydia Wang /* codec parameterization */ 11290dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1131f2e99feSLydia Wang unsigned int num_mixers; 1141f2e99feSLydia Wang 11590dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1161f2e99feSLydia Wang unsigned int num_iverbs; 1171f2e99feSLydia Wang 11882673bc8STakashi Iwai char stream_name_analog[32]; 1197eb56e84STakashi Iwai char stream_name_hp[32]; 12090dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12190dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1221f2e99feSLydia Wang 12382673bc8STakashi Iwai char stream_name_digital[32]; 12490dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 12590dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1261f2e99feSLydia Wang 1271f2e99feSLydia Wang /* playback */ 1281f2e99feSLydia Wang struct hda_multi_out multiout; 1291f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 130ece8d043STakashi Iwai hda_nid_t hp_dac_nid; 131ada509ecSTakashi Iwai int num_active_streams; 1321f2e99feSLydia Wang 1334a79616dSTakashi Iwai struct nid_path out_path[4]; 1344a79616dSTakashi Iwai struct nid_path hp_path; 1354a79616dSTakashi Iwai struct nid_path hp_dep_path; 1364a918ffeSTakashi Iwai struct nid_path speaker_path; 1374a79616dSTakashi Iwai 1381f2e99feSLydia Wang /* capture */ 1391f2e99feSLydia Wang unsigned int num_adc_nids; 140a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1411f2e99feSLydia Wang hda_nid_t mux_nids[3]; 142620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1431f2e99feSLydia Wang hda_nid_t dig_in_nid; 1441f2e99feSLydia Wang 1451f2e99feSLydia Wang /* capture source */ 146a86a88eaSTakashi Iwai bool dyn_adc_switch; 147a86a88eaSTakashi Iwai int num_inputs; 148a86a88eaSTakashi Iwai struct via_input inputs[AUTO_CFG_MAX_INS + 1]; 1491f2e99feSLydia Wang unsigned int cur_mux[3]; 1501f2e99feSLydia Wang 151a86a88eaSTakashi Iwai /* dynamic ADC switching */ 152a86a88eaSTakashi Iwai hda_nid_t cur_adc; 153a86a88eaSTakashi Iwai unsigned int cur_adc_stream_tag; 154a86a88eaSTakashi Iwai unsigned int cur_adc_format; 155a86a88eaSTakashi Iwai 1561f2e99feSLydia Wang /* PCM information */ 1571f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1581f2e99feSLydia Wang 1591f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1601f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1611f2e99feSLydia Wang struct snd_array kctls; 1621f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1631f2e99feSLydia Wang 1641f2e99feSLydia Wang /* HP mode source */ 1651f2e99feSLydia Wang unsigned int hp_independent_mode; 166f3db423dSLydia Wang unsigned int dmic_enabled; 16724088a58STakashi Iwai unsigned int no_pin_power_ctl; 1681f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1691f2e99feSLydia Wang 170e3d7a143STakashi Iwai /* smart51 setup */ 171e3d7a143STakashi Iwai unsigned int smart51_nums; 172e3d7a143STakashi Iwai hda_nid_t smart51_pins[2]; 173e3d7a143STakashi Iwai int smart51_idxs[2]; 174e3d7a143STakashi Iwai const char *smart51_labels[2]; 175e3d7a143STakashi Iwai unsigned int smart51_enabled; 176e3d7a143STakashi Iwai 1771f2e99feSLydia Wang /* work to check hp jack state */ 1781f2e99feSLydia Wang struct hda_codec *codec; 1791f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 180e06e5a29STakashi Iwai int vt1708_jack_detect; 1811f2e99feSLydia Wang int vt1708_hp_present; 1823e95b9abSLydia Wang 1833e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1843e95b9abSLydia Wang 1851f2e99feSLydia Wang struct hda_loopback_check loopback; 18613af8e77STakashi Iwai int num_loopbacks; 18713af8e77STakashi Iwai struct hda_amp_list loopback_list[8]; 188a86a88eaSTakashi Iwai 189a86a88eaSTakashi Iwai /* bind capture-volume */ 190a86a88eaSTakashi Iwai struct hda_bind_ctls *bind_cap_vol; 191a86a88eaSTakashi Iwai struct hda_bind_ctls *bind_cap_sw; 1921f2e99feSLydia Wang }; 1931f2e99feSLydia Wang 1940341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1955b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1965b0cb1d8SJaroslav Kysela { 1975b0cb1d8SJaroslav Kysela struct via_spec *spec; 1985b0cb1d8SJaroslav Kysela 1995b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2005b0cb1d8SJaroslav Kysela if (spec == NULL) 2015b0cb1d8SJaroslav Kysela return NULL; 2025b0cb1d8SJaroslav Kysela 2035b0cb1d8SJaroslav Kysela codec->spec = spec; 2045b0cb1d8SJaroslav Kysela spec->codec = codec; 2050341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 2060341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 2070341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 2080341ccd7SLydia Wang spec->codec_type = VT1708S; 2095b0cb1d8SJaroslav Kysela return spec; 2105b0cb1d8SJaroslav Kysela } 2115b0cb1d8SJaroslav Kysela 212744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 213d7426329SHarald Welte { 214744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 215d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 216d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 217d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 218d7426329SHarald Welte 219d7426329SHarald Welte /* get codec type */ 220d7426329SHarald Welte if (ven_id != 0x1106) 221d7426329SHarald Welte codec_type = UNKNOWN; 222d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 223d7426329SHarald Welte codec_type = VT1708; 224d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 225d7426329SHarald Welte codec_type = VT1709_10CH; 226d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 227d7426329SHarald Welte codec_type = VT1709_6CH; 228518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 229d7426329SHarald Welte codec_type = VT1708B_8CH; 230518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 231518bf3baSLydia Wang codec_type = VT1708BCE; 232518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 233d7426329SHarald Welte codec_type = VT1708B_4CH; 234d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 235d7426329SHarald Welte && (dev_id >> 12) < 8) 236d7426329SHarald Welte codec_type = VT1708S; 237d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 238d7426329SHarald Welte && (dev_id >> 12) < 8) 239d7426329SHarald Welte codec_type = VT1702; 240eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 241eb7188caSLydia Wang && (dev_id >> 12) < 8) 242eb7188caSLydia Wang codec_type = VT1718S; 243f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 244f3db423dSLydia Wang codec_type = VT1716S; 245bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 246bb3c6bfcSLydia Wang codec_type = VT1718S; 24725eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 24825eaba2fSLydia Wang codec_type = VT2002P; 249ab6734e7SLydia Wang else if (dev_id == 0x0448) 250ab6734e7SLydia Wang codec_type = VT1812; 25136dd5c4aSLydia Wang else if (dev_id == 0x0440) 25236dd5c4aSLydia Wang codec_type = VT1708S; 25311890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 25411890956SLydia Wang codec_type = VT1802; 255d7426329SHarald Welte else 256d7426329SHarald Welte codec_type = UNKNOWN; 257d7426329SHarald Welte return codec_type; 258d7426329SHarald Welte }; 259d7426329SHarald Welte 260ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 26169e52a80SHarald Welte #define VIA_HP_EVENT 0x01 26269e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 2634a918ffeSTakashi Iwai #define VIA_LINE_EVENT 0x03 26469e52a80SHarald Welte 265c577b8a1SJoseph Chan enum { 266c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 267c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 268f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 269c577b8a1SJoseph Chan }; 270c577b8a1SJoseph Chan 271ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 272ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 2731f2e99feSLydia Wang 2741f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2751f2e99feSLydia Wang { 2761f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2771f2e99feSLydia Wang return; 2781f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 279e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2801f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2811f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2821f2e99feSLydia Wang msecs_to_jiffies(100)); 2831f2e99feSLydia Wang } 2841f2e99feSLydia Wang 2851f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2861f2e99feSLydia Wang { 2871f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2881f2e99feSLydia Wang return; 2891f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2901f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2911f2e99feSLydia Wang return; 2921f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 293e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2945b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2951f2e99feSLydia Wang } 296f5271101SLydia Wang 2973e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2983e95b9abSLydia Wang { 2993e95b9abSLydia Wang struct via_spec *spec = codec->spec; 3003e95b9abSLydia Wang if (spec->set_widgets_power_state) 3013e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3023e95b9abSLydia Wang } 30325eaba2fSLydia Wang 304f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 305f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 306f5271101SLydia Wang { 307f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 308f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 309f5271101SLydia Wang 3103e95b9abSLydia Wang set_widgets_power_state(codec); 311ada509ecSTakashi Iwai analog_low_current_mode(snd_kcontrol_chip(kcontrol)); 3121f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3131f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3141f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3151f2e99feSLydia Wang else 3161f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3171f2e99feSLydia Wang } 318f5271101SLydia Wang return change; 319f5271101SLydia Wang } 320f5271101SLydia Wang 321f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 322f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 323f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 324f5271101SLydia Wang .name = NULL, \ 325f5271101SLydia Wang .index = 0, \ 326f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 327f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 328f5271101SLydia Wang .put = analog_input_switch_put, \ 329f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 330f5271101SLydia Wang 33190dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 332c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 333c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 334f5271101SLydia Wang ANALOG_INPUT_MUTE, 335c577b8a1SJoseph Chan }; 336c577b8a1SJoseph Chan 337ab6734e7SLydia Wang 338c577b8a1SJoseph Chan /* add dynamic controls */ 339291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 340291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 341291c9e33STakashi Iwai const char *name) 342c577b8a1SJoseph Chan { 343c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 344c577b8a1SJoseph Chan 345603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 346603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 347c577b8a1SJoseph Chan if (!knew) 348291c9e33STakashi Iwai return NULL; 349291c9e33STakashi Iwai *knew = *tmpl; 350291c9e33STakashi Iwai if (!name) 351291c9e33STakashi Iwai name = tmpl->name; 352291c9e33STakashi Iwai if (name) { 353c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 354c577b8a1SJoseph Chan if (!knew->name) 355291c9e33STakashi Iwai return NULL; 356291c9e33STakashi Iwai } 357291c9e33STakashi Iwai return knew; 358291c9e33STakashi Iwai } 359291c9e33STakashi Iwai 360291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 361291c9e33STakashi Iwai int idx, unsigned long val) 362291c9e33STakashi Iwai { 363291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 364291c9e33STakashi Iwai 365291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 366291c9e33STakashi Iwai if (!knew) 367c577b8a1SJoseph Chan return -ENOMEM; 368d7a99cceSTakashi Iwai knew->index = idx; 3694d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 3705e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 371c577b8a1SJoseph Chan knew->private_value = val; 372c577b8a1SJoseph Chan return 0; 373c577b8a1SJoseph Chan } 374c577b8a1SJoseph Chan 3757b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 3767b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 3777b315bb4STakashi Iwai 378291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 3795b0cb1d8SJaroslav Kysela 380603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 381603c4019STakashi Iwai { 382603c4019STakashi Iwai struct via_spec *spec = codec->spec; 383603c4019STakashi Iwai 384603c4019STakashi Iwai if (spec->kctls.list) { 385603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 386603c4019STakashi Iwai int i; 387603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 388603c4019STakashi Iwai kfree(kctl[i].name); 389603c4019STakashi Iwai } 390603c4019STakashi Iwai snd_array_free(&spec->kctls); 391603c4019STakashi Iwai } 392603c4019STakashi Iwai 393c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 3949510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 3957b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 396c577b8a1SJoseph Chan { 397c577b8a1SJoseph Chan char name[32]; 398c577b8a1SJoseph Chan int err; 399c577b8a1SJoseph Chan 400c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4017b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 402c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 403c577b8a1SJoseph Chan if (err < 0) 404c577b8a1SJoseph Chan return err; 405c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 4067b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 407c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 408c577b8a1SJoseph Chan if (err < 0) 409c577b8a1SJoseph Chan return err; 410c577b8a1SJoseph Chan return 0; 411c577b8a1SJoseph Chan } 412c577b8a1SJoseph Chan 4135d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \ 414*8d087c76STakashi Iwai snd_hda_get_conn_index(codec, mux, nid, 0) 4155d41762aSTakashi Iwai 4168df2a312STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, 4178df2a312STakashi Iwai unsigned int mask) 4188df2a312STakashi Iwai { 419a934d5a9STakashi Iwai unsigned int caps; 420a934d5a9STakashi Iwai if (!nid) 421a934d5a9STakashi Iwai return false; 422a934d5a9STakashi Iwai caps = get_wcaps(codec, nid); 4238df2a312STakashi Iwai if (dir == HDA_INPUT) 4248df2a312STakashi Iwai caps &= AC_WCAP_IN_AMP; 4258df2a312STakashi Iwai else 4268df2a312STakashi Iwai caps &= AC_WCAP_OUT_AMP; 4278df2a312STakashi Iwai if (!caps) 4288df2a312STakashi Iwai return false; 4298df2a312STakashi Iwai if (query_amp_caps(codec, nid, dir) & mask) 4308df2a312STakashi Iwai return true; 4318df2a312STakashi Iwai return false; 4328df2a312STakashi Iwai } 4338df2a312STakashi Iwai 43409a9ad69STakashi Iwai #define have_mute(codec, nid, dir) \ 43509a9ad69STakashi Iwai check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) 4368df2a312STakashi Iwai 43709a9ad69STakashi Iwai /* enable/disable the output-route */ 43809a9ad69STakashi Iwai static void activate_output_path(struct hda_codec *codec, struct nid_path *path, 43909a9ad69STakashi Iwai bool enable, bool force) 4405d41762aSTakashi Iwai { 44109a9ad69STakashi Iwai int i; 44209a9ad69STakashi Iwai for (i = 0; i < path->depth; i++) { 44309a9ad69STakashi Iwai hda_nid_t src, dst; 44409a9ad69STakashi Iwai int idx = path->idx[i]; 44509a9ad69STakashi Iwai src = path->path[i]; 44609a9ad69STakashi Iwai if (i < path->depth - 1) 44709a9ad69STakashi Iwai dst = path->path[i + 1]; 44809a9ad69STakashi Iwai else 44909a9ad69STakashi Iwai dst = 0; 45009a9ad69STakashi Iwai if (enable && path->multi[i]) 45109a9ad69STakashi Iwai snd_hda_codec_write(codec, dst, 0, 4525d41762aSTakashi Iwai AC_VERB_SET_CONNECT_SEL, idx); 45309a9ad69STakashi Iwai if (have_mute(codec, dst, HDA_INPUT)) { 45409a9ad69STakashi Iwai int val = enable ? AMP_IN_UNMUTE(idx) : 45509a9ad69STakashi Iwai AMP_IN_MUTE(idx); 45609a9ad69STakashi Iwai snd_hda_codec_write(codec, dst, 0, 45709a9ad69STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, val); 45809a9ad69STakashi Iwai } 45909a9ad69STakashi Iwai if (!force && (src == path->vol_ctl || src == path->mute_ctl)) 46009a9ad69STakashi Iwai continue; 46109a9ad69STakashi Iwai if (have_mute(codec, src, HDA_OUTPUT)) { 46209a9ad69STakashi Iwai int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; 46309a9ad69STakashi Iwai snd_hda_codec_write(codec, src, 0, 46409a9ad69STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, val); 46509a9ad69STakashi Iwai } 46609a9ad69STakashi Iwai } 4675d41762aSTakashi Iwai } 4685d41762aSTakashi Iwai 4695d41762aSTakashi Iwai /* set the given pin as output */ 4705d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, 4715d41762aSTakashi Iwai int pin_type) 4725d41762aSTakashi Iwai { 4735d41762aSTakashi Iwai if (!pin) 4745d41762aSTakashi Iwai return; 4755d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 4765d41762aSTakashi Iwai pin_type); 4775d41762aSTakashi Iwai if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) 4785d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 479d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 480c577b8a1SJoseph Chan } 481c577b8a1SJoseph Chan 48209a9ad69STakashi Iwai static void via_auto_init_output(struct hda_codec *codec, 48309a9ad69STakashi Iwai struct nid_path *path, int pin_type, 48409a9ad69STakashi Iwai bool force) 4855d41762aSTakashi Iwai { 4865d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 4875d41762aSTakashi Iwai unsigned int caps; 48809a9ad69STakashi Iwai hda_nid_t pin, nid; 48909a9ad69STakashi Iwai int i, idx; 4905d41762aSTakashi Iwai 49109a9ad69STakashi Iwai if (!path->depth) 4925d41762aSTakashi Iwai return; 49309a9ad69STakashi Iwai pin = path->path[path->depth - 1]; 4945d41762aSTakashi Iwai 4955d41762aSTakashi Iwai init_output_pin(codec, pin, pin_type); 4965d41762aSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_OUTPUT); 4975d41762aSTakashi Iwai if (caps & AC_AMPCAP_MUTE) { 4985d41762aSTakashi Iwai unsigned int val; 4995d41762aSTakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 5005d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, 5015d41762aSTakashi Iwai AMP_OUT_MUTE | val); 5025d41762aSTakashi Iwai } 5035d41762aSTakashi Iwai 50409a9ad69STakashi Iwai activate_output_path(codec, path, true, force); 5055d41762aSTakashi Iwai 50609a9ad69STakashi Iwai /* initialize the AA-path */ 50709a9ad69STakashi Iwai if (!spec->aa_mix_nid) 50809a9ad69STakashi Iwai return; 50909a9ad69STakashi Iwai for (i = path->depth - 1; i > 0; i--) { 51009a9ad69STakashi Iwai nid = path->path[i]; 51109a9ad69STakashi Iwai idx = get_connection_index(codec, nid, spec->aa_mix_nid); 51209a9ad69STakashi Iwai if (idx >= 0) { 51309a9ad69STakashi Iwai if (have_mute(codec, nid, HDA_INPUT)) 51409a9ad69STakashi Iwai snd_hda_codec_write(codec, nid, 0, 51509a9ad69STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 51609a9ad69STakashi Iwai AMP_IN_UNMUTE(idx)); 51709a9ad69STakashi Iwai break; 51809a9ad69STakashi Iwai } 51909a9ad69STakashi Iwai } 52009a9ad69STakashi Iwai } 521c577b8a1SJoseph Chan 522c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 523c577b8a1SJoseph Chan { 524c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 525c577b8a1SJoseph Chan int i; 526c577b8a1SJoseph Chan 527e3d7a143STakashi Iwai for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) 52809a9ad69STakashi Iwai via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true); 529c577b8a1SJoseph Chan } 530c577b8a1SJoseph Chan 531c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 532c577b8a1SJoseph Chan { 533c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 534c577b8a1SJoseph Chan 53509a9ad69STakashi Iwai if (!spec->hp_dac_nid) { 53609a9ad69STakashi Iwai via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); 53709a9ad69STakashi Iwai return; 53809a9ad69STakashi Iwai } 53909a9ad69STakashi Iwai if (spec->hp_independent_mode) { 54009a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_dep_path, false, false); 54109a9ad69STakashi Iwai via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); 54209a9ad69STakashi Iwai } else { 54309a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_path, false, false); 54409a9ad69STakashi Iwai via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true); 54509a9ad69STakashi Iwai } 54625eaba2fSLydia Wang } 547c577b8a1SJoseph Chan 5484a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec) 5494a918ffeSTakashi Iwai { 5504a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 5514a918ffeSTakashi Iwai 5524a918ffeSTakashi Iwai if (spec->autocfg.speaker_outs) 55309a9ad69STakashi Iwai via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true); 5544a918ffeSTakashi Iwai } 5554a918ffeSTakashi Iwai 556f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 55732e0191dSClemens Ladisch 558c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 559c577b8a1SJoseph Chan { 560c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5617b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 562096a8854STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 56332e0191dSClemens Ladisch unsigned int ctl; 564096a8854STakashi Iwai int i, num_conns; 565c577b8a1SJoseph Chan 566096a8854STakashi Iwai /* init ADCs */ 567096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 568096a8854STakashi Iwai snd_hda_codec_write(codec, spec->adc_nids[i], 0, 569096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 570096a8854STakashi Iwai AMP_IN_UNMUTE(0)); 571096a8854STakashi Iwai } 572096a8854STakashi Iwai 573096a8854STakashi Iwai /* init pins */ 5747b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5757b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 576f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 57732e0191dSClemens Ladisch ctl = PIN_OUT; 57830649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 57932e0191dSClemens Ladisch ctl = PIN_VREF50; 58032e0191dSClemens Ladisch else 58132e0191dSClemens Ladisch ctl = PIN_IN; 582c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 58332e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 584c577b8a1SJoseph Chan } 585096a8854STakashi Iwai 586096a8854STakashi Iwai /* init input-src */ 587096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 588a86a88eaSTakashi Iwai int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; 589a86a88eaSTakashi Iwai if (spec->mux_nids[adc_idx]) { 590a86a88eaSTakashi Iwai int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; 591a86a88eaSTakashi Iwai snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 592096a8854STakashi Iwai AC_VERB_SET_CONNECT_SEL, 593a86a88eaSTakashi Iwai mux_idx); 594a86a88eaSTakashi Iwai } 595a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 596a86a88eaSTakashi Iwai break; /* only one input-src */ 597096a8854STakashi Iwai } 598096a8854STakashi Iwai 599096a8854STakashi Iwai /* init aa-mixer */ 600096a8854STakashi Iwai if (!spec->aa_mix_nid) 601096a8854STakashi Iwai return; 602096a8854STakashi Iwai num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 603096a8854STakashi Iwai ARRAY_SIZE(conn)); 604096a8854STakashi Iwai for (i = 0; i < num_conns; i++) { 605096a8854STakashi Iwai unsigned int caps = get_wcaps(codec, conn[i]); 606096a8854STakashi Iwai if (get_wcaps_type(caps) == AC_WID_PIN) 607096a8854STakashi Iwai snd_hda_codec_write(codec, spec->aa_mix_nid, 0, 608096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 609096a8854STakashi Iwai AMP_IN_MUTE(i)); 610096a8854STakashi Iwai } 611c577b8a1SJoseph Chan } 612f5271101SLydia Wang 613f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 614f5271101SLydia Wang unsigned int *affected_parm) 615f5271101SLydia Wang { 616f5271101SLydia Wang unsigned parm; 617f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 618f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 619f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 620f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 6211564b287SLydia Wang struct via_spec *spec = codec->spec; 62224088a58STakashi Iwai unsigned present = 0; 62324088a58STakashi Iwai 62424088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 62524088a58STakashi Iwai if (!no_presence) 62624088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 627f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 6281564b287SLydia Wang || ((no_presence || present) 6291564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 630f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 631f5271101SLydia Wang parm = AC_PWRST_D0; 632f5271101SLydia Wang } else 633f5271101SLydia Wang parm = AC_PWRST_D3; 634f5271101SLydia Wang 635f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 636f5271101SLydia Wang } 637f5271101SLydia Wang 63824088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 63924088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 64024088a58STakashi Iwai { 64124088a58STakashi Iwai static const char * const texts[] = { 64224088a58STakashi Iwai "Disabled", "Enabled" 64324088a58STakashi Iwai }; 64424088a58STakashi Iwai 64524088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 64624088a58STakashi Iwai uinfo->count = 1; 64724088a58STakashi Iwai uinfo->value.enumerated.items = 2; 64824088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 64924088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 65024088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 65124088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 65224088a58STakashi Iwai return 0; 65324088a58STakashi Iwai } 65424088a58STakashi Iwai 65524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 65624088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 65724088a58STakashi Iwai { 65824088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 65924088a58STakashi Iwai struct via_spec *spec = codec->spec; 66024088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 66124088a58STakashi Iwai return 0; 66224088a58STakashi Iwai } 66324088a58STakashi Iwai 66424088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 66524088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 66624088a58STakashi Iwai { 66724088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 66824088a58STakashi Iwai struct via_spec *spec = codec->spec; 66924088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 67024088a58STakashi Iwai 67124088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 67224088a58STakashi Iwai return 0; 67324088a58STakashi Iwai spec->no_pin_power_ctl = val; 67424088a58STakashi Iwai set_widgets_power_state(codec); 67524088a58STakashi Iwai return 1; 67624088a58STakashi Iwai } 67724088a58STakashi Iwai 67824088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 67924088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 68024088a58STakashi Iwai .name = "Dynamic Power-Control", 68124088a58STakashi Iwai .info = via_pin_power_ctl_info, 68224088a58STakashi Iwai .get = via_pin_power_ctl_get, 68324088a58STakashi Iwai .put = via_pin_power_ctl_put, 68424088a58STakashi Iwai }; 68524088a58STakashi Iwai 68624088a58STakashi Iwai 6870aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6880aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6890aa62aefSHarald Welte { 6908df2a312STakashi Iwai static const char * const texts[] = { "OFF", "ON" }; 6918df2a312STakashi Iwai 6928df2a312STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 6938df2a312STakashi Iwai uinfo->count = 1; 6948df2a312STakashi Iwai uinfo->value.enumerated.items = 2; 6958df2a312STakashi Iwai if (uinfo->value.enumerated.item >= 2) 6968df2a312STakashi Iwai uinfo->value.enumerated.item = 1; 6978df2a312STakashi Iwai strcpy(uinfo->value.enumerated.name, 6988df2a312STakashi Iwai texts[uinfo->value.enumerated.item]); 6998df2a312STakashi Iwai return 0; 7000aa62aefSHarald Welte } 7010aa62aefSHarald Welte 7020aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7030aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7040aa62aefSHarald Welte { 7050aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 706cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 707cdc1784dSLydia Wang 708ece8d043STakashi Iwai ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; 709cdc1784dSLydia Wang return 0; 710cdc1784dSLydia Wang } 711cdc1784dSLydia Wang 7120aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7130aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7140aa62aefSHarald Welte { 7150aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7160aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7178df2a312STakashi Iwai 7188df2a312STakashi Iwai spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0]; 71909a9ad69STakashi Iwai if (spec->hp_independent_mode) { 72009a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_dep_path, false, false); 72109a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_path, true, false); 72209a9ad69STakashi Iwai } else { 72309a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_path, false, false); 72409a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_dep_path, true, false); 7258df2a312STakashi Iwai } 7260aa62aefSHarald Welte 727ce0e5a9eSLydia Wang /* update jack power state */ 7283e95b9abSLydia Wang set_widgets_power_state(codec); 7290aa62aefSHarald Welte return 0; 7300aa62aefSHarald Welte } 7310aa62aefSHarald Welte 732ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = { 7330aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7340aa62aefSHarald Welte .name = "Independent HP", 7350aa62aefSHarald Welte .info = via_independent_hp_info, 7360aa62aefSHarald Welte .get = via_independent_hp_get, 7370aa62aefSHarald Welte .put = via_independent_hp_put, 7380aa62aefSHarald Welte }; 7390aa62aefSHarald Welte 7403d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7415b0cb1d8SJaroslav Kysela { 7423d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7435b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7445b0cb1d8SJaroslav Kysela hda_nid_t nid; 7455b0cb1d8SJaroslav Kysela 7465b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 747ece8d043STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer); 7483d83e577STakashi Iwai if (knew == NULL) 7493d83e577STakashi Iwai return -ENOMEM; 7503d83e577STakashi Iwai 7515b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 7525b0cb1d8SJaroslav Kysela 7535b0cb1d8SJaroslav Kysela return 0; 7545b0cb1d8SJaroslav Kysela } 7555b0cb1d8SJaroslav Kysela 7561564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 7571564b287SLydia Wang { 758e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 7591564b287SLydia Wang int i; 7601564b287SLydia Wang 761e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 762e3d7a143STakashi Iwai struct snd_kcontrol *ctl; 763e3d7a143STakashi Iwai struct snd_ctl_elem_id id; 7641564b287SLydia Wang memset(&id, 0, sizeof(id)); 7651564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 766e3d7a143STakashi Iwai sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); 767525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 768525566cbSLydia Wang if (ctl) 769525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 770525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 771525566cbSLydia Wang &ctl->id); 7721564b287SLydia Wang } 7731564b287SLydia Wang } 7741564b287SLydia Wang 7751564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 7761564b287SLydia Wang { 7771564b287SLydia Wang struct via_spec *spec = codec->spec; 7781564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 779e3d7a143STakashi Iwai int i; 780e3d7a143STakashi Iwai 781e3d7a143STakashi Iwai /* check AA path's mute status */ 782e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 783e3d7a143STakashi Iwai if (spec->smart51_idxs[i] < 0) 784e3d7a143STakashi Iwai continue; 785e3d7a143STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, 786e3d7a143STakashi Iwai HDA_INPUT, spec->smart51_idxs[i], 7871564b287SLydia Wang HDA_AMP_MUTE, val); 7881564b287SLydia Wang } 7891564b287SLydia Wang } 790f4a7828bSTakashi Iwai 791e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 792e3d7a143STakashi Iwai { 793e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 794e3d7a143STakashi Iwai int i; 795e3d7a143STakashi Iwai 796e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) 797e3d7a143STakashi Iwai if (spec->smart51_pins[i] == pin) 798e3d7a143STakashi Iwai return true; 799e3d7a143STakashi Iwai return false; 800e3d7a143STakashi Iwai } 801e3d7a143STakashi Iwai 8021564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 8031564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8041564b287SLydia Wang { 8051564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8061564b287SLydia Wang struct via_spec *spec = codec->spec; 8071564b287SLydia Wang 808f2b1c9f0STakashi Iwai *ucontrol->value.integer.value = spec->smart51_enabled; 8091564b287SLydia Wang return 0; 8101564b287SLydia Wang } 8111564b287SLydia Wang 8121564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 8131564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8141564b287SLydia Wang { 8151564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8161564b287SLydia Wang struct via_spec *spec = codec->spec; 8171564b287SLydia Wang int out_in = *ucontrol->value.integer.value 8181564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 8191564b287SLydia Wang int i; 8201564b287SLydia Wang 821e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 822e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 8237b315bb4STakashi Iwai unsigned int parm; 8247b315bb4STakashi Iwai 8257b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 8261564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8271564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 8281564b287SLydia Wang parm |= out_in; 8291564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 8301564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 8311564b287SLydia Wang parm); 8321564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 8331564b287SLydia Wang mute_aa_path(codec, 1); 8341564b287SLydia Wang notify_aa_path_ctls(codec); 8351564b287SLydia Wang } 8361564b287SLydia Wang } 8371564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 8383e95b9abSLydia Wang set_widgets_power_state(codec); 8391564b287SLydia Wang return 1; 8401564b287SLydia Wang } 8411564b287SLydia Wang 8425f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 8431564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8441564b287SLydia Wang .name = "Smart 5.1", 8451564b287SLydia Wang .count = 1, 846f2b1c9f0STakashi Iwai .info = snd_ctl_boolean_mono_info, 8471564b287SLydia Wang .get = via_smart51_get, 8481564b287SLydia Wang .put = via_smart51_put, 8491564b287SLydia Wang }; 8501564b287SLydia Wang 851f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 8525b0cb1d8SJaroslav Kysela { 853f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 8545b0cb1d8SJaroslav Kysela 855e3d7a143STakashi Iwai if (!spec->smart51_nums) 856cb34c207SLydia Wang return 0; 857e3d7a143STakashi Iwai if (!via_clone_control(spec, &via_smart51_mixer)) 8585b0cb1d8SJaroslav Kysela return -ENOMEM; 8595b0cb1d8SJaroslav Kysela return 0; 8605b0cb1d8SJaroslav Kysela } 8615b0cb1d8SJaroslav Kysela 862f5271101SLydia Wang /* check AA path's mute status */ 863ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 864ada509ecSTakashi Iwai { 865ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 866ada509ecSTakashi Iwai const struct hda_amp_list *p; 867ada509ecSTakashi Iwai int i, ch, v; 868ada509ecSTakashi Iwai 869ada509ecSTakashi Iwai for (i = 0; i < spec->num_loopbacks; i++) { 870ada509ecSTakashi Iwai p = &spec->loopback_list[i]; 871ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 872ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 873ada509ecSTakashi Iwai p->idx); 874ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 875ada509ecSTakashi Iwai return false; 876f5271101SLydia Wang } 877f5271101SLydia Wang } 878ada509ecSTakashi Iwai return true; 879f5271101SLydia Wang } 880f5271101SLydia Wang 881f5271101SLydia Wang /* enter/exit analog low-current mode */ 882ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 883f5271101SLydia Wang { 884f5271101SLydia Wang struct via_spec *spec = codec->spec; 885ada509ecSTakashi Iwai bool enable; 886ada509ecSTakashi Iwai unsigned int verb, parm; 887f5271101SLydia Wang 888ada509ecSTakashi Iwai enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); 889f5271101SLydia Wang 890f5271101SLydia Wang /* decide low current mode's verb & parameter */ 891f5271101SLydia Wang switch (spec->codec_type) { 892f5271101SLydia Wang case VT1708B_8CH: 893f5271101SLydia Wang case VT1708B_4CH: 894f5271101SLydia Wang verb = 0xf70; 895f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 896f5271101SLydia Wang break; 897f5271101SLydia Wang case VT1708S: 898eb7188caSLydia Wang case VT1718S: 899f3db423dSLydia Wang case VT1716S: 900f5271101SLydia Wang verb = 0xf73; 901f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 902f5271101SLydia Wang break; 903f5271101SLydia Wang case VT1702: 904f5271101SLydia Wang verb = 0xf73; 905f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 906f5271101SLydia Wang break; 90725eaba2fSLydia Wang case VT2002P: 908ab6734e7SLydia Wang case VT1812: 90911890956SLydia Wang case VT1802: 91025eaba2fSLydia Wang verb = 0xf93; 91125eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 91225eaba2fSLydia Wang break; 913f5271101SLydia Wang default: 914f5271101SLydia Wang return; /* other codecs are not supported */ 915f5271101SLydia Wang } 916f5271101SLydia Wang /* send verb */ 917f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 918f5271101SLydia Wang } 919f5271101SLydia Wang 920c577b8a1SJoseph Chan /* 921c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 922c577b8a1SJoseph Chan */ 923096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 924aa266fccSLydia Wang /* power down jack detect function */ 925aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 926f7278fd0SJosepch Chan { } 927c577b8a1SJoseph Chan }; 928c577b8a1SJoseph Chan 929ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active) 9307eb56e84STakashi Iwai { 931ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 932ada509ecSTakashi Iwai 933ada509ecSTakashi Iwai if (active) 934ada509ecSTakashi Iwai spec->num_active_streams++; 935ada509ecSTakashi Iwai else 936ada509ecSTakashi Iwai spec->num_active_streams--; 937ada509ecSTakashi Iwai analog_low_current_mode(codec); 9387eb56e84STakashi Iwai } 9397eb56e84STakashi Iwai 940ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, 941c577b8a1SJoseph Chan struct hda_codec *codec, 942c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 943c577b8a1SJoseph Chan { 944c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 945ada509ecSTakashi Iwai int err; 946ece8d043STakashi Iwai 947ece8d043STakashi Iwai if (!spec->hp_independent_mode) 948ece8d043STakashi Iwai spec->multiout.hp_nid = spec->hp_dac_nid; 949ada509ecSTakashi Iwai set_stream_active(codec, true); 950ada509ecSTakashi Iwai err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 9519a08160bSTakashi Iwai hinfo); 952ada509ecSTakashi Iwai if (err < 0) { 953ada509ecSTakashi Iwai spec->multiout.hp_nid = 0; 954ada509ecSTakashi Iwai set_stream_active(codec, false); 955ada509ecSTakashi Iwai return err; 956ada509ecSTakashi Iwai } 957ada509ecSTakashi Iwai return 0; 958c577b8a1SJoseph Chan } 959c577b8a1SJoseph Chan 960ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, 9619af74210STakashi Iwai struct hda_codec *codec, 9629af74210STakashi Iwai struct snd_pcm_substream *substream) 9639af74210STakashi Iwai { 964ece8d043STakashi Iwai struct via_spec *spec = codec->spec; 965ece8d043STakashi Iwai 966ece8d043STakashi Iwai spec->multiout.hp_nid = 0; 967ada509ecSTakashi Iwai set_stream_active(codec, false); 9689af74210STakashi Iwai return 0; 9699af74210STakashi Iwai } 9709af74210STakashi Iwai 9717eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 9727eb56e84STakashi Iwai struct hda_codec *codec, 9737eb56e84STakashi Iwai struct snd_pcm_substream *substream) 9747eb56e84STakashi Iwai { 9757eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 9767eb56e84STakashi Iwai 977ece8d043STakashi Iwai if (snd_BUG_ON(!spec->hp_dac_nid)) 9787eb56e84STakashi Iwai return -EINVAL; 979ece8d043STakashi Iwai if (!spec->hp_independent_mode || spec->multiout.hp_nid) 980ece8d043STakashi Iwai return -EBUSY; 981ada509ecSTakashi Iwai set_stream_active(codec, true); 982ece8d043STakashi Iwai return 0; 983ece8d043STakashi Iwai } 984ece8d043STakashi Iwai 985ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, 986ece8d043STakashi Iwai struct hda_codec *codec, 987ece8d043STakashi Iwai struct snd_pcm_substream *substream) 988ece8d043STakashi Iwai { 989ada509ecSTakashi Iwai set_stream_active(codec, false); 9907eb56e84STakashi Iwai return 0; 9917eb56e84STakashi Iwai } 9927eb56e84STakashi Iwai 9937eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 9947eb56e84STakashi Iwai struct hda_codec *codec, 9950aa62aefSHarald Welte unsigned int stream_tag, 9960aa62aefSHarald Welte unsigned int format, 9970aa62aefSHarald Welte struct snd_pcm_substream *substream) 9980aa62aefSHarald Welte { 9990aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10000aa62aefSHarald Welte 1001ece8d043STakashi Iwai snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1002ece8d043STakashi Iwai format, substream); 10037eb56e84STakashi Iwai vt1708_start_hp_work(spec); 10047eb56e84STakashi Iwai return 0; 10050aa62aefSHarald Welte } 10060aa62aefSHarald Welte 10077eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 10080aa62aefSHarald Welte struct hda_codec *codec, 10090aa62aefSHarald Welte unsigned int stream_tag, 10100aa62aefSHarald Welte unsigned int format, 10110aa62aefSHarald Welte struct snd_pcm_substream *substream) 10120aa62aefSHarald Welte { 10130aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10140aa62aefSHarald Welte 1015ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1016ece8d043STakashi Iwai stream_tag, 0, format); 10171f2e99feSLydia Wang vt1708_start_hp_work(spec); 10180aa62aefSHarald Welte return 0; 10190aa62aefSHarald Welte } 10200aa62aefSHarald Welte 10210aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 10220aa62aefSHarald Welte struct hda_codec *codec, 10230aa62aefSHarald Welte struct snd_pcm_substream *substream) 10240aa62aefSHarald Welte { 10250aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10260aa62aefSHarald Welte 1027ece8d043STakashi Iwai snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 10287eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 10297eb56e84STakashi Iwai return 0; 10300aa62aefSHarald Welte } 10317eb56e84STakashi Iwai 10327eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 10337eb56e84STakashi Iwai struct hda_codec *codec, 10347eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10357eb56e84STakashi Iwai { 10367eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 10377eb56e84STakashi Iwai 1038ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 10391f2e99feSLydia Wang vt1708_stop_hp_work(spec); 10400aa62aefSHarald Welte return 0; 10410aa62aefSHarald Welte } 10420aa62aefSHarald Welte 1043c577b8a1SJoseph Chan /* 1044c577b8a1SJoseph Chan * Digital out 1045c577b8a1SJoseph Chan */ 1046c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1047c577b8a1SJoseph Chan struct hda_codec *codec, 1048c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1049c577b8a1SJoseph Chan { 1050c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1051c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1052c577b8a1SJoseph Chan } 1053c577b8a1SJoseph Chan 1054c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1055c577b8a1SJoseph Chan struct hda_codec *codec, 1056c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1057c577b8a1SJoseph Chan { 1058c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1059c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1060c577b8a1SJoseph Chan } 1061c577b8a1SJoseph Chan 10625691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 106398aa34c0SHarald Welte struct hda_codec *codec, 106498aa34c0SHarald Welte unsigned int stream_tag, 106598aa34c0SHarald Welte unsigned int format, 106698aa34c0SHarald Welte struct snd_pcm_substream *substream) 106798aa34c0SHarald Welte { 106898aa34c0SHarald Welte struct via_spec *spec = codec->spec; 10699da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 10709da29271STakashi Iwai stream_tag, format, substream); 10719da29271STakashi Iwai } 10725691ec7fSHarald Welte 10739da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 10749da29271STakashi Iwai struct hda_codec *codec, 10759da29271STakashi Iwai struct snd_pcm_substream *substream) 10769da29271STakashi Iwai { 10779da29271STakashi Iwai struct via_spec *spec = codec->spec; 10789da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 107998aa34c0SHarald Welte return 0; 108098aa34c0SHarald Welte } 108198aa34c0SHarald Welte 1082c577b8a1SJoseph Chan /* 1083c577b8a1SJoseph Chan * Analog capture 1084c577b8a1SJoseph Chan */ 1085c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1086c577b8a1SJoseph Chan struct hda_codec *codec, 1087c577b8a1SJoseph Chan unsigned int stream_tag, 1088c577b8a1SJoseph Chan unsigned int format, 1089c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1090c577b8a1SJoseph Chan { 1091c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1092c577b8a1SJoseph Chan 1093c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1094c577b8a1SJoseph Chan stream_tag, 0, format); 1095c577b8a1SJoseph Chan return 0; 1096c577b8a1SJoseph Chan } 1097c577b8a1SJoseph Chan 1098c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1099c577b8a1SJoseph Chan struct hda_codec *codec, 1100c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1101c577b8a1SJoseph Chan { 1102c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1103888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1104c577b8a1SJoseph Chan return 0; 1105c577b8a1SJoseph Chan } 1106c577b8a1SJoseph Chan 1107a86a88eaSTakashi Iwai /* analog capture with dynamic ADC switching */ 1108a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1109a86a88eaSTakashi Iwai struct hda_codec *codec, 1110a86a88eaSTakashi Iwai unsigned int stream_tag, 1111a86a88eaSTakashi Iwai unsigned int format, 1112a86a88eaSTakashi Iwai struct snd_pcm_substream *substream) 1113a86a88eaSTakashi Iwai { 1114a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1115a86a88eaSTakashi Iwai int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; 1116a86a88eaSTakashi Iwai 1117a86a88eaSTakashi Iwai spec->cur_adc = spec->adc_nids[adc_idx]; 1118a86a88eaSTakashi Iwai spec->cur_adc_stream_tag = stream_tag; 1119a86a88eaSTakashi Iwai spec->cur_adc_format = format; 1120a86a88eaSTakashi Iwai snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 1121a86a88eaSTakashi Iwai return 0; 1122a86a88eaSTakashi Iwai } 1123a86a88eaSTakashi Iwai 1124a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1125a86a88eaSTakashi Iwai struct hda_codec *codec, 1126a86a88eaSTakashi Iwai struct snd_pcm_substream *substream) 1127a86a88eaSTakashi Iwai { 1128a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1129a86a88eaSTakashi Iwai 1130a86a88eaSTakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 1131a86a88eaSTakashi Iwai spec->cur_adc = 0; 1132a86a88eaSTakashi Iwai return 0; 1133a86a88eaSTakashi Iwai } 1134a86a88eaSTakashi Iwai 1135a86a88eaSTakashi Iwai /* re-setup the stream if running; called from input-src put */ 1136a86a88eaSTakashi Iwai static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) 1137a86a88eaSTakashi Iwai { 1138a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1139a86a88eaSTakashi Iwai int adc_idx = spec->inputs[cur].adc_idx; 1140a86a88eaSTakashi Iwai hda_nid_t adc = spec->adc_nids[adc_idx]; 1141a86a88eaSTakashi Iwai 1142a86a88eaSTakashi Iwai if (spec->cur_adc && spec->cur_adc != adc) { 1143a86a88eaSTakashi Iwai /* stream is running, let's swap the current ADC */ 1144a86a88eaSTakashi Iwai __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 1145a86a88eaSTakashi Iwai spec->cur_adc = adc; 1146a86a88eaSTakashi Iwai snd_hda_codec_setup_stream(codec, adc, 1147a86a88eaSTakashi Iwai spec->cur_adc_stream_tag, 0, 1148a86a88eaSTakashi Iwai spec->cur_adc_format); 1149a86a88eaSTakashi Iwai return true; 1150a86a88eaSTakashi Iwai } 1151a86a88eaSTakashi Iwai return false; 1152a86a88eaSTakashi Iwai } 1153a86a88eaSTakashi Iwai 11549af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 11557eb56e84STakashi Iwai .substreams = 1, 1156c577b8a1SJoseph Chan .channels_min = 2, 1157c577b8a1SJoseph Chan .channels_max = 8, 11589af74210STakashi Iwai /* NID is set in via_build_pcms */ 1159c577b8a1SJoseph Chan .ops = { 1160ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1161ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 11620aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 11630aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1164c577b8a1SJoseph Chan }, 1165c577b8a1SJoseph Chan }; 1166c577b8a1SJoseph Chan 11677eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 11687eb56e84STakashi Iwai .substreams = 1, 11697eb56e84STakashi Iwai .channels_min = 2, 11707eb56e84STakashi Iwai .channels_max = 2, 11717eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 11727eb56e84STakashi Iwai .ops = { 11737eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1174ece8d043STakashi Iwai .close = via_playback_hp_pcm_close, 11757eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 11767eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 11777eb56e84STakashi Iwai }, 11787eb56e84STakashi Iwai }; 11797eb56e84STakashi Iwai 118090dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 11817eb56e84STakashi Iwai .substreams = 1, 1182bc9b5623STakashi Iwai .channels_min = 2, 1183bc9b5623STakashi Iwai .channels_max = 8, 11849af74210STakashi Iwai /* NID is set in via_build_pcms */ 1185bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1186bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1187bc9b5623STakashi Iwai * disable the 24bit format, so far. 1188bc9b5623STakashi Iwai */ 1189bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1190bc9b5623STakashi Iwai .ops = { 1191ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1192ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 1193c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1194c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1195bc9b5623STakashi Iwai }, 1196bc9b5623STakashi Iwai }; 1197bc9b5623STakashi Iwai 11989af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 11997eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1200c577b8a1SJoseph Chan .channels_min = 2, 1201c577b8a1SJoseph Chan .channels_max = 2, 12029af74210STakashi Iwai /* NID is set in via_build_pcms */ 1203c577b8a1SJoseph Chan .ops = { 1204c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1205c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1206c577b8a1SJoseph Chan }, 1207c577b8a1SJoseph Chan }; 1208c577b8a1SJoseph Chan 1209a86a88eaSTakashi Iwai static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { 1210a86a88eaSTakashi Iwai .substreams = 1, 1211a86a88eaSTakashi Iwai .channels_min = 2, 1212a86a88eaSTakashi Iwai .channels_max = 2, 1213a86a88eaSTakashi Iwai /* NID is set in via_build_pcms */ 1214a86a88eaSTakashi Iwai .ops = { 1215a86a88eaSTakashi Iwai .prepare = via_dyn_adc_capture_pcm_prepare, 1216a86a88eaSTakashi Iwai .cleanup = via_dyn_adc_capture_pcm_cleanup, 1217a86a88eaSTakashi Iwai }, 1218a86a88eaSTakashi Iwai }; 1219a86a88eaSTakashi Iwai 12209af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1221c577b8a1SJoseph Chan .substreams = 1, 1222c577b8a1SJoseph Chan .channels_min = 2, 1223c577b8a1SJoseph Chan .channels_max = 2, 1224c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1225c577b8a1SJoseph Chan .ops = { 1226c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 12276b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 12289da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 12299da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1230c577b8a1SJoseph Chan }, 1231c577b8a1SJoseph Chan }; 1232c577b8a1SJoseph Chan 12339af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1234c577b8a1SJoseph Chan .substreams = 1, 1235c577b8a1SJoseph Chan .channels_min = 2, 1236c577b8a1SJoseph Chan .channels_max = 2, 1237c577b8a1SJoseph Chan }; 1238c577b8a1SJoseph Chan 1239370bafbdSTakashi Iwai /* 1240370bafbdSTakashi Iwai * slave controls for virtual master 1241370bafbdSTakashi Iwai */ 1242370bafbdSTakashi Iwai static const char * const via_slave_vols[] = { 1243370bafbdSTakashi Iwai "Front Playback Volume", 1244370bafbdSTakashi Iwai "Surround Playback Volume", 1245370bafbdSTakashi Iwai "Center Playback Volume", 1246370bafbdSTakashi Iwai "LFE Playback Volume", 1247370bafbdSTakashi Iwai "Side Playback Volume", 1248370bafbdSTakashi Iwai "Headphone Playback Volume", 1249370bafbdSTakashi Iwai "Speaker Playback Volume", 1250370bafbdSTakashi Iwai NULL, 1251370bafbdSTakashi Iwai }; 1252370bafbdSTakashi Iwai 1253370bafbdSTakashi Iwai static const char * const via_slave_sws[] = { 1254370bafbdSTakashi Iwai "Front Playback Switch", 1255370bafbdSTakashi Iwai "Surround Playback Switch", 1256370bafbdSTakashi Iwai "Center Playback Switch", 1257370bafbdSTakashi Iwai "LFE Playback Switch", 1258370bafbdSTakashi Iwai "Side Playback Switch", 1259370bafbdSTakashi Iwai "Headphone Playback Switch", 1260370bafbdSTakashi Iwai "Speaker Playback Switch", 1261370bafbdSTakashi Iwai NULL, 1262370bafbdSTakashi Iwai }; 1263370bafbdSTakashi Iwai 1264c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1265c577b8a1SJoseph Chan { 1266c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 12675b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 12685b0cb1d8SJaroslav Kysela int err, i; 1269c577b8a1SJoseph Chan 127024088a58STakashi Iwai if (spec->set_widgets_power_state) 127124088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 127224088a58STakashi Iwai return -ENOMEM; 127324088a58STakashi Iwai 1274c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1275c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1276c577b8a1SJoseph Chan if (err < 0) 1277c577b8a1SJoseph Chan return err; 1278c577b8a1SJoseph Chan } 1279c577b8a1SJoseph Chan 1280c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1281c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 128274b654c9SStephen Warren spec->multiout.dig_out_nid, 1283c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1284c577b8a1SJoseph Chan if (err < 0) 1285c577b8a1SJoseph Chan return err; 12869a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 12879a08160bSTakashi Iwai &spec->multiout); 12889a08160bSTakashi Iwai if (err < 0) 12899a08160bSTakashi Iwai return err; 12909a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1291c577b8a1SJoseph Chan } 1292c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1293c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1294c577b8a1SJoseph Chan if (err < 0) 1295c577b8a1SJoseph Chan return err; 1296c577b8a1SJoseph Chan } 129717314379SLydia Wang 1298370bafbdSTakashi Iwai /* if we have no master control, let's create it */ 1299370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 1300370bafbdSTakashi Iwai unsigned int vmaster_tlv[4]; 1301370bafbdSTakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], 1302370bafbdSTakashi Iwai HDA_OUTPUT, vmaster_tlv); 1303370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 1304370bafbdSTakashi Iwai vmaster_tlv, via_slave_vols); 1305370bafbdSTakashi Iwai if (err < 0) 1306370bafbdSTakashi Iwai return err; 1307370bafbdSTakashi Iwai } 1308370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 1309370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Switch", 1310370bafbdSTakashi Iwai NULL, via_slave_sws); 1311370bafbdSTakashi Iwai if (err < 0) 1312370bafbdSTakashi Iwai return err; 1313370bafbdSTakashi Iwai } 1314370bafbdSTakashi Iwai 13155b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 13165b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 13175b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 131821949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 13195b0cb1d8SJaroslav Kysela if (err < 0) 13205b0cb1d8SJaroslav Kysela return err; 13215b0cb1d8SJaroslav Kysela } 13225b0cb1d8SJaroslav Kysela 132317314379SLydia Wang /* init power states */ 13243e95b9abSLydia Wang set_widgets_power_state(codec); 1325ada509ecSTakashi Iwai analog_low_current_mode(codec); 132617314379SLydia Wang 1327603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1328c577b8a1SJoseph Chan return 0; 1329c577b8a1SJoseph Chan } 1330c577b8a1SJoseph Chan 1331c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1332c577b8a1SJoseph Chan { 1333c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1334c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1335c577b8a1SJoseph Chan 1336c577b8a1SJoseph Chan codec->num_pcms = 1; 1337c577b8a1SJoseph Chan codec->pcm_info = info; 1338c577b8a1SJoseph Chan 133982673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 134082673bc8STakashi Iwai "%s Analog", codec->chip_name); 1341c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 13429af74210STakashi Iwai 13439af74210STakashi Iwai if (!spec->stream_analog_playback) 13449af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1345377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 13469af74210STakashi Iwai *spec->stream_analog_playback; 1347377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1348377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1349c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1350c577b8a1SJoseph Chan spec->multiout.max_channels; 13519af74210STakashi Iwai 1352a86a88eaSTakashi Iwai if (!spec->stream_analog_capture) { 1353a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 1354a86a88eaSTakashi Iwai spec->stream_analog_capture = 1355a86a88eaSTakashi Iwai &via_pcm_dyn_adc_analog_capture; 1356a86a88eaSTakashi Iwai else 13579af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 1358a86a88eaSTakashi Iwai } 13599af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 13609af74210STakashi Iwai *spec->stream_analog_capture; 13619af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1362a86a88eaSTakashi Iwai if (!spec->dyn_adc_switch) 13639af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 13649af74210STakashi Iwai spec->num_adc_nids; 1365c577b8a1SJoseph Chan 1366c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1367c577b8a1SJoseph Chan codec->num_pcms++; 1368c577b8a1SJoseph Chan info++; 136982673bc8STakashi Iwai snprintf(spec->stream_name_digital, 137082673bc8STakashi Iwai sizeof(spec->stream_name_digital), 137182673bc8STakashi Iwai "%s Digital", codec->chip_name); 1372c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 13737ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1374c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 13759af74210STakashi Iwai if (!spec->stream_digital_playback) 13769af74210STakashi Iwai spec->stream_digital_playback = 13779af74210STakashi Iwai &via_pcm_digital_playback; 1378c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 13799af74210STakashi Iwai *spec->stream_digital_playback; 1380c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1381c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1382c577b8a1SJoseph Chan } 1383c577b8a1SJoseph Chan if (spec->dig_in_nid) { 13849af74210STakashi Iwai if (!spec->stream_digital_capture) 13859af74210STakashi Iwai spec->stream_digital_capture = 13869af74210STakashi Iwai &via_pcm_digital_capture; 1387c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 13889af74210STakashi Iwai *spec->stream_digital_capture; 1389c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1390c577b8a1SJoseph Chan spec->dig_in_nid; 1391c577b8a1SJoseph Chan } 1392c577b8a1SJoseph Chan } 1393c577b8a1SJoseph Chan 1394ece8d043STakashi Iwai if (spec->hp_dac_nid) { 13957eb56e84STakashi Iwai codec->num_pcms++; 13967eb56e84STakashi Iwai info++; 13977eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 13987eb56e84STakashi Iwai "%s HP", codec->chip_name); 13997eb56e84STakashi Iwai info->name = spec->stream_name_hp; 14007eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 14017eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1402ece8d043STakashi Iwai spec->hp_dac_nid; 14037eb56e84STakashi Iwai } 1404c577b8a1SJoseph Chan return 0; 1405c577b8a1SJoseph Chan } 1406c577b8a1SJoseph Chan 1407c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1408c577b8a1SJoseph Chan { 1409c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1410c577b8a1SJoseph Chan 1411c577b8a1SJoseph Chan if (!spec) 1412c577b8a1SJoseph Chan return; 1413c577b8a1SJoseph Chan 1414603c4019STakashi Iwai via_free_kctls(codec); 14151f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1416a86a88eaSTakashi Iwai kfree(spec->bind_cap_vol); 1417a86a88eaSTakashi Iwai kfree(spec->bind_cap_sw); 1418a86a88eaSTakashi Iwai kfree(spec); 1419c577b8a1SJoseph Chan } 1420c577b8a1SJoseph Chan 142164be285bSTakashi Iwai /* mute/unmute outputs */ 142264be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 142364be285bSTakashi Iwai hda_nid_t *pins, bool mute) 142464be285bSTakashi Iwai { 142564be285bSTakashi Iwai int i; 142664be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 142764be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 142864be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 142964be285bSTakashi Iwai mute ? 0 : PIN_OUT); 143064be285bSTakashi Iwai } 143164be285bSTakashi Iwai 14324a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */ 14334a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present) 14344a918ffeSTakashi Iwai { 14354a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 14364a918ffeSTakashi Iwai 14374a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs) 14384a918ffeSTakashi Iwai return; 14394a918ffeSTakashi Iwai if (!present) 14404a918ffeSTakashi Iwai present = snd_hda_jack_detect(codec, 14414a918ffeSTakashi Iwai spec->autocfg.line_out_pins[0]); 14424a918ffeSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 14434a918ffeSTakashi Iwai spec->autocfg.speaker_pins, 14444a918ffeSTakashi Iwai present); 14454a918ffeSTakashi Iwai } 14464a918ffeSTakashi Iwai 144769e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 144869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 144969e52a80SHarald Welte { 14504a918ffeSTakashi Iwai int present = 0; 145169e52a80SHarald Welte struct via_spec *spec = codec->spec; 145269e52a80SHarald Welte 14534a918ffeSTakashi Iwai if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { 1454f2b1c9f0STakashi Iwai int nums; 1455d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1456f2b1c9f0STakashi Iwai if (spec->smart51_enabled) 1457f2b1c9f0STakashi Iwai nums = spec->autocfg.line_outs + spec->smart51_nums; 1458f2b1c9f0STakashi Iwai else 1459f2b1c9f0STakashi Iwai nums = spec->autocfg.line_outs; 1460f2b1c9f0STakashi Iwai toggle_output_mutes(codec, nums, 146164be285bSTakashi Iwai spec->autocfg.line_out_pins, 146264be285bSTakashi Iwai present); 146369e52a80SHarald Welte } 14644a918ffeSTakashi Iwai via_line_automute(codec, present); 1465f3db423dSLydia Wang } 1466f3db423dSLydia Wang 146769e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 146869e52a80SHarald Welte { 146969e52a80SHarald Welte unsigned int gpio_data; 147069e52a80SHarald Welte unsigned int vol_counter; 147169e52a80SHarald Welte unsigned int vol; 147269e52a80SHarald Welte unsigned int master_vol; 147369e52a80SHarald Welte 147469e52a80SHarald Welte struct via_spec *spec = codec->spec; 147569e52a80SHarald Welte 147669e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 147769e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 147869e52a80SHarald Welte 147969e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 148069e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 148169e52a80SHarald Welte 148269e52a80SHarald Welte vol = vol_counter & 0x1F; 148369e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 148469e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 148569e52a80SHarald Welte AC_AMP_GET_INPUT); 148669e52a80SHarald Welte 148769e52a80SHarald Welte if (gpio_data == 0x02) { 148869e52a80SHarald Welte /* unmute line out */ 14893e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 14903e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 14913e0693e2STakashi Iwai PIN_OUT); 149269e52a80SHarald Welte if (vol_counter & 0x20) { 149369e52a80SHarald Welte /* decrease volume */ 149469e52a80SHarald Welte if (vol > master_vol) 149569e52a80SHarald Welte vol = master_vol; 149669e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 149769e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 149869e52a80SHarald Welte master_vol-vol); 149969e52a80SHarald Welte } else { 150069e52a80SHarald Welte /* increase volume */ 150169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 150269e52a80SHarald Welte HDA_AMP_VOLMASK, 150369e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 150469e52a80SHarald Welte (master_vol+vol)); 150569e52a80SHarald Welte } 150669e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 150769e52a80SHarald Welte /* mute line out */ 15083e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15093e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15103e0693e2STakashi Iwai 0); 151169e52a80SHarald Welte } 151269e52a80SHarald Welte } 151369e52a80SHarald Welte 151469e52a80SHarald Welte /* unsolicited event for jack sensing */ 151569e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 151669e52a80SHarald Welte unsigned int res) 151769e52a80SHarald Welte { 151869e52a80SHarald Welte res >>= 26; 1519ec7e7e42SLydia Wang 1520a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 15213e95b9abSLydia Wang set_widgets_power_state(codec); 1522ec7e7e42SLydia Wang 1523ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1524ec7e7e42SLydia Wang 1525ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1526ec7e7e42SLydia Wang via_hp_automute(codec); 1527ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1528ec7e7e42SLydia Wang via_gpio_control(codec); 15294a918ffeSTakashi Iwai else if (res == VIA_LINE_EVENT) 15304a918ffeSTakashi Iwai via_line_automute(codec, false); 153169e52a80SHarald Welte } 153269e52a80SHarald Welte 15331f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 15341f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 15351f2e99feSLydia Wang { 15361f2e99feSLydia Wang struct via_spec *spec = codec->spec; 15371f2e99feSLydia Wang vt1708_stop_hp_work(spec); 15381f2e99feSLydia Wang return 0; 15391f2e99feSLydia Wang } 15401f2e99feSLydia Wang #endif 15411f2e99feSLydia Wang 1542cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1543cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1544cb53c626STakashi Iwai { 1545cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1546cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1547cb53c626STakashi Iwai } 1548cb53c626STakashi Iwai #endif 1549cb53c626STakashi Iwai 1550c577b8a1SJoseph Chan /* 1551c577b8a1SJoseph Chan */ 15525d41762aSTakashi Iwai 15535d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 15545d41762aSTakashi Iwai 155590dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1556c577b8a1SJoseph Chan .build_controls = via_build_controls, 1557c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1558c577b8a1SJoseph Chan .init = via_init, 1559c577b8a1SJoseph Chan .free = via_free, 15604a918ffeSTakashi Iwai .unsol_event = via_unsol_event, 15611f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 15621f2e99feSLydia Wang .suspend = via_suspend, 15631f2e99feSLydia Wang #endif 1564cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1565cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1566cb53c626STakashi Iwai #endif 1567c577b8a1SJoseph Chan }; 1568c577b8a1SJoseph Chan 15694a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1570c577b8a1SJoseph Chan { 15714a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 15724a79616dSTakashi Iwai int i; 15734a79616dSTakashi Iwai 15744a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 15754a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 15764a79616dSTakashi Iwai return false; 15774a79616dSTakashi Iwai } 1578ece8d043STakashi Iwai if (spec->hp_dac_nid == dac) 15794a79616dSTakashi Iwai return false; 15804a79616dSTakashi Iwai return true; 15814a79616dSTakashi Iwai } 15824a79616dSTakashi Iwai 15838e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, 15844a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 15854a79616dSTakashi Iwai int depth, int wid_type) 15864a79616dSTakashi Iwai { 15874a79616dSTakashi Iwai hda_nid_t conn[8]; 15884a79616dSTakashi Iwai int i, nums; 15894a79616dSTakashi Iwai 15904a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 15914a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 15924a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 15934a79616dSTakashi Iwai continue; 159409a9ad69STakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) 159509a9ad69STakashi Iwai goto found; 15964a79616dSTakashi Iwai } 15978e3679dcSTakashi Iwai if (depth >= MAX_NID_PATH_DEPTH) 15984a79616dSTakashi Iwai return false; 15994a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16004a79616dSTakashi Iwai unsigned int type; 16014a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 16024a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 16034a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 16044a79616dSTakashi Iwai continue; 16058e3679dcSTakashi Iwai if (__parse_output_path(codec, conn[i], target_dac, 160609a9ad69STakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) 160709a9ad69STakashi Iwai goto found; 16084a79616dSTakashi Iwai } 16094a79616dSTakashi Iwai return false; 161009a9ad69STakashi Iwai 161109a9ad69STakashi Iwai found: 161209a9ad69STakashi Iwai path->path[path->depth] = conn[i]; 161309a9ad69STakashi Iwai path->idx[path->depth] = i; 161409a9ad69STakashi Iwai if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) 161509a9ad69STakashi Iwai path->multi[path->depth] = 1; 161609a9ad69STakashi Iwai path->depth++; 161709a9ad69STakashi Iwai return true; 16184a79616dSTakashi Iwai } 16194a79616dSTakashi Iwai 16208e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 16218e3679dcSTakashi Iwai hda_nid_t target_dac, struct nid_path *path) 16228e3679dcSTakashi Iwai { 16238e3679dcSTakashi Iwai if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) { 16248e3679dcSTakashi Iwai path->path[path->depth] = nid; 16258e3679dcSTakashi Iwai path->depth++; 16268e3679dcSTakashi Iwai return true; 16278e3679dcSTakashi Iwai } 16288e3679dcSTakashi Iwai return false; 16298e3679dcSTakashi Iwai } 16308e3679dcSTakashi Iwai 16314a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 16324a79616dSTakashi Iwai { 16334a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16344a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1635c577b8a1SJoseph Chan int i; 1636c577b8a1SJoseph Chan hda_nid_t nid; 1637c577b8a1SJoseph Chan 1638c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 16394a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 16404a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1641c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 16424a79616dSTakashi Iwai if (!nid) 16434a79616dSTakashi Iwai continue; 16448e3679dcSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i])) 16458e3679dcSTakashi Iwai spec->private_dac_nids[i] = spec->out_path[i].path[0]; 1646c577b8a1SJoseph Chan } 1647c577b8a1SJoseph Chan return 0; 1648c577b8a1SJoseph Chan } 1649c577b8a1SJoseph Chan 16504a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 165109a9ad69STakashi Iwai int chs, bool check_dac, struct nid_path *path) 1652c577b8a1SJoseph Chan { 16534a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1654c577b8a1SJoseph Chan char name[32]; 165509a9ad69STakashi Iwai hda_nid_t dac, pin, sel, nid; 165609a9ad69STakashi Iwai int err; 1657a934d5a9STakashi Iwai 165809a9ad69STakashi Iwai dac = check_dac ? path->path[0] : 0; 165909a9ad69STakashi Iwai pin = path->path[path->depth - 1]; 166009a9ad69STakashi Iwai sel = path->depth > 1 ? path->path[1] : 0; 1661c577b8a1SJoseph Chan 16628df2a312STakashi Iwai if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 16634a79616dSTakashi Iwai nid = dac; 16648df2a312STakashi Iwai else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 16654a79616dSTakashi Iwai nid = pin; 1666a934d5a9STakashi Iwai else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 1667a934d5a9STakashi Iwai nid = sel; 16684a79616dSTakashi Iwai else 16694a79616dSTakashi Iwai nid = 0; 16704a79616dSTakashi Iwai if (nid) { 16714a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1672c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1673a00a5fadSLydia Wang HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 1674c577b8a1SJoseph Chan if (err < 0) 1675c577b8a1SJoseph Chan return err; 167609a9ad69STakashi Iwai path->vol_ctl = nid; 1677c577b8a1SJoseph Chan } 16784a79616dSTakashi Iwai 16798df2a312STakashi Iwai if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) 16804a79616dSTakashi Iwai nid = dac; 16818df2a312STakashi Iwai else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) 16824a79616dSTakashi Iwai nid = pin; 1683a934d5a9STakashi Iwai else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) 1684a934d5a9STakashi Iwai nid = sel; 16854a79616dSTakashi Iwai else 16864a79616dSTakashi Iwai nid = 0; 16874a79616dSTakashi Iwai if (nid) { 16884a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 16894a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 16904a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 16914a79616dSTakashi Iwai if (err < 0) 16924a79616dSTakashi Iwai return err; 169309a9ad69STakashi Iwai path->mute_ctl = nid; 16944a79616dSTakashi Iwai } 16954a79616dSTakashi Iwai return 0; 16964a79616dSTakashi Iwai } 16974a79616dSTakashi Iwai 1698f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1699f4a7828bSTakashi Iwai { 1700f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1701f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 17020f98c24bSTakashi Iwai struct auto_pin_cfg_item *ins = cfg->inputs; 17030f98c24bSTakashi Iwai int i, j, nums, attr; 17040f98c24bSTakashi Iwai int pins[AUTO_CFG_MAX_INS]; 1705f4a7828bSTakashi Iwai 17060f98c24bSTakashi Iwai for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { 17070f98c24bSTakashi Iwai nums = 0; 1708f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 17090f98c24bSTakashi Iwai unsigned int def; 17100f98c24bSTakashi Iwai if (ins[i].type > AUTO_PIN_LINE_IN) 17110f98c24bSTakashi Iwai continue; 17120f98c24bSTakashi Iwai def = snd_hda_codec_get_pincfg(codec, ins[i].pin); 17130f98c24bSTakashi Iwai if (snd_hda_get_input_pin_attr(def) != attr) 17140f98c24bSTakashi Iwai continue; 17150f98c24bSTakashi Iwai for (j = 0; j < nums; j++) 17160f98c24bSTakashi Iwai if (ins[pins[j]].type < ins[i].type) { 17170f98c24bSTakashi Iwai memmove(pins + j + 1, pins + j, 17180f98c24bSTakashi Iwai (nums - j - 1) * sizeof(int)); 17190f98c24bSTakashi Iwai break; 17200f98c24bSTakashi Iwai } 17210f98c24bSTakashi Iwai pins[j] = i; 1722e3d7a143STakashi Iwai nums++; 1723e3d7a143STakashi Iwai } 1724e3d7a143STakashi Iwai if (cfg->line_outs + nums < 3) 1725f4a7828bSTakashi Iwai continue; 17260f98c24bSTakashi Iwai for (i = 0; i < nums; i++) { 17270f98c24bSTakashi Iwai hda_nid_t pin = ins[pins[i]].pin; 17280f98c24bSTakashi Iwai spec->smart51_pins[spec->smart51_nums++] = pin; 17290f98c24bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = pin; 1730f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1731f4a7828bSTakashi Iwai break; 1732f4a7828bSTakashi Iwai } 17330f98c24bSTakashi Iwai return; 17340f98c24bSTakashi 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 1753e3d7a143STakashi Iwai err = via_auto_fill_dac_nids(codec); 1754e3d7a143STakashi Iwai if (err < 0) 1755e3d7a143STakashi Iwai return err; 1756e3d7a143STakashi 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) { 176409a9ad69STakashi Iwai err = create_ch_ctls(codec, "Center", 1, true, 176509a9ad69STakashi Iwai &spec->out_path[i]); 17664a79616dSTakashi Iwai if (err < 0) 17674a79616dSTakashi Iwai return err; 176809a9ad69STakashi Iwai err = create_ch_ctls(codec, "LFE", 2, true, 176909a9ad69STakashi Iwai &spec->out_path[i]); 17704a79616dSTakashi Iwai if (err < 0) 17714a79616dSTakashi Iwai return err; 17724a79616dSTakashi Iwai } else { 17736aadf41dSTakashi Iwai const char *pfx = chname[i]; 17746aadf41dSTakashi Iwai if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 17756aadf41dSTakashi Iwai cfg->line_outs == 1) 17766aadf41dSTakashi Iwai pfx = "Speaker"; 177709a9ad69STakashi Iwai err = create_ch_ctls(codec, pfx, 3, true, 177809a9ad69STakashi Iwai &spec->out_path[i]); 17794a79616dSTakashi Iwai if (err < 0) 17804a79616dSTakashi Iwai return err; 17814a79616dSTakashi Iwai } 17824a79616dSTakashi Iwai } 17834a79616dSTakashi Iwai 17844a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 17854a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 17864a79616dSTakashi Iwai if (idx >= 0) { 17874a79616dSTakashi Iwai /* add control to mixer */ 17884a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 17894a79616dSTakashi Iwai "PCM Playback Volume", 17904a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17914a79616dSTakashi Iwai idx, HDA_INPUT)); 17924a79616dSTakashi Iwai if (err < 0) 17934a79616dSTakashi Iwai return err; 17944a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 17954a79616dSTakashi Iwai "PCM Playback Switch", 17964a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17974a79616dSTakashi Iwai idx, HDA_INPUT)); 17984a79616dSTakashi Iwai if (err < 0) 17994a79616dSTakashi Iwai return err; 1800c577b8a1SJoseph Chan } 1801c577b8a1SJoseph Chan 1802f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 1803f4a7828bSTakashi Iwai 1804c577b8a1SJoseph Chan return 0; 1805c577b8a1SJoseph Chan } 1806c577b8a1SJoseph Chan 18074a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 1808c577b8a1SJoseph Chan { 18094a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 181009a9ad69STakashi Iwai struct nid_path *path; 1811c577b8a1SJoseph Chan int err; 1812c577b8a1SJoseph Chan 1813c577b8a1SJoseph Chan if (!pin) 1814c577b8a1SJoseph Chan return 0; 1815c577b8a1SJoseph Chan 18168df2a312STakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path)) 18178e3679dcSTakashi Iwai spec->hp_dac_nid = spec->hp_path.path[0]; 18184a79616dSTakashi Iwai 1819ece8d043STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 18208e3679dcSTakashi Iwai &spec->hp_dep_path) && 1821ece8d043STakashi Iwai !spec->hp_dac_nid) 1822ece8d043STakashi Iwai return 0; 1823ece8d043STakashi Iwai 182409a9ad69STakashi Iwai if (spec->hp_dac_nid) 182509a9ad69STakashi Iwai path = &spec->hp_path; 182609a9ad69STakashi Iwai else 182709a9ad69STakashi Iwai path = &spec->hp_dep_path; 182809a9ad69STakashi Iwai err = create_ch_ctls(codec, "Headphone", 3, false, path); 18294a79616dSTakashi Iwai if (err < 0) 18304a79616dSTakashi Iwai return err; 183109a9ad69STakashi Iwai if (spec->hp_dac_nid) { 183209a9ad69STakashi Iwai spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl; 183309a9ad69STakashi Iwai spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl; 183409a9ad69STakashi Iwai } 18350aa62aefSHarald Welte 1836c577b8a1SJoseph Chan return 0; 1837c577b8a1SJoseph Chan } 1838c577b8a1SJoseph Chan 18394a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec) 18404a918ffeSTakashi Iwai { 18414a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 18424a918ffeSTakashi Iwai hda_nid_t pin, dac; 18434a918ffeSTakashi Iwai 18444a918ffeSTakashi Iwai pin = spec->autocfg.speaker_pins[0]; 18454a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs || !pin) 18464a918ffeSTakashi Iwai return 0; 18474a918ffeSTakashi Iwai 18488e3679dcSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { 18498e3679dcSTakashi Iwai dac = spec->speaker_path.path[0]; 18504a918ffeSTakashi Iwai spec->multiout.extra_out_nid[0] = dac; 185109a9ad69STakashi Iwai return create_ch_ctls(codec, "Speaker", 3, true, 185209a9ad69STakashi Iwai &spec->speaker_path); 18534a918ffeSTakashi Iwai } 18544a918ffeSTakashi Iwai if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 18558e3679dcSTakashi Iwai &spec->speaker_path)) 185609a9ad69STakashi Iwai return create_ch_ctls(codec, "Speaker", 3, false, 185709a9ad69STakashi Iwai &spec->speaker_path); 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 1884a86a88eaSTakashi Iwai /* input-src control */ 1885a86a88eaSTakashi Iwai static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 1886a86a88eaSTakashi Iwai struct snd_ctl_elem_info *uinfo) 1887a86a88eaSTakashi Iwai { 1888a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1889a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1890a86a88eaSTakashi Iwai 1891a86a88eaSTakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 1892a86a88eaSTakashi Iwai uinfo->count = 1; 1893a86a88eaSTakashi Iwai uinfo->value.enumerated.items = spec->num_inputs; 1894a86a88eaSTakashi Iwai if (uinfo->value.enumerated.item >= spec->num_inputs) 1895a86a88eaSTakashi Iwai uinfo->value.enumerated.item = spec->num_inputs - 1; 1896a86a88eaSTakashi Iwai strcpy(uinfo->value.enumerated.name, 1897a86a88eaSTakashi Iwai spec->inputs[uinfo->value.enumerated.item].label); 1898a86a88eaSTakashi Iwai return 0; 1899a86a88eaSTakashi Iwai } 1900a86a88eaSTakashi Iwai 1901a86a88eaSTakashi Iwai static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 1902a86a88eaSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 1903a86a88eaSTakashi Iwai { 1904a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1905a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1906a86a88eaSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1907a86a88eaSTakashi Iwai 1908a86a88eaSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; 1909a86a88eaSTakashi Iwai return 0; 1910a86a88eaSTakashi Iwai } 1911a86a88eaSTakashi Iwai 1912a86a88eaSTakashi Iwai static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 1913a86a88eaSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 1914a86a88eaSTakashi Iwai { 1915a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1916a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1917a86a88eaSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1918a86a88eaSTakashi Iwai hda_nid_t mux; 1919a86a88eaSTakashi Iwai int cur; 1920a86a88eaSTakashi Iwai 1921a86a88eaSTakashi Iwai cur = ucontrol->value.enumerated.item[0]; 1922a86a88eaSTakashi Iwai if (cur < 0 || cur >= spec->num_inputs) 1923a86a88eaSTakashi Iwai return -EINVAL; 1924a86a88eaSTakashi Iwai if (spec->cur_mux[idx] == cur) 1925a86a88eaSTakashi Iwai return 0; 1926a86a88eaSTakashi Iwai spec->cur_mux[idx] = cur; 1927a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) { 1928a86a88eaSTakashi Iwai int adc_idx = spec->inputs[cur].adc_idx; 1929a86a88eaSTakashi Iwai mux = spec->mux_nids[adc_idx]; 1930a86a88eaSTakashi Iwai via_dyn_adc_pcm_resetup(codec, cur); 1931a86a88eaSTakashi Iwai } else { 1932a86a88eaSTakashi Iwai mux = spec->mux_nids[idx]; 1933a86a88eaSTakashi Iwai if (snd_BUG_ON(!mux)) 1934a86a88eaSTakashi Iwai return -EINVAL; 1935a86a88eaSTakashi Iwai } 1936a86a88eaSTakashi Iwai 1937a86a88eaSTakashi Iwai if (mux) { 1938a86a88eaSTakashi Iwai /* switch to D0 beofre change index */ 1939a86a88eaSTakashi Iwai if (snd_hda_codec_read(codec, mux, 0, 1940a86a88eaSTakashi Iwai AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 1941a86a88eaSTakashi Iwai snd_hda_codec_write(codec, mux, 0, 1942a86a88eaSTakashi Iwai AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1943a86a88eaSTakashi Iwai snd_hda_codec_write(codec, mux, 0, 1944a86a88eaSTakashi Iwai AC_VERB_SET_CONNECT_SEL, 1945a86a88eaSTakashi Iwai spec->inputs[cur].mux_idx); 1946a86a88eaSTakashi Iwai } 1947a86a88eaSTakashi Iwai 1948a86a88eaSTakashi Iwai /* update jack power state */ 1949a86a88eaSTakashi Iwai set_widgets_power_state(codec); 1950a86a88eaSTakashi Iwai return 0; 1951a86a88eaSTakashi Iwai } 1952a766d0d7STakashi Iwai 1953d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 1954d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1955d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 1956d7a99cceSTakashi Iwai * So call somewhat different.. 1957d7a99cceSTakashi Iwai */ 1958d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 1959d7a99cceSTakashi Iwai .name = "Input Source", 1960d7a99cceSTakashi Iwai .info = via_mux_enum_info, 1961d7a99cceSTakashi Iwai .get = via_mux_enum_get, 1962d7a99cceSTakashi Iwai .put = via_mux_enum_put, 1963d7a99cceSTakashi Iwai }; 1964d7a99cceSTakashi Iwai 1965a86a88eaSTakashi Iwai static int create_input_src_ctls(struct hda_codec *codec, int count) 1966a86a88eaSTakashi Iwai { 1967a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1968a86a88eaSTakashi Iwai struct snd_kcontrol_new *knew; 1969a86a88eaSTakashi Iwai 1970a86a88eaSTakashi Iwai if (spec->num_inputs <= 1 || !count) 1971a86a88eaSTakashi Iwai return 0; /* no need for single src */ 1972a86a88eaSTakashi Iwai 1973a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 1974a86a88eaSTakashi Iwai if (!knew) 1975a86a88eaSTakashi Iwai return -ENOMEM; 1976a86a88eaSTakashi Iwai knew->count = count; 1977a86a88eaSTakashi Iwai return 0; 1978a86a88eaSTakashi Iwai } 1979a86a88eaSTakashi Iwai 1980a86a88eaSTakashi Iwai /* add the powersave loopback-list entry */ 198113af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) 198213af8e77STakashi Iwai { 198313af8e77STakashi Iwai struct hda_amp_list *list; 198413af8e77STakashi Iwai 198513af8e77STakashi Iwai if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) 198613af8e77STakashi Iwai return; 198713af8e77STakashi Iwai list = spec->loopback_list + spec->num_loopbacks; 198813af8e77STakashi Iwai list->nid = mix; 198913af8e77STakashi Iwai list->dir = HDA_INPUT; 199013af8e77STakashi Iwai list->idx = idx; 199113af8e77STakashi Iwai spec->num_loopbacks++; 199213af8e77STakashi Iwai spec->loopback.amplist = spec->loopback_list; 199313af8e77STakashi Iwai } 199413af8e77STakashi Iwai 1995a86a88eaSTakashi Iwai static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, 1996*8d087c76STakashi Iwai hda_nid_t dst) 1997a86a88eaSTakashi Iwai { 1998*8d087c76STakashi Iwai return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; 1999a86a88eaSTakashi Iwai } 2000a86a88eaSTakashi Iwai 2001a86a88eaSTakashi Iwai /* add the input-route to the given pin */ 2002a86a88eaSTakashi Iwai static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) 2003c577b8a1SJoseph Chan { 200410a20af7STakashi Iwai struct via_spec *spec = codec->spec; 2005a86a88eaSTakashi Iwai int c, idx; 2006a86a88eaSTakashi Iwai 2007a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].adc_idx = -1; 2008a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].pin = pin; 2009a86a88eaSTakashi Iwai for (c = 0; c < spec->num_adc_nids; c++) { 2010a86a88eaSTakashi Iwai if (spec->mux_nids[c]) { 2011a86a88eaSTakashi Iwai idx = get_connection_index(codec, spec->mux_nids[c], 2012a86a88eaSTakashi Iwai pin); 2013a86a88eaSTakashi Iwai if (idx < 0) 2014a86a88eaSTakashi Iwai continue; 2015a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].mux_idx = idx; 2016a86a88eaSTakashi Iwai } else { 2017*8d087c76STakashi Iwai if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) 2018a86a88eaSTakashi Iwai continue; 2019a86a88eaSTakashi Iwai } 2020a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].adc_idx = c; 2021a86a88eaSTakashi Iwai /* Can primary ADC satisfy all inputs? */ 2022a86a88eaSTakashi Iwai if (!spec->dyn_adc_switch && 2023a86a88eaSTakashi Iwai spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { 2024a86a88eaSTakashi Iwai snd_printd(KERN_INFO 2025a86a88eaSTakashi Iwai "via: dynamic ADC switching enabled\n"); 2026a86a88eaSTakashi Iwai spec->dyn_adc_switch = 1; 2027a86a88eaSTakashi Iwai } 2028a86a88eaSTakashi Iwai return true; 2029a86a88eaSTakashi Iwai } 2030a86a88eaSTakashi Iwai return false; 2031a86a88eaSTakashi Iwai } 2032a86a88eaSTakashi Iwai 2033a86a88eaSTakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2034a86a88eaSTakashi Iwai 2035a86a88eaSTakashi Iwai /* parse input-routes; fill ADCs, MUXs and input-src entries */ 2036a86a88eaSTakashi Iwai static int parse_analog_inputs(struct hda_codec *codec) 2037a86a88eaSTakashi Iwai { 2038a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2039a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2040a86a88eaSTakashi Iwai int i, err; 2041a766d0d7STakashi Iwai 2042a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2043a766d0d7STakashi Iwai if (err < 0) 2044a766d0d7STakashi Iwai return err; 2045a766d0d7STakashi Iwai err = get_mux_nids(codec); 2046a766d0d7STakashi Iwai if (err < 0) 2047a766d0d7STakashi Iwai return err; 2048a766d0d7STakashi Iwai 2049a86a88eaSTakashi Iwai /* fill all input-routes */ 2050a86a88eaSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2051a86a88eaSTakashi Iwai if (add_input_route(codec, cfg->inputs[i].pin)) 2052a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs++].label = 2053a86a88eaSTakashi Iwai hda_get_autocfg_input_label(codec, cfg, i); 2054a86a88eaSTakashi Iwai } 2055a86a88eaSTakashi Iwai 2056a86a88eaSTakashi Iwai /* check for internal loopback recording */ 2057a86a88eaSTakashi Iwai if (spec->aa_mix_nid && 2058a86a88eaSTakashi Iwai add_input_route(codec, spec->aa_mix_nid)) 2059a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; 2060a86a88eaSTakashi Iwai 2061a86a88eaSTakashi Iwai return 0; 2062a86a88eaSTakashi Iwai } 2063a86a88eaSTakashi Iwai 2064a86a88eaSTakashi Iwai /* create analog-loopback volume/switch controls */ 2065a86a88eaSTakashi Iwai static int create_loopback_ctls(struct hda_codec *codec) 2066a86a88eaSTakashi Iwai { 2067a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2068a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2069a86a88eaSTakashi Iwai const char *prev_label = NULL; 2070a86a88eaSTakashi Iwai int type_idx = 0; 2071a86a88eaSTakashi Iwai int i, j, err, idx; 2072a86a88eaSTakashi Iwai 2073a86a88eaSTakashi Iwai if (!spec->aa_mix_nid) 2074a766d0d7STakashi Iwai return 0; 2075c577b8a1SJoseph Chan 20767b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2077a86a88eaSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2078a86a88eaSTakashi Iwai const char *label = hda_get_autocfg_input_label(codec, cfg, i); 2079a86a88eaSTakashi Iwai 20801e11cae1STakashi Iwai if (prev_label && !strcmp(label, prev_label)) 20817b315bb4STakashi Iwai type_idx++; 20827b315bb4STakashi Iwai else 20837b315bb4STakashi Iwai type_idx = 0; 20841e11cae1STakashi Iwai prev_label = label; 2085a86a88eaSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, pin); 2086a86a88eaSTakashi Iwai if (idx >= 0) { 208716922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2088a86a88eaSTakashi Iwai idx, spec->aa_mix_nid); 2089c577b8a1SJoseph Chan if (err < 0) 2090c577b8a1SJoseph Chan return err; 2091a86a88eaSTakashi Iwai add_loopback_list(spec, spec->aa_mix_nid, idx); 209213af8e77STakashi Iwai } 2093e3d7a143STakashi Iwai 2094e3d7a143STakashi Iwai /* remember the label for smart51 control */ 2095e3d7a143STakashi Iwai for (j = 0; j < spec->smart51_nums; j++) { 2096a86a88eaSTakashi Iwai if (spec->smart51_pins[j] == pin) { 2097e3d7a143STakashi Iwai spec->smart51_idxs[j] = idx; 2098e3d7a143STakashi Iwai spec->smart51_labels[j] = label; 2099e3d7a143STakashi Iwai break; 2100e3d7a143STakashi Iwai } 2101e3d7a143STakashi Iwai } 2102c577b8a1SJoseph Chan } 2103a86a88eaSTakashi Iwai return 0; 2104a86a88eaSTakashi Iwai } 2105a86a88eaSTakashi Iwai 2106a86a88eaSTakashi Iwai /* create mic-boost controls (if present) */ 2107a86a88eaSTakashi Iwai static int create_mic_boost_ctls(struct hda_codec *codec) 2108a86a88eaSTakashi Iwai { 2109a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2110a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2111a86a88eaSTakashi Iwai int i, err; 2112a86a88eaSTakashi Iwai 2113a86a88eaSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2114a86a88eaSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2115a86a88eaSTakashi Iwai unsigned int caps; 2116a86a88eaSTakashi Iwai const char *label; 2117a86a88eaSTakashi Iwai char name[32]; 2118a86a88eaSTakashi Iwai 2119a86a88eaSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 2120a86a88eaSTakashi Iwai continue; 2121a86a88eaSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 2122a86a88eaSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 2123a86a88eaSTakashi Iwai continue; 2124a86a88eaSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2125a86a88eaSTakashi Iwai snprintf(name, sizeof(name), "%s Boost Volume", label); 2126a86a88eaSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2127a86a88eaSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 2128a86a88eaSTakashi Iwai if (err < 0) 2129a86a88eaSTakashi Iwai return err; 2130a86a88eaSTakashi Iwai } 2131a86a88eaSTakashi Iwai return 0; 2132a86a88eaSTakashi Iwai } 2133a86a88eaSTakashi Iwai 2134a86a88eaSTakashi Iwai /* create capture and input-src controls for multiple streams */ 2135a86a88eaSTakashi Iwai static int create_multi_adc_ctls(struct hda_codec *codec) 2136a86a88eaSTakashi Iwai { 2137a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2138a86a88eaSTakashi Iwai int i, err; 2139d7a99cceSTakashi Iwai 2140d7a99cceSTakashi Iwai /* create capture mixer elements */ 2141d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2142d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 2143d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 2144d7a99cceSTakashi Iwai "Capture Volume", i, 2145d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2146d7a99cceSTakashi Iwai HDA_INPUT)); 2147d7a99cceSTakashi Iwai if (err < 0) 2148d7a99cceSTakashi Iwai return err; 2149d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2150d7a99cceSTakashi Iwai "Capture Switch", i, 2151d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2152d7a99cceSTakashi Iwai HDA_INPUT)); 2153d7a99cceSTakashi Iwai if (err < 0) 2154d7a99cceSTakashi Iwai return err; 2155d7a99cceSTakashi Iwai } 2156d7a99cceSTakashi Iwai 2157d7a99cceSTakashi Iwai /* input-source control */ 2158d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 2159d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 2160d7a99cceSTakashi Iwai break; 2161a86a88eaSTakashi Iwai err = create_input_src_ctls(codec, i); 2162d7a99cceSTakashi Iwai if (err < 0) 2163d7a99cceSTakashi Iwai return err; 2164a86a88eaSTakashi Iwai return 0; 2165d7a99cceSTakashi Iwai } 2166d7a99cceSTakashi Iwai 2167a86a88eaSTakashi Iwai /* bind capture volume/switch */ 2168a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_vol_ctl = 2169a86a88eaSTakashi Iwai HDA_BIND_VOL("Capture Volume", 0); 2170a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_sw_ctl = 2171a86a88eaSTakashi Iwai HDA_BIND_SW("Capture Switch", 0); 2172a86a88eaSTakashi Iwai 2173a86a88eaSTakashi Iwai static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, 2174a86a88eaSTakashi Iwai struct hda_ctl_ops *ops) 2175a86a88eaSTakashi Iwai { 2176a86a88eaSTakashi Iwai struct hda_bind_ctls *ctl; 2177a86a88eaSTakashi Iwai int i; 2178a86a88eaSTakashi Iwai 2179a86a88eaSTakashi Iwai ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); 2180a86a88eaSTakashi Iwai if (!ctl) 2181a86a88eaSTakashi Iwai return -ENOMEM; 2182a86a88eaSTakashi Iwai ctl->ops = ops; 2183a86a88eaSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 2184a86a88eaSTakashi Iwai ctl->values[i] = 2185a86a88eaSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); 2186a86a88eaSTakashi Iwai *ctl_ret = ctl; 2187a86a88eaSTakashi Iwai return 0; 2188a86a88eaSTakashi Iwai } 2189a86a88eaSTakashi Iwai 2190a86a88eaSTakashi Iwai /* create capture and input-src controls for dynamic ADC-switch case */ 2191a86a88eaSTakashi Iwai static int create_dyn_adc_ctls(struct hda_codec *codec) 2192a86a88eaSTakashi Iwai { 2193a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2194a86a88eaSTakashi Iwai struct snd_kcontrol_new *knew; 2195a86a88eaSTakashi Iwai int err; 2196a86a88eaSTakashi Iwai 2197a86a88eaSTakashi Iwai /* set up the bind capture ctls */ 2198a86a88eaSTakashi Iwai err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); 2199a86a88eaSTakashi Iwai if (err < 0) 2200a86a88eaSTakashi Iwai return err; 2201a86a88eaSTakashi Iwai err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); 2202a86a88eaSTakashi Iwai if (err < 0) 2203a86a88eaSTakashi Iwai return err; 2204a86a88eaSTakashi Iwai 2205a86a88eaSTakashi Iwai /* create capture mixer elements */ 2206a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_bind_cap_vol_ctl); 2207a86a88eaSTakashi Iwai if (!knew) 2208a86a88eaSTakashi Iwai return -ENOMEM; 2209a86a88eaSTakashi Iwai knew->private_value = (long)spec->bind_cap_vol; 2210a86a88eaSTakashi Iwai 2211a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_bind_cap_sw_ctl); 2212a86a88eaSTakashi Iwai if (!knew) 2213a86a88eaSTakashi Iwai return -ENOMEM; 2214a86a88eaSTakashi Iwai knew->private_value = (long)spec->bind_cap_sw; 2215a86a88eaSTakashi Iwai 2216a86a88eaSTakashi Iwai /* input-source control */ 2217a86a88eaSTakashi Iwai err = create_input_src_ctls(codec, 1); 2218a86a88eaSTakashi Iwai if (err < 0) 2219a86a88eaSTakashi Iwai return err; 2220a86a88eaSTakashi Iwai return 0; 2221a86a88eaSTakashi Iwai } 2222a86a88eaSTakashi Iwai 2223a86a88eaSTakashi Iwai /* parse and create capture-related stuff */ 2224a86a88eaSTakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec) 2225a86a88eaSTakashi Iwai { 2226a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2227a86a88eaSTakashi Iwai int err; 2228a86a88eaSTakashi Iwai 2229a86a88eaSTakashi Iwai err = parse_analog_inputs(codec); 2230a86a88eaSTakashi Iwai if (err < 0) 2231a86a88eaSTakashi Iwai return err; 2232a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 2233a86a88eaSTakashi Iwai err = create_dyn_adc_ctls(codec); 2234a86a88eaSTakashi Iwai else 2235a86a88eaSTakashi Iwai err = create_multi_adc_ctls(codec); 2236a86a88eaSTakashi Iwai if (err < 0) 2237a86a88eaSTakashi Iwai return err; 2238a86a88eaSTakashi Iwai err = create_loopback_ctls(codec); 2239a86a88eaSTakashi Iwai if (err < 0) 2240a86a88eaSTakashi Iwai return err; 2241a86a88eaSTakashi Iwai err = create_mic_boost_ctls(codec); 2242a86a88eaSTakashi Iwai if (err < 0) 2243a86a88eaSTakashi Iwai return err; 2244c577b8a1SJoseph Chan return 0; 2245c577b8a1SJoseph Chan } 2246c577b8a1SJoseph Chan 224776d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 224876d9b0ddSHarald Welte { 224976d9b0ddSHarald Welte unsigned int def_conf; 225076d9b0ddSHarald Welte unsigned char seqassoc; 225176d9b0ddSHarald Welte 22522f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 225376d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 225476d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 225582ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 225682ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 225776d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 22582f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 225976d9b0ddSHarald Welte } 226076d9b0ddSHarald Welte 226176d9b0ddSHarald Welte return; 226276d9b0ddSHarald Welte } 226376d9b0ddSHarald Welte 2264e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 22651f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22661f2e99feSLydia Wang { 22671f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22681f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22691f2e99feSLydia Wang 22701f2e99feSLydia Wang if (spec->codec_type != VT1708) 22711f2e99feSLydia Wang return 0; 2272e06e5a29STakashi Iwai spec->vt1708_jack_detect = 22731f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2274e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 22751f2e99feSLydia Wang return 0; 22761f2e99feSLydia Wang } 22771f2e99feSLydia Wang 2278e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 22791f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 22801f2e99feSLydia Wang { 22811f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 22821f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22831f2e99feSLydia Wang int change; 22841f2e99feSLydia Wang 22851f2e99feSLydia Wang if (spec->codec_type != VT1708) 22861f2e99feSLydia Wang return 0; 2287e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 22881f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2289e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2290e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 22911f2e99feSLydia Wang mute_aa_path(codec, 1); 22921f2e99feSLydia Wang notify_aa_path_ctls(codec); 22931f2e99feSLydia Wang } 22941f2e99feSLydia Wang return change; 22951f2e99feSLydia Wang } 22961f2e99feSLydia Wang 2297e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 22981f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 22991f2e99feSLydia Wang .name = "Jack Detect", 23001f2e99feSLydia Wang .count = 1, 23011f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2302e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2303e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 23041f2e99feSLydia Wang }; 23051f2e99feSLydia Wang 230612daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec); 230712daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec); 230812daef65STakashi Iwai 230912daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 2310c577b8a1SJoseph Chan { 2311c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2312c577b8a1SJoseph Chan int err; 2313c577b8a1SJoseph Chan 2314c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2315c577b8a1SJoseph Chan if (err < 0) 2316c577b8a1SJoseph Chan return err; 2317c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 23187f0df88cSTakashi Iwai return -EINVAL; 2319c577b8a1SJoseph Chan 23204a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2321c577b8a1SJoseph Chan if (err < 0) 2322c577b8a1SJoseph Chan return err; 23234a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2324c577b8a1SJoseph Chan if (err < 0) 2325c577b8a1SJoseph Chan return err; 23264a918ffeSTakashi Iwai err = via_auto_create_speaker_ctls(codec); 23274a918ffeSTakashi Iwai if (err < 0) 23284a918ffeSTakashi Iwai return err; 2329a86a88eaSTakashi Iwai err = via_auto_create_analog_input_ctls(codec); 2330c577b8a1SJoseph Chan if (err < 0) 2331c577b8a1SJoseph Chan return err; 2332c577b8a1SJoseph Chan 2333c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2334c577b8a1SJoseph Chan 233512daef65STakashi Iwai fill_dig_outs(codec); 233612daef65STakashi Iwai fill_dig_in(codec); 2337c577b8a1SJoseph Chan 2338603c4019STakashi Iwai if (spec->kctls.list) 2339603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2340c577b8a1SJoseph Chan 2341096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 2342c577b8a1SJoseph Chan 23438df2a312STakashi Iwai if (spec->hp_dac_nid && spec->hp_dep_path.depth) { 2344ece8d043STakashi Iwai err = via_hp_build(codec); 2345ece8d043STakashi Iwai if (err < 0) 2346ece8d043STakashi Iwai return err; 2347ece8d043STakashi Iwai } 2348c577b8a1SJoseph Chan 2349f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2350f4a7828bSTakashi Iwai if (err < 0) 2351f4a7828bSTakashi Iwai return err; 2352f4a7828bSTakashi Iwai 23535d41762aSTakashi Iwai /* assign slave outs */ 23545d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 23555d41762aSTakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 23565d41762aSTakashi Iwai 2357c577b8a1SJoseph Chan return 1; 2358c577b8a1SJoseph Chan } 2359c577b8a1SJoseph Chan 23605d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec) 2361c577b8a1SJoseph Chan { 236225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 23635d41762aSTakashi Iwai if (spec->multiout.dig_out_nid) 23645d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); 23655d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 23665d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); 23675d41762aSTakashi Iwai } 236825eaba2fSLydia Wang 23695d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec) 23705d41762aSTakashi Iwai { 23715d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 23725d41762aSTakashi Iwai if (!spec->dig_in_nid) 23735d41762aSTakashi Iwai return; 23745d41762aSTakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 23755d41762aSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 23765d41762aSTakashi Iwai } 23775d41762aSTakashi Iwai 23784a918ffeSTakashi Iwai /* initialize the unsolicited events */ 23794a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec) 23804a918ffeSTakashi Iwai { 23814a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 23824a918ffeSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 23834a918ffeSTakashi Iwai unsigned int ev; 23844a918ffeSTakashi Iwai int i; 23854a918ffeSTakashi Iwai 23864a918ffeSTakashi Iwai if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) 23874a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->hp_pins[0], 0, 23884a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 23894a918ffeSTakashi Iwai AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); 23904a918ffeSTakashi Iwai 23914a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 23924a918ffeSTakashi Iwai ev = VIA_LINE_EVENT; 23934a918ffeSTakashi Iwai else 23944a918ffeSTakashi Iwai ev = 0; 23954a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 23964a918ffeSTakashi Iwai if (cfg->line_out_pins[i] && 23974a918ffeSTakashi Iwai is_jack_detectable(codec, cfg->line_out_pins[i])) 239863f10d2cSLydia Wang snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, 23994a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 24004a918ffeSTakashi Iwai AC_USRSP_EN | ev | VIA_JACK_EVENT); 24014a918ffeSTakashi Iwai } 24024a918ffeSTakashi Iwai 24034a918ffeSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 24044a918ffeSTakashi Iwai if (is_jack_detectable(codec, cfg->inputs[i].pin)) 24054a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, 24064a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 24074a918ffeSTakashi Iwai AC_USRSP_EN | VIA_JACK_EVENT); 24084a918ffeSTakashi Iwai } 24094a918ffeSTakashi Iwai } 24104a918ffeSTakashi Iwai 24115d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 24125d41762aSTakashi Iwai { 24135d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 24145d41762aSTakashi Iwai int i; 24155d41762aSTakashi Iwai 24165d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 24175d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 24185d41762aSTakashi Iwai 2419c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2420c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 24214a918ffeSTakashi Iwai via_auto_init_speaker_out(codec); 2422c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 24235d41762aSTakashi Iwai via_auto_init_dig_outs(codec); 24245d41762aSTakashi Iwai via_auto_init_dig_in(codec); 242511890956SLydia Wang 24264a918ffeSTakashi Iwai via_auto_init_unsol_event(codec); 24274a918ffeSTakashi Iwai 242825eaba2fSLydia Wang via_hp_automute(codec); 24294a918ffeSTakashi Iwai via_line_automute(codec, false); 243025eaba2fSLydia Wang 2431c577b8a1SJoseph Chan return 0; 2432c577b8a1SJoseph Chan } 2433c577b8a1SJoseph Chan 24341f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 24351f2e99feSLydia Wang { 24361f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 24371f2e99feSLydia Wang vt1708_hp_work.work); 24381f2e99feSLydia Wang if (spec->codec_type != VT1708) 24391f2e99feSLydia Wang return; 24401f2e99feSLydia Wang /* if jack state toggled */ 24411f2e99feSLydia Wang if (spec->vt1708_hp_present 2442d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 24431f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 24441f2e99feSLydia Wang via_hp_automute(spec->codec); 24451f2e99feSLydia Wang } 24461f2e99feSLydia Wang vt1708_start_hp_work(spec); 24471f2e99feSLydia Wang } 24481f2e99feSLydia Wang 2449337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2450337b9d02STakashi Iwai { 2451337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2452337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2453337b9d02STakashi Iwai unsigned int type; 2454337b9d02STakashi Iwai int i, n; 2455337b9d02STakashi Iwai 2456337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2457337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2458337b9d02STakashi Iwai while (nid) { 2459a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 24601c55d521STakashi Iwai if (type == AC_WID_PIN) 24611c55d521STakashi Iwai break; 2462337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2463337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2464337b9d02STakashi Iwai if (n <= 0) 2465337b9d02STakashi Iwai break; 2466337b9d02STakashi Iwai if (n > 1) { 2467337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2468337b9d02STakashi Iwai break; 2469337b9d02STakashi Iwai } 2470337b9d02STakashi Iwai nid = conn[0]; 2471337b9d02STakashi Iwai } 2472337b9d02STakashi Iwai } 24731c55d521STakashi Iwai return 0; 2474337b9d02STakashi Iwai } 2475337b9d02STakashi Iwai 2476c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2477c577b8a1SJoseph Chan { 2478c577b8a1SJoseph Chan struct via_spec *spec; 2479c577b8a1SJoseph Chan int err; 2480c577b8a1SJoseph Chan 2481c577b8a1SJoseph Chan /* create a codec specific record */ 24825b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2483c577b8a1SJoseph Chan if (spec == NULL) 2484c577b8a1SJoseph Chan return -ENOMEM; 2485c577b8a1SJoseph Chan 2486620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2487620e2b28STakashi Iwai 248812daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 248912daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 249012daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 249112daef65STakashi Iwai 2492c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 249312daef65STakashi Iwai err = via_parse_auto_config(codec); 2494c577b8a1SJoseph Chan if (err < 0) { 2495c577b8a1SJoseph Chan via_free(codec); 2496c577b8a1SJoseph Chan return err; 2497c577b8a1SJoseph Chan } 2498c577b8a1SJoseph Chan 249912daef65STakashi Iwai /* add jack detect on/off control */ 250012daef65STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 250112daef65STakashi Iwai return -ENOMEM; 250212daef65STakashi Iwai 2503bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2504bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2505bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2506c577b8a1SJoseph Chan 2507c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2508c577b8a1SJoseph Chan 25091f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2510c577b8a1SJoseph Chan return 0; 2511c577b8a1SJoseph Chan } 2512c577b8a1SJoseph Chan 2513ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 2514c577b8a1SJoseph Chan { 2515c577b8a1SJoseph Chan struct via_spec *spec; 2516c577b8a1SJoseph Chan int err; 2517c577b8a1SJoseph Chan 2518c577b8a1SJoseph Chan /* create a codec specific record */ 25195b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2520c577b8a1SJoseph Chan if (spec == NULL) 2521c577b8a1SJoseph Chan return -ENOMEM; 2522c577b8a1SJoseph Chan 2523620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2524620e2b28STakashi Iwai 252512daef65STakashi Iwai err = via_parse_auto_config(codec); 2526c577b8a1SJoseph Chan if (err < 0) { 2527c577b8a1SJoseph Chan via_free(codec); 2528c577b8a1SJoseph Chan return err; 2529c577b8a1SJoseph Chan } 2530c577b8a1SJoseph Chan 2531c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2532c577b8a1SJoseph Chan 2533f7278fd0SJosepch Chan return 0; 2534f7278fd0SJosepch Chan } 2535f7278fd0SJosepch Chan 25363e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 25373e95b9abSLydia Wang { 25383e95b9abSLydia Wang struct via_spec *spec = codec->spec; 25393e95b9abSLydia Wang int imux_is_smixer; 25403e95b9abSLydia Wang unsigned int parm; 25413e95b9abSLydia Wang int is_8ch = 0; 2542bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2543bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 25443e95b9abSLydia Wang is_8ch = 1; 25453e95b9abSLydia Wang 25463e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 25473e95b9abSLydia Wang imux_is_smixer = 25483e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 25493e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 25503e95b9abSLydia Wang /* inputs */ 25513e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 25523e95b9abSLydia Wang parm = AC_PWRST_D3; 25533e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 25543e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 25553e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 25563e95b9abSLydia Wang if (imux_is_smixer) 25573e95b9abSLydia Wang parm = AC_PWRST_D0; 25583e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 25593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 25603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 25613e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 25623e95b9abSLydia Wang 25633e95b9abSLydia Wang /* outputs */ 25643e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 25653e95b9abSLydia Wang parm = AC_PWRST_D3; 25663e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 25673e95b9abSLydia Wang if (spec->smart51_enabled) 25683e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 25693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 25703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 25713e95b9abSLydia Wang 25723e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 25733e95b9abSLydia Wang if (is_8ch) { 25743e95b9abSLydia Wang parm = AC_PWRST_D3; 25753e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 25763e95b9abSLydia Wang if (spec->smart51_enabled) 25773e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 25783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 25793e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 25803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 25813e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2582bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2583bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2584bc92df7fSLydia Wang parm = AC_PWRST_D3; 2585bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2586bc92df7fSLydia Wang if (spec->smart51_enabled) 2587bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2588bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2589bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2590bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2591bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 25923e95b9abSLydia Wang } 25933e95b9abSLydia Wang 25943e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 25953e95b9abSLydia Wang parm = AC_PWRST_D3; 25963e95b9abSLydia Wang /* force to D0 for internal Speaker */ 25973e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 25983e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 25993e95b9abSLydia Wang if (is_8ch) 26003e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 26013e95b9abSLydia Wang 26023e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 26033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 26043e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 26053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 26063e95b9abSLydia Wang if (is_8ch) { 26073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 26083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 26093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 26103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2611bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2612bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2613bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 26143e95b9abSLydia Wang } 26153e95b9abSLydia Wang 2616518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2617ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 2618f7278fd0SJosepch Chan { 2619f7278fd0SJosepch Chan struct via_spec *spec; 2620f7278fd0SJosepch Chan int err; 2621f7278fd0SJosepch Chan 2622518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2623518bf3baSLydia Wang return patch_vt1708S(codec); 2624ddd304d8STakashi Iwai 2625f7278fd0SJosepch Chan /* create a codec specific record */ 26265b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2627f7278fd0SJosepch Chan if (spec == NULL) 2628f7278fd0SJosepch Chan return -ENOMEM; 2629f7278fd0SJosepch Chan 2630620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2631620e2b28STakashi Iwai 2632f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 263312daef65STakashi Iwai err = via_parse_auto_config(codec); 2634f7278fd0SJosepch Chan if (err < 0) { 2635f7278fd0SJosepch Chan via_free(codec); 2636f7278fd0SJosepch Chan return err; 2637f7278fd0SJosepch Chan } 2638f7278fd0SJosepch Chan 2639f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2640f7278fd0SJosepch Chan 26413e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 26423e95b9abSLydia Wang 2643f7278fd0SJosepch Chan return 0; 2644f7278fd0SJosepch Chan } 2645f7278fd0SJosepch Chan 2646d949cac1SHarald Welte /* Patch for VT1708S */ 2647096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 2648d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2649d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2650bc7e7e5cSLydia Wang /* don't bybass mixer */ 2651bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 2652d949cac1SHarald Welte { } 2653d949cac1SHarald Welte }; 2654d949cac1SHarald Welte 26559da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 26569da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 26579da29271STakashi Iwai { 26589da29271STakashi Iwai struct via_spec *spec = codec->spec; 26599da29271STakashi Iwai int i; 26609da29271STakashi Iwai 26619da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 26629da29271STakashi Iwai hda_nid_t nid; 26639da29271STakashi Iwai int conn; 26649da29271STakashi Iwai 26659da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 26669da29271STakashi Iwai if (!nid) 26679da29271STakashi Iwai continue; 26689da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 26699da29271STakashi Iwai if (conn < 1) 26709da29271STakashi Iwai continue; 26719da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 26729da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 26739da29271STakashi Iwai else { 26749da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 26759da29271STakashi Iwai break; /* at most two dig outs */ 26769da29271STakashi Iwai } 26779da29271STakashi Iwai } 26789da29271STakashi Iwai } 26799da29271STakashi Iwai 268012daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec) 2681d949cac1SHarald Welte { 2682d949cac1SHarald Welte struct via_spec *spec = codec->spec; 268312daef65STakashi Iwai hda_nid_t dig_nid; 268412daef65STakashi Iwai int i, err; 2685d949cac1SHarald Welte 268612daef65STakashi Iwai if (!spec->autocfg.dig_in_pin) 268712daef65STakashi Iwai return; 2688d949cac1SHarald Welte 268912daef65STakashi Iwai dig_nid = codec->start_nid; 269012daef65STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 269112daef65STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 269212daef65STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 269312daef65STakashi Iwai continue; 269412daef65STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 269512daef65STakashi Iwai continue; 269612daef65STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 269712daef65STakashi Iwai continue; 269812daef65STakashi Iwai err = get_connection_index(codec, dig_nid, 269912daef65STakashi Iwai spec->autocfg.dig_in_pin); 270012daef65STakashi Iwai if (err >= 0) { 270112daef65STakashi Iwai spec->dig_in_nid = dig_nid; 270212daef65STakashi Iwai break; 270312daef65STakashi Iwai } 270412daef65STakashi Iwai } 2705d949cac1SHarald Welte } 2706d949cac1SHarald Welte 27076369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 27086369bcfcSLydia Wang int offset, int num_steps, int step_size) 27096369bcfcSLydia Wang { 27106369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 27116369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 27126369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 27136369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 27146369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 27156369bcfcSLydia Wang } 27166369bcfcSLydia Wang 2717d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2718d949cac1SHarald Welte { 2719d949cac1SHarald Welte struct via_spec *spec; 2720d949cac1SHarald Welte int err; 2721d949cac1SHarald Welte 2722d949cac1SHarald Welte /* create a codec specific record */ 27235b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2724d949cac1SHarald Welte if (spec == NULL) 2725d949cac1SHarald Welte return -ENOMEM; 2726d949cac1SHarald Welte 2727620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2728d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2729d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2730620e2b28STakashi Iwai 2731d949cac1SHarald Welte /* automatic parse from the BIOS config */ 273212daef65STakashi Iwai err = via_parse_auto_config(codec); 2733d949cac1SHarald Welte if (err < 0) { 2734d949cac1SHarald Welte via_free(codec); 2735d949cac1SHarald Welte return err; 2736d949cac1SHarald Welte } 2737d949cac1SHarald Welte 2738096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 2739d949cac1SHarald Welte 2740d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2741d949cac1SHarald Welte 2742518bf3baSLydia Wang /* correct names for VT1708BCE */ 2743518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 2744518bf3baSLydia Wang kfree(codec->chip_name); 2745518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 2746518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 2747518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 2748518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2749970f630fSLydia Wang } 2750bc92df7fSLydia Wang /* correct names for VT1705 */ 2751bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 2752bc92df7fSLydia Wang kfree(codec->chip_name); 2753bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 2754bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 2755bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 2756bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2757bc92df7fSLydia Wang } 27583e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 2759d949cac1SHarald Welte return 0; 2760d949cac1SHarald Welte } 2761d949cac1SHarald Welte 2762d949cac1SHarald Welte /* Patch for VT1702 */ 2763d949cac1SHarald Welte 2764096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 2765bc7e7e5cSLydia Wang /* mixer enable */ 2766bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 2767bc7e7e5cSLydia Wang /* GPIO 0~2 */ 2768bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 2769d949cac1SHarald Welte { } 2770d949cac1SHarald Welte }; 2771d949cac1SHarald Welte 27723e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 27733e95b9abSLydia Wang { 27743e95b9abSLydia Wang int imux_is_smixer = 27753e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 27763e95b9abSLydia Wang unsigned int parm; 27773e95b9abSLydia Wang /* inputs */ 27783e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 27793e95b9abSLydia Wang parm = AC_PWRST_D3; 27803e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 27813e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 27823e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 27833e95b9abSLydia Wang if (imux_is_smixer) 27843e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 27853e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 27863e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 27873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 27883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 27893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 27903e95b9abSLydia Wang 27913e95b9abSLydia Wang /* outputs */ 27923e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 27933e95b9abSLydia Wang parm = AC_PWRST_D3; 27943e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 27953e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 27963e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 27973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 27983e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 27993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 28003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 28013e95b9abSLydia Wang } 28023e95b9abSLydia Wang 2803d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 2804d949cac1SHarald Welte { 2805d949cac1SHarald Welte struct via_spec *spec; 2806d949cac1SHarald Welte int err; 2807d949cac1SHarald Welte 2808d949cac1SHarald Welte /* create a codec specific record */ 28095b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2810d949cac1SHarald Welte if (spec == NULL) 2811d949cac1SHarald Welte return -ENOMEM; 2812d949cac1SHarald Welte 2813620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 2814620e2b28STakashi Iwai 281512daef65STakashi Iwai /* limit AA path volume to 0 dB */ 281612daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 281712daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 281812daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 281912daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 282012daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 282112daef65STakashi Iwai 2822d949cac1SHarald Welte /* automatic parse from the BIOS config */ 282312daef65STakashi Iwai err = via_parse_auto_config(codec); 2824d949cac1SHarald Welte if (err < 0) { 2825d949cac1SHarald Welte via_free(codec); 2826d949cac1SHarald Welte return err; 2827d949cac1SHarald Welte } 2828d949cac1SHarald Welte 2829096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 2830d949cac1SHarald Welte 2831d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2832d949cac1SHarald Welte 28333e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 2834d949cac1SHarald Welte return 0; 2835d949cac1SHarald Welte } 2836d949cac1SHarald Welte 2837eb7188caSLydia Wang /* Patch for VT1718S */ 2838eb7188caSLydia Wang 2839096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 28404ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 28414ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 2842eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 2843eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 28445d41762aSTakashi Iwai 2845eb7188caSLydia Wang { } 2846eb7188caSLydia Wang }; 2847eb7188caSLydia Wang 28483e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 28493e95b9abSLydia Wang { 28503e95b9abSLydia Wang struct via_spec *spec = codec->spec; 28513e95b9abSLydia Wang int imux_is_smixer; 28523e95b9abSLydia Wang unsigned int parm; 28533e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 28543e95b9abSLydia Wang imux_is_smixer = 28553e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 28563e95b9abSLydia Wang /* inputs */ 28573e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 28583e95b9abSLydia Wang parm = AC_PWRST_D3; 28593e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 28603e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 28613e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 28623e95b9abSLydia Wang if (imux_is_smixer) 28633e95b9abSLydia Wang parm = AC_PWRST_D0; 28643e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 28653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 28663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 28673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 28683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28693e95b9abSLydia Wang 28703e95b9abSLydia Wang /* outputs */ 28713e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 28723e95b9abSLydia Wang parm = AC_PWRST_D3; 28733e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 28743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 28753e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 28763e95b9abSLydia Wang 28773e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 28783e95b9abSLydia Wang parm = AC_PWRST_D3; 28793e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 28803e95b9abSLydia Wang if (spec->smart51_enabled) 28813e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 28823e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 28833e95b9abSLydia Wang 28843e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 28853e95b9abSLydia Wang parm = AC_PWRST_D3; 28863e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 28873e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 28883e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 28893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 28903e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 28913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 28923e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28933e95b9abSLydia Wang 28943e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 28953e95b9abSLydia Wang parm = AC_PWRST_D3; 28963e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 28973e95b9abSLydia Wang if (spec->smart51_enabled) 28983e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 28993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 29003e95b9abSLydia Wang 29013e95b9abSLydia Wang if (spec->hp_independent_mode) { 29023e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 29033e95b9abSLydia Wang parm = AC_PWRST_D3; 29043e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 29053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 29063e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 29083e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29093e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 29103e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29113e95b9abSLydia Wang } 29123e95b9abSLydia Wang } 29133e95b9abSLydia Wang 2914eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 2915eb7188caSLydia Wang { 2916eb7188caSLydia Wang struct via_spec *spec; 2917eb7188caSLydia Wang int err; 2918eb7188caSLydia Wang 2919eb7188caSLydia Wang /* create a codec specific record */ 29205b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2921eb7188caSLydia Wang if (spec == NULL) 2922eb7188caSLydia Wang return -ENOMEM; 2923eb7188caSLydia Wang 2924620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 2925d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 2926d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 2927620e2b28STakashi Iwai 2928eb7188caSLydia Wang /* automatic parse from the BIOS config */ 292912daef65STakashi Iwai err = via_parse_auto_config(codec); 2930eb7188caSLydia Wang if (err < 0) { 2931eb7188caSLydia Wang via_free(codec); 2932eb7188caSLydia Wang return err; 2933eb7188caSLydia Wang } 2934eb7188caSLydia Wang 2935096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 2936eb7188caSLydia Wang 2937eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 2938eb7188caSLydia Wang 29393e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 29403e95b9abSLydia Wang 2941eb7188caSLydia Wang return 0; 2942eb7188caSLydia Wang } 2943f3db423dSLydia Wang 2944f3db423dSLydia Wang /* Patch for VT1716S */ 2945f3db423dSLydia Wang 2946f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 2947f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 2948f3db423dSLydia Wang { 2949f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 2950f3db423dSLydia Wang uinfo->count = 1; 2951f3db423dSLydia Wang uinfo->value.integer.min = 0; 2952f3db423dSLydia Wang uinfo->value.integer.max = 1; 2953f3db423dSLydia Wang return 0; 2954f3db423dSLydia Wang } 2955f3db423dSLydia Wang 2956f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 2957f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2958f3db423dSLydia Wang { 2959f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2960f3db423dSLydia Wang int index = 0; 2961f3db423dSLydia Wang 2962f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 2963f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 2964f3db423dSLydia Wang if (index != -1) 2965f3db423dSLydia Wang *ucontrol->value.integer.value = index; 2966f3db423dSLydia Wang 2967f3db423dSLydia Wang return 0; 2968f3db423dSLydia Wang } 2969f3db423dSLydia Wang 2970f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 2971f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2972f3db423dSLydia Wang { 2973f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2974f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2975f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 2976f3db423dSLydia Wang 2977f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 2978f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 2979f3db423dSLydia Wang spec->dmic_enabled = index; 29803e95b9abSLydia Wang set_widgets_power_state(codec); 2981f3db423dSLydia Wang return 1; 2982f3db423dSLydia Wang } 2983f3db423dSLydia Wang 298490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 2985f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 2986f3db423dSLydia Wang { 2987f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2988f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 29895b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 2990f3db423dSLydia Wang .count = 1, 2991f3db423dSLydia Wang .info = vt1716s_dmic_info, 2992f3db423dSLydia Wang .get = vt1716s_dmic_get, 2993f3db423dSLydia Wang .put = vt1716s_dmic_put, 2994f3db423dSLydia Wang }, 2995f3db423dSLydia Wang {} /* end */ 2996f3db423dSLydia Wang }; 2997f3db423dSLydia Wang 2998f3db423dSLydia Wang 2999f3db423dSLydia Wang /* mono-out mixer elements */ 300090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3001f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3002f3db423dSLydia Wang { } /* end */ 3003f3db423dSLydia Wang }; 3004f3db423dSLydia Wang 3005096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 3006f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 3007f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 3008f3db423dSLydia Wang /* don't bybass mixer */ 3009f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 3010f3db423dSLydia Wang /* Enable mono output */ 3011f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 3012f3db423dSLydia Wang { } 3013f3db423dSLydia Wang }; 3014f3db423dSLydia Wang 30153e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 30163e95b9abSLydia Wang { 30173e95b9abSLydia Wang struct via_spec *spec = codec->spec; 30183e95b9abSLydia Wang int imux_is_smixer; 30193e95b9abSLydia Wang unsigned int parm; 30203e95b9abSLydia Wang unsigned int mono_out, present; 30213e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 30223e95b9abSLydia Wang imux_is_smixer = 30233e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 30243e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 30253e95b9abSLydia Wang /* inputs */ 30263e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 30273e95b9abSLydia Wang parm = AC_PWRST_D3; 30283e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 30293e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 30303e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 30313e95b9abSLydia Wang if (imux_is_smixer) 30323e95b9abSLydia Wang parm = AC_PWRST_D0; 30333e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 30343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 30353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 30363e95b9abSLydia Wang 30373e95b9abSLydia Wang parm = AC_PWRST_D3; 30383e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 30393e95b9abSLydia Wang /* PW11 (22h) */ 30403e95b9abSLydia Wang if (spec->dmic_enabled) 30413e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 30423e95b9abSLydia Wang else 30433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 30443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 30453e95b9abSLydia Wang 30463e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 30473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 30483e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 30493e95b9abSLydia Wang 30503e95b9abSLydia Wang /* outputs */ 30513e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 30523e95b9abSLydia Wang parm = AC_PWRST_D3; 30533e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 30543e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 30553e95b9abSLydia Wang if (spec->smart51_enabled) 30563e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 30573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 30583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 30593e95b9abSLydia Wang 30603e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 30613e95b9abSLydia Wang parm = AC_PWRST_D3; 30623e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 30633e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 30643e95b9abSLydia Wang if (spec->smart51_enabled) 30653e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 30663e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 30673e95b9abSLydia Wang 30683e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 30693e95b9abSLydia Wang if (spec->smart51_enabled) 30703e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 30713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 30723e95b9abSLydia Wang 30733e95b9abSLydia Wang /* Mono out */ 30743e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 30753e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 30763e95b9abSLydia Wang 30773e95b9abSLydia Wang if (present) 30783e95b9abSLydia Wang mono_out = 0; 30793e95b9abSLydia Wang else { 30803e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 30813e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 30823e95b9abSLydia Wang mono_out = 0; 30833e95b9abSLydia Wang else 30843e95b9abSLydia Wang mono_out = 1; 30853e95b9abSLydia Wang } 30863e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 30873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 30883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 30893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 30903e95b9abSLydia Wang 30913e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 30923e95b9abSLydia Wang parm = AC_PWRST_D3; 30933e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 30943e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 30953e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 30963e95b9abSLydia Wang if (spec->hp_independent_mode) 30973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 30983e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30993e95b9abSLydia Wang 31003e95b9abSLydia Wang /* force to D0 for internal Speaker */ 31013e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 31023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 31033e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 31043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 31053e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 31063e95b9abSLydia Wang } 31073e95b9abSLydia Wang 3108f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3109f3db423dSLydia Wang { 3110f3db423dSLydia Wang struct via_spec *spec; 3111f3db423dSLydia Wang int err; 3112f3db423dSLydia Wang 3113f3db423dSLydia Wang /* create a codec specific record */ 31145b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3115f3db423dSLydia Wang if (spec == NULL) 3116f3db423dSLydia Wang return -ENOMEM; 3117f3db423dSLydia Wang 3118620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3119d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3120d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3121620e2b28STakashi Iwai 3122f3db423dSLydia Wang /* automatic parse from the BIOS config */ 312312daef65STakashi Iwai err = via_parse_auto_config(codec); 3124f3db423dSLydia Wang if (err < 0) { 3125f3db423dSLydia Wang via_free(codec); 3126f3db423dSLydia Wang return err; 3127f3db423dSLydia Wang } 3128f3db423dSLydia Wang 3129096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 3130f3db423dSLydia Wang 3131f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3132f3db423dSLydia Wang spec->num_mixers++; 3133f3db423dSLydia Wang 3134f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3135f3db423dSLydia Wang 3136f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3137f3db423dSLydia Wang 31383e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 3139f3db423dSLydia Wang return 0; 3140f3db423dSLydia Wang } 314125eaba2fSLydia Wang 314225eaba2fSLydia Wang /* for vt2002P */ 314325eaba2fSLydia Wang 3144096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 3145eadb9a80SLydia Wang /* Class-D speaker related verbs */ 3146eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 3147eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 3148eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 314925eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 315025eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 315125eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 315225eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 315325eaba2fSLydia Wang { } 315425eaba2fSLydia Wang }; 31554a918ffeSTakashi Iwai 3156096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 315711890956SLydia Wang /* Enable Boost Volume backdoor */ 315811890956SLydia Wang {0x1, 0xfb9, 0x24}, 315911890956SLydia Wang /* Enable AOW0 to MW9 */ 316011890956SLydia Wang {0x1, 0xfb8, 0x88}, 316111890956SLydia Wang { } 316211890956SLydia Wang }; 316325eaba2fSLydia Wang 31643e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 31653e95b9abSLydia Wang { 31663e95b9abSLydia Wang struct via_spec *spec = codec->spec; 31673e95b9abSLydia Wang int imux_is_smixer; 31683e95b9abSLydia Wang unsigned int parm; 31693e95b9abSLydia Wang unsigned int present; 31703e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 31713e95b9abSLydia Wang imux_is_smixer = 31723e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 31733e95b9abSLydia Wang /* inputs */ 31743e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 31753e95b9abSLydia Wang parm = AC_PWRST_D3; 31763e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 31773e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 31783e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 31793e95b9abSLydia Wang parm = AC_PWRST_D0; 31803e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 31813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 31823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 31833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 31843e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 31853e95b9abSLydia Wang 31863e95b9abSLydia Wang /* outputs */ 31873e95b9abSLydia Wang /* AOW0 (8h)*/ 31883e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 31893e95b9abSLydia Wang 319011890956SLydia Wang if (spec->codec_type == VT1802) { 319111890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 319211890956SLydia Wang parm = AC_PWRST_D3; 319311890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 319411890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 319511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 319611890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 319711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 319811890956SLydia Wang } else { 31993e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 32003e95b9abSLydia Wang parm = AC_PWRST_D3; 32013e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 32023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 32033e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 32053e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 320611890956SLydia Wang } 32073e95b9abSLydia Wang 320811890956SLydia Wang if (spec->codec_type == VT1802) { 320911890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 321011890956SLydia Wang parm = AC_PWRST_D3; 321111890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 321211890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 321311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 321411890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 321511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 321611890956SLydia Wang } else { 32173e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 32183e95b9abSLydia Wang parm = AC_PWRST_D3; 32193e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 32203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 32213e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 32233e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 322411890956SLydia Wang } 32253e95b9abSLydia Wang 32263e95b9abSLydia Wang if (spec->hp_independent_mode) 32273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 32283e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 32293e95b9abSLydia Wang 32303e95b9abSLydia Wang /* Class-D */ 32313e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 32323e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 32333e95b9abSLydia Wang 32343e95b9abSLydia Wang parm = AC_PWRST_D3; 32353e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 32363e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 323711890956SLydia Wang if (spec->codec_type == VT1802) 323811890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 323911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 324011890956SLydia Wang else 32413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 32423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 32443e95b9abSLydia Wang 32453e95b9abSLydia Wang /* Mono Out */ 32463e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 32473e95b9abSLydia Wang 32483e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 324911890956SLydia Wang if (spec->codec_type == VT1802) { 325011890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 325111890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 325211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 325311890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 325411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 325511890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 325611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 325711890956SLydia Wang } else { 32583e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 32593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 32603e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32613e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 32623e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 32633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 32643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 326511890956SLydia Wang } 32663e95b9abSLydia Wang /* MW9 (21h) */ 32673e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 32683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32693e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 32703e95b9abSLydia Wang else 32713e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 32723e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 32733e95b9abSLydia Wang } 327425eaba2fSLydia Wang 327525eaba2fSLydia Wang /* patch for vt2002P */ 327625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 327725eaba2fSLydia Wang { 327825eaba2fSLydia Wang struct via_spec *spec; 327925eaba2fSLydia Wang int err; 328025eaba2fSLydia Wang 328125eaba2fSLydia Wang /* create a codec specific record */ 32825b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 328325eaba2fSLydia Wang if (spec == NULL) 328425eaba2fSLydia Wang return -ENOMEM; 328525eaba2fSLydia Wang 3286620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3287d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3288d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3289620e2b28STakashi Iwai 329025eaba2fSLydia Wang /* automatic parse from the BIOS config */ 329112daef65STakashi Iwai err = via_parse_auto_config(codec); 329225eaba2fSLydia Wang if (err < 0) { 329325eaba2fSLydia Wang via_free(codec); 329425eaba2fSLydia Wang return err; 329525eaba2fSLydia Wang } 329625eaba2fSLydia Wang 329711890956SLydia Wang if (spec->codec_type == VT1802) 32984a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 329911890956SLydia Wang else 33004a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 330111890956SLydia Wang 330225eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 330325eaba2fSLydia Wang 33043e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 330525eaba2fSLydia Wang return 0; 330625eaba2fSLydia Wang } 3307ab6734e7SLydia Wang 3308ab6734e7SLydia Wang /* for vt1812 */ 3309ab6734e7SLydia Wang 3310096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 3311ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 3312ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 3313ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 3314ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 3315ab6734e7SLydia Wang { } 3316ab6734e7SLydia Wang }; 3317ab6734e7SLydia Wang 33183e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 33193e95b9abSLydia Wang { 33203e95b9abSLydia Wang struct via_spec *spec = codec->spec; 33213e95b9abSLydia Wang int imux_is_smixer = 33223e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 33233e95b9abSLydia Wang unsigned int parm; 33243e95b9abSLydia Wang unsigned int present; 33253e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 33263e95b9abSLydia Wang imux_is_smixer = 33273e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 33283e95b9abSLydia Wang /* inputs */ 33293e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 33303e95b9abSLydia Wang parm = AC_PWRST_D3; 33313e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 33323e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 33333e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 33343e95b9abSLydia Wang parm = AC_PWRST_D0; 33353e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 33363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 33373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 33383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 33393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 33403e95b9abSLydia Wang 33413e95b9abSLydia Wang /* outputs */ 33423e95b9abSLydia Wang /* AOW0 (8h)*/ 33433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 33443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33453e95b9abSLydia Wang 33463e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 33473e95b9abSLydia Wang parm = AC_PWRST_D3; 33483e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 33493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 33503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 33513e95b9abSLydia Wang 33523e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 33533e95b9abSLydia Wang parm = AC_PWRST_D3; 33543e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 33553e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 33563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 33573e95b9abSLydia Wang if (spec->hp_independent_mode) 33583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 33593e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33603e95b9abSLydia Wang 33613e95b9abSLydia Wang /* Internal Speaker */ 33623e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 33633e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 33643e95b9abSLydia Wang 33653e95b9abSLydia Wang parm = AC_PWRST_D3; 33663e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 33673e95b9abSLydia Wang if (present) { 33683e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33693e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33713e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33723e95b9abSLydia Wang } else { 33733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 33743e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 33763e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33773e95b9abSLydia Wang } 33783e95b9abSLydia Wang 33793e95b9abSLydia Wang 33803e95b9abSLydia Wang /* Mono Out */ 33813e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 33823e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 33833e95b9abSLydia Wang 33843e95b9abSLydia Wang parm = AC_PWRST_D3; 33853e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 33863e95b9abSLydia Wang if (present) { 33873e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33883e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33893e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33903e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33913e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33923e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33933e95b9abSLydia Wang } else { 33943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 33953e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 33973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 33983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 33993e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 34003e95b9abSLydia Wang } 34013e95b9abSLydia Wang 34023e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 34033e95b9abSLydia Wang parm = AC_PWRST_D3; 34043e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 34053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 34063e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 34073e95b9abSLydia Wang 34083e95b9abSLydia Wang } 3409ab6734e7SLydia Wang 3410ab6734e7SLydia Wang /* patch for vt1812 */ 3411ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 3412ab6734e7SLydia Wang { 3413ab6734e7SLydia Wang struct via_spec *spec; 3414ab6734e7SLydia Wang int err; 3415ab6734e7SLydia Wang 3416ab6734e7SLydia Wang /* create a codec specific record */ 34175b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3418ab6734e7SLydia Wang if (spec == NULL) 3419ab6734e7SLydia Wang return -ENOMEM; 3420ab6734e7SLydia Wang 3421620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3422d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3423d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3424620e2b28STakashi Iwai 3425ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 342612daef65STakashi Iwai err = via_parse_auto_config(codec); 3427ab6734e7SLydia Wang if (err < 0) { 3428ab6734e7SLydia Wang via_free(codec); 3429ab6734e7SLydia Wang return err; 3430ab6734e7SLydia Wang } 3431ab6734e7SLydia Wang 3432096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 3433ab6734e7SLydia Wang 3434ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 3435ab6734e7SLydia Wang 34363e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 3437ab6734e7SLydia Wang return 0; 3438ab6734e7SLydia Wang } 3439ab6734e7SLydia Wang 3440c577b8a1SJoseph Chan /* 3441c577b8a1SJoseph Chan * patch entries 3442c577b8a1SJoseph Chan */ 344390dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 34443218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 34453218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 34463218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 34473218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 34483218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3449ddd304d8STakashi Iwai .patch = patch_vt1709}, 34503218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3451ddd304d8STakashi Iwai .patch = patch_vt1709}, 34523218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3453ddd304d8STakashi Iwai .patch = patch_vt1709}, 34543218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3455ddd304d8STakashi Iwai .patch = patch_vt1709}, 34563218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3457ddd304d8STakashi Iwai .patch = patch_vt1709}, 34583218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3459ddd304d8STakashi Iwai .patch = patch_vt1709}, 34603218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3461ddd304d8STakashi Iwai .patch = patch_vt1709}, 34623218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3463ddd304d8STakashi Iwai .patch = patch_vt1709}, 34643218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3465ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34663218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3467ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34683218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3469ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34703218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3471ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34723218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3473ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34743218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3475ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34763218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3477ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34783218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3479ddd304d8STakashi Iwai .patch = patch_vt1708B}, 34803218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3481d949cac1SHarald Welte .patch = patch_vt1708S}, 34823218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3483d949cac1SHarald Welte .patch = patch_vt1708S}, 34843218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3485d949cac1SHarald Welte .patch = patch_vt1708S}, 34863218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3487d949cac1SHarald Welte .patch = patch_vt1708S}, 3488bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 3489d949cac1SHarald Welte .patch = patch_vt1708S}, 34903218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3491d949cac1SHarald Welte .patch = patch_vt1708S}, 34923218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3493d949cac1SHarald Welte .patch = patch_vt1708S}, 34943218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3495d949cac1SHarald Welte .patch = patch_vt1708S}, 34963218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3497d949cac1SHarald Welte .patch = patch_vt1702}, 34983218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3499d949cac1SHarald Welte .patch = patch_vt1702}, 35003218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3501d949cac1SHarald Welte .patch = patch_vt1702}, 35023218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3503d949cac1SHarald Welte .patch = patch_vt1702}, 35043218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3505d949cac1SHarald Welte .patch = patch_vt1702}, 35063218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3507d949cac1SHarald Welte .patch = patch_vt1702}, 35083218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3509d949cac1SHarald Welte .patch = patch_vt1702}, 35103218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3511d949cac1SHarald Welte .patch = patch_vt1702}, 3512eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 3513eb7188caSLydia Wang .patch = patch_vt1718S}, 3514eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 3515eb7188caSLydia Wang .patch = patch_vt1718S}, 3516bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 3517bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3518bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 3519bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3520f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 3521f3db423dSLydia Wang .patch = patch_vt1716S}, 3522f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 3523f3db423dSLydia Wang .patch = patch_vt1716S}, 352425eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 352525eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 3526ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 352736dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 352836dd5c4aSLydia Wang .patch = patch_vt1708S}, 352911890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 353011890956SLydia Wang .patch = patch_vt2002P}, 353111890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 353211890956SLydia Wang .patch = patch_vt2002P}, 3533c577b8a1SJoseph Chan {} /* terminator */ 3534c577b8a1SJoseph Chan }; 35351289e9e8STakashi Iwai 35361289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 35371289e9e8STakashi Iwai 35381289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 35391289e9e8STakashi Iwai .preset = snd_hda_preset_via, 35401289e9e8STakashi Iwai .owner = THIS_MODULE, 35411289e9e8STakashi Iwai }; 35421289e9e8STakashi Iwai 35431289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 35441289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 35451289e9e8STakashi Iwai 35461289e9e8STakashi Iwai static int __init patch_via_init(void) 35471289e9e8STakashi Iwai { 35481289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 35491289e9e8STakashi Iwai } 35501289e9e8STakashi Iwai 35511289e9e8STakashi Iwai static void __exit patch_via_exit(void) 35521289e9e8STakashi Iwai { 35531289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 35541289e9e8STakashi Iwai } 35551289e9e8STakashi Iwai 35561289e9e8STakashi Iwai module_init(patch_via_init) 35571289e9e8STakashi Iwai module_exit(patch_via_exit) 3558