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 110de6c74f3STakashi Iwai #define VIA_MAX_ADCS 3 111de6c74f3STakashi Iwai 1121f2e99feSLydia Wang struct via_spec { 1131f2e99feSLydia Wang /* codec parameterization */ 11490dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 1151f2e99feSLydia Wang unsigned int num_mixers; 1161f2e99feSLydia Wang 11790dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 1181f2e99feSLydia Wang unsigned int num_iverbs; 1191f2e99feSLydia Wang 12082673bc8STakashi Iwai char stream_name_analog[32]; 1217eb56e84STakashi Iwai char stream_name_hp[32]; 12290dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 12390dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1241f2e99feSLydia Wang 12582673bc8STakashi Iwai char stream_name_digital[32]; 12690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 12790dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1281f2e99feSLydia Wang 1291f2e99feSLydia Wang /* playback */ 1301f2e99feSLydia Wang struct hda_multi_out multiout; 1311f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 132ece8d043STakashi Iwai hda_nid_t hp_dac_nid; 133*3214b966STakashi Iwai hda_nid_t speaker_dac_nid; 134*3214b966STakashi Iwai int hp_indep_shared; /* indep HP-DAC is shared with side ch */ 135ada509ecSTakashi Iwai int num_active_streams; 136*3214b966STakashi Iwai int aamix_mode; /* loopback is enabled for output-path? */ 1371f2e99feSLydia Wang 138*3214b966STakashi Iwai /* Output-paths: 139*3214b966STakashi Iwai * There are different output-paths depending on the setup. 140*3214b966STakashi Iwai * out_path, hp_path and speaker_path are primary paths. If both 141*3214b966STakashi Iwai * direct DAC and aa-loopback routes are available, these contain 142*3214b966STakashi Iwai * the former paths. Meanwhile *_mix_path contain the paths with 143*3214b966STakashi Iwai * loopback mixer. (Since the loopback is only for front channel, 144*3214b966STakashi Iwai * no out_mix_path for surround channels.) 145*3214b966STakashi Iwai * The HP output has another path, hp_indep_path, which is used in 146*3214b966STakashi Iwai * the independent-HP mode. 147*3214b966STakashi Iwai */ 148de6c74f3STakashi Iwai struct nid_path out_path[HDA_SIDE + 1]; 149*3214b966STakashi Iwai struct nid_path out_mix_path; 1504a79616dSTakashi Iwai struct nid_path hp_path; 151*3214b966STakashi Iwai struct nid_path hp_mix_path; 152*3214b966STakashi Iwai struct nid_path hp_indep_path; 1534a918ffeSTakashi Iwai struct nid_path speaker_path; 154*3214b966STakashi Iwai struct nid_path speaker_mix_path; 1554a79616dSTakashi Iwai 1561f2e99feSLydia Wang /* capture */ 1571f2e99feSLydia Wang unsigned int num_adc_nids; 158de6c74f3STakashi Iwai hda_nid_t adc_nids[VIA_MAX_ADCS]; 159de6c74f3STakashi Iwai hda_nid_t mux_nids[VIA_MAX_ADCS]; 160620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1611f2e99feSLydia Wang hda_nid_t dig_in_nid; 1621f2e99feSLydia Wang 1631f2e99feSLydia Wang /* capture source */ 164a86a88eaSTakashi Iwai bool dyn_adc_switch; 165a86a88eaSTakashi Iwai int num_inputs; 166a86a88eaSTakashi Iwai struct via_input inputs[AUTO_CFG_MAX_INS + 1]; 167de6c74f3STakashi Iwai unsigned int cur_mux[VIA_MAX_ADCS]; 1681f2e99feSLydia Wang 169a86a88eaSTakashi Iwai /* dynamic ADC switching */ 170a86a88eaSTakashi Iwai hda_nid_t cur_adc; 171a86a88eaSTakashi Iwai unsigned int cur_adc_stream_tag; 172a86a88eaSTakashi Iwai unsigned int cur_adc_format; 173a86a88eaSTakashi Iwai 1741f2e99feSLydia Wang /* PCM information */ 1751f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1761f2e99feSLydia Wang 1771f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1781f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1791f2e99feSLydia Wang struct snd_array kctls; 1801f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1811f2e99feSLydia Wang 1821f2e99feSLydia Wang /* HP mode source */ 1831f2e99feSLydia Wang unsigned int hp_independent_mode; 184f3db423dSLydia Wang unsigned int dmic_enabled; 18524088a58STakashi Iwai unsigned int no_pin_power_ctl; 1861f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1871f2e99feSLydia Wang 188e3d7a143STakashi Iwai /* smart51 setup */ 189e3d7a143STakashi Iwai unsigned int smart51_nums; 190e3d7a143STakashi Iwai hda_nid_t smart51_pins[2]; 191e3d7a143STakashi Iwai int smart51_idxs[2]; 192e3d7a143STakashi Iwai const char *smart51_labels[2]; 193e3d7a143STakashi Iwai unsigned int smart51_enabled; 194e3d7a143STakashi Iwai 1951f2e99feSLydia Wang /* work to check hp jack state */ 1961f2e99feSLydia Wang struct hda_codec *codec; 1971f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 198e06e5a29STakashi Iwai int vt1708_jack_detect; 1991f2e99feSLydia Wang int vt1708_hp_present; 2003e95b9abSLydia Wang 2013e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 2023e95b9abSLydia Wang 2031f2e99feSLydia Wang struct hda_loopback_check loopback; 20413af8e77STakashi Iwai int num_loopbacks; 20513af8e77STakashi Iwai struct hda_amp_list loopback_list[8]; 206a86a88eaSTakashi Iwai 207a86a88eaSTakashi Iwai /* bind capture-volume */ 208a86a88eaSTakashi Iwai struct hda_bind_ctls *bind_cap_vol; 209a86a88eaSTakashi Iwai struct hda_bind_ctls *bind_cap_sw; 2101f2e99feSLydia Wang }; 2111f2e99feSLydia Wang 2120341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 2135b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 2145b0cb1d8SJaroslav Kysela { 2155b0cb1d8SJaroslav Kysela struct via_spec *spec; 2165b0cb1d8SJaroslav Kysela 2175b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 2185b0cb1d8SJaroslav Kysela if (spec == NULL) 2195b0cb1d8SJaroslav Kysela return NULL; 2205b0cb1d8SJaroslav Kysela 2215b0cb1d8SJaroslav Kysela codec->spec = spec; 2225b0cb1d8SJaroslav Kysela spec->codec = codec; 2230341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 2240341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 2250341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 2260341ccd7SLydia Wang spec->codec_type = VT1708S; 2275b0cb1d8SJaroslav Kysela return spec; 2285b0cb1d8SJaroslav Kysela } 2295b0cb1d8SJaroslav Kysela 230744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 231d7426329SHarald Welte { 232744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 233d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 234d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 235d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 236d7426329SHarald Welte 237d7426329SHarald Welte /* get codec type */ 238d7426329SHarald Welte if (ven_id != 0x1106) 239d7426329SHarald Welte codec_type = UNKNOWN; 240d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 241d7426329SHarald Welte codec_type = VT1708; 242d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 243d7426329SHarald Welte codec_type = VT1709_10CH; 244d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 245d7426329SHarald Welte codec_type = VT1709_6CH; 246518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 247d7426329SHarald Welte codec_type = VT1708B_8CH; 248518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 249518bf3baSLydia Wang codec_type = VT1708BCE; 250518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 251d7426329SHarald Welte codec_type = VT1708B_4CH; 252d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 253d7426329SHarald Welte && (dev_id >> 12) < 8) 254d7426329SHarald Welte codec_type = VT1708S; 255d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 256d7426329SHarald Welte && (dev_id >> 12) < 8) 257d7426329SHarald Welte codec_type = VT1702; 258eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 259eb7188caSLydia Wang && (dev_id >> 12) < 8) 260eb7188caSLydia Wang codec_type = VT1718S; 261f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 262f3db423dSLydia Wang codec_type = VT1716S; 263bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 264bb3c6bfcSLydia Wang codec_type = VT1718S; 26525eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 26625eaba2fSLydia Wang codec_type = VT2002P; 267ab6734e7SLydia Wang else if (dev_id == 0x0448) 268ab6734e7SLydia Wang codec_type = VT1812; 26936dd5c4aSLydia Wang else if (dev_id == 0x0440) 27036dd5c4aSLydia Wang codec_type = VT1708S; 27111890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 27211890956SLydia Wang codec_type = VT1802; 273d7426329SHarald Welte else 274d7426329SHarald Welte codec_type = UNKNOWN; 275d7426329SHarald Welte return codec_type; 276d7426329SHarald Welte }; 277d7426329SHarald Welte 278ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 27969e52a80SHarald Welte #define VIA_HP_EVENT 0x01 28069e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 2814a918ffeSTakashi Iwai #define VIA_LINE_EVENT 0x03 28269e52a80SHarald Welte 283c577b8a1SJoseph Chan enum { 284c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 285c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 286f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 287c577b8a1SJoseph Chan }; 288c577b8a1SJoseph Chan 289ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 290ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 2911f2e99feSLydia Wang 2921f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2931f2e99feSLydia Wang { 2941f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2951f2e99feSLydia Wang return; 2961f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 297e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2981f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2991f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 3001f2e99feSLydia Wang msecs_to_jiffies(100)); 3011f2e99feSLydia Wang } 3021f2e99feSLydia Wang 3031f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 3041f2e99feSLydia Wang { 3051f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 3061f2e99feSLydia Wang return; 3071f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 3081f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 3091f2e99feSLydia Wang return; 3101f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 311e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 3125b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 3131f2e99feSLydia Wang } 314f5271101SLydia Wang 3153e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 3163e95b9abSLydia Wang { 3173e95b9abSLydia Wang struct via_spec *spec = codec->spec; 3183e95b9abSLydia Wang if (spec->set_widgets_power_state) 3193e95b9abSLydia Wang spec->set_widgets_power_state(codec); 3203e95b9abSLydia Wang } 32125eaba2fSLydia Wang 322f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 323f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 324f5271101SLydia Wang { 325f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 326f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 327f5271101SLydia Wang 3283e95b9abSLydia Wang set_widgets_power_state(codec); 329ada509ecSTakashi Iwai analog_low_current_mode(snd_kcontrol_chip(kcontrol)); 3301f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 3311f2e99feSLydia Wang if (is_aa_path_mute(codec)) 3321f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 3331f2e99feSLydia Wang else 3341f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 3351f2e99feSLydia Wang } 336f5271101SLydia Wang return change; 337f5271101SLydia Wang } 338f5271101SLydia Wang 339f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 340f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 341f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 342f5271101SLydia Wang .name = NULL, \ 343f5271101SLydia Wang .index = 0, \ 344f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 345f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 346f5271101SLydia Wang .put = analog_input_switch_put, \ 347f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 348f5271101SLydia Wang 34990dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 350c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 351c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 352f5271101SLydia Wang ANALOG_INPUT_MUTE, 353c577b8a1SJoseph Chan }; 354c577b8a1SJoseph Chan 355ab6734e7SLydia Wang 356c577b8a1SJoseph Chan /* add dynamic controls */ 357291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 358291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 359291c9e33STakashi Iwai const char *name) 360c577b8a1SJoseph Chan { 361c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 362c577b8a1SJoseph Chan 363603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 364603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 365c577b8a1SJoseph Chan if (!knew) 366291c9e33STakashi Iwai return NULL; 367291c9e33STakashi Iwai *knew = *tmpl; 368291c9e33STakashi Iwai if (!name) 369291c9e33STakashi Iwai name = tmpl->name; 370291c9e33STakashi Iwai if (name) { 371c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 372c577b8a1SJoseph Chan if (!knew->name) 373291c9e33STakashi Iwai return NULL; 374291c9e33STakashi Iwai } 375291c9e33STakashi Iwai return knew; 376291c9e33STakashi Iwai } 377291c9e33STakashi Iwai 378291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 379291c9e33STakashi Iwai int idx, unsigned long val) 380291c9e33STakashi Iwai { 381291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 382291c9e33STakashi Iwai 383291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 384291c9e33STakashi Iwai if (!knew) 385c577b8a1SJoseph Chan return -ENOMEM; 386d7a99cceSTakashi Iwai knew->index = idx; 3874d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 3885e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 389c577b8a1SJoseph Chan knew->private_value = val; 390c577b8a1SJoseph Chan return 0; 391c577b8a1SJoseph Chan } 392c577b8a1SJoseph Chan 3937b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 3947b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 3957b315bb4STakashi Iwai 396291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 3975b0cb1d8SJaroslav Kysela 398603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 399603c4019STakashi Iwai { 400603c4019STakashi Iwai struct via_spec *spec = codec->spec; 401603c4019STakashi Iwai 402603c4019STakashi Iwai if (spec->kctls.list) { 403603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 404603c4019STakashi Iwai int i; 405603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 406603c4019STakashi Iwai kfree(kctl[i].name); 407603c4019STakashi Iwai } 408603c4019STakashi Iwai snd_array_free(&spec->kctls); 409603c4019STakashi Iwai } 410603c4019STakashi Iwai 411c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4129510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4137b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 414c577b8a1SJoseph Chan { 415c577b8a1SJoseph Chan char name[32]; 416c577b8a1SJoseph Chan int err; 417c577b8a1SJoseph Chan 418c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 4197b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 420c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 421c577b8a1SJoseph Chan if (err < 0) 422c577b8a1SJoseph Chan return err; 423c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 4247b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 425c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 426c577b8a1SJoseph Chan if (err < 0) 427c577b8a1SJoseph Chan return err; 428c577b8a1SJoseph Chan return 0; 429c577b8a1SJoseph Chan } 430c577b8a1SJoseph Chan 4315d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \ 4328d087c76STakashi Iwai snd_hda_get_conn_index(codec, mux, nid, 0) 4335d41762aSTakashi Iwai 4348df2a312STakashi Iwai static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir, 4358df2a312STakashi Iwai unsigned int mask) 4368df2a312STakashi Iwai { 437a934d5a9STakashi Iwai unsigned int caps; 438a934d5a9STakashi Iwai if (!nid) 439a934d5a9STakashi Iwai return false; 440a934d5a9STakashi Iwai caps = get_wcaps(codec, nid); 4418df2a312STakashi Iwai if (dir == HDA_INPUT) 4428df2a312STakashi Iwai caps &= AC_WCAP_IN_AMP; 4438df2a312STakashi Iwai else 4448df2a312STakashi Iwai caps &= AC_WCAP_OUT_AMP; 4458df2a312STakashi Iwai if (!caps) 4468df2a312STakashi Iwai return false; 4478df2a312STakashi Iwai if (query_amp_caps(codec, nid, dir) & mask) 4488df2a312STakashi Iwai return true; 4498df2a312STakashi Iwai return false; 4508df2a312STakashi Iwai } 4518df2a312STakashi Iwai 45209a9ad69STakashi Iwai #define have_mute(codec, nid, dir) \ 45309a9ad69STakashi Iwai check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE) 4548df2a312STakashi Iwai 455d69607b3SLydia Wang /* enable/disable the output-route mixers */ 456d69607b3SLydia Wang static void activate_output_mix(struct hda_codec *codec, struct nid_path *path, 457*3214b966STakashi Iwai hda_nid_t mix_nid, int idx, bool enable) 458d69607b3SLydia Wang { 459d69607b3SLydia Wang int i, num, val; 460d69607b3SLydia Wang 461d69607b3SLydia Wang if (!path) 462d69607b3SLydia Wang return; 463d69607b3SLydia Wang num = snd_hda_get_conn_list(codec, mix_nid, NULL); 464d69607b3SLydia Wang for (i = 0; i < num; i++) { 465*3214b966STakashi Iwai if (i == idx) 466d69607b3SLydia Wang val = AMP_IN_UNMUTE(i); 467d69607b3SLydia Wang else 468d69607b3SLydia Wang val = AMP_IN_MUTE(i); 469d69607b3SLydia Wang snd_hda_codec_write(codec, mix_nid, 0, 470d69607b3SLydia Wang AC_VERB_SET_AMP_GAIN_MUTE, val); 471d69607b3SLydia Wang } 472d69607b3SLydia Wang } 473d69607b3SLydia Wang 47409a9ad69STakashi Iwai /* enable/disable the output-route */ 47509a9ad69STakashi Iwai static void activate_output_path(struct hda_codec *codec, struct nid_path *path, 47609a9ad69STakashi Iwai bool enable, bool force) 4775d41762aSTakashi Iwai { 478d69607b3SLydia Wang struct via_spec *spec = codec->spec; 479*3214b966STakashi Iwai int i; 48009a9ad69STakashi Iwai for (i = 0; i < path->depth; i++) { 48109a9ad69STakashi Iwai hda_nid_t src, dst; 48209a9ad69STakashi Iwai int idx = path->idx[i]; 48309a9ad69STakashi Iwai src = path->path[i]; 48409a9ad69STakashi Iwai if (i < path->depth - 1) 48509a9ad69STakashi Iwai dst = path->path[i + 1]; 48609a9ad69STakashi Iwai else 48709a9ad69STakashi Iwai dst = 0; 48809a9ad69STakashi Iwai if (enable && path->multi[i]) 48909a9ad69STakashi Iwai snd_hda_codec_write(codec, dst, 0, 4905d41762aSTakashi Iwai AC_VERB_SET_CONNECT_SEL, idx); 491*3214b966STakashi Iwai if (!force && (dst == spec->aa_mix_nid)) 492e5e14681SLydia Wang continue; 493*3214b966STakashi Iwai if (have_mute(codec, dst, HDA_INPUT)) 494*3214b966STakashi Iwai activate_output_mix(codec, path, dst, idx, enable); 49509a9ad69STakashi Iwai if (!force && (src == path->vol_ctl || src == path->mute_ctl)) 49609a9ad69STakashi Iwai continue; 49709a9ad69STakashi Iwai if (have_mute(codec, src, HDA_OUTPUT)) { 49809a9ad69STakashi Iwai int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE; 49909a9ad69STakashi Iwai snd_hda_codec_write(codec, src, 0, 50009a9ad69STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, val); 50109a9ad69STakashi Iwai } 50209a9ad69STakashi Iwai } 5035d41762aSTakashi Iwai } 5045d41762aSTakashi Iwai 5055d41762aSTakashi Iwai /* set the given pin as output */ 5065d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, 5075d41762aSTakashi Iwai int pin_type) 5085d41762aSTakashi Iwai { 5095d41762aSTakashi Iwai if (!pin) 5105d41762aSTakashi Iwai return; 5115d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 5125d41762aSTakashi Iwai pin_type); 5135d41762aSTakashi Iwai if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) 5145d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 515d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 516c577b8a1SJoseph Chan } 517c577b8a1SJoseph Chan 51809a9ad69STakashi Iwai static void via_auto_init_output(struct hda_codec *codec, 51909a9ad69STakashi Iwai struct nid_path *path, int pin_type, 520*3214b966STakashi Iwai bool force) 5215d41762aSTakashi Iwai { 5225d41762aSTakashi Iwai unsigned int caps; 523d69607b3SLydia Wang hda_nid_t pin; 5245d41762aSTakashi Iwai 52509a9ad69STakashi Iwai if (!path->depth) 5265d41762aSTakashi Iwai return; 52709a9ad69STakashi Iwai pin = path->path[path->depth - 1]; 5285d41762aSTakashi Iwai 5295d41762aSTakashi Iwai init_output_pin(codec, pin, pin_type); 5305d41762aSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_OUTPUT); 5315d41762aSTakashi Iwai if (caps & AC_AMPCAP_MUTE) { 5325d41762aSTakashi Iwai unsigned int val; 5335d41762aSTakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 5345d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, 5355d41762aSTakashi Iwai AMP_OUT_MUTE | val); 5365d41762aSTakashi Iwai } 537d69607b3SLydia Wang activate_output_path(codec, path, true, force); 53809a9ad69STakashi Iwai } 539c577b8a1SJoseph Chan 540c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 541c577b8a1SJoseph Chan { 542c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 543*3214b966STakashi Iwai struct nid_path *path; 544c577b8a1SJoseph Chan int i; 545c577b8a1SJoseph Chan 546*3214b966STakashi Iwai for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) { 547*3214b966STakashi Iwai path = &spec->out_path[i]; 548*3214b966STakashi Iwai if (!i && spec->aamix_mode && spec->out_mix_path.depth) 549*3214b966STakashi Iwai path = &spec->out_mix_path; 550*3214b966STakashi Iwai via_auto_init_output(codec, path, PIN_OUT, true); 551*3214b966STakashi Iwai } 552c577b8a1SJoseph Chan } 553c577b8a1SJoseph Chan 554c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 555c577b8a1SJoseph Chan { 556c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 557*3214b966STakashi Iwai int shared = spec->hp_indep_shared; 558c577b8a1SJoseph Chan 559*3214b966STakashi Iwai if (!spec->hp_path.depth) { 560*3214b966STakashi Iwai via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); 56109a9ad69STakashi Iwai return; 56209a9ad69STakashi Iwai } 56309a9ad69STakashi Iwai if (spec->hp_independent_mode) { 56409a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_path, false, false); 565*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_mix_path, false, false); 566*3214b966STakashi Iwai if (shared) 567*3214b966STakashi Iwai activate_output_path(codec, &spec->out_path[shared], 568*3214b966STakashi Iwai false, false); 569*3214b966STakashi Iwai via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true); 570*3214b966STakashi Iwai } else if (spec->aamix_mode) { 571*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_path, false, false); 572*3214b966STakashi Iwai via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true); 573*3214b966STakashi Iwai } else { 574*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_mix_path, false, false); 575*3214b966STakashi Iwai via_auto_init_output(codec, &spec->hp_path, PIN_HP, true); 57609a9ad69STakashi Iwai } 57725eaba2fSLydia Wang } 578c577b8a1SJoseph Chan 5794a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec) 5804a918ffeSTakashi Iwai { 5814a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 5824a918ffeSTakashi Iwai 583*3214b966STakashi Iwai if (!spec->autocfg.speaker_outs) 584*3214b966STakashi Iwai return; 585*3214b966STakashi Iwai if (!spec->speaker_path.depth) { 586*3214b966STakashi Iwai via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, 587*3214b966STakashi Iwai true); 588*3214b966STakashi Iwai return; 589*3214b966STakashi Iwai } 590*3214b966STakashi Iwai if (!spec->aamix_mode) { 591*3214b966STakashi Iwai activate_output_path(codec, &spec->speaker_mix_path, 592*3214b966STakashi Iwai false, false); 593bac4b92cSTakashi Iwai via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, 594*3214b966STakashi Iwai true); 595*3214b966STakashi Iwai } else { 596*3214b966STakashi Iwai activate_output_path(codec, &spec->speaker_path, false, false); 597*3214b966STakashi Iwai via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT, 598*3214b966STakashi Iwai true); 599*3214b966STakashi Iwai } 6004a918ffeSTakashi Iwai } 6014a918ffeSTakashi Iwai 602f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 6036e969d91STakashi Iwai static void via_hp_automute(struct hda_codec *codec); 60432e0191dSClemens Ladisch 605c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 606c577b8a1SJoseph Chan { 607c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 6087b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 609096a8854STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 61032e0191dSClemens Ladisch unsigned int ctl; 611096a8854STakashi Iwai int i, num_conns; 612c577b8a1SJoseph Chan 613096a8854STakashi Iwai /* init ADCs */ 614096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 615096a8854STakashi Iwai snd_hda_codec_write(codec, spec->adc_nids[i], 0, 616096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 617096a8854STakashi Iwai AMP_IN_UNMUTE(0)); 618096a8854STakashi Iwai } 619096a8854STakashi Iwai 620096a8854STakashi Iwai /* init pins */ 6217b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 6227b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 623f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 62432e0191dSClemens Ladisch ctl = PIN_OUT; 62530649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 62632e0191dSClemens Ladisch ctl = PIN_VREF50; 62732e0191dSClemens Ladisch else 62832e0191dSClemens Ladisch ctl = PIN_IN; 629c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 63032e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 631c577b8a1SJoseph Chan } 632096a8854STakashi Iwai 633096a8854STakashi Iwai /* init input-src */ 634096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 635a86a88eaSTakashi Iwai int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx; 636a86a88eaSTakashi Iwai if (spec->mux_nids[adc_idx]) { 637a86a88eaSTakashi Iwai int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx; 638a86a88eaSTakashi Iwai snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 639096a8854STakashi Iwai AC_VERB_SET_CONNECT_SEL, 640a86a88eaSTakashi Iwai mux_idx); 641a86a88eaSTakashi Iwai } 642a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 643a86a88eaSTakashi Iwai break; /* only one input-src */ 644096a8854STakashi Iwai } 645096a8854STakashi Iwai 646096a8854STakashi Iwai /* init aa-mixer */ 647096a8854STakashi Iwai if (!spec->aa_mix_nid) 648096a8854STakashi Iwai return; 649096a8854STakashi Iwai num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 650096a8854STakashi Iwai ARRAY_SIZE(conn)); 651096a8854STakashi Iwai for (i = 0; i < num_conns; i++) { 652096a8854STakashi Iwai unsigned int caps = get_wcaps(codec, conn[i]); 653096a8854STakashi Iwai if (get_wcaps_type(caps) == AC_WID_PIN) 654096a8854STakashi Iwai snd_hda_codec_write(codec, spec->aa_mix_nid, 0, 655096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 656096a8854STakashi Iwai AMP_IN_MUTE(i)); 657096a8854STakashi Iwai } 658c577b8a1SJoseph Chan } 659f5271101SLydia Wang 660f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 661f5271101SLydia Wang unsigned int *affected_parm) 662f5271101SLydia Wang { 663f5271101SLydia Wang unsigned parm; 664f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 665f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 666f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 667f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 6681564b287SLydia Wang struct via_spec *spec = codec->spec; 66924088a58STakashi Iwai unsigned present = 0; 67024088a58STakashi Iwai 67124088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 67224088a58STakashi Iwai if (!no_presence) 67324088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 674f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 6751564b287SLydia Wang || ((no_presence || present) 6761564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 677f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 678f5271101SLydia Wang parm = AC_PWRST_D0; 679f5271101SLydia Wang } else 680f5271101SLydia Wang parm = AC_PWRST_D3; 681f5271101SLydia Wang 682f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 683f5271101SLydia Wang } 684f5271101SLydia Wang 68524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 68624088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 68724088a58STakashi Iwai { 68824088a58STakashi Iwai static const char * const texts[] = { 68924088a58STakashi Iwai "Disabled", "Enabled" 69024088a58STakashi Iwai }; 69124088a58STakashi Iwai 69224088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 69324088a58STakashi Iwai uinfo->count = 1; 69424088a58STakashi Iwai uinfo->value.enumerated.items = 2; 69524088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 69624088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 69724088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 69824088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 69924088a58STakashi Iwai return 0; 70024088a58STakashi Iwai } 70124088a58STakashi Iwai 70224088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 70324088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 70424088a58STakashi Iwai { 70524088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 70624088a58STakashi Iwai struct via_spec *spec = codec->spec; 70724088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 70824088a58STakashi Iwai return 0; 70924088a58STakashi Iwai } 71024088a58STakashi Iwai 71124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 71224088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 71324088a58STakashi Iwai { 71424088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 71524088a58STakashi Iwai struct via_spec *spec = codec->spec; 71624088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 71724088a58STakashi Iwai 71824088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 71924088a58STakashi Iwai return 0; 72024088a58STakashi Iwai spec->no_pin_power_ctl = val; 72124088a58STakashi Iwai set_widgets_power_state(codec); 72224088a58STakashi Iwai return 1; 72324088a58STakashi Iwai } 72424088a58STakashi Iwai 72524088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 72624088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 72724088a58STakashi Iwai .name = "Dynamic Power-Control", 72824088a58STakashi Iwai .info = via_pin_power_ctl_info, 72924088a58STakashi Iwai .get = via_pin_power_ctl_get, 73024088a58STakashi Iwai .put = via_pin_power_ctl_put, 73124088a58STakashi Iwai }; 73224088a58STakashi Iwai 73324088a58STakashi Iwai 7340aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 7350aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 7360aa62aefSHarald Welte { 7378df2a312STakashi Iwai static const char * const texts[] = { "OFF", "ON" }; 7388df2a312STakashi Iwai 7398df2a312STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 7408df2a312STakashi Iwai uinfo->count = 1; 7418df2a312STakashi Iwai uinfo->value.enumerated.items = 2; 7428df2a312STakashi Iwai if (uinfo->value.enumerated.item >= 2) 7438df2a312STakashi Iwai uinfo->value.enumerated.item = 1; 7448df2a312STakashi Iwai strcpy(uinfo->value.enumerated.name, 7458df2a312STakashi Iwai texts[uinfo->value.enumerated.item]); 7468df2a312STakashi Iwai return 0; 7470aa62aefSHarald Welte } 7480aa62aefSHarald Welte 7490aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7500aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7510aa62aefSHarald Welte { 7520aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 753cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 754cdc1784dSLydia Wang 755ece8d043STakashi Iwai ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; 756cdc1784dSLydia Wang return 0; 757cdc1784dSLydia Wang } 758cdc1784dSLydia Wang 7590aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7600aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7610aa62aefSHarald Welte { 7620aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7630aa62aefSHarald Welte struct via_spec *spec = codec->spec; 764*3214b966STakashi Iwai int cur, shared; 7658df2a312STakashi Iwai 76625250505STakashi Iwai /* no independent-hp status change during PCM playback is running */ 76725250505STakashi Iwai if (spec->num_active_streams) 76825250505STakashi Iwai return -EBUSY; 76925250505STakashi Iwai 77025250505STakashi Iwai cur = !!ucontrol->value.enumerated.item[0]; 77125250505STakashi Iwai if (spec->hp_independent_mode == cur) 77225250505STakashi Iwai return 0; 77325250505STakashi Iwai spec->hp_independent_mode = cur; 774*3214b966STakashi Iwai shared = spec->hp_indep_shared; 77525250505STakashi Iwai if (cur) { 776*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_mix_path, false, false); 777*3214b966STakashi Iwai if (shared) 778*3214b966STakashi Iwai activate_output_path(codec, &spec->out_path[shared], 77925250505STakashi Iwai false, false); 780*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_path, true, false); 78109a9ad69STakashi Iwai } else { 78209a9ad69STakashi Iwai activate_output_path(codec, &spec->hp_path, false, false); 783*3214b966STakashi Iwai if (shared) 784*3214b966STakashi Iwai activate_output_path(codec, &spec->out_path[shared], 78525250505STakashi Iwai true, false); 786*3214b966STakashi Iwai activate_output_path(codec, &spec->hp_mix_path, true, false); 7878df2a312STakashi Iwai } 7880aa62aefSHarald Welte 789ce0e5a9eSLydia Wang /* update jack power state */ 7903e95b9abSLydia Wang set_widgets_power_state(codec); 7916e969d91STakashi Iwai via_hp_automute(codec); 79225250505STakashi Iwai return 1; 7930aa62aefSHarald Welte } 7940aa62aefSHarald Welte 795ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = { 7960aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7970aa62aefSHarald Welte .name = "Independent HP", 7980aa62aefSHarald Welte .info = via_independent_hp_info, 7990aa62aefSHarald Welte .get = via_independent_hp_get, 8000aa62aefSHarald Welte .put = via_independent_hp_put, 8010aa62aefSHarald Welte }; 8020aa62aefSHarald Welte 8033d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 8045b0cb1d8SJaroslav Kysela { 8053d83e577STakashi Iwai struct via_spec *spec = codec->spec; 8065b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 8075b0cb1d8SJaroslav Kysela hda_nid_t nid; 8085b0cb1d8SJaroslav Kysela 8095b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 810ece8d043STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer); 8113d83e577STakashi Iwai if (knew == NULL) 8123d83e577STakashi Iwai return -ENOMEM; 8133d83e577STakashi Iwai 8145b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 8155b0cb1d8SJaroslav Kysela 8165b0cb1d8SJaroslav Kysela return 0; 8175b0cb1d8SJaroslav Kysela } 8185b0cb1d8SJaroslav Kysela 8191564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 8201564b287SLydia Wang { 821e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 8221564b287SLydia Wang int i; 8231564b287SLydia Wang 824e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 825e3d7a143STakashi Iwai struct snd_kcontrol *ctl; 826e3d7a143STakashi Iwai struct snd_ctl_elem_id id; 8271564b287SLydia Wang memset(&id, 0, sizeof(id)); 8281564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 829e3d7a143STakashi Iwai sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); 830525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 831525566cbSLydia Wang if (ctl) 832525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 833525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 834525566cbSLydia Wang &ctl->id); 8351564b287SLydia Wang } 8361564b287SLydia Wang } 8371564b287SLydia Wang 8381564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 8391564b287SLydia Wang { 8401564b287SLydia Wang struct via_spec *spec = codec->spec; 8411564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 842e3d7a143STakashi Iwai int i; 843e3d7a143STakashi Iwai 844e3d7a143STakashi Iwai /* check AA path's mute status */ 845e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 846e3d7a143STakashi Iwai if (spec->smart51_idxs[i] < 0) 847e3d7a143STakashi Iwai continue; 848e3d7a143STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, 849e3d7a143STakashi Iwai HDA_INPUT, spec->smart51_idxs[i], 8501564b287SLydia Wang HDA_AMP_MUTE, val); 8511564b287SLydia Wang } 8521564b287SLydia Wang } 853f4a7828bSTakashi Iwai 854e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 855e3d7a143STakashi Iwai { 856e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 857e3d7a143STakashi Iwai int i; 858e3d7a143STakashi Iwai 859e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) 860e3d7a143STakashi Iwai if (spec->smart51_pins[i] == pin) 861e3d7a143STakashi Iwai return true; 862e3d7a143STakashi Iwai return false; 863e3d7a143STakashi Iwai } 864e3d7a143STakashi Iwai 8651564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 8661564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8671564b287SLydia Wang { 8681564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8691564b287SLydia Wang struct via_spec *spec = codec->spec; 8701564b287SLydia Wang 871f2b1c9f0STakashi Iwai *ucontrol->value.integer.value = spec->smart51_enabled; 8721564b287SLydia Wang return 0; 8731564b287SLydia Wang } 8741564b287SLydia Wang 8751564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 8761564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8771564b287SLydia Wang { 8781564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8791564b287SLydia Wang struct via_spec *spec = codec->spec; 8801564b287SLydia Wang int out_in = *ucontrol->value.integer.value 8811564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 8821564b287SLydia Wang int i; 8831564b287SLydia Wang 884e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 885e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 8867b315bb4STakashi Iwai unsigned int parm; 8877b315bb4STakashi Iwai 8887b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 8891564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8901564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 8911564b287SLydia Wang parm |= out_in; 8921564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 8931564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 8941564b287SLydia Wang parm); 8951564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 8961564b287SLydia Wang mute_aa_path(codec, 1); 8971564b287SLydia Wang notify_aa_path_ctls(codec); 8981564b287SLydia Wang } 8991564b287SLydia Wang } 9001564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 9013e95b9abSLydia Wang set_widgets_power_state(codec); 9021564b287SLydia Wang return 1; 9031564b287SLydia Wang } 9041564b287SLydia Wang 9055f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 9061564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 9071564b287SLydia Wang .name = "Smart 5.1", 9081564b287SLydia Wang .count = 1, 909f2b1c9f0STakashi Iwai .info = snd_ctl_boolean_mono_info, 9101564b287SLydia Wang .get = via_smart51_get, 9111564b287SLydia Wang .put = via_smart51_put, 9121564b287SLydia Wang }; 9131564b287SLydia Wang 914f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 9155b0cb1d8SJaroslav Kysela { 916f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 9175b0cb1d8SJaroslav Kysela 918e3d7a143STakashi Iwai if (!spec->smart51_nums) 919cb34c207SLydia Wang return 0; 920e3d7a143STakashi Iwai if (!via_clone_control(spec, &via_smart51_mixer)) 9215b0cb1d8SJaroslav Kysela return -ENOMEM; 9225b0cb1d8SJaroslav Kysela return 0; 9235b0cb1d8SJaroslav Kysela } 9245b0cb1d8SJaroslav Kysela 925f5271101SLydia Wang /* check AA path's mute status */ 926ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 927ada509ecSTakashi Iwai { 928ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 929ada509ecSTakashi Iwai const struct hda_amp_list *p; 930ada509ecSTakashi Iwai int i, ch, v; 931ada509ecSTakashi Iwai 932ada509ecSTakashi Iwai for (i = 0; i < spec->num_loopbacks; i++) { 933ada509ecSTakashi Iwai p = &spec->loopback_list[i]; 934ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 935ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 936ada509ecSTakashi Iwai p->idx); 937ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 938ada509ecSTakashi Iwai return false; 939f5271101SLydia Wang } 940f5271101SLydia Wang } 941ada509ecSTakashi Iwai return true; 942f5271101SLydia Wang } 943f5271101SLydia Wang 944f5271101SLydia Wang /* enter/exit analog low-current mode */ 945ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 946f5271101SLydia Wang { 947f5271101SLydia Wang struct via_spec *spec = codec->spec; 948ada509ecSTakashi Iwai bool enable; 949ada509ecSTakashi Iwai unsigned int verb, parm; 950f5271101SLydia Wang 951ada509ecSTakashi Iwai enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); 952f5271101SLydia Wang 953f5271101SLydia Wang /* decide low current mode's verb & parameter */ 954f5271101SLydia Wang switch (spec->codec_type) { 955f5271101SLydia Wang case VT1708B_8CH: 956f5271101SLydia Wang case VT1708B_4CH: 957f5271101SLydia Wang verb = 0xf70; 958f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 959f5271101SLydia Wang break; 960f5271101SLydia Wang case VT1708S: 961eb7188caSLydia Wang case VT1718S: 962f3db423dSLydia Wang case VT1716S: 963f5271101SLydia Wang verb = 0xf73; 964f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 965f5271101SLydia Wang break; 966f5271101SLydia Wang case VT1702: 967f5271101SLydia Wang verb = 0xf73; 968f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 969f5271101SLydia Wang break; 97025eaba2fSLydia Wang case VT2002P: 971ab6734e7SLydia Wang case VT1812: 97211890956SLydia Wang case VT1802: 97325eaba2fSLydia Wang verb = 0xf93; 97425eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 97525eaba2fSLydia Wang break; 976f5271101SLydia Wang default: 977f5271101SLydia Wang return; /* other codecs are not supported */ 978f5271101SLydia Wang } 979f5271101SLydia Wang /* send verb */ 980f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 981f5271101SLydia Wang } 982f5271101SLydia Wang 983c577b8a1SJoseph Chan /* 984c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 985c577b8a1SJoseph Chan */ 986096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 987aa266fccSLydia Wang /* power down jack detect function */ 988aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 989f7278fd0SJosepch Chan { } 990c577b8a1SJoseph Chan }; 991c577b8a1SJoseph Chan 992ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active) 9937eb56e84STakashi Iwai { 994ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 995ada509ecSTakashi Iwai 996ada509ecSTakashi Iwai if (active) 997ada509ecSTakashi Iwai spec->num_active_streams++; 998ada509ecSTakashi Iwai else 999ada509ecSTakashi Iwai spec->num_active_streams--; 1000ada509ecSTakashi Iwai analog_low_current_mode(codec); 10017eb56e84STakashi Iwai } 10027eb56e84STakashi Iwai 1003ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, 1004c577b8a1SJoseph Chan struct hda_codec *codec, 1005c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1006c577b8a1SJoseph Chan { 1007c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 100825250505STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1009ada509ecSTakashi Iwai int err; 1010ece8d043STakashi Iwai 101125250505STakashi Iwai spec->multiout.hp_nid = 0; 101225250505STakashi Iwai spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; 101325250505STakashi Iwai if (!spec->hp_independent_mode) { 101425250505STakashi Iwai if (!spec->hp_indep_shared) 1015ece8d043STakashi Iwai spec->multiout.hp_nid = spec->hp_dac_nid; 101625250505STakashi Iwai } else { 101725250505STakashi Iwai if (spec->hp_indep_shared) 101825250505STakashi Iwai spec->multiout.num_dacs = cfg->line_outs - 1; 101925250505STakashi Iwai } 102025250505STakashi Iwai spec->multiout.max_channels = spec->multiout.num_dacs * 2; 1021ada509ecSTakashi Iwai set_stream_active(codec, true); 1022ada509ecSTakashi Iwai err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 10239a08160bSTakashi Iwai hinfo); 1024ada509ecSTakashi Iwai if (err < 0) { 1025ada509ecSTakashi Iwai spec->multiout.hp_nid = 0; 1026ada509ecSTakashi Iwai set_stream_active(codec, false); 1027ada509ecSTakashi Iwai return err; 1028ada509ecSTakashi Iwai } 1029ada509ecSTakashi Iwai return 0; 1030c577b8a1SJoseph Chan } 1031c577b8a1SJoseph Chan 1032ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, 10339af74210STakashi Iwai struct hda_codec *codec, 10349af74210STakashi Iwai struct snd_pcm_substream *substream) 10359af74210STakashi Iwai { 1036ece8d043STakashi Iwai struct via_spec *spec = codec->spec; 1037ece8d043STakashi Iwai 1038ece8d043STakashi Iwai spec->multiout.hp_nid = 0; 1039ada509ecSTakashi Iwai set_stream_active(codec, false); 10409af74210STakashi Iwai return 0; 10419af74210STakashi Iwai } 10429af74210STakashi Iwai 10437eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 10447eb56e84STakashi Iwai struct hda_codec *codec, 10457eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10467eb56e84STakashi Iwai { 10477eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 10487eb56e84STakashi Iwai 1049ece8d043STakashi Iwai if (snd_BUG_ON(!spec->hp_dac_nid)) 10507eb56e84STakashi Iwai return -EINVAL; 1051ece8d043STakashi Iwai if (!spec->hp_independent_mode || spec->multiout.hp_nid) 1052ece8d043STakashi Iwai return -EBUSY; 1053ada509ecSTakashi Iwai set_stream_active(codec, true); 1054ece8d043STakashi Iwai return 0; 1055ece8d043STakashi Iwai } 1056ece8d043STakashi Iwai 1057ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, 1058ece8d043STakashi Iwai struct hda_codec *codec, 1059ece8d043STakashi Iwai struct snd_pcm_substream *substream) 1060ece8d043STakashi Iwai { 1061ada509ecSTakashi Iwai set_stream_active(codec, false); 10627eb56e84STakashi Iwai return 0; 10637eb56e84STakashi Iwai } 10647eb56e84STakashi Iwai 10657eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 10667eb56e84STakashi Iwai struct hda_codec *codec, 10670aa62aefSHarald Welte unsigned int stream_tag, 10680aa62aefSHarald Welte unsigned int format, 10690aa62aefSHarald Welte struct snd_pcm_substream *substream) 10700aa62aefSHarald Welte { 10710aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10720aa62aefSHarald Welte 1073ece8d043STakashi Iwai snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1074ece8d043STakashi Iwai format, substream); 10757eb56e84STakashi Iwai vt1708_start_hp_work(spec); 10767eb56e84STakashi Iwai return 0; 10770aa62aefSHarald Welte } 10780aa62aefSHarald Welte 10797eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 10800aa62aefSHarald Welte struct hda_codec *codec, 10810aa62aefSHarald Welte unsigned int stream_tag, 10820aa62aefSHarald Welte unsigned int format, 10830aa62aefSHarald Welte struct snd_pcm_substream *substream) 10840aa62aefSHarald Welte { 10850aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10860aa62aefSHarald Welte 1087ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1088ece8d043STakashi Iwai stream_tag, 0, format); 10891f2e99feSLydia Wang vt1708_start_hp_work(spec); 10900aa62aefSHarald Welte return 0; 10910aa62aefSHarald Welte } 10920aa62aefSHarald Welte 10930aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 10940aa62aefSHarald Welte struct hda_codec *codec, 10950aa62aefSHarald Welte struct snd_pcm_substream *substream) 10960aa62aefSHarald Welte { 10970aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10980aa62aefSHarald Welte 1099ece8d043STakashi Iwai snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 11007eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 11017eb56e84STakashi Iwai return 0; 11020aa62aefSHarald Welte } 11037eb56e84STakashi Iwai 11047eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 11057eb56e84STakashi Iwai struct hda_codec *codec, 11067eb56e84STakashi Iwai struct snd_pcm_substream *substream) 11077eb56e84STakashi Iwai { 11087eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 11097eb56e84STakashi Iwai 1110ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 11111f2e99feSLydia Wang vt1708_stop_hp_work(spec); 11120aa62aefSHarald Welte return 0; 11130aa62aefSHarald Welte } 11140aa62aefSHarald Welte 1115c577b8a1SJoseph Chan /* 1116c577b8a1SJoseph Chan * Digital out 1117c577b8a1SJoseph Chan */ 1118c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1119c577b8a1SJoseph Chan struct hda_codec *codec, 1120c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1121c577b8a1SJoseph Chan { 1122c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1123c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1124c577b8a1SJoseph Chan } 1125c577b8a1SJoseph Chan 1126c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1127c577b8a1SJoseph Chan struct hda_codec *codec, 1128c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1129c577b8a1SJoseph Chan { 1130c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1131c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1132c577b8a1SJoseph Chan } 1133c577b8a1SJoseph Chan 11345691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 113598aa34c0SHarald Welte struct hda_codec *codec, 113698aa34c0SHarald Welte unsigned int stream_tag, 113798aa34c0SHarald Welte unsigned int format, 113898aa34c0SHarald Welte struct snd_pcm_substream *substream) 113998aa34c0SHarald Welte { 114098aa34c0SHarald Welte struct via_spec *spec = codec->spec; 11419da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 11429da29271STakashi Iwai stream_tag, format, substream); 11439da29271STakashi Iwai } 11445691ec7fSHarald Welte 11459da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 11469da29271STakashi Iwai struct hda_codec *codec, 11479da29271STakashi Iwai struct snd_pcm_substream *substream) 11489da29271STakashi Iwai { 11499da29271STakashi Iwai struct via_spec *spec = codec->spec; 11509da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 115198aa34c0SHarald Welte return 0; 115298aa34c0SHarald Welte } 115398aa34c0SHarald Welte 1154c577b8a1SJoseph Chan /* 1155c577b8a1SJoseph Chan * Analog capture 1156c577b8a1SJoseph Chan */ 1157c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1158c577b8a1SJoseph Chan struct hda_codec *codec, 1159c577b8a1SJoseph Chan unsigned int stream_tag, 1160c577b8a1SJoseph Chan unsigned int format, 1161c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1162c577b8a1SJoseph Chan { 1163c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1164c577b8a1SJoseph Chan 1165c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1166c577b8a1SJoseph Chan stream_tag, 0, format); 1167c577b8a1SJoseph Chan return 0; 1168c577b8a1SJoseph Chan } 1169c577b8a1SJoseph Chan 1170c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1171c577b8a1SJoseph Chan struct hda_codec *codec, 1172c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1173c577b8a1SJoseph Chan { 1174c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1175888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1176c577b8a1SJoseph Chan return 0; 1177c577b8a1SJoseph Chan } 1178c577b8a1SJoseph Chan 1179a86a88eaSTakashi Iwai /* analog capture with dynamic ADC switching */ 1180a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1181a86a88eaSTakashi Iwai struct hda_codec *codec, 1182a86a88eaSTakashi Iwai unsigned int stream_tag, 1183a86a88eaSTakashi Iwai unsigned int format, 1184a86a88eaSTakashi Iwai struct snd_pcm_substream *substream) 1185a86a88eaSTakashi Iwai { 1186a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1187a86a88eaSTakashi Iwai int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; 1188a86a88eaSTakashi Iwai 1189a86a88eaSTakashi Iwai spec->cur_adc = spec->adc_nids[adc_idx]; 1190a86a88eaSTakashi Iwai spec->cur_adc_stream_tag = stream_tag; 1191a86a88eaSTakashi Iwai spec->cur_adc_format = format; 1192a86a88eaSTakashi Iwai snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 1193a86a88eaSTakashi Iwai return 0; 1194a86a88eaSTakashi Iwai } 1195a86a88eaSTakashi Iwai 1196a86a88eaSTakashi Iwai static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1197a86a88eaSTakashi Iwai struct hda_codec *codec, 1198a86a88eaSTakashi Iwai struct snd_pcm_substream *substream) 1199a86a88eaSTakashi Iwai { 1200a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1201a86a88eaSTakashi Iwai 1202a86a88eaSTakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 1203a86a88eaSTakashi Iwai spec->cur_adc = 0; 1204a86a88eaSTakashi Iwai return 0; 1205a86a88eaSTakashi Iwai } 1206a86a88eaSTakashi Iwai 1207a86a88eaSTakashi Iwai /* re-setup the stream if running; called from input-src put */ 1208a86a88eaSTakashi Iwai static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur) 1209a86a88eaSTakashi Iwai { 1210a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 1211a86a88eaSTakashi Iwai int adc_idx = spec->inputs[cur].adc_idx; 1212a86a88eaSTakashi Iwai hda_nid_t adc = spec->adc_nids[adc_idx]; 1213a86a88eaSTakashi Iwai 1214a86a88eaSTakashi Iwai if (spec->cur_adc && spec->cur_adc != adc) { 1215a86a88eaSTakashi Iwai /* stream is running, let's swap the current ADC */ 1216a86a88eaSTakashi Iwai __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 1217a86a88eaSTakashi Iwai spec->cur_adc = adc; 1218a86a88eaSTakashi Iwai snd_hda_codec_setup_stream(codec, adc, 1219a86a88eaSTakashi Iwai spec->cur_adc_stream_tag, 0, 1220a86a88eaSTakashi Iwai spec->cur_adc_format); 1221a86a88eaSTakashi Iwai return true; 1222a86a88eaSTakashi Iwai } 1223a86a88eaSTakashi Iwai return false; 1224a86a88eaSTakashi Iwai } 1225a86a88eaSTakashi Iwai 12269af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 12277eb56e84STakashi Iwai .substreams = 1, 1228c577b8a1SJoseph Chan .channels_min = 2, 1229c577b8a1SJoseph Chan .channels_max = 8, 12309af74210STakashi Iwai /* NID is set in via_build_pcms */ 1231c577b8a1SJoseph Chan .ops = { 1232ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1233ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 12340aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 12350aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1236c577b8a1SJoseph Chan }, 1237c577b8a1SJoseph Chan }; 1238c577b8a1SJoseph Chan 12397eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 12407eb56e84STakashi Iwai .substreams = 1, 12417eb56e84STakashi Iwai .channels_min = 2, 12427eb56e84STakashi Iwai .channels_max = 2, 12437eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 12447eb56e84STakashi Iwai .ops = { 12457eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1246ece8d043STakashi Iwai .close = via_playback_hp_pcm_close, 12477eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 12487eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 12497eb56e84STakashi Iwai }, 12507eb56e84STakashi Iwai }; 12517eb56e84STakashi Iwai 125290dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 12537eb56e84STakashi Iwai .substreams = 1, 1254bc9b5623STakashi Iwai .channels_min = 2, 1255bc9b5623STakashi Iwai .channels_max = 8, 12569af74210STakashi Iwai /* NID is set in via_build_pcms */ 1257bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1258bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1259bc9b5623STakashi Iwai * disable the 24bit format, so far. 1260bc9b5623STakashi Iwai */ 1261bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1262bc9b5623STakashi Iwai .ops = { 1263ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1264ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 1265c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1266c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1267bc9b5623STakashi Iwai }, 1268bc9b5623STakashi Iwai }; 1269bc9b5623STakashi Iwai 12709af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 12717eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1272c577b8a1SJoseph Chan .channels_min = 2, 1273c577b8a1SJoseph Chan .channels_max = 2, 12749af74210STakashi Iwai /* NID is set in via_build_pcms */ 1275c577b8a1SJoseph Chan .ops = { 1276c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1277c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1278c577b8a1SJoseph Chan }, 1279c577b8a1SJoseph Chan }; 1280c577b8a1SJoseph Chan 1281a86a88eaSTakashi Iwai static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = { 1282a86a88eaSTakashi Iwai .substreams = 1, 1283a86a88eaSTakashi Iwai .channels_min = 2, 1284a86a88eaSTakashi Iwai .channels_max = 2, 1285a86a88eaSTakashi Iwai /* NID is set in via_build_pcms */ 1286a86a88eaSTakashi Iwai .ops = { 1287a86a88eaSTakashi Iwai .prepare = via_dyn_adc_capture_pcm_prepare, 1288a86a88eaSTakashi Iwai .cleanup = via_dyn_adc_capture_pcm_cleanup, 1289a86a88eaSTakashi Iwai }, 1290a86a88eaSTakashi Iwai }; 1291a86a88eaSTakashi Iwai 12929af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1293c577b8a1SJoseph Chan .substreams = 1, 1294c577b8a1SJoseph Chan .channels_min = 2, 1295c577b8a1SJoseph Chan .channels_max = 2, 1296c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1297c577b8a1SJoseph Chan .ops = { 1298c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 12996b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 13009da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 13019da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1302c577b8a1SJoseph Chan }, 1303c577b8a1SJoseph Chan }; 1304c577b8a1SJoseph Chan 13059af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1306c577b8a1SJoseph Chan .substreams = 1, 1307c577b8a1SJoseph Chan .channels_min = 2, 1308c577b8a1SJoseph Chan .channels_max = 2, 1309c577b8a1SJoseph Chan }; 1310c577b8a1SJoseph Chan 1311370bafbdSTakashi Iwai /* 1312370bafbdSTakashi Iwai * slave controls for virtual master 1313370bafbdSTakashi Iwai */ 1314370bafbdSTakashi Iwai static const char * const via_slave_vols[] = { 1315370bafbdSTakashi Iwai "Front Playback Volume", 1316370bafbdSTakashi Iwai "Surround Playback Volume", 1317370bafbdSTakashi Iwai "Center Playback Volume", 1318370bafbdSTakashi Iwai "LFE Playback Volume", 1319370bafbdSTakashi Iwai "Side Playback Volume", 1320370bafbdSTakashi Iwai "Headphone Playback Volume", 1321370bafbdSTakashi Iwai "Speaker Playback Volume", 1322370bafbdSTakashi Iwai NULL, 1323370bafbdSTakashi Iwai }; 1324370bafbdSTakashi Iwai 1325370bafbdSTakashi Iwai static const char * const via_slave_sws[] = { 1326370bafbdSTakashi Iwai "Front Playback Switch", 1327370bafbdSTakashi Iwai "Surround Playback Switch", 1328370bafbdSTakashi Iwai "Center Playback Switch", 1329370bafbdSTakashi Iwai "LFE Playback Switch", 1330370bafbdSTakashi Iwai "Side Playback Switch", 1331370bafbdSTakashi Iwai "Headphone Playback Switch", 1332370bafbdSTakashi Iwai "Speaker Playback Switch", 1333370bafbdSTakashi Iwai NULL, 1334370bafbdSTakashi Iwai }; 1335370bafbdSTakashi Iwai 1336c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1337c577b8a1SJoseph Chan { 1338c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 13395b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 13405b0cb1d8SJaroslav Kysela int err, i; 1341c577b8a1SJoseph Chan 134224088a58STakashi Iwai if (spec->set_widgets_power_state) 134324088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 134424088a58STakashi Iwai return -ENOMEM; 134524088a58STakashi Iwai 1346c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1347c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1348c577b8a1SJoseph Chan if (err < 0) 1349c577b8a1SJoseph Chan return err; 1350c577b8a1SJoseph Chan } 1351c577b8a1SJoseph Chan 1352c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1353c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 135474b654c9SStephen Warren spec->multiout.dig_out_nid, 1355c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1356c577b8a1SJoseph Chan if (err < 0) 1357c577b8a1SJoseph Chan return err; 13589a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 13599a08160bSTakashi Iwai &spec->multiout); 13609a08160bSTakashi Iwai if (err < 0) 13619a08160bSTakashi Iwai return err; 13629a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1363c577b8a1SJoseph Chan } 1364c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1365c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1366c577b8a1SJoseph Chan if (err < 0) 1367c577b8a1SJoseph Chan return err; 1368c577b8a1SJoseph Chan } 136917314379SLydia Wang 1370370bafbdSTakashi Iwai /* if we have no master control, let's create it */ 1371370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 1372370bafbdSTakashi Iwai unsigned int vmaster_tlv[4]; 1373370bafbdSTakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], 1374370bafbdSTakashi Iwai HDA_OUTPUT, vmaster_tlv); 1375370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 1376370bafbdSTakashi Iwai vmaster_tlv, via_slave_vols); 1377370bafbdSTakashi Iwai if (err < 0) 1378370bafbdSTakashi Iwai return err; 1379370bafbdSTakashi Iwai } 1380370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 1381370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Switch", 1382370bafbdSTakashi Iwai NULL, via_slave_sws); 1383370bafbdSTakashi Iwai if (err < 0) 1384370bafbdSTakashi Iwai return err; 1385370bafbdSTakashi Iwai } 1386370bafbdSTakashi Iwai 13875b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 13885b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 13895b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 139021949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 13915b0cb1d8SJaroslav Kysela if (err < 0) 13925b0cb1d8SJaroslav Kysela return err; 13935b0cb1d8SJaroslav Kysela } 13945b0cb1d8SJaroslav Kysela 139517314379SLydia Wang /* init power states */ 13963e95b9abSLydia Wang set_widgets_power_state(codec); 1397ada509ecSTakashi Iwai analog_low_current_mode(codec); 139817314379SLydia Wang 1399603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1400c577b8a1SJoseph Chan return 0; 1401c577b8a1SJoseph Chan } 1402c577b8a1SJoseph Chan 1403c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1404c577b8a1SJoseph Chan { 1405c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1406c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1407c577b8a1SJoseph Chan 1408c577b8a1SJoseph Chan codec->num_pcms = 1; 1409c577b8a1SJoseph Chan codec->pcm_info = info; 1410c577b8a1SJoseph Chan 141182673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 141282673bc8STakashi Iwai "%s Analog", codec->chip_name); 1413c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 14149af74210STakashi Iwai 14159af74210STakashi Iwai if (!spec->stream_analog_playback) 14169af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1417377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 14189af74210STakashi Iwai *spec->stream_analog_playback; 1419377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1420377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1421c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1422c577b8a1SJoseph Chan spec->multiout.max_channels; 14239af74210STakashi Iwai 1424a86a88eaSTakashi Iwai if (!spec->stream_analog_capture) { 1425a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 1426a86a88eaSTakashi Iwai spec->stream_analog_capture = 1427a86a88eaSTakashi Iwai &via_pcm_dyn_adc_analog_capture; 1428a86a88eaSTakashi Iwai else 14299af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 1430a86a88eaSTakashi Iwai } 14319af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14329af74210STakashi Iwai *spec->stream_analog_capture; 14339af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1434a86a88eaSTakashi Iwai if (!spec->dyn_adc_switch) 14359af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 14369af74210STakashi Iwai spec->num_adc_nids; 1437c577b8a1SJoseph Chan 1438c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1439c577b8a1SJoseph Chan codec->num_pcms++; 1440c577b8a1SJoseph Chan info++; 144182673bc8STakashi Iwai snprintf(spec->stream_name_digital, 144282673bc8STakashi Iwai sizeof(spec->stream_name_digital), 144382673bc8STakashi Iwai "%s Digital", codec->chip_name); 1444c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 14457ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1446c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 14479af74210STakashi Iwai if (!spec->stream_digital_playback) 14489af74210STakashi Iwai spec->stream_digital_playback = 14499af74210STakashi Iwai &via_pcm_digital_playback; 1450c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 14519af74210STakashi Iwai *spec->stream_digital_playback; 1452c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1453c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1454c577b8a1SJoseph Chan } 1455c577b8a1SJoseph Chan if (spec->dig_in_nid) { 14569af74210STakashi Iwai if (!spec->stream_digital_capture) 14579af74210STakashi Iwai spec->stream_digital_capture = 14589af74210STakashi Iwai &via_pcm_digital_capture; 1459c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 14609af74210STakashi Iwai *spec->stream_digital_capture; 1461c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1462c577b8a1SJoseph Chan spec->dig_in_nid; 1463c577b8a1SJoseph Chan } 1464c577b8a1SJoseph Chan } 1465c577b8a1SJoseph Chan 1466ece8d043STakashi Iwai if (spec->hp_dac_nid) { 14677eb56e84STakashi Iwai codec->num_pcms++; 14687eb56e84STakashi Iwai info++; 14697eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 14707eb56e84STakashi Iwai "%s HP", codec->chip_name); 14717eb56e84STakashi Iwai info->name = spec->stream_name_hp; 14727eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 14737eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1474ece8d043STakashi Iwai spec->hp_dac_nid; 14757eb56e84STakashi Iwai } 1476c577b8a1SJoseph Chan return 0; 1477c577b8a1SJoseph Chan } 1478c577b8a1SJoseph Chan 1479c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1480c577b8a1SJoseph Chan { 1481c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1482c577b8a1SJoseph Chan 1483c577b8a1SJoseph Chan if (!spec) 1484c577b8a1SJoseph Chan return; 1485c577b8a1SJoseph Chan 1486603c4019STakashi Iwai via_free_kctls(codec); 14871f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1488a86a88eaSTakashi Iwai kfree(spec->bind_cap_vol); 1489a86a88eaSTakashi Iwai kfree(spec->bind_cap_sw); 1490a86a88eaSTakashi Iwai kfree(spec); 1491c577b8a1SJoseph Chan } 1492c577b8a1SJoseph Chan 149364be285bSTakashi Iwai /* mute/unmute outputs */ 149464be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 149564be285bSTakashi Iwai hda_nid_t *pins, bool mute) 149664be285bSTakashi Iwai { 149764be285bSTakashi Iwai int i; 149894994734STakashi Iwai for (i = 0; i < num_pins; i++) { 149994994734STakashi Iwai unsigned int parm = snd_hda_codec_read(codec, pins[i], 0, 150094994734STakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 150194994734STakashi Iwai if (parm & AC_PINCTL_IN_EN) 150294994734STakashi Iwai continue; 150394994734STakashi Iwai if (mute) 150494994734STakashi Iwai parm &= ~AC_PINCTL_OUT_EN; 150594994734STakashi Iwai else 150694994734STakashi Iwai parm |= AC_PINCTL_OUT_EN; 150764be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 150894994734STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, parm); 150994994734STakashi Iwai } 151064be285bSTakashi Iwai } 151164be285bSTakashi Iwai 15124a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */ 15134a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present) 15144a918ffeSTakashi Iwai { 15154a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 15164a918ffeSTakashi Iwai 15174a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs) 15184a918ffeSTakashi Iwai return; 15194a918ffeSTakashi Iwai if (!present) 15204a918ffeSTakashi Iwai present = snd_hda_jack_detect(codec, 15214a918ffeSTakashi Iwai spec->autocfg.line_out_pins[0]); 15224a918ffeSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 15234a918ffeSTakashi Iwai spec->autocfg.speaker_pins, 15244a918ffeSTakashi Iwai present); 15254a918ffeSTakashi Iwai } 15264a918ffeSTakashi Iwai 152769e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 152869e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 152969e52a80SHarald Welte { 15304a918ffeSTakashi Iwai int present = 0; 15316e969d91STakashi Iwai int nums; 153269e52a80SHarald Welte struct via_spec *spec = codec->spec; 153369e52a80SHarald Welte 15346e969d91STakashi Iwai if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) 1535d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 15366e969d91STakashi Iwai 1537f2b1c9f0STakashi Iwai if (spec->smart51_enabled) 1538f2b1c9f0STakashi Iwai nums = spec->autocfg.line_outs + spec->smart51_nums; 1539f2b1c9f0STakashi Iwai else 1540f2b1c9f0STakashi Iwai nums = spec->autocfg.line_outs; 15416e969d91STakashi Iwai toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present); 15426e969d91STakashi Iwai 15434a918ffeSTakashi Iwai via_line_automute(codec, present); 1544f3db423dSLydia Wang } 1545f3db423dSLydia Wang 154669e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 154769e52a80SHarald Welte { 154869e52a80SHarald Welte unsigned int gpio_data; 154969e52a80SHarald Welte unsigned int vol_counter; 155069e52a80SHarald Welte unsigned int vol; 155169e52a80SHarald Welte unsigned int master_vol; 155269e52a80SHarald Welte 155369e52a80SHarald Welte struct via_spec *spec = codec->spec; 155469e52a80SHarald Welte 155569e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 155669e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 155769e52a80SHarald Welte 155869e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 155969e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 156069e52a80SHarald Welte 156169e52a80SHarald Welte vol = vol_counter & 0x1F; 156269e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 156369e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 156469e52a80SHarald Welte AC_AMP_GET_INPUT); 156569e52a80SHarald Welte 156669e52a80SHarald Welte if (gpio_data == 0x02) { 156769e52a80SHarald Welte /* unmute line out */ 15683e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15693e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15703e0693e2STakashi Iwai PIN_OUT); 157169e52a80SHarald Welte if (vol_counter & 0x20) { 157269e52a80SHarald Welte /* decrease volume */ 157369e52a80SHarald Welte if (vol > master_vol) 157469e52a80SHarald Welte vol = master_vol; 157569e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 157669e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 157769e52a80SHarald Welte master_vol-vol); 157869e52a80SHarald Welte } else { 157969e52a80SHarald Welte /* increase volume */ 158069e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 158169e52a80SHarald Welte HDA_AMP_VOLMASK, 158269e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 158369e52a80SHarald Welte (master_vol+vol)); 158469e52a80SHarald Welte } 158569e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 158669e52a80SHarald Welte /* mute line out */ 15873e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 15883e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 15893e0693e2STakashi Iwai 0); 159069e52a80SHarald Welte } 159169e52a80SHarald Welte } 159269e52a80SHarald Welte 159369e52a80SHarald Welte /* unsolicited event for jack sensing */ 159469e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 159569e52a80SHarald Welte unsigned int res) 159669e52a80SHarald Welte { 159769e52a80SHarald Welte res >>= 26; 1598ec7e7e42SLydia Wang 1599a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 16003e95b9abSLydia Wang set_widgets_power_state(codec); 1601ec7e7e42SLydia Wang 1602ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1603ec7e7e42SLydia Wang 160421ce0b65STakashi Iwai if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT) 1605ec7e7e42SLydia Wang via_hp_automute(codec); 1606ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1607ec7e7e42SLydia Wang via_gpio_control(codec); 160869e52a80SHarald Welte } 160969e52a80SHarald Welte 16101f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 16111f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 16121f2e99feSLydia Wang { 16131f2e99feSLydia Wang struct via_spec *spec = codec->spec; 16141f2e99feSLydia Wang vt1708_stop_hp_work(spec); 16151f2e99feSLydia Wang return 0; 16161f2e99feSLydia Wang } 16171f2e99feSLydia Wang #endif 16181f2e99feSLydia Wang 1619cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1620cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1621cb53c626STakashi Iwai { 1622cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1623cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1624cb53c626STakashi Iwai } 1625cb53c626STakashi Iwai #endif 1626cb53c626STakashi Iwai 1627c577b8a1SJoseph Chan /* 1628c577b8a1SJoseph Chan */ 16295d41762aSTakashi Iwai 16305d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 16315d41762aSTakashi Iwai 163290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1633c577b8a1SJoseph Chan .build_controls = via_build_controls, 1634c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1635c577b8a1SJoseph Chan .init = via_init, 1636c577b8a1SJoseph Chan .free = via_free, 16374a918ffeSTakashi Iwai .unsol_event = via_unsol_event, 16381f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 16391f2e99feSLydia Wang .suspend = via_suspend, 16401f2e99feSLydia Wang #endif 1641cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1642cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1643cb53c626STakashi Iwai #endif 1644c577b8a1SJoseph Chan }; 1645c577b8a1SJoseph Chan 16464a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1647c577b8a1SJoseph Chan { 16484a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16494a79616dSTakashi Iwai int i; 16504a79616dSTakashi Iwai 16514a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 16524a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 16534a79616dSTakashi Iwai return false; 16544a79616dSTakashi Iwai } 1655ece8d043STakashi Iwai if (spec->hp_dac_nid == dac) 16564a79616dSTakashi Iwai return false; 16574a79616dSTakashi Iwai return true; 16584a79616dSTakashi Iwai } 16594a79616dSTakashi Iwai 16608e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, 1661*3214b966STakashi Iwai hda_nid_t target_dac, int with_aa_mix, 1662*3214b966STakashi Iwai struct nid_path *path, int depth) 16634a79616dSTakashi Iwai { 1664*3214b966STakashi Iwai struct via_spec *spec = codec->spec; 16654a79616dSTakashi Iwai hda_nid_t conn[8]; 16664a79616dSTakashi Iwai int i, nums; 16674a79616dSTakashi Iwai 1668*3214b966STakashi Iwai if (nid == spec->aa_mix_nid) { 1669*3214b966STakashi Iwai if (!with_aa_mix) 1670*3214b966STakashi Iwai return false; 1671*3214b966STakashi Iwai with_aa_mix = 2; /* mark aa-mix is included */ 1672*3214b966STakashi Iwai } 1673*3214b966STakashi Iwai 16744a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 16754a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16764a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 16774a79616dSTakashi Iwai continue; 1678*3214b966STakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 1679*3214b966STakashi Iwai /* aa-mix is requested but not included? */ 1680*3214b966STakashi Iwai if (!(spec->aa_mix_nid && with_aa_mix == 1)) 168109a9ad69STakashi Iwai goto found; 16824a79616dSTakashi Iwai } 1683*3214b966STakashi Iwai } 16848e3679dcSTakashi Iwai if (depth >= MAX_NID_PATH_DEPTH) 16854a79616dSTakashi Iwai return false; 16864a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 16874a79616dSTakashi Iwai unsigned int type; 16884a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 1689*3214b966STakashi Iwai if (type == AC_WID_AUD_OUT) 16904a79616dSTakashi Iwai continue; 16918e3679dcSTakashi Iwai if (__parse_output_path(codec, conn[i], target_dac, 1692*3214b966STakashi Iwai with_aa_mix, path, depth + 1)) 169309a9ad69STakashi Iwai goto found; 16944a79616dSTakashi Iwai } 16954a79616dSTakashi Iwai return false; 169609a9ad69STakashi Iwai 169709a9ad69STakashi Iwai found: 169809a9ad69STakashi Iwai path->path[path->depth] = conn[i]; 169909a9ad69STakashi Iwai path->idx[path->depth] = i; 170009a9ad69STakashi Iwai if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) 170109a9ad69STakashi Iwai path->multi[path->depth] = 1; 170209a9ad69STakashi Iwai path->depth++; 170309a9ad69STakashi Iwai return true; 17044a79616dSTakashi Iwai } 17054a79616dSTakashi Iwai 17068e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 1707*3214b966STakashi Iwai hda_nid_t target_dac, int with_aa_mix, 1708*3214b966STakashi Iwai struct nid_path *path) 17098e3679dcSTakashi Iwai { 1710*3214b966STakashi Iwai if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) { 17118e3679dcSTakashi Iwai path->path[path->depth] = nid; 17128e3679dcSTakashi Iwai path->depth++; 1713*3214b966STakashi Iwai snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n", 1714*3214b966STakashi Iwai path->depth, path->path[0], path->path[1], 1715*3214b966STakashi Iwai path->path[2], path->path[3], path->path[4]); 17168e3679dcSTakashi Iwai return true; 17178e3679dcSTakashi Iwai } 17188e3679dcSTakashi Iwai return false; 17198e3679dcSTakashi Iwai } 17208e3679dcSTakashi Iwai 17214a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 17224a79616dSTakashi Iwai { 17234a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 17244a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 17255c9a5615SLydia Wang int i, dac_num; 1726c577b8a1SJoseph Chan hda_nid_t nid; 1727c577b8a1SJoseph Chan 1728c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 17295c9a5615SLydia Wang dac_num = 0; 17304a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1731*3214b966STakashi Iwai hda_nid_t dac = 0; 1732c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 17334a79616dSTakashi Iwai if (!nid) 17344a79616dSTakashi Iwai continue; 1735*3214b966STakashi Iwai if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i])) 1736*3214b966STakashi Iwai dac = spec->out_path[i].path[0]; 1737*3214b966STakashi Iwai if (!i && parse_output_path(codec, nid, dac, 1, 1738*3214b966STakashi Iwai &spec->out_mix_path)) 1739*3214b966STakashi Iwai dac = spec->out_mix_path.path[0]; 1740*3214b966STakashi Iwai if (dac) { 1741*3214b966STakashi Iwai spec->private_dac_nids[i] = dac; 17425c9a5615SLydia Wang dac_num++; 1743c577b8a1SJoseph Chan } 17445c9a5615SLydia Wang } 1745*3214b966STakashi Iwai if (!spec->out_path[0].depth && spec->out_mix_path.depth) { 1746*3214b966STakashi Iwai spec->out_path[0] = spec->out_mix_path; 1747*3214b966STakashi Iwai spec->out_mix_path.depth = 0; 1748*3214b966STakashi Iwai } 17495c9a5615SLydia Wang spec->multiout.num_dacs = dac_num; 1750c577b8a1SJoseph Chan return 0; 1751c577b8a1SJoseph Chan } 1752c577b8a1SJoseph Chan 17534a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 175409a9ad69STakashi Iwai int chs, bool check_dac, struct nid_path *path) 1755c577b8a1SJoseph Chan { 17564a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1757c577b8a1SJoseph Chan char name[32]; 175809a9ad69STakashi Iwai hda_nid_t dac, pin, sel, nid; 175909a9ad69STakashi Iwai int err; 1760a934d5a9STakashi Iwai 176109a9ad69STakashi Iwai dac = check_dac ? path->path[0] : 0; 176209a9ad69STakashi Iwai pin = path->path[path->depth - 1]; 176309a9ad69STakashi Iwai sel = path->depth > 1 ? path->path[1] : 0; 1764c577b8a1SJoseph Chan 17658df2a312STakashi Iwai if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 17664a79616dSTakashi Iwai nid = dac; 17678df2a312STakashi Iwai else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 17684a79616dSTakashi Iwai nid = pin; 1769a934d5a9STakashi Iwai else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS)) 1770a934d5a9STakashi Iwai nid = sel; 17714a79616dSTakashi Iwai else 17724a79616dSTakashi Iwai nid = 0; 17734a79616dSTakashi Iwai if (nid) { 17744a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1775c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1776a00a5fadSLydia Wang HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 1777c577b8a1SJoseph Chan if (err < 0) 1778c577b8a1SJoseph Chan return err; 177909a9ad69STakashi Iwai path->vol_ctl = nid; 1780c577b8a1SJoseph Chan } 17814a79616dSTakashi Iwai 17828df2a312STakashi Iwai if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE)) 17834a79616dSTakashi Iwai nid = dac; 17848df2a312STakashi Iwai else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE)) 17854a79616dSTakashi Iwai nid = pin; 1786a934d5a9STakashi Iwai else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE)) 1787a934d5a9STakashi Iwai nid = sel; 17884a79616dSTakashi Iwai else 17894a79616dSTakashi Iwai nid = 0; 17904a79616dSTakashi Iwai if (nid) { 17914a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 17924a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 17934a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 17944a79616dSTakashi Iwai if (err < 0) 17954a79616dSTakashi Iwai return err; 179609a9ad69STakashi Iwai path->mute_ctl = nid; 17974a79616dSTakashi Iwai } 17984a79616dSTakashi Iwai return 0; 17994a79616dSTakashi Iwai } 18004a79616dSTakashi Iwai 1801f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1802f4a7828bSTakashi Iwai { 1803f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1804f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 18050f98c24bSTakashi Iwai struct auto_pin_cfg_item *ins = cfg->inputs; 18060f98c24bSTakashi Iwai int i, j, nums, attr; 18070f98c24bSTakashi Iwai int pins[AUTO_CFG_MAX_INS]; 1808f4a7828bSTakashi Iwai 18090f98c24bSTakashi Iwai for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) { 18100f98c24bSTakashi Iwai nums = 0; 1811f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 18120f98c24bSTakashi Iwai unsigned int def; 18130f98c24bSTakashi Iwai if (ins[i].type > AUTO_PIN_LINE_IN) 18140f98c24bSTakashi Iwai continue; 18150f98c24bSTakashi Iwai def = snd_hda_codec_get_pincfg(codec, ins[i].pin); 18160f98c24bSTakashi Iwai if (snd_hda_get_input_pin_attr(def) != attr) 18170f98c24bSTakashi Iwai continue; 18180f98c24bSTakashi Iwai for (j = 0; j < nums; j++) 18190f98c24bSTakashi Iwai if (ins[pins[j]].type < ins[i].type) { 18200f98c24bSTakashi Iwai memmove(pins + j + 1, pins + j, 182121d45d2bSTakashi Iwai (nums - j) * sizeof(int)); 18220f98c24bSTakashi Iwai break; 18230f98c24bSTakashi Iwai } 18240f98c24bSTakashi Iwai pins[j] = i; 1825e3d7a143STakashi Iwai nums++; 1826e3d7a143STakashi Iwai } 1827e3d7a143STakashi Iwai if (cfg->line_outs + nums < 3) 1828f4a7828bSTakashi Iwai continue; 18290f98c24bSTakashi Iwai for (i = 0; i < nums; i++) { 18300f98c24bSTakashi Iwai hda_nid_t pin = ins[pins[i]].pin; 18310f98c24bSTakashi Iwai spec->smart51_pins[spec->smart51_nums++] = pin; 18320f98c24bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = pin; 1833f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1834f4a7828bSTakashi Iwai break; 1835f4a7828bSTakashi Iwai } 18360f98c24bSTakashi Iwai return; 18370f98c24bSTakashi Iwai } 1838f4a7828bSTakashi Iwai } 1839f4a7828bSTakashi Iwai 18404a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 18414a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 18424a79616dSTakashi Iwai { 18434a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1844f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1845*3214b966STakashi Iwai struct nid_path *path; 18464a79616dSTakashi Iwai static const char * const chname[4] = { 18474a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 18484a79616dSTakashi Iwai }; 18494a79616dSTakashi Iwai int i, idx, err; 1850f4a7828bSTakashi Iwai int old_line_outs; 1851f4a7828bSTakashi Iwai 1852f4a7828bSTakashi Iwai /* check smart51 */ 1853f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 1854f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 1855f4a7828bSTakashi Iwai mangle_smart51(codec); 18564a79616dSTakashi Iwai 1857e3d7a143STakashi Iwai err = via_auto_fill_dac_nids(codec); 1858e3d7a143STakashi Iwai if (err < 0) 1859e3d7a143STakashi Iwai return err; 1860e3d7a143STakashi Iwai 18615c9a5615SLydia Wang if (spec->multiout.num_dacs < 3) { 18625c9a5615SLydia Wang spec->smart51_nums = 0; 18635c9a5615SLydia Wang cfg->line_outs = old_line_outs; 18645c9a5615SLydia Wang } 18654a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 18664a79616dSTakashi Iwai hda_nid_t pin, dac; 18674a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 18684a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 18694a79616dSTakashi Iwai if (!pin || !dac) 18704a79616dSTakashi Iwai continue; 1871*3214b966STakashi Iwai path = spec->out_path + i; 18720fe0adf8STakashi Iwai if (i == HDA_CLFE) { 1873*3214b966STakashi Iwai err = create_ch_ctls(codec, "Center", 1, true, path); 18744a79616dSTakashi Iwai if (err < 0) 18754a79616dSTakashi Iwai return err; 1876*3214b966STakashi Iwai err = create_ch_ctls(codec, "LFE", 2, true, path); 18774a79616dSTakashi Iwai if (err < 0) 18784a79616dSTakashi Iwai return err; 18794a79616dSTakashi Iwai } else { 18806aadf41dSTakashi Iwai const char *pfx = chname[i]; 18816aadf41dSTakashi Iwai if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 18826aadf41dSTakashi Iwai cfg->line_outs == 1) 18836aadf41dSTakashi Iwai pfx = "Speaker"; 1884*3214b966STakashi Iwai err = create_ch_ctls(codec, pfx, 3, true, path); 18854a79616dSTakashi Iwai if (err < 0) 18864a79616dSTakashi Iwai return err; 18874a79616dSTakashi Iwai } 1888*3214b966STakashi Iwai if (path != spec->out_path + i) { 1889*3214b966STakashi Iwai spec->out_path[i].vol_ctl = path->vol_ctl; 1890*3214b966STakashi Iwai spec->out_path[i].mute_ctl = path->mute_ctl; 1891*3214b966STakashi Iwai } 1892*3214b966STakashi Iwai if (path == spec->out_path && spec->out_mix_path.depth) { 1893*3214b966STakashi Iwai spec->out_mix_path.vol_ctl = path->vol_ctl; 1894*3214b966STakashi Iwai spec->out_mix_path.mute_ctl = path->mute_ctl; 1895*3214b966STakashi Iwai } 18964a79616dSTakashi Iwai } 18974a79616dSTakashi Iwai 18984a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 18994a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 19004a79616dSTakashi Iwai if (idx >= 0) { 19014a79616dSTakashi Iwai /* add control to mixer */ 1902*3214b966STakashi Iwai const char *name; 1903*3214b966STakashi Iwai name = spec->out_mix_path.depth ? 1904*3214b966STakashi Iwai "PCM Loopback Playback Volume" : "PCM Playback Volume"; 1905*3214b966STakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 19064a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 19074a79616dSTakashi Iwai idx, HDA_INPUT)); 19084a79616dSTakashi Iwai if (err < 0) 19094a79616dSTakashi Iwai return err; 1910*3214b966STakashi Iwai name = spec->out_mix_path.depth ? 1911*3214b966STakashi Iwai "PCM Loopback Playback Switch" : "PCM Playback Switch"; 1912*3214b966STakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 19134a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 19144a79616dSTakashi Iwai idx, HDA_INPUT)); 19154a79616dSTakashi Iwai if (err < 0) 19164a79616dSTakashi Iwai return err; 1917c577b8a1SJoseph Chan } 1918c577b8a1SJoseph Chan 1919f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 1920f4a7828bSTakashi Iwai 1921c577b8a1SJoseph Chan return 0; 1922c577b8a1SJoseph Chan } 1923c577b8a1SJoseph Chan 19244a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 1925c577b8a1SJoseph Chan { 19264a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 192709a9ad69STakashi Iwai struct nid_path *path; 192818bd2c44STakashi Iwai bool check_dac; 1929*3214b966STakashi Iwai int i, err; 1930c577b8a1SJoseph Chan 1931c577b8a1SJoseph Chan if (!pin) 1932c577b8a1SJoseph Chan return 0; 1933c577b8a1SJoseph Chan 1934*3214b966STakashi Iwai if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) { 1935*3214b966STakashi Iwai for (i = HDA_SIDE; i >= HDA_CLFE; i--) { 1936*3214b966STakashi Iwai if (i < spec->multiout.num_dacs && 193725250505STakashi Iwai parse_output_path(codec, pin, 1938*3214b966STakashi Iwai spec->multiout.dac_nids[i], 0, 1939*3214b966STakashi Iwai &spec->hp_indep_path)) { 1940*3214b966STakashi Iwai spec->hp_indep_shared = i; 1941*3214b966STakashi Iwai break; 194225250505STakashi Iwai } 1943*3214b966STakashi Iwai } 1944*3214b966STakashi Iwai } 1945*3214b966STakashi Iwai if (spec->hp_indep_path.depth) { 1946*3214b966STakashi Iwai spec->hp_dac_nid = spec->hp_indep_path.path[0]; 1947*3214b966STakashi Iwai if (!spec->hp_indep_shared) 1948*3214b966STakashi Iwai spec->hp_path = spec->hp_indep_path; 1949*3214b966STakashi Iwai } 1950*3214b966STakashi Iwai /* optionally check front-path w/o AA-mix */ 1951*3214b966STakashi Iwai if (!spec->hp_path.depth) 1952*3214b966STakashi Iwai parse_output_path(codec, pin, 1953*3214b966STakashi Iwai spec->multiout.dac_nids[HDA_FRONT], 0, 1954*3214b966STakashi Iwai &spec->hp_path); 19554a79616dSTakashi Iwai 1956ece8d043STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 1957*3214b966STakashi Iwai 1, &spec->hp_mix_path) && !spec->hp_path.depth) 1958ece8d043STakashi Iwai return 0; 1959ece8d043STakashi Iwai 1960*3214b966STakashi Iwai if (spec->hp_path.depth) { 196109a9ad69STakashi Iwai path = &spec->hp_path; 196218bd2c44STakashi Iwai check_dac = true; 196318bd2c44STakashi Iwai } else { 1964*3214b966STakashi Iwai path = &spec->hp_mix_path; 196518bd2c44STakashi Iwai check_dac = false; 196618bd2c44STakashi Iwai } 196718bd2c44STakashi Iwai err = create_ch_ctls(codec, "Headphone", 3, check_dac, path); 19684a79616dSTakashi Iwai if (err < 0) 19694a79616dSTakashi Iwai return err; 1970*3214b966STakashi Iwai if (check_dac) { 1971*3214b966STakashi Iwai spec->hp_mix_path.vol_ctl = path->vol_ctl; 1972*3214b966STakashi Iwai spec->hp_mix_path.mute_ctl = path->mute_ctl; 1973*3214b966STakashi Iwai } else { 1974*3214b966STakashi Iwai spec->hp_path.vol_ctl = path->vol_ctl; 1975*3214b966STakashi Iwai spec->hp_path.mute_ctl = path->mute_ctl; 197609a9ad69STakashi Iwai } 1977c577b8a1SJoseph Chan return 0; 1978c577b8a1SJoseph Chan } 1979c577b8a1SJoseph Chan 19804a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec) 19814a918ffeSTakashi Iwai { 19824a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 1983*3214b966STakashi Iwai struct nid_path *path; 1984*3214b966STakashi Iwai bool check_dac; 19854a918ffeSTakashi Iwai hda_nid_t pin, dac; 1986*3214b966STakashi Iwai int err; 19874a918ffeSTakashi Iwai 19884a918ffeSTakashi Iwai pin = spec->autocfg.speaker_pins[0]; 19894a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs || !pin) 19904a918ffeSTakashi Iwai return 0; 19914a918ffeSTakashi Iwai 1992*3214b966STakashi Iwai if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path)) 19938e3679dcSTakashi Iwai dac = spec->speaker_path.path[0]; 1994*3214b966STakashi Iwai if (!dac) 1995*3214b966STakashi Iwai parse_output_path(codec, pin, 1996*3214b966STakashi Iwai spec->multiout.dac_nids[HDA_FRONT], 0, 199709a9ad69STakashi Iwai &spec->speaker_path); 1998*3214b966STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 1999*3214b966STakashi Iwai 1, &spec->speaker_mix_path) && !dac) 2000*3214b966STakashi Iwai return 0; 20014a918ffeSTakashi Iwai 2002*3214b966STakashi Iwai /* no AA-path for front? */ 2003*3214b966STakashi Iwai if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth) 2004*3214b966STakashi Iwai dac = 0; 2005*3214b966STakashi Iwai 2006*3214b966STakashi Iwai spec->speaker_dac_nid = dac; 2007*3214b966STakashi Iwai spec->multiout.extra_out_nid[0] = dac; 2008*3214b966STakashi Iwai if (dac) { 2009*3214b966STakashi Iwai path = &spec->speaker_path; 2010*3214b966STakashi Iwai check_dac = true; 2011*3214b966STakashi Iwai } else { 2012*3214b966STakashi Iwai path = &spec->speaker_mix_path; 2013*3214b966STakashi Iwai check_dac = false; 2014*3214b966STakashi Iwai } 2015*3214b966STakashi Iwai err = create_ch_ctls(codec, "Speaker", 3, check_dac, path); 2016*3214b966STakashi Iwai if (err < 0) 2017*3214b966STakashi Iwai return err; 2018*3214b966STakashi Iwai if (check_dac) { 2019*3214b966STakashi Iwai spec->speaker_mix_path.vol_ctl = path->vol_ctl; 2020*3214b966STakashi Iwai spec->speaker_mix_path.mute_ctl = path->mute_ctl; 2021*3214b966STakashi Iwai } else { 2022*3214b966STakashi Iwai spec->speaker_path.vol_ctl = path->vol_ctl; 2023*3214b966STakashi Iwai spec->speaker_path.mute_ctl = path->mute_ctl; 2024*3214b966STakashi Iwai } 2025*3214b966STakashi Iwai return 0; 2026*3214b966STakashi Iwai } 2027*3214b966STakashi Iwai 2028*3214b966STakashi Iwai #define via_aamix_ctl_info via_pin_power_ctl_info 2029*3214b966STakashi Iwai 2030*3214b966STakashi Iwai static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol, 2031*3214b966STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2032*3214b966STakashi Iwai { 2033*3214b966STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2034*3214b966STakashi Iwai struct via_spec *spec = codec->spec; 2035*3214b966STakashi Iwai ucontrol->value.enumerated.item[0] = spec->aamix_mode; 2036*3214b966STakashi Iwai return 0; 2037*3214b966STakashi Iwai } 2038*3214b966STakashi Iwai 2039*3214b966STakashi Iwai static void update_aamix_paths(struct hda_codec *codec, int do_mix, 2040*3214b966STakashi Iwai struct nid_path *nomix, struct nid_path *mix) 2041*3214b966STakashi Iwai { 2042*3214b966STakashi Iwai if (do_mix) { 2043*3214b966STakashi Iwai activate_output_path(codec, nomix, false, false); 2044*3214b966STakashi Iwai activate_output_path(codec, mix, true, false); 2045*3214b966STakashi Iwai } else { 2046*3214b966STakashi Iwai activate_output_path(codec, mix, false, false); 2047*3214b966STakashi Iwai activate_output_path(codec, nomix, true, false); 2048*3214b966STakashi Iwai } 2049*3214b966STakashi Iwai } 2050*3214b966STakashi Iwai 2051*3214b966STakashi Iwai static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol, 2052*3214b966STakashi Iwai struct snd_ctl_elem_value *ucontrol) 2053*3214b966STakashi Iwai { 2054*3214b966STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2055*3214b966STakashi Iwai struct via_spec *spec = codec->spec; 2056*3214b966STakashi Iwai unsigned int val = ucontrol->value.enumerated.item[0]; 2057*3214b966STakashi Iwai 2058*3214b966STakashi Iwai if (val == spec->aamix_mode) 2059*3214b966STakashi Iwai return 0; 2060*3214b966STakashi Iwai spec->aamix_mode = val; 2061*3214b966STakashi Iwai /* update front path */ 2062*3214b966STakashi Iwai update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path); 2063*3214b966STakashi Iwai /* update HP path */ 2064*3214b966STakashi Iwai if (!spec->hp_independent_mode) { 2065*3214b966STakashi Iwai update_aamix_paths(codec, val, &spec->hp_path, 2066*3214b966STakashi Iwai &spec->hp_mix_path); 2067*3214b966STakashi Iwai } 2068*3214b966STakashi Iwai /* update speaker path */ 2069*3214b966STakashi Iwai update_aamix_paths(codec, val, &spec->speaker_path, 2070*3214b966STakashi Iwai &spec->speaker_mix_path); 2071*3214b966STakashi Iwai return 1; 2072*3214b966STakashi Iwai } 2073*3214b966STakashi Iwai 2074*3214b966STakashi Iwai static const struct snd_kcontrol_new via_aamix_ctl_enum = { 2075*3214b966STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2076*3214b966STakashi Iwai .name = "Loopback Mixing", 2077*3214b966STakashi Iwai .info = via_aamix_ctl_info, 2078*3214b966STakashi Iwai .get = via_aamix_ctl_get, 2079*3214b966STakashi Iwai .put = via_aamix_ctl_put, 2080*3214b966STakashi Iwai }; 2081*3214b966STakashi Iwai 2082*3214b966STakashi Iwai static int via_auto_create_loopback_switch(struct hda_codec *codec) 2083*3214b966STakashi Iwai { 2084*3214b966STakashi Iwai struct via_spec *spec = codec->spec; 2085*3214b966STakashi Iwai 2086*3214b966STakashi Iwai if (!spec->aa_mix_nid || !spec->out_mix_path.depth) 2087*3214b966STakashi Iwai return 0; /* no loopback switching available */ 2088*3214b966STakashi Iwai if (!via_clone_control(spec, &via_aamix_ctl_enum)) 2089*3214b966STakashi Iwai return -ENOMEM; 20904a918ffeSTakashi Iwai return 0; 20914a918ffeSTakashi Iwai } 20924a918ffeSTakashi Iwai 2093a766d0d7STakashi Iwai /* look for ADCs */ 2094a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 2095a766d0d7STakashi Iwai { 2096a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 2097a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 2098a766d0d7STakashi Iwai int i; 2099a766d0d7STakashi Iwai 2100a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 2101a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 2102a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 2103a766d0d7STakashi Iwai continue; 2104a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 2105a766d0d7STakashi Iwai continue; 2106a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 2107a766d0d7STakashi Iwai continue; 2108a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 2109a766d0d7STakashi Iwai return -ENOMEM; 2110a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 2111a766d0d7STakashi Iwai } 2112a766d0d7STakashi Iwai return 0; 2113a766d0d7STakashi Iwai } 2114a766d0d7STakashi Iwai 2115a86a88eaSTakashi Iwai /* input-src control */ 2116a86a88eaSTakashi Iwai static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 2117a86a88eaSTakashi Iwai struct snd_ctl_elem_info *uinfo) 2118a86a88eaSTakashi Iwai { 2119a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2120a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2121a86a88eaSTakashi Iwai 2122a86a88eaSTakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 2123a86a88eaSTakashi Iwai uinfo->count = 1; 2124a86a88eaSTakashi Iwai uinfo->value.enumerated.items = spec->num_inputs; 2125a86a88eaSTakashi Iwai if (uinfo->value.enumerated.item >= spec->num_inputs) 2126a86a88eaSTakashi Iwai uinfo->value.enumerated.item = spec->num_inputs - 1; 2127a86a88eaSTakashi Iwai strcpy(uinfo->value.enumerated.name, 2128a86a88eaSTakashi Iwai spec->inputs[uinfo->value.enumerated.item].label); 2129a86a88eaSTakashi Iwai return 0; 2130a86a88eaSTakashi Iwai } 2131a86a88eaSTakashi Iwai 2132a86a88eaSTakashi Iwai static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 2133a86a88eaSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 2134a86a88eaSTakashi Iwai { 2135a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2136a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2137a86a88eaSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2138a86a88eaSTakashi Iwai 2139a86a88eaSTakashi Iwai ucontrol->value.enumerated.item[0] = spec->cur_mux[idx]; 2140a86a88eaSTakashi Iwai return 0; 2141a86a88eaSTakashi Iwai } 2142a86a88eaSTakashi Iwai 2143a86a88eaSTakashi Iwai static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 2144a86a88eaSTakashi Iwai struct snd_ctl_elem_value *ucontrol) 2145a86a88eaSTakashi Iwai { 2146a86a88eaSTakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2147a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2148a86a88eaSTakashi Iwai unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 2149a86a88eaSTakashi Iwai hda_nid_t mux; 2150a86a88eaSTakashi Iwai int cur; 2151a86a88eaSTakashi Iwai 2152a86a88eaSTakashi Iwai cur = ucontrol->value.enumerated.item[0]; 2153a86a88eaSTakashi Iwai if (cur < 0 || cur >= spec->num_inputs) 2154a86a88eaSTakashi Iwai return -EINVAL; 2155a86a88eaSTakashi Iwai if (spec->cur_mux[idx] == cur) 2156a86a88eaSTakashi Iwai return 0; 2157a86a88eaSTakashi Iwai spec->cur_mux[idx] = cur; 2158a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) { 2159a86a88eaSTakashi Iwai int adc_idx = spec->inputs[cur].adc_idx; 2160a86a88eaSTakashi Iwai mux = spec->mux_nids[adc_idx]; 2161a86a88eaSTakashi Iwai via_dyn_adc_pcm_resetup(codec, cur); 2162a86a88eaSTakashi Iwai } else { 2163a86a88eaSTakashi Iwai mux = spec->mux_nids[idx]; 2164a86a88eaSTakashi Iwai if (snd_BUG_ON(!mux)) 2165a86a88eaSTakashi Iwai return -EINVAL; 2166a86a88eaSTakashi Iwai } 2167a86a88eaSTakashi Iwai 2168a86a88eaSTakashi Iwai if (mux) { 2169a86a88eaSTakashi Iwai /* switch to D0 beofre change index */ 2170a86a88eaSTakashi Iwai if (snd_hda_codec_read(codec, mux, 0, 2171a86a88eaSTakashi Iwai AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 2172a86a88eaSTakashi Iwai snd_hda_codec_write(codec, mux, 0, 2173a86a88eaSTakashi Iwai AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 2174a86a88eaSTakashi Iwai snd_hda_codec_write(codec, mux, 0, 2175a86a88eaSTakashi Iwai AC_VERB_SET_CONNECT_SEL, 2176a86a88eaSTakashi Iwai spec->inputs[cur].mux_idx); 2177a86a88eaSTakashi Iwai } 2178a86a88eaSTakashi Iwai 2179a86a88eaSTakashi Iwai /* update jack power state */ 2180a86a88eaSTakashi Iwai set_widgets_power_state(codec); 2181a86a88eaSTakashi Iwai return 0; 2182a86a88eaSTakashi Iwai } 2183a766d0d7STakashi Iwai 2184d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 2185d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2186d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 2187d7a99cceSTakashi Iwai * So call somewhat different.. 2188d7a99cceSTakashi Iwai */ 2189d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 2190d7a99cceSTakashi Iwai .name = "Input Source", 2191d7a99cceSTakashi Iwai .info = via_mux_enum_info, 2192d7a99cceSTakashi Iwai .get = via_mux_enum_get, 2193d7a99cceSTakashi Iwai .put = via_mux_enum_put, 2194d7a99cceSTakashi Iwai }; 2195d7a99cceSTakashi Iwai 2196a86a88eaSTakashi Iwai static int create_input_src_ctls(struct hda_codec *codec, int count) 2197a86a88eaSTakashi Iwai { 2198a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2199a86a88eaSTakashi Iwai struct snd_kcontrol_new *knew; 2200a86a88eaSTakashi Iwai 2201a86a88eaSTakashi Iwai if (spec->num_inputs <= 1 || !count) 2202a86a88eaSTakashi Iwai return 0; /* no need for single src */ 2203a86a88eaSTakashi Iwai 2204a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 2205a86a88eaSTakashi Iwai if (!knew) 2206a86a88eaSTakashi Iwai return -ENOMEM; 2207a86a88eaSTakashi Iwai knew->count = count; 2208a86a88eaSTakashi Iwai return 0; 2209a86a88eaSTakashi Iwai } 2210a86a88eaSTakashi Iwai 2211a86a88eaSTakashi Iwai /* add the powersave loopback-list entry */ 221213af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) 221313af8e77STakashi Iwai { 221413af8e77STakashi Iwai struct hda_amp_list *list; 221513af8e77STakashi Iwai 221613af8e77STakashi Iwai if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) 221713af8e77STakashi Iwai return; 221813af8e77STakashi Iwai list = spec->loopback_list + spec->num_loopbacks; 221913af8e77STakashi Iwai list->nid = mix; 222013af8e77STakashi Iwai list->dir = HDA_INPUT; 222113af8e77STakashi Iwai list->idx = idx; 222213af8e77STakashi Iwai spec->num_loopbacks++; 222313af8e77STakashi Iwai spec->loopback.amplist = spec->loopback_list; 222413af8e77STakashi Iwai } 222513af8e77STakashi Iwai 2226a86a88eaSTakashi Iwai static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src, 22278d087c76STakashi Iwai hda_nid_t dst) 2228a86a88eaSTakashi Iwai { 22298d087c76STakashi Iwai return snd_hda_get_conn_index(codec, src, dst, 1) >= 0; 2230a86a88eaSTakashi Iwai } 2231a86a88eaSTakashi Iwai 2232a86a88eaSTakashi Iwai /* add the input-route to the given pin */ 2233a86a88eaSTakashi Iwai static bool add_input_route(struct hda_codec *codec, hda_nid_t pin) 2234c577b8a1SJoseph Chan { 223510a20af7STakashi Iwai struct via_spec *spec = codec->spec; 2236a86a88eaSTakashi Iwai int c, idx; 2237a86a88eaSTakashi Iwai 2238a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].adc_idx = -1; 2239a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].pin = pin; 2240a86a88eaSTakashi Iwai for (c = 0; c < spec->num_adc_nids; c++) { 2241a86a88eaSTakashi Iwai if (spec->mux_nids[c]) { 2242a86a88eaSTakashi Iwai idx = get_connection_index(codec, spec->mux_nids[c], 2243a86a88eaSTakashi Iwai pin); 2244a86a88eaSTakashi Iwai if (idx < 0) 2245a86a88eaSTakashi Iwai continue; 2246a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].mux_idx = idx; 2247a86a88eaSTakashi Iwai } else { 22488d087c76STakashi Iwai if (!is_reachable_nid(codec, spec->adc_nids[c], pin)) 2249a86a88eaSTakashi Iwai continue; 2250a86a88eaSTakashi Iwai } 2251a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs].adc_idx = c; 2252a86a88eaSTakashi Iwai /* Can primary ADC satisfy all inputs? */ 2253a86a88eaSTakashi Iwai if (!spec->dyn_adc_switch && 2254a86a88eaSTakashi Iwai spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) { 2255a86a88eaSTakashi Iwai snd_printd(KERN_INFO 2256a86a88eaSTakashi Iwai "via: dynamic ADC switching enabled\n"); 2257a86a88eaSTakashi Iwai spec->dyn_adc_switch = 1; 2258a86a88eaSTakashi Iwai } 2259a86a88eaSTakashi Iwai return true; 2260a86a88eaSTakashi Iwai } 2261a86a88eaSTakashi Iwai return false; 2262a86a88eaSTakashi Iwai } 2263a86a88eaSTakashi Iwai 2264a86a88eaSTakashi Iwai static int get_mux_nids(struct hda_codec *codec); 2265a86a88eaSTakashi Iwai 2266a86a88eaSTakashi Iwai /* parse input-routes; fill ADCs, MUXs and input-src entries */ 2267a86a88eaSTakashi Iwai static int parse_analog_inputs(struct hda_codec *codec) 2268a86a88eaSTakashi Iwai { 2269a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2270a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2271a86a88eaSTakashi Iwai int i, err; 2272a766d0d7STakashi Iwai 2273a766d0d7STakashi Iwai err = via_fill_adcs(codec); 2274a766d0d7STakashi Iwai if (err < 0) 2275a766d0d7STakashi Iwai return err; 2276a766d0d7STakashi Iwai err = get_mux_nids(codec); 2277a766d0d7STakashi Iwai if (err < 0) 2278a766d0d7STakashi Iwai return err; 2279a766d0d7STakashi Iwai 2280a86a88eaSTakashi Iwai /* fill all input-routes */ 2281a86a88eaSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2282a86a88eaSTakashi Iwai if (add_input_route(codec, cfg->inputs[i].pin)) 2283a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs++].label = 2284a86a88eaSTakashi Iwai hda_get_autocfg_input_label(codec, cfg, i); 2285a86a88eaSTakashi Iwai } 2286a86a88eaSTakashi Iwai 2287a86a88eaSTakashi Iwai /* check for internal loopback recording */ 2288a86a88eaSTakashi Iwai if (spec->aa_mix_nid && 2289a86a88eaSTakashi Iwai add_input_route(codec, spec->aa_mix_nid)) 2290a86a88eaSTakashi Iwai spec->inputs[spec->num_inputs++].label = "Stereo Mixer"; 2291a86a88eaSTakashi Iwai 2292a86a88eaSTakashi Iwai return 0; 2293a86a88eaSTakashi Iwai } 2294a86a88eaSTakashi Iwai 2295a86a88eaSTakashi Iwai /* create analog-loopback volume/switch controls */ 2296a86a88eaSTakashi Iwai static int create_loopback_ctls(struct hda_codec *codec) 2297a86a88eaSTakashi Iwai { 2298a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2299a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2300a86a88eaSTakashi Iwai const char *prev_label = NULL; 2301a86a88eaSTakashi Iwai int type_idx = 0; 2302a86a88eaSTakashi Iwai int i, j, err, idx; 2303a86a88eaSTakashi Iwai 2304a86a88eaSTakashi Iwai if (!spec->aa_mix_nid) 2305a766d0d7STakashi Iwai return 0; 2306c577b8a1SJoseph Chan 23077b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2308a86a88eaSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2309a86a88eaSTakashi Iwai const char *label = hda_get_autocfg_input_label(codec, cfg, i); 2310a86a88eaSTakashi Iwai 23111e11cae1STakashi Iwai if (prev_label && !strcmp(label, prev_label)) 23127b315bb4STakashi Iwai type_idx++; 23137b315bb4STakashi Iwai else 23147b315bb4STakashi Iwai type_idx = 0; 23151e11cae1STakashi Iwai prev_label = label; 2316a86a88eaSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, pin); 2317a86a88eaSTakashi Iwai if (idx >= 0) { 231816922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 2319a86a88eaSTakashi Iwai idx, spec->aa_mix_nid); 2320c577b8a1SJoseph Chan if (err < 0) 2321c577b8a1SJoseph Chan return err; 2322a86a88eaSTakashi Iwai add_loopback_list(spec, spec->aa_mix_nid, idx); 232313af8e77STakashi Iwai } 2324e3d7a143STakashi Iwai 2325e3d7a143STakashi Iwai /* remember the label for smart51 control */ 2326e3d7a143STakashi Iwai for (j = 0; j < spec->smart51_nums; j++) { 2327a86a88eaSTakashi Iwai if (spec->smart51_pins[j] == pin) { 2328e3d7a143STakashi Iwai spec->smart51_idxs[j] = idx; 2329e3d7a143STakashi Iwai spec->smart51_labels[j] = label; 2330e3d7a143STakashi Iwai break; 2331e3d7a143STakashi Iwai } 2332e3d7a143STakashi Iwai } 2333c577b8a1SJoseph Chan } 2334a86a88eaSTakashi Iwai return 0; 2335a86a88eaSTakashi Iwai } 2336a86a88eaSTakashi Iwai 2337a86a88eaSTakashi Iwai /* create mic-boost controls (if present) */ 2338a86a88eaSTakashi Iwai static int create_mic_boost_ctls(struct hda_codec *codec) 2339a86a88eaSTakashi Iwai { 2340a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2341a86a88eaSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 2342a86a88eaSTakashi Iwai int i, err; 2343a86a88eaSTakashi Iwai 2344a86a88eaSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 2345a86a88eaSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 2346a86a88eaSTakashi Iwai unsigned int caps; 2347a86a88eaSTakashi Iwai const char *label; 2348a86a88eaSTakashi Iwai char name[32]; 2349a86a88eaSTakashi Iwai 2350a86a88eaSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 2351a86a88eaSTakashi Iwai continue; 2352a86a88eaSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 2353a86a88eaSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 2354a86a88eaSTakashi Iwai continue; 2355a86a88eaSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 2356a86a88eaSTakashi Iwai snprintf(name, sizeof(name), "%s Boost Volume", label); 2357a86a88eaSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2358a86a88eaSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 2359a86a88eaSTakashi Iwai if (err < 0) 2360a86a88eaSTakashi Iwai return err; 2361a86a88eaSTakashi Iwai } 2362a86a88eaSTakashi Iwai return 0; 2363a86a88eaSTakashi Iwai } 2364a86a88eaSTakashi Iwai 2365a86a88eaSTakashi Iwai /* create capture and input-src controls for multiple streams */ 2366a86a88eaSTakashi Iwai static int create_multi_adc_ctls(struct hda_codec *codec) 2367a86a88eaSTakashi Iwai { 2368a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2369a86a88eaSTakashi Iwai int i, err; 2370d7a99cceSTakashi Iwai 2371d7a99cceSTakashi Iwai /* create capture mixer elements */ 2372d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2373d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 2374d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 2375d7a99cceSTakashi Iwai "Capture Volume", i, 2376d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2377d7a99cceSTakashi Iwai HDA_INPUT)); 2378d7a99cceSTakashi Iwai if (err < 0) 2379d7a99cceSTakashi Iwai return err; 2380d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2381d7a99cceSTakashi Iwai "Capture Switch", i, 2382d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 2383d7a99cceSTakashi Iwai HDA_INPUT)); 2384d7a99cceSTakashi Iwai if (err < 0) 2385d7a99cceSTakashi Iwai return err; 2386d7a99cceSTakashi Iwai } 2387d7a99cceSTakashi Iwai 2388d7a99cceSTakashi Iwai /* input-source control */ 2389d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 2390d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 2391d7a99cceSTakashi Iwai break; 2392a86a88eaSTakashi Iwai err = create_input_src_ctls(codec, i); 2393d7a99cceSTakashi Iwai if (err < 0) 2394d7a99cceSTakashi Iwai return err; 2395a86a88eaSTakashi Iwai return 0; 2396d7a99cceSTakashi Iwai } 2397d7a99cceSTakashi Iwai 2398a86a88eaSTakashi Iwai /* bind capture volume/switch */ 2399a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_vol_ctl = 2400a86a88eaSTakashi Iwai HDA_BIND_VOL("Capture Volume", 0); 2401a86a88eaSTakashi Iwai static struct snd_kcontrol_new via_bind_cap_sw_ctl = 2402a86a88eaSTakashi Iwai HDA_BIND_SW("Capture Switch", 0); 2403a86a88eaSTakashi Iwai 2404a86a88eaSTakashi Iwai static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret, 2405a86a88eaSTakashi Iwai struct hda_ctl_ops *ops) 2406a86a88eaSTakashi Iwai { 2407a86a88eaSTakashi Iwai struct hda_bind_ctls *ctl; 2408a86a88eaSTakashi Iwai int i; 2409a86a88eaSTakashi Iwai 2410a86a88eaSTakashi Iwai ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL); 2411a86a88eaSTakashi Iwai if (!ctl) 2412a86a88eaSTakashi Iwai return -ENOMEM; 2413a86a88eaSTakashi Iwai ctl->ops = ops; 2414a86a88eaSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 2415a86a88eaSTakashi Iwai ctl->values[i] = 2416a86a88eaSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT); 2417a86a88eaSTakashi Iwai *ctl_ret = ctl; 2418a86a88eaSTakashi Iwai return 0; 2419a86a88eaSTakashi Iwai } 2420a86a88eaSTakashi Iwai 2421a86a88eaSTakashi Iwai /* create capture and input-src controls for dynamic ADC-switch case */ 2422a86a88eaSTakashi Iwai static int create_dyn_adc_ctls(struct hda_codec *codec) 2423a86a88eaSTakashi Iwai { 2424a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2425a86a88eaSTakashi Iwai struct snd_kcontrol_new *knew; 2426a86a88eaSTakashi Iwai int err; 2427a86a88eaSTakashi Iwai 2428a86a88eaSTakashi Iwai /* set up the bind capture ctls */ 2429a86a88eaSTakashi Iwai err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol); 2430a86a88eaSTakashi Iwai if (err < 0) 2431a86a88eaSTakashi Iwai return err; 2432a86a88eaSTakashi Iwai err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw); 2433a86a88eaSTakashi Iwai if (err < 0) 2434a86a88eaSTakashi Iwai return err; 2435a86a88eaSTakashi Iwai 2436a86a88eaSTakashi Iwai /* create capture mixer elements */ 2437a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_bind_cap_vol_ctl); 2438a86a88eaSTakashi Iwai if (!knew) 2439a86a88eaSTakashi Iwai return -ENOMEM; 2440a86a88eaSTakashi Iwai knew->private_value = (long)spec->bind_cap_vol; 2441a86a88eaSTakashi Iwai 2442a86a88eaSTakashi Iwai knew = via_clone_control(spec, &via_bind_cap_sw_ctl); 2443a86a88eaSTakashi Iwai if (!knew) 2444a86a88eaSTakashi Iwai return -ENOMEM; 2445a86a88eaSTakashi Iwai knew->private_value = (long)spec->bind_cap_sw; 2446a86a88eaSTakashi Iwai 2447a86a88eaSTakashi Iwai /* input-source control */ 2448a86a88eaSTakashi Iwai err = create_input_src_ctls(codec, 1); 2449a86a88eaSTakashi Iwai if (err < 0) 2450a86a88eaSTakashi Iwai return err; 2451a86a88eaSTakashi Iwai return 0; 2452a86a88eaSTakashi Iwai } 2453a86a88eaSTakashi Iwai 2454a86a88eaSTakashi Iwai /* parse and create capture-related stuff */ 2455a86a88eaSTakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec) 2456a86a88eaSTakashi Iwai { 2457a86a88eaSTakashi Iwai struct via_spec *spec = codec->spec; 2458a86a88eaSTakashi Iwai int err; 2459a86a88eaSTakashi Iwai 2460a86a88eaSTakashi Iwai err = parse_analog_inputs(codec); 2461a86a88eaSTakashi Iwai if (err < 0) 2462a86a88eaSTakashi Iwai return err; 2463a86a88eaSTakashi Iwai if (spec->dyn_adc_switch) 2464a86a88eaSTakashi Iwai err = create_dyn_adc_ctls(codec); 2465a86a88eaSTakashi Iwai else 2466a86a88eaSTakashi Iwai err = create_multi_adc_ctls(codec); 2467a86a88eaSTakashi Iwai if (err < 0) 2468a86a88eaSTakashi Iwai return err; 2469a86a88eaSTakashi Iwai err = create_loopback_ctls(codec); 2470a86a88eaSTakashi Iwai if (err < 0) 2471a86a88eaSTakashi Iwai return err; 2472a86a88eaSTakashi Iwai err = create_mic_boost_ctls(codec); 2473a86a88eaSTakashi Iwai if (err < 0) 2474a86a88eaSTakashi Iwai return err; 2475c577b8a1SJoseph Chan return 0; 2476c577b8a1SJoseph Chan } 2477c577b8a1SJoseph Chan 247876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 247976d9b0ddSHarald Welte { 248076d9b0ddSHarald Welte unsigned int def_conf; 248176d9b0ddSHarald Welte unsigned char seqassoc; 248276d9b0ddSHarald Welte 24832f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 248476d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 248576d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 248682ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 248782ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 248876d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 24892f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 249076d9b0ddSHarald Welte } 249176d9b0ddSHarald Welte 249276d9b0ddSHarald Welte return; 249376d9b0ddSHarald Welte } 249476d9b0ddSHarald Welte 2495e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 24961f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 24971f2e99feSLydia Wang { 24981f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24991f2e99feSLydia Wang struct via_spec *spec = codec->spec; 25001f2e99feSLydia Wang 25011f2e99feSLydia Wang if (spec->codec_type != VT1708) 25021f2e99feSLydia Wang return 0; 2503e06e5a29STakashi Iwai spec->vt1708_jack_detect = 25041f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2505e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 25061f2e99feSLydia Wang return 0; 25071f2e99feSLydia Wang } 25081f2e99feSLydia Wang 2509e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 25101f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 25111f2e99feSLydia Wang { 25121f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 25131f2e99feSLydia Wang struct via_spec *spec = codec->spec; 25141f2e99feSLydia Wang int change; 25151f2e99feSLydia Wang 25161f2e99feSLydia Wang if (spec->codec_type != VT1708) 25171f2e99feSLydia Wang return 0; 2518e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 25191f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2520e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2521e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 25221f2e99feSLydia Wang mute_aa_path(codec, 1); 25231f2e99feSLydia Wang notify_aa_path_ctls(codec); 25241f2e99feSLydia Wang } 25251f2e99feSLydia Wang return change; 25261f2e99feSLydia Wang } 25271f2e99feSLydia Wang 2528e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 25291f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25301f2e99feSLydia Wang .name = "Jack Detect", 25311f2e99feSLydia Wang .count = 1, 25321f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2533e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2534e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 25351f2e99feSLydia Wang }; 25361f2e99feSLydia Wang 253712daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec); 253812daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec); 253912daef65STakashi Iwai 254012daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 2541c577b8a1SJoseph Chan { 2542c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2543c577b8a1SJoseph Chan int err; 2544c577b8a1SJoseph Chan 2545c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2546c577b8a1SJoseph Chan if (err < 0) 2547c577b8a1SJoseph Chan return err; 2548c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 25497f0df88cSTakashi Iwai return -EINVAL; 2550c577b8a1SJoseph Chan 25514a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2552c577b8a1SJoseph Chan if (err < 0) 2553c577b8a1SJoseph Chan return err; 25544a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2555c577b8a1SJoseph Chan if (err < 0) 2556c577b8a1SJoseph Chan return err; 25574a918ffeSTakashi Iwai err = via_auto_create_speaker_ctls(codec); 25584a918ffeSTakashi Iwai if (err < 0) 25594a918ffeSTakashi Iwai return err; 2560*3214b966STakashi Iwai err = via_auto_create_loopback_switch(codec); 2561*3214b966STakashi Iwai if (err < 0) 2562*3214b966STakashi Iwai return err; 2563a86a88eaSTakashi Iwai err = via_auto_create_analog_input_ctls(codec); 2564c577b8a1SJoseph Chan if (err < 0) 2565c577b8a1SJoseph Chan return err; 2566c577b8a1SJoseph Chan 2567c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2568c577b8a1SJoseph Chan 256912daef65STakashi Iwai fill_dig_outs(codec); 257012daef65STakashi Iwai fill_dig_in(codec); 2571c577b8a1SJoseph Chan 2572603c4019STakashi Iwai if (spec->kctls.list) 2573603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2574c577b8a1SJoseph Chan 2575c577b8a1SJoseph Chan 2576*3214b966STakashi Iwai if (spec->hp_dac_nid && spec->hp_mix_path.depth) { 2577ece8d043STakashi Iwai err = via_hp_build(codec); 2578ece8d043STakashi Iwai if (err < 0) 2579ece8d043STakashi Iwai return err; 2580ece8d043STakashi Iwai } 2581c577b8a1SJoseph Chan 2582f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2583f4a7828bSTakashi Iwai if (err < 0) 2584f4a7828bSTakashi Iwai return err; 2585f4a7828bSTakashi Iwai 25865d41762aSTakashi Iwai /* assign slave outs */ 25875d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 25885d41762aSTakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 25895d41762aSTakashi Iwai 2590c577b8a1SJoseph Chan return 1; 2591c577b8a1SJoseph Chan } 2592c577b8a1SJoseph Chan 25935d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec) 2594c577b8a1SJoseph Chan { 259525eaba2fSLydia Wang struct via_spec *spec = codec->spec; 25965d41762aSTakashi Iwai if (spec->multiout.dig_out_nid) 25975d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); 25985d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 25995d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); 26005d41762aSTakashi Iwai } 260125eaba2fSLydia Wang 26025d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec) 26035d41762aSTakashi Iwai { 26045d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 26055d41762aSTakashi Iwai if (!spec->dig_in_nid) 26065d41762aSTakashi Iwai return; 26075d41762aSTakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 26085d41762aSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 26095d41762aSTakashi Iwai } 26105d41762aSTakashi Iwai 26114a918ffeSTakashi Iwai /* initialize the unsolicited events */ 26124a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec) 26134a918ffeSTakashi Iwai { 26144a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 26154a918ffeSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 26164a918ffeSTakashi Iwai unsigned int ev; 26174a918ffeSTakashi Iwai int i; 26184a918ffeSTakashi Iwai 26194a918ffeSTakashi Iwai if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) 26204a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->hp_pins[0], 0, 26214a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 26224a918ffeSTakashi Iwai AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); 26234a918ffeSTakashi Iwai 26244a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 26254a918ffeSTakashi Iwai ev = VIA_LINE_EVENT; 26264a918ffeSTakashi Iwai else 26274a918ffeSTakashi Iwai ev = 0; 26284a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 26294a918ffeSTakashi Iwai if (cfg->line_out_pins[i] && 26304a918ffeSTakashi Iwai is_jack_detectable(codec, cfg->line_out_pins[i])) 263163f10d2cSLydia Wang snd_hda_codec_write(codec, cfg->line_out_pins[i], 0, 26324a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 26334a918ffeSTakashi Iwai AC_USRSP_EN | ev | VIA_JACK_EVENT); 26344a918ffeSTakashi Iwai } 26354a918ffeSTakashi Iwai 26364a918ffeSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 26374a918ffeSTakashi Iwai if (is_jack_detectable(codec, cfg->inputs[i].pin)) 26384a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, 26394a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 26404a918ffeSTakashi Iwai AC_USRSP_EN | VIA_JACK_EVENT); 26414a918ffeSTakashi Iwai } 26424a918ffeSTakashi Iwai } 26434a918ffeSTakashi Iwai 26445d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 26455d41762aSTakashi Iwai { 26465d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 26475d41762aSTakashi Iwai int i; 26485d41762aSTakashi Iwai 26495d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 26505d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 26515d41762aSTakashi Iwai 2652c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2653c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 26544a918ffeSTakashi Iwai via_auto_init_speaker_out(codec); 2655c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 26565d41762aSTakashi Iwai via_auto_init_dig_outs(codec); 26575d41762aSTakashi Iwai via_auto_init_dig_in(codec); 265811890956SLydia Wang 26594a918ffeSTakashi Iwai via_auto_init_unsol_event(codec); 26604a918ffeSTakashi Iwai 266125eaba2fSLydia Wang via_hp_automute(codec); 266225eaba2fSLydia Wang 2663c577b8a1SJoseph Chan return 0; 2664c577b8a1SJoseph Chan } 2665c577b8a1SJoseph Chan 26661f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 26671f2e99feSLydia Wang { 26681f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 26691f2e99feSLydia Wang vt1708_hp_work.work); 26701f2e99feSLydia Wang if (spec->codec_type != VT1708) 26711f2e99feSLydia Wang return; 26721f2e99feSLydia Wang /* if jack state toggled */ 26731f2e99feSLydia Wang if (spec->vt1708_hp_present 2674d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 26751f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 26761f2e99feSLydia Wang via_hp_automute(spec->codec); 26771f2e99feSLydia Wang } 26781f2e99feSLydia Wang vt1708_start_hp_work(spec); 26791f2e99feSLydia Wang } 26801f2e99feSLydia Wang 2681337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2682337b9d02STakashi Iwai { 2683337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2684337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2685337b9d02STakashi Iwai unsigned int type; 2686337b9d02STakashi Iwai int i, n; 2687337b9d02STakashi Iwai 2688337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2689337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2690337b9d02STakashi Iwai while (nid) { 2691a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 26921c55d521STakashi Iwai if (type == AC_WID_PIN) 26931c55d521STakashi Iwai break; 2694337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2695337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2696337b9d02STakashi Iwai if (n <= 0) 2697337b9d02STakashi Iwai break; 2698337b9d02STakashi Iwai if (n > 1) { 2699337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2700337b9d02STakashi Iwai break; 2701337b9d02STakashi Iwai } 2702337b9d02STakashi Iwai nid = conn[0]; 2703337b9d02STakashi Iwai } 2704337b9d02STakashi Iwai } 27051c55d521STakashi Iwai return 0; 2706337b9d02STakashi Iwai } 2707337b9d02STakashi Iwai 2708c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2709c577b8a1SJoseph Chan { 2710c577b8a1SJoseph Chan struct via_spec *spec; 2711c577b8a1SJoseph Chan int err; 2712c577b8a1SJoseph Chan 2713c577b8a1SJoseph Chan /* create a codec specific record */ 27145b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2715c577b8a1SJoseph Chan if (spec == NULL) 2716c577b8a1SJoseph Chan return -ENOMEM; 2717c577b8a1SJoseph Chan 2718620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2719620e2b28STakashi Iwai 272012daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 272112daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 272212daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 272312daef65STakashi Iwai 2724c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 272512daef65STakashi Iwai err = via_parse_auto_config(codec); 2726c577b8a1SJoseph Chan if (err < 0) { 2727c577b8a1SJoseph Chan via_free(codec); 2728c577b8a1SJoseph Chan return err; 2729c577b8a1SJoseph Chan } 2730c577b8a1SJoseph Chan 273112daef65STakashi Iwai /* add jack detect on/off control */ 273212daef65STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 273312daef65STakashi Iwai return -ENOMEM; 273412daef65STakashi Iwai 2735bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2736bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2737bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2738c577b8a1SJoseph Chan 2739e322a36dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 2740e322a36dSLydia Wang 2741c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2742c577b8a1SJoseph Chan 27431f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2744c577b8a1SJoseph Chan return 0; 2745c577b8a1SJoseph Chan } 2746c577b8a1SJoseph Chan 2747ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec) 2748c577b8a1SJoseph Chan { 2749c577b8a1SJoseph Chan struct via_spec *spec; 2750c577b8a1SJoseph Chan int err; 2751c577b8a1SJoseph Chan 2752c577b8a1SJoseph Chan /* create a codec specific record */ 27535b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2754c577b8a1SJoseph Chan if (spec == NULL) 2755c577b8a1SJoseph Chan return -ENOMEM; 2756c577b8a1SJoseph Chan 2757620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2758620e2b28STakashi Iwai 275912daef65STakashi Iwai err = via_parse_auto_config(codec); 2760c577b8a1SJoseph Chan if (err < 0) { 2761c577b8a1SJoseph Chan via_free(codec); 2762c577b8a1SJoseph Chan return err; 2763c577b8a1SJoseph Chan } 2764c577b8a1SJoseph Chan 2765c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2766c577b8a1SJoseph Chan 2767f7278fd0SJosepch Chan return 0; 2768f7278fd0SJosepch Chan } 2769f7278fd0SJosepch Chan 27703e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 27713e95b9abSLydia Wang { 27723e95b9abSLydia Wang struct via_spec *spec = codec->spec; 27733e95b9abSLydia Wang int imux_is_smixer; 27743e95b9abSLydia Wang unsigned int parm; 27753e95b9abSLydia Wang int is_8ch = 0; 2776bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2777bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 27783e95b9abSLydia Wang is_8ch = 1; 27793e95b9abSLydia Wang 27803e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 27813e95b9abSLydia Wang imux_is_smixer = 27823e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 27833e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 27843e95b9abSLydia Wang /* inputs */ 27853e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 27863e95b9abSLydia Wang parm = AC_PWRST_D3; 27873e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 27883e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 27893e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 27903e95b9abSLydia Wang if (imux_is_smixer) 27913e95b9abSLydia Wang parm = AC_PWRST_D0; 27923e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 27933e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 27943e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 27953e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 27963e95b9abSLydia Wang 27973e95b9abSLydia Wang /* outputs */ 27983e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 27993e95b9abSLydia Wang parm = AC_PWRST_D3; 28003e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 28013e95b9abSLydia Wang if (spec->smart51_enabled) 28023e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 28043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28053e95b9abSLydia Wang 28063e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 28073e95b9abSLydia Wang if (is_8ch) { 28083e95b9abSLydia Wang parm = AC_PWRST_D3; 28093e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 28103e95b9abSLydia Wang if (spec->smart51_enabled) 28113e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 28133e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 28153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2816bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2817bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2818bc92df7fSLydia Wang parm = AC_PWRST_D3; 2819bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2820bc92df7fSLydia Wang if (spec->smart51_enabled) 2821bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2822bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2823bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2824bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2825bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28263e95b9abSLydia Wang } 28273e95b9abSLydia Wang 28283e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 28293e95b9abSLydia Wang parm = AC_PWRST_D3; 28303e95b9abSLydia Wang /* force to D0 for internal Speaker */ 28313e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 28323e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 28333e95b9abSLydia Wang if (is_8ch) 28343e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 28353e95b9abSLydia Wang 28363e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 28373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 28383e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 28403e95b9abSLydia Wang if (is_8ch) { 28413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 28423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 28443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2845bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2846bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2847bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28483e95b9abSLydia Wang } 28493e95b9abSLydia Wang 2850518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2851ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec) 2852f7278fd0SJosepch Chan { 2853f7278fd0SJosepch Chan struct via_spec *spec; 2854f7278fd0SJosepch Chan int err; 2855f7278fd0SJosepch Chan 2856518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2857518bf3baSLydia Wang return patch_vt1708S(codec); 2858ddd304d8STakashi Iwai 2859f7278fd0SJosepch Chan /* create a codec specific record */ 28605b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2861f7278fd0SJosepch Chan if (spec == NULL) 2862f7278fd0SJosepch Chan return -ENOMEM; 2863f7278fd0SJosepch Chan 2864620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2865620e2b28STakashi Iwai 2866f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 286712daef65STakashi Iwai err = via_parse_auto_config(codec); 2868f7278fd0SJosepch Chan if (err < 0) { 2869f7278fd0SJosepch Chan via_free(codec); 2870f7278fd0SJosepch Chan return err; 2871f7278fd0SJosepch Chan } 2872f7278fd0SJosepch Chan 2873f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2874f7278fd0SJosepch Chan 28753e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 28763e95b9abSLydia Wang 2877f7278fd0SJosepch Chan return 0; 2878f7278fd0SJosepch Chan } 2879f7278fd0SJosepch Chan 2880d949cac1SHarald Welte /* Patch for VT1708S */ 2881096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 2882d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2883d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2884bc7e7e5cSLydia Wang /* don't bybass mixer */ 2885bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 2886d949cac1SHarald Welte { } 2887d949cac1SHarald Welte }; 2888d949cac1SHarald Welte 28899da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 28909da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 28919da29271STakashi Iwai { 28929da29271STakashi Iwai struct via_spec *spec = codec->spec; 28939da29271STakashi Iwai int i; 28949da29271STakashi Iwai 28959da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 28969da29271STakashi Iwai hda_nid_t nid; 28979da29271STakashi Iwai int conn; 28989da29271STakashi Iwai 28999da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 29009da29271STakashi Iwai if (!nid) 29019da29271STakashi Iwai continue; 29029da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 29039da29271STakashi Iwai if (conn < 1) 29049da29271STakashi Iwai continue; 29059da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 29069da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 29079da29271STakashi Iwai else { 29089da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 29099da29271STakashi Iwai break; /* at most two dig outs */ 29109da29271STakashi Iwai } 29119da29271STakashi Iwai } 29129da29271STakashi Iwai } 29139da29271STakashi Iwai 291412daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec) 2915d949cac1SHarald Welte { 2916d949cac1SHarald Welte struct via_spec *spec = codec->spec; 291712daef65STakashi Iwai hda_nid_t dig_nid; 291812daef65STakashi Iwai int i, err; 2919d949cac1SHarald Welte 292012daef65STakashi Iwai if (!spec->autocfg.dig_in_pin) 292112daef65STakashi Iwai return; 2922d949cac1SHarald Welte 292312daef65STakashi Iwai dig_nid = codec->start_nid; 292412daef65STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 292512daef65STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 292612daef65STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 292712daef65STakashi Iwai continue; 292812daef65STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 292912daef65STakashi Iwai continue; 293012daef65STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 293112daef65STakashi Iwai continue; 293212daef65STakashi Iwai err = get_connection_index(codec, dig_nid, 293312daef65STakashi Iwai spec->autocfg.dig_in_pin); 293412daef65STakashi Iwai if (err >= 0) { 293512daef65STakashi Iwai spec->dig_in_nid = dig_nid; 293612daef65STakashi Iwai break; 293712daef65STakashi Iwai } 293812daef65STakashi Iwai } 2939d949cac1SHarald Welte } 2940d949cac1SHarald Welte 29416369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 29426369bcfcSLydia Wang int offset, int num_steps, int step_size) 29436369bcfcSLydia Wang { 29446369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 29456369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 29466369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 29476369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 29486369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 29496369bcfcSLydia Wang } 29506369bcfcSLydia Wang 2951d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2952d949cac1SHarald Welte { 2953d949cac1SHarald Welte struct via_spec *spec; 2954d949cac1SHarald Welte int err; 2955d949cac1SHarald Welte 2956d949cac1SHarald Welte /* create a codec specific record */ 29575b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2958d949cac1SHarald Welte if (spec == NULL) 2959d949cac1SHarald Welte return -ENOMEM; 2960d949cac1SHarald Welte 2961620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2962d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2963d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2964620e2b28STakashi Iwai 2965d949cac1SHarald Welte /* automatic parse from the BIOS config */ 296612daef65STakashi Iwai err = via_parse_auto_config(codec); 2967d949cac1SHarald Welte if (err < 0) { 2968d949cac1SHarald Welte via_free(codec); 2969d949cac1SHarald Welte return err; 2970d949cac1SHarald Welte } 2971d949cac1SHarald Welte 2972096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 2973d949cac1SHarald Welte 2974d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2975d949cac1SHarald Welte 2976518bf3baSLydia Wang /* correct names for VT1708BCE */ 2977518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 2978518bf3baSLydia Wang kfree(codec->chip_name); 2979518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 2980518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 2981518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 2982518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2983970f630fSLydia Wang } 2984bc92df7fSLydia Wang /* correct names for VT1705 */ 2985bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 2986bc92df7fSLydia Wang kfree(codec->chip_name); 2987bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 2988bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 2989bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 2990bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2991bc92df7fSLydia Wang } 29923e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 2993d949cac1SHarald Welte return 0; 2994d949cac1SHarald Welte } 2995d949cac1SHarald Welte 2996d949cac1SHarald Welte /* Patch for VT1702 */ 2997d949cac1SHarald Welte 2998096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 2999bc7e7e5cSLydia Wang /* mixer enable */ 3000bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 3001bc7e7e5cSLydia Wang /* GPIO 0~2 */ 3002bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 3003d949cac1SHarald Welte { } 3004d949cac1SHarald Welte }; 3005d949cac1SHarald Welte 30063e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 30073e95b9abSLydia Wang { 30083e95b9abSLydia Wang int imux_is_smixer = 30093e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 30103e95b9abSLydia Wang unsigned int parm; 30113e95b9abSLydia Wang /* inputs */ 30123e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 30133e95b9abSLydia Wang parm = AC_PWRST_D3; 30143e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 30153e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 30163e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 30173e95b9abSLydia Wang if (imux_is_smixer) 30183e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 30193e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 30203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 30213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 30223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 30233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 30243e95b9abSLydia Wang 30253e95b9abSLydia Wang /* outputs */ 30263e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 30273e95b9abSLydia Wang parm = AC_PWRST_D3; 30283e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 30293e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 30303e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 30313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 30323e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 30333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 30343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 30353e95b9abSLydia Wang } 30363e95b9abSLydia Wang 3037d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 3038d949cac1SHarald Welte { 3039d949cac1SHarald Welte struct via_spec *spec; 3040d949cac1SHarald Welte int err; 3041d949cac1SHarald Welte 3042d949cac1SHarald Welte /* create a codec specific record */ 30435b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3044d949cac1SHarald Welte if (spec == NULL) 3045d949cac1SHarald Welte return -ENOMEM; 3046d949cac1SHarald Welte 3047620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 3048620e2b28STakashi Iwai 304912daef65STakashi Iwai /* limit AA path volume to 0 dB */ 305012daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 305112daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 305212daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 305312daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 305412daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 305512daef65STakashi Iwai 3056d949cac1SHarald Welte /* automatic parse from the BIOS config */ 305712daef65STakashi Iwai err = via_parse_auto_config(codec); 3058d949cac1SHarald Welte if (err < 0) { 3059d949cac1SHarald Welte via_free(codec); 3060d949cac1SHarald Welte return err; 3061d949cac1SHarald Welte } 3062d949cac1SHarald Welte 3063096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 3064d949cac1SHarald Welte 3065d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 3066d949cac1SHarald Welte 30673e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 3068d949cac1SHarald Welte return 0; 3069d949cac1SHarald Welte } 3070d949cac1SHarald Welte 3071eb7188caSLydia Wang /* Patch for VT1718S */ 3072eb7188caSLydia Wang 3073096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 30744ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 30754ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 3076eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 3077eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 30785d41762aSTakashi Iwai 3079eb7188caSLydia Wang { } 3080eb7188caSLydia Wang }; 3081eb7188caSLydia Wang 30823e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 30833e95b9abSLydia Wang { 30843e95b9abSLydia Wang struct via_spec *spec = codec->spec; 30853e95b9abSLydia Wang int imux_is_smixer; 30863e95b9abSLydia Wang unsigned int parm; 30873e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 30883e95b9abSLydia Wang imux_is_smixer = 30893e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 30903e95b9abSLydia Wang /* inputs */ 30913e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 30923e95b9abSLydia Wang parm = AC_PWRST_D3; 30933e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 30943e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 30953e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 30963e95b9abSLydia Wang if (imux_is_smixer) 30973e95b9abSLydia Wang parm = AC_PWRST_D0; 30983e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 30993e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 31003e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 31013e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 31023e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 31033e95b9abSLydia Wang 31043e95b9abSLydia Wang /* outputs */ 31053e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 31063e95b9abSLydia Wang parm = AC_PWRST_D3; 31073e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 31083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 31093e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 31103e95b9abSLydia Wang 31113e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 31123e95b9abSLydia Wang parm = AC_PWRST_D3; 31133e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 31143e95b9abSLydia Wang if (spec->smart51_enabled) 31153e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 31163e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 31173e95b9abSLydia Wang 31183e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 31193e95b9abSLydia Wang parm = AC_PWRST_D3; 31203e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 31213e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 31223e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 31233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 31243e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 31253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 31263e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 31273e95b9abSLydia Wang 31283e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 31293e95b9abSLydia Wang parm = AC_PWRST_D3; 31303e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 31313e95b9abSLydia Wang if (spec->smart51_enabled) 31323e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 31333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 31343e95b9abSLydia Wang 31353e95b9abSLydia Wang if (spec->hp_independent_mode) { 31363e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 31373e95b9abSLydia Wang parm = AC_PWRST_D3; 31383e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 31393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 31403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 31423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31433e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 31443e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 31453e95b9abSLydia Wang } 31463e95b9abSLydia Wang } 31473e95b9abSLydia Wang 314830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs 314930b45033STakashi Iwai * This isn't listed from the raw info, but the chip has a secret connection. 315030b45033STakashi Iwai */ 315130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec) 315230b45033STakashi Iwai { 315330b45033STakashi Iwai struct via_spec *spec = codec->spec; 315430b45033STakashi Iwai int i, nums; 315530b45033STakashi Iwai hda_nid_t conn[8]; 315630b45033STakashi Iwai hda_nid_t nid; 315730b45033STakashi Iwai 315830b45033STakashi Iwai if (!spec->aa_mix_nid) 315930b45033STakashi Iwai return 0; 316030b45033STakashi Iwai nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 316130b45033STakashi Iwai ARRAY_SIZE(conn) - 1); 316230b45033STakashi Iwai for (i = 0; i < nums; i++) { 316330b45033STakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT) 316430b45033STakashi Iwai return 0; 316530b45033STakashi Iwai } 316630b45033STakashi Iwai 316730b45033STakashi Iwai /* find the primary DAC and add to the connection list */ 316830b45033STakashi Iwai nid = codec->start_nid; 316930b45033STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 317030b45033STakashi Iwai unsigned int caps = get_wcaps(codec, nid); 317130b45033STakashi Iwai if (get_wcaps_type(caps) == AC_WID_AUD_OUT && 317230b45033STakashi Iwai !(caps & AC_WCAP_DIGITAL)) { 317330b45033STakashi Iwai conn[nums++] = nid; 317430b45033STakashi Iwai return snd_hda_override_conn_list(codec, 317530b45033STakashi Iwai spec->aa_mix_nid, 317630b45033STakashi Iwai nums, conn); 317730b45033STakashi Iwai } 317830b45033STakashi Iwai } 317930b45033STakashi Iwai return 0; 318030b45033STakashi Iwai } 318130b45033STakashi Iwai 318230b45033STakashi Iwai 3183eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 3184eb7188caSLydia Wang { 3185eb7188caSLydia Wang struct via_spec *spec; 3186eb7188caSLydia Wang int err; 3187eb7188caSLydia Wang 3188eb7188caSLydia Wang /* create a codec specific record */ 31895b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3190eb7188caSLydia Wang if (spec == NULL) 3191eb7188caSLydia Wang return -ENOMEM; 3192eb7188caSLydia Wang 3193620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3194d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3195d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 319630b45033STakashi Iwai add_secret_dac_path(codec); 3197620e2b28STakashi Iwai 3198eb7188caSLydia Wang /* automatic parse from the BIOS config */ 319912daef65STakashi Iwai err = via_parse_auto_config(codec); 3200eb7188caSLydia Wang if (err < 0) { 3201eb7188caSLydia Wang via_free(codec); 3202eb7188caSLydia Wang return err; 3203eb7188caSLydia Wang } 3204eb7188caSLydia Wang 3205096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 3206eb7188caSLydia Wang 3207eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 3208eb7188caSLydia Wang 32093e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 32103e95b9abSLydia Wang 3211eb7188caSLydia Wang return 0; 3212eb7188caSLydia Wang } 3213f3db423dSLydia Wang 3214f3db423dSLydia Wang /* Patch for VT1716S */ 3215f3db423dSLydia Wang 3216f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 3217f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 3218f3db423dSLydia Wang { 3219f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 3220f3db423dSLydia Wang uinfo->count = 1; 3221f3db423dSLydia Wang uinfo->value.integer.min = 0; 3222f3db423dSLydia Wang uinfo->value.integer.max = 1; 3223f3db423dSLydia Wang return 0; 3224f3db423dSLydia Wang } 3225f3db423dSLydia Wang 3226f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 3227f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3228f3db423dSLydia Wang { 3229f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3230f3db423dSLydia Wang int index = 0; 3231f3db423dSLydia Wang 3232f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 3233f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 3234f3db423dSLydia Wang if (index != -1) 3235f3db423dSLydia Wang *ucontrol->value.integer.value = index; 3236f3db423dSLydia Wang 3237f3db423dSLydia Wang return 0; 3238f3db423dSLydia Wang } 3239f3db423dSLydia Wang 3240f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 3241f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 3242f3db423dSLydia Wang { 3243f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 3244f3db423dSLydia Wang struct via_spec *spec = codec->spec; 3245f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 3246f3db423dSLydia Wang 3247f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 3248f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 3249f3db423dSLydia Wang spec->dmic_enabled = index; 32503e95b9abSLydia Wang set_widgets_power_state(codec); 3251f3db423dSLydia Wang return 1; 3252f3db423dSLydia Wang } 3253f3db423dSLydia Wang 325490dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 3255f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 3256f3db423dSLydia Wang { 3257f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3258f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 32595b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 3260f3db423dSLydia Wang .count = 1, 3261f3db423dSLydia Wang .info = vt1716s_dmic_info, 3262f3db423dSLydia Wang .get = vt1716s_dmic_get, 3263f3db423dSLydia Wang .put = vt1716s_dmic_put, 3264f3db423dSLydia Wang }, 3265f3db423dSLydia Wang {} /* end */ 3266f3db423dSLydia Wang }; 3267f3db423dSLydia Wang 3268f3db423dSLydia Wang 3269f3db423dSLydia Wang /* mono-out mixer elements */ 327090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 3271f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 3272f3db423dSLydia Wang { } /* end */ 3273f3db423dSLydia Wang }; 3274f3db423dSLydia Wang 3275096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 3276f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 3277f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 3278f3db423dSLydia Wang /* don't bybass mixer */ 3279f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 3280f3db423dSLydia Wang /* Enable mono output */ 3281f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 3282f3db423dSLydia Wang { } 3283f3db423dSLydia Wang }; 3284f3db423dSLydia Wang 32853e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 32863e95b9abSLydia Wang { 32873e95b9abSLydia Wang struct via_spec *spec = codec->spec; 32883e95b9abSLydia Wang int imux_is_smixer; 32893e95b9abSLydia Wang unsigned int parm; 32903e95b9abSLydia Wang unsigned int mono_out, present; 32913e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 32923e95b9abSLydia Wang imux_is_smixer = 32933e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 32943e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 32953e95b9abSLydia Wang /* inputs */ 32963e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 32973e95b9abSLydia Wang parm = AC_PWRST_D3; 32983e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 32993e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 33003e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 33013e95b9abSLydia Wang if (imux_is_smixer) 33023e95b9abSLydia Wang parm = AC_PWRST_D0; 33033e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 33043e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 33053e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 33063e95b9abSLydia Wang 33073e95b9abSLydia Wang parm = AC_PWRST_D3; 33083e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 33093e95b9abSLydia Wang /* PW11 (22h) */ 33103e95b9abSLydia Wang if (spec->dmic_enabled) 33113e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 33123e95b9abSLydia Wang else 33133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 33143e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 33153e95b9abSLydia Wang 33163e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 33173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 33183e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 33193e95b9abSLydia Wang 33203e95b9abSLydia Wang /* outputs */ 33213e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 33223e95b9abSLydia Wang parm = AC_PWRST_D3; 33233e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 33243e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 33253e95b9abSLydia Wang if (spec->smart51_enabled) 33263e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 33273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 33283e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 33293e95b9abSLydia Wang 33303e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 33313e95b9abSLydia Wang parm = AC_PWRST_D3; 33323e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 33333e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 33343e95b9abSLydia Wang if (spec->smart51_enabled) 33353e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 33363e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 33373e95b9abSLydia Wang 33383e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 33393e95b9abSLydia Wang if (spec->smart51_enabled) 33403e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 33413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 33423e95b9abSLydia Wang 33433e95b9abSLydia Wang /* Mono out */ 33443e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 33453e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 33463e95b9abSLydia Wang 33473e95b9abSLydia Wang if (present) 33483e95b9abSLydia Wang mono_out = 0; 33493e95b9abSLydia Wang else { 33503e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 33513e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 33523e95b9abSLydia Wang mono_out = 0; 33533e95b9abSLydia Wang else 33543e95b9abSLydia Wang mono_out = 1; 33553e95b9abSLydia Wang } 33563e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 33573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 33583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 33593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 33603e95b9abSLydia Wang 33613e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 33623e95b9abSLydia Wang parm = AC_PWRST_D3; 33633e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 33643e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 33653e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 33663e95b9abSLydia Wang if (spec->hp_independent_mode) 33673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 33683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 33693e95b9abSLydia Wang 33703e95b9abSLydia Wang /* force to D0 for internal Speaker */ 33713e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 33723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 33733e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 33743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 33753e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 33763e95b9abSLydia Wang } 33773e95b9abSLydia Wang 3378f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 3379f3db423dSLydia Wang { 3380f3db423dSLydia Wang struct via_spec *spec; 3381f3db423dSLydia Wang int err; 3382f3db423dSLydia Wang 3383f3db423dSLydia Wang /* create a codec specific record */ 33845b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3385f3db423dSLydia Wang if (spec == NULL) 3386f3db423dSLydia Wang return -ENOMEM; 3387f3db423dSLydia Wang 3388620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 3389d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 3390d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 3391620e2b28STakashi Iwai 3392f3db423dSLydia Wang /* automatic parse from the BIOS config */ 339312daef65STakashi Iwai err = via_parse_auto_config(codec); 3394f3db423dSLydia Wang if (err < 0) { 3395f3db423dSLydia Wang via_free(codec); 3396f3db423dSLydia Wang return err; 3397f3db423dSLydia Wang } 3398f3db423dSLydia Wang 3399096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 3400f3db423dSLydia Wang 3401f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 3402f3db423dSLydia Wang spec->num_mixers++; 3403f3db423dSLydia Wang 3404f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 3405f3db423dSLydia Wang 3406f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 3407f3db423dSLydia Wang 34083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 3409f3db423dSLydia Wang return 0; 3410f3db423dSLydia Wang } 341125eaba2fSLydia Wang 341225eaba2fSLydia Wang /* for vt2002P */ 341325eaba2fSLydia Wang 3414096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 3415eadb9a80SLydia Wang /* Class-D speaker related verbs */ 3416eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 3417eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 3418eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 341925eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 342025eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 342125eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 342225eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 342325eaba2fSLydia Wang { } 342425eaba2fSLydia Wang }; 34254a918ffeSTakashi Iwai 3426096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 342711890956SLydia Wang /* Enable Boost Volume backdoor */ 342811890956SLydia Wang {0x1, 0xfb9, 0x24}, 342911890956SLydia Wang /* Enable AOW0 to MW9 */ 343011890956SLydia Wang {0x1, 0xfb8, 0x88}, 343111890956SLydia Wang { } 343211890956SLydia Wang }; 343325eaba2fSLydia Wang 34343e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 34353e95b9abSLydia Wang { 34363e95b9abSLydia Wang struct via_spec *spec = codec->spec; 34373e95b9abSLydia Wang int imux_is_smixer; 34383e95b9abSLydia Wang unsigned int parm; 34393e95b9abSLydia Wang unsigned int present; 34403e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 34413e95b9abSLydia Wang imux_is_smixer = 34423e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 34433e95b9abSLydia Wang /* inputs */ 34443e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 34453e95b9abSLydia Wang parm = AC_PWRST_D3; 34463e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 34473e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 34483e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 34493e95b9abSLydia Wang parm = AC_PWRST_D0; 34503e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 34513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 34523e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 34533e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 34543e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 34553e95b9abSLydia Wang 34563e95b9abSLydia Wang /* outputs */ 34573e95b9abSLydia Wang /* AOW0 (8h)*/ 34583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 34593e95b9abSLydia Wang 346011890956SLydia Wang if (spec->codec_type == VT1802) { 346111890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 346211890956SLydia Wang parm = AC_PWRST_D3; 346311890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 346411890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 346511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 346611890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 346711890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 346811890956SLydia Wang } else { 34693e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 34703e95b9abSLydia Wang parm = AC_PWRST_D3; 34713e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 34723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 34733e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 34743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 34753e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 347611890956SLydia Wang } 34773e95b9abSLydia Wang 347811890956SLydia Wang if (spec->codec_type == VT1802) { 347911890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 348011890956SLydia Wang parm = AC_PWRST_D3; 348111890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 348211890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 348311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 348411890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 348511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 348611890956SLydia Wang } else { 34873e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 34883e95b9abSLydia Wang parm = AC_PWRST_D3; 34893e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 34903e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 34913e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 34923e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 34933e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 349411890956SLydia Wang } 34953e95b9abSLydia Wang 34963e95b9abSLydia Wang if (spec->hp_independent_mode) 34973e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 34983e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 34993e95b9abSLydia Wang 35003e95b9abSLydia Wang /* Class-D */ 35013e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 35023e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 35033e95b9abSLydia Wang 35043e95b9abSLydia Wang parm = AC_PWRST_D3; 35053e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 35063e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 350711890956SLydia Wang if (spec->codec_type == VT1802) 350811890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 350911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 351011890956SLydia Wang else 35113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 35123e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 35143e95b9abSLydia Wang 35153e95b9abSLydia Wang /* Mono Out */ 35163e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 35173e95b9abSLydia Wang 35183e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 351911890956SLydia Wang if (spec->codec_type == VT1802) { 352011890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 352111890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 352211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 352311890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 352411890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 352511890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 352611890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 352711890956SLydia Wang } else { 35283e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 35293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 35303e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 35323e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 35333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 35343e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 353511890956SLydia Wang } 35363e95b9abSLydia Wang /* MW9 (21h) */ 35373e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 35383e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 35393e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 35403e95b9abSLydia Wang else 35413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 35423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 35433e95b9abSLydia Wang } 354425eaba2fSLydia Wang 354525eaba2fSLydia Wang /* patch for vt2002P */ 354625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 354725eaba2fSLydia Wang { 354825eaba2fSLydia Wang struct via_spec *spec; 354925eaba2fSLydia Wang int err; 355025eaba2fSLydia Wang 355125eaba2fSLydia Wang /* create a codec specific record */ 35525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 355325eaba2fSLydia Wang if (spec == NULL) 355425eaba2fSLydia Wang return -ENOMEM; 355525eaba2fSLydia Wang 3556620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3557d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3558d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 355930b45033STakashi Iwai add_secret_dac_path(codec); 3560620e2b28STakashi Iwai 356125eaba2fSLydia Wang /* automatic parse from the BIOS config */ 356212daef65STakashi Iwai err = via_parse_auto_config(codec); 356325eaba2fSLydia Wang if (err < 0) { 356425eaba2fSLydia Wang via_free(codec); 356525eaba2fSLydia Wang return err; 356625eaba2fSLydia Wang } 356725eaba2fSLydia Wang 356811890956SLydia Wang if (spec->codec_type == VT1802) 35694a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 357011890956SLydia Wang else 35714a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 357211890956SLydia Wang 357325eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 357425eaba2fSLydia Wang 35753e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 357625eaba2fSLydia Wang return 0; 357725eaba2fSLydia Wang } 3578ab6734e7SLydia Wang 3579ab6734e7SLydia Wang /* for vt1812 */ 3580ab6734e7SLydia Wang 3581096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 3582ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 3583ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 3584ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 3585ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 3586ab6734e7SLydia Wang { } 3587ab6734e7SLydia Wang }; 3588ab6734e7SLydia Wang 35893e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 35903e95b9abSLydia Wang { 35913e95b9abSLydia Wang struct via_spec *spec = codec->spec; 35923e95b9abSLydia Wang int imux_is_smixer = 35933e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 35943e95b9abSLydia Wang unsigned int parm; 35953e95b9abSLydia Wang unsigned int present; 35963e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 35973e95b9abSLydia Wang imux_is_smixer = 35983e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 35993e95b9abSLydia Wang /* inputs */ 36003e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 36013e95b9abSLydia Wang parm = AC_PWRST_D3; 36023e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 36033e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 36043e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 36053e95b9abSLydia Wang parm = AC_PWRST_D0; 36063e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 36073e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 36083e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 36093e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 36103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 36113e95b9abSLydia Wang 36123e95b9abSLydia Wang /* outputs */ 36133e95b9abSLydia Wang /* AOW0 (8h)*/ 36143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 36153e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36163e95b9abSLydia Wang 36173e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 36183e95b9abSLydia Wang parm = AC_PWRST_D3; 36193e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 36203e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 36213e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 36223e95b9abSLydia Wang 36233e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 36243e95b9abSLydia Wang parm = AC_PWRST_D3; 36253e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 36263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 36273e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 36283e95b9abSLydia Wang if (spec->hp_independent_mode) 36293e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 36303e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36313e95b9abSLydia Wang 36323e95b9abSLydia Wang /* Internal Speaker */ 36333e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 36343e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 36353e95b9abSLydia Wang 36363e95b9abSLydia Wang parm = AC_PWRST_D3; 36373e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 36383e95b9abSLydia Wang if (present) { 36393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 36403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 36413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 36423e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 36433e95b9abSLydia Wang } else { 36443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 36453e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 36473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36483e95b9abSLydia Wang } 36493e95b9abSLydia Wang 36503e95b9abSLydia Wang 36513e95b9abSLydia Wang /* Mono Out */ 36523e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 36533e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 36543e95b9abSLydia Wang 36553e95b9abSLydia Wang parm = AC_PWRST_D3; 36563e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 36573e95b9abSLydia Wang if (present) { 36583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 36593e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 36603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 36613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 36623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 36633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 36643e95b9abSLydia Wang } else { 36653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 36663e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 36683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36693e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 36703e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 36713e95b9abSLydia Wang } 36723e95b9abSLydia Wang 36733e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 36743e95b9abSLydia Wang parm = AC_PWRST_D3; 36753e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 36763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 36773e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 36783e95b9abSLydia Wang 36793e95b9abSLydia Wang } 3680ab6734e7SLydia Wang 3681ab6734e7SLydia Wang /* patch for vt1812 */ 3682ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 3683ab6734e7SLydia Wang { 3684ab6734e7SLydia Wang struct via_spec *spec; 3685ab6734e7SLydia Wang int err; 3686ab6734e7SLydia Wang 3687ab6734e7SLydia Wang /* create a codec specific record */ 36885b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3689ab6734e7SLydia Wang if (spec == NULL) 3690ab6734e7SLydia Wang return -ENOMEM; 3691ab6734e7SLydia Wang 3692620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3693d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3694d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 369530b45033STakashi Iwai add_secret_dac_path(codec); 3696620e2b28STakashi Iwai 3697ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 369812daef65STakashi Iwai err = via_parse_auto_config(codec); 3699ab6734e7SLydia Wang if (err < 0) { 3700ab6734e7SLydia Wang via_free(codec); 3701ab6734e7SLydia Wang return err; 3702ab6734e7SLydia Wang } 3703ab6734e7SLydia Wang 3704096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 3705ab6734e7SLydia Wang 3706ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 3707ab6734e7SLydia Wang 37083e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 3709ab6734e7SLydia Wang return 0; 3710ab6734e7SLydia Wang } 3711ab6734e7SLydia Wang 3712c577b8a1SJoseph Chan /* 3713c577b8a1SJoseph Chan * patch entries 3714c577b8a1SJoseph Chan */ 371590dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 37163218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 37173218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 37183218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 37193218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 37203218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3721ddd304d8STakashi Iwai .patch = patch_vt1709}, 37223218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3723ddd304d8STakashi Iwai .patch = patch_vt1709}, 37243218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3725ddd304d8STakashi Iwai .patch = patch_vt1709}, 37263218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3727ddd304d8STakashi Iwai .patch = patch_vt1709}, 37283218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3729ddd304d8STakashi Iwai .patch = patch_vt1709}, 37303218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3731ddd304d8STakashi Iwai .patch = patch_vt1709}, 37323218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3733ddd304d8STakashi Iwai .patch = patch_vt1709}, 37343218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3735ddd304d8STakashi Iwai .patch = patch_vt1709}, 37363218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3737ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37383218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3739ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37403218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3741ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37423218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3743ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37443218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3745ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37463218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3747ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37483218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3749ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37503218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3751ddd304d8STakashi Iwai .patch = patch_vt1708B}, 37523218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3753d949cac1SHarald Welte .patch = patch_vt1708S}, 37543218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3755d949cac1SHarald Welte .patch = patch_vt1708S}, 37563218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3757d949cac1SHarald Welte .patch = patch_vt1708S}, 37583218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3759d949cac1SHarald Welte .patch = patch_vt1708S}, 3760bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 3761d949cac1SHarald Welte .patch = patch_vt1708S}, 37623218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3763d949cac1SHarald Welte .patch = patch_vt1708S}, 37643218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3765d949cac1SHarald Welte .patch = patch_vt1708S}, 37663218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3767d949cac1SHarald Welte .patch = patch_vt1708S}, 37683218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3769d949cac1SHarald Welte .patch = patch_vt1702}, 37703218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3771d949cac1SHarald Welte .patch = patch_vt1702}, 37723218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3773d949cac1SHarald Welte .patch = patch_vt1702}, 37743218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3775d949cac1SHarald Welte .patch = patch_vt1702}, 37763218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3777d949cac1SHarald Welte .patch = patch_vt1702}, 37783218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3779d949cac1SHarald Welte .patch = patch_vt1702}, 37803218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3781d949cac1SHarald Welte .patch = patch_vt1702}, 37823218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3783d949cac1SHarald Welte .patch = patch_vt1702}, 3784eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 3785eb7188caSLydia Wang .patch = patch_vt1718S}, 3786eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 3787eb7188caSLydia Wang .patch = patch_vt1718S}, 3788bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 3789bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3790bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 3791bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3792f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 3793f3db423dSLydia Wang .patch = patch_vt1716S}, 3794f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 3795f3db423dSLydia Wang .patch = patch_vt1716S}, 379625eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 379725eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 3798ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 379936dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 380036dd5c4aSLydia Wang .patch = patch_vt1708S}, 380111890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 380211890956SLydia Wang .patch = patch_vt2002P}, 380311890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 380411890956SLydia Wang .patch = patch_vt2002P}, 3805c577b8a1SJoseph Chan {} /* terminator */ 3806c577b8a1SJoseph Chan }; 38071289e9e8STakashi Iwai 38081289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 38091289e9e8STakashi Iwai 38101289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 38111289e9e8STakashi Iwai .preset = snd_hda_preset_via, 38121289e9e8STakashi Iwai .owner = THIS_MODULE, 38131289e9e8STakashi Iwai }; 38141289e9e8STakashi Iwai 38151289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 38161289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 38171289e9e8STakashi Iwai 38181289e9e8STakashi Iwai static int __init patch_via_init(void) 38191289e9e8STakashi Iwai { 38201289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 38211289e9e8STakashi Iwai } 38221289e9e8STakashi Iwai 38231289e9e8STakashi Iwai static void __exit patch_via_exit(void) 38241289e9e8STakashi Iwai { 38251289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 38261289e9e8STakashi Iwai } 38271289e9e8STakashi Iwai 38281289e9e8STakashi Iwai module_init(patch_via_init) 38291289e9e8STakashi Iwai module_exit(patch_via_exit) 3830