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 864a79616dSTakashi Iwai struct nid_path { 874a79616dSTakashi Iwai int depth; 888e3679dcSTakashi Iwai hda_nid_t path[MAX_NID_PATH_DEPTH]; 898e3679dcSTakashi Iwai short idx[MAX_NID_PATH_DEPTH]; 904a79616dSTakashi Iwai }; 914a79616dSTakashi Iwai 921f2e99feSLydia Wang struct via_spec { 931f2e99feSLydia Wang /* codec parameterization */ 9490dd48a1STakashi Iwai const struct snd_kcontrol_new *mixers[6]; 951f2e99feSLydia Wang unsigned int num_mixers; 961f2e99feSLydia Wang 9790dd48a1STakashi Iwai const struct hda_verb *init_verbs[5]; 981f2e99feSLydia Wang unsigned int num_iverbs; 991f2e99feSLydia Wang 10082673bc8STakashi Iwai char stream_name_analog[32]; 1017eb56e84STakashi Iwai char stream_name_hp[32]; 10290dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_playback; 10390dd48a1STakashi Iwai const struct hda_pcm_stream *stream_analog_capture; 1041f2e99feSLydia Wang 10582673bc8STakashi Iwai char stream_name_digital[32]; 10690dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_playback; 10790dd48a1STakashi Iwai const struct hda_pcm_stream *stream_digital_capture; 1081f2e99feSLydia Wang 1091f2e99feSLydia Wang /* playback */ 1101f2e99feSLydia Wang struct hda_multi_out multiout; 1111f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 112ece8d043STakashi Iwai hda_nid_t hp_dac_nid; 113ada509ecSTakashi Iwai int num_active_streams; 1141f2e99feSLydia Wang 1154a79616dSTakashi Iwai struct nid_path out_path[4]; 1164a79616dSTakashi Iwai struct nid_path hp_path; 1174a79616dSTakashi Iwai struct nid_path hp_dep_path; 1184a918ffeSTakashi Iwai struct nid_path speaker_path; 1194a79616dSTakashi Iwai 1201f2e99feSLydia Wang /* capture */ 1211f2e99feSLydia Wang unsigned int num_adc_nids; 122a766d0d7STakashi Iwai hda_nid_t adc_nids[3]; 1231f2e99feSLydia Wang hda_nid_t mux_nids[3]; 124620e2b28STakashi Iwai hda_nid_t aa_mix_nid; 1251f2e99feSLydia Wang hda_nid_t dig_in_nid; 1261f2e99feSLydia Wang 1271f2e99feSLydia Wang /* capture source */ 1281f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1291f2e99feSLydia Wang unsigned int cur_mux[3]; 1301f2e99feSLydia Wang 1311f2e99feSLydia Wang /* PCM information */ 1321f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1331f2e99feSLydia Wang 1341f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1351f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1361f2e99feSLydia Wang struct snd_array kctls; 1371f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1381f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1391f2e99feSLydia Wang 1401f2e99feSLydia Wang /* HP mode source */ 1411f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1421f2e99feSLydia Wang unsigned int hp_independent_mode; 1431f2e99feSLydia Wang unsigned int hp_independent_mode_index; 144f3db423dSLydia Wang unsigned int dmic_enabled; 14524088a58STakashi Iwai unsigned int no_pin_power_ctl; 1461f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1471f2e99feSLydia Wang 148e3d7a143STakashi Iwai /* smart51 setup */ 149e3d7a143STakashi Iwai unsigned int smart51_nums; 150e3d7a143STakashi Iwai hda_nid_t smart51_pins[2]; 151e3d7a143STakashi Iwai int smart51_idxs[2]; 152e3d7a143STakashi Iwai const char *smart51_labels[2]; 153e3d7a143STakashi Iwai unsigned int smart51_enabled; 154e3d7a143STakashi Iwai 1551f2e99feSLydia Wang /* work to check hp jack state */ 1561f2e99feSLydia Wang struct hda_codec *codec; 1571f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 158e06e5a29STakashi Iwai int vt1708_jack_detect; 1591f2e99feSLydia Wang int vt1708_hp_present; 1603e95b9abSLydia Wang 1613e95b9abSLydia Wang void (*set_widgets_power_state)(struct hda_codec *codec); 1623e95b9abSLydia Wang 1631f2e99feSLydia Wang struct hda_loopback_check loopback; 16413af8e77STakashi Iwai int num_loopbacks; 16513af8e77STakashi Iwai struct hda_amp_list loopback_list[8]; 1661f2e99feSLydia Wang }; 1671f2e99feSLydia Wang 1680341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 1695b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1705b0cb1d8SJaroslav Kysela { 1715b0cb1d8SJaroslav Kysela struct via_spec *spec; 1725b0cb1d8SJaroslav Kysela 1735b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1745b0cb1d8SJaroslav Kysela if (spec == NULL) 1755b0cb1d8SJaroslav Kysela return NULL; 1765b0cb1d8SJaroslav Kysela 1775b0cb1d8SJaroslav Kysela codec->spec = spec; 1785b0cb1d8SJaroslav Kysela spec->codec = codec; 1790341ccd7SLydia Wang spec->codec_type = get_codec_type(codec); 1800341ccd7SLydia Wang /* VT1708BCE & VT1708S are almost same */ 1810341ccd7SLydia Wang if (spec->codec_type == VT1708BCE) 1820341ccd7SLydia Wang spec->codec_type = VT1708S; 1835b0cb1d8SJaroslav Kysela return spec; 1845b0cb1d8SJaroslav Kysela } 1855b0cb1d8SJaroslav Kysela 186744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 187d7426329SHarald Welte { 188744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 189d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 190d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 191d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 192d7426329SHarald Welte 193d7426329SHarald Welte /* get codec type */ 194d7426329SHarald Welte if (ven_id != 0x1106) 195d7426329SHarald Welte codec_type = UNKNOWN; 196d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 197d7426329SHarald Welte codec_type = VT1708; 198d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 199d7426329SHarald Welte codec_type = VT1709_10CH; 200d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 201d7426329SHarald Welte codec_type = VT1709_6CH; 202518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 203d7426329SHarald Welte codec_type = VT1708B_8CH; 204518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 205518bf3baSLydia Wang codec_type = VT1708BCE; 206518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 207d7426329SHarald Welte codec_type = VT1708B_4CH; 208d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 209d7426329SHarald Welte && (dev_id >> 12) < 8) 210d7426329SHarald Welte codec_type = VT1708S; 211d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 212d7426329SHarald Welte && (dev_id >> 12) < 8) 213d7426329SHarald Welte codec_type = VT1702; 214eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 215eb7188caSLydia Wang && (dev_id >> 12) < 8) 216eb7188caSLydia Wang codec_type = VT1718S; 217f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 218f3db423dSLydia Wang codec_type = VT1716S; 219bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 220bb3c6bfcSLydia Wang codec_type = VT1718S; 22125eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 22225eaba2fSLydia Wang codec_type = VT2002P; 223ab6734e7SLydia Wang else if (dev_id == 0x0448) 224ab6734e7SLydia Wang codec_type = VT1812; 22536dd5c4aSLydia Wang else if (dev_id == 0x0440) 22636dd5c4aSLydia Wang codec_type = VT1708S; 22711890956SLydia Wang else if ((dev_id & 0xfff) == 0x446) 22811890956SLydia Wang codec_type = VT1802; 229d7426329SHarald Welte else 230d7426329SHarald Welte codec_type = UNKNOWN; 231d7426329SHarald Welte return codec_type; 232d7426329SHarald Welte }; 233d7426329SHarald Welte 234ec7e7e42SLydia Wang #define VIA_JACK_EVENT 0x20 23569e52a80SHarald Welte #define VIA_HP_EVENT 0x01 23669e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 2374a918ffeSTakashi Iwai #define VIA_LINE_EVENT 0x03 23869e52a80SHarald Welte 239c577b8a1SJoseph Chan enum { 240c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 241c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 242f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 243c577b8a1SJoseph Chan }; 244c577b8a1SJoseph Chan 245ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec); 246ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec); 2471f2e99feSLydia Wang 2481f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2491f2e99feSLydia Wang { 2501f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2511f2e99feSLydia Wang return; 2521f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 253e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2541f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2551f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2561f2e99feSLydia Wang msecs_to_jiffies(100)); 2571f2e99feSLydia Wang } 2581f2e99feSLydia Wang 2591f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2601f2e99feSLydia Wang { 2611f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2621f2e99feSLydia Wang return; 2631f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2641f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2651f2e99feSLydia Wang return; 2661f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 267e06e5a29STakashi Iwai !spec->vt1708_jack_detect); 2685b84ba26STejun Heo cancel_delayed_work_sync(&spec->vt1708_hp_work); 2691f2e99feSLydia Wang } 270f5271101SLydia Wang 2713e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec) 2723e95b9abSLydia Wang { 2733e95b9abSLydia Wang struct via_spec *spec = codec->spec; 2743e95b9abSLydia Wang if (spec->set_widgets_power_state) 2753e95b9abSLydia Wang spec->set_widgets_power_state(codec); 2763e95b9abSLydia Wang } 27725eaba2fSLydia Wang 278f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 279f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 280f5271101SLydia Wang { 281f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 282f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 283f5271101SLydia Wang 2843e95b9abSLydia Wang set_widgets_power_state(codec); 285ada509ecSTakashi Iwai analog_low_current_mode(snd_kcontrol_chip(kcontrol)); 2861f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2871f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2881f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2891f2e99feSLydia Wang else 2901f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2911f2e99feSLydia Wang } 292f5271101SLydia Wang return change; 293f5271101SLydia Wang } 294f5271101SLydia Wang 295f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 296f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 297f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 298f5271101SLydia Wang .name = NULL, \ 299f5271101SLydia Wang .index = 0, \ 300f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 301f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 302f5271101SLydia Wang .put = analog_input_switch_put, \ 303f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 304f5271101SLydia Wang 30590dd48a1STakashi Iwai static const struct snd_kcontrol_new via_control_templates[] = { 306c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 307c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 308f5271101SLydia Wang ANALOG_INPUT_MUTE, 309c577b8a1SJoseph Chan }; 310c577b8a1SJoseph Chan 311ab6734e7SLydia Wang 312c577b8a1SJoseph Chan /* add dynamic controls */ 313291c9e33STakashi Iwai static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec, 314291c9e33STakashi Iwai const struct snd_kcontrol_new *tmpl, 315291c9e33STakashi Iwai const char *name) 316c577b8a1SJoseph Chan { 317c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 318c577b8a1SJoseph Chan 319603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 320603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 321c577b8a1SJoseph Chan if (!knew) 322291c9e33STakashi Iwai return NULL; 323291c9e33STakashi Iwai *knew = *tmpl; 324291c9e33STakashi Iwai if (!name) 325291c9e33STakashi Iwai name = tmpl->name; 326291c9e33STakashi Iwai if (name) { 327c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 328c577b8a1SJoseph Chan if (!knew->name) 329291c9e33STakashi Iwai return NULL; 330291c9e33STakashi Iwai } 331291c9e33STakashi Iwai return knew; 332291c9e33STakashi Iwai } 333291c9e33STakashi Iwai 334291c9e33STakashi Iwai static int __via_add_control(struct via_spec *spec, int type, const char *name, 335291c9e33STakashi Iwai int idx, unsigned long val) 336291c9e33STakashi Iwai { 337291c9e33STakashi Iwai struct snd_kcontrol_new *knew; 338291c9e33STakashi Iwai 339291c9e33STakashi Iwai knew = __via_clone_ctl(spec, &via_control_templates[type], name); 340291c9e33STakashi Iwai if (!knew) 341c577b8a1SJoseph Chan return -ENOMEM; 342d7a99cceSTakashi Iwai knew->index = idx; 3434d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 3445e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 345c577b8a1SJoseph Chan knew->private_value = val; 346c577b8a1SJoseph Chan return 0; 347c577b8a1SJoseph Chan } 348c577b8a1SJoseph Chan 3497b315bb4STakashi Iwai #define via_add_control(spec, type, name, val) \ 3507b315bb4STakashi Iwai __via_add_control(spec, type, name, 0, val) 3517b315bb4STakashi Iwai 352291c9e33STakashi Iwai #define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL) 3535b0cb1d8SJaroslav Kysela 354603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 355603c4019STakashi Iwai { 356603c4019STakashi Iwai struct via_spec *spec = codec->spec; 357603c4019STakashi Iwai 358603c4019STakashi Iwai if (spec->kctls.list) { 359603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 360603c4019STakashi Iwai int i; 361603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 362603c4019STakashi Iwai kfree(kctl[i].name); 363603c4019STakashi Iwai } 364603c4019STakashi Iwai snd_array_free(&spec->kctls); 365603c4019STakashi Iwai } 366603c4019STakashi Iwai 367c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 3689510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 3697b315bb4STakashi Iwai int type_idx, int idx, int mix_nid) 370c577b8a1SJoseph Chan { 371c577b8a1SJoseph Chan char name[32]; 372c577b8a1SJoseph Chan int err; 373c577b8a1SJoseph Chan 374c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 3757b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx, 376c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 377c577b8a1SJoseph Chan if (err < 0) 378c577b8a1SJoseph Chan return err; 379c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 3807b315bb4STakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx, 381c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 382c577b8a1SJoseph Chan if (err < 0) 383c577b8a1SJoseph Chan return err; 384c577b8a1SJoseph Chan return 0; 385c577b8a1SJoseph Chan } 386c577b8a1SJoseph Chan 3875d41762aSTakashi Iwai /* return the index of the given widget nid as the source of mux; 3885d41762aSTakashi Iwai * return -1 if not found; 3895d41762aSTakashi Iwai * if num_conns is non-NULL, set the total number of connections 3905d41762aSTakashi Iwai */ 3915d41762aSTakashi Iwai static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux, 3925d41762aSTakashi Iwai hda_nid_t nid, int *num_conns) 393c577b8a1SJoseph Chan { 3945d41762aSTakashi Iwai hda_nid_t conn[HDA_MAX_NUM_INPUTS]; 3955d41762aSTakashi Iwai int i, nums; 3965d41762aSTakashi Iwai 3975d41762aSTakashi Iwai nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn)); 3985d41762aSTakashi Iwai if (num_conns) 3995d41762aSTakashi Iwai *num_conns = nums; 4005d41762aSTakashi Iwai for (i = 0; i < nums; i++) 4015d41762aSTakashi Iwai if (conn[i] == nid) 4025d41762aSTakashi Iwai return i; 4035d41762aSTakashi Iwai return -1; 4045d41762aSTakashi Iwai } 4055d41762aSTakashi Iwai 4065d41762aSTakashi Iwai #define get_connection_index(codec, mux, nid) \ 4075d41762aSTakashi Iwai __get_connection_index(codec, mux, nid, NULL) 4085d41762aSTakashi Iwai 4095d41762aSTakashi Iwai /* unmute input amp and select the specificed source */ 4105d41762aSTakashi Iwai static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid, 4115d41762aSTakashi Iwai hda_nid_t src, hda_nid_t mix) 4125d41762aSTakashi Iwai { 4135d41762aSTakashi Iwai int idx, num_conns; 4145d41762aSTakashi Iwai 4155d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, src, &num_conns); 4165d41762aSTakashi Iwai if (idx < 0) 4175d41762aSTakashi Iwai return; 4185d41762aSTakashi Iwai 4195d41762aSTakashi Iwai /* select the route explicitly when multiple connections exist */ 4208e3679dcSTakashi Iwai if (num_conns > 1 && 4218e3679dcSTakashi Iwai get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX) 422d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 4235d41762aSTakashi Iwai AC_VERB_SET_CONNECT_SEL, idx); 4248e3679dcSTakashi Iwai 4255d41762aSTakashi Iwai /* unmute if the input amp is present */ 4268e3679dcSTakashi Iwai if (query_amp_caps(codec, nid, HDA_INPUT) & 4278e3679dcSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) 4285d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4295d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4305d41762aSTakashi Iwai 4318e3679dcSTakashi Iwai /* unmute the src output */ 4328e3679dcSTakashi Iwai if (query_amp_caps(codec, src, HDA_OUTPUT) & 4338e3679dcSTakashi Iwai (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)) 4348e3679dcSTakashi Iwai snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4358e3679dcSTakashi Iwai AMP_OUT_UNMUTE); 4368e3679dcSTakashi Iwai 4375d41762aSTakashi Iwai /* unmute AA-path if present */ 4385d41762aSTakashi Iwai if (!mix) 4395d41762aSTakashi Iwai return; 4405d41762aSTakashi Iwai idx = __get_connection_index(codec, nid, mix, NULL); 4415d41762aSTakashi Iwai if (idx >= 0) 4425d41762aSTakashi Iwai snd_hda_codec_write(codec, nid, 0, 4435d41762aSTakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 4445d41762aSTakashi Iwai AMP_IN_UNMUTE(idx)); 4455d41762aSTakashi Iwai } 4465d41762aSTakashi Iwai 4475d41762aSTakashi Iwai /* set the given pin as output */ 4485d41762aSTakashi Iwai static void init_output_pin(struct hda_codec *codec, hda_nid_t pin, 4495d41762aSTakashi Iwai int pin_type) 4505d41762aSTakashi Iwai { 4515d41762aSTakashi Iwai if (!pin) 4525d41762aSTakashi Iwai return; 4535d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 4545d41762aSTakashi Iwai pin_type); 4555d41762aSTakashi Iwai if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD) 4565d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, 457d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 458c577b8a1SJoseph Chan } 459c577b8a1SJoseph Chan 4605d41762aSTakashi Iwai static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin, 4615d41762aSTakashi Iwai int pin_type, struct nid_path *path) 4625d41762aSTakashi Iwai { 4635d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 4645d41762aSTakashi Iwai unsigned int caps; 4655d41762aSTakashi Iwai hda_nid_t nid; 4665d41762aSTakashi Iwai int i; 4675d41762aSTakashi Iwai 4685d41762aSTakashi Iwai if (!pin) 4695d41762aSTakashi Iwai return; 4705d41762aSTakashi Iwai 4715d41762aSTakashi Iwai init_output_pin(codec, pin, pin_type); 4725d41762aSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_OUTPUT); 4735d41762aSTakashi Iwai if (caps & AC_AMPCAP_MUTE) { 4745d41762aSTakashi Iwai unsigned int val; 4755d41762aSTakashi Iwai val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT; 4765d41762aSTakashi Iwai snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE, 4775d41762aSTakashi Iwai AMP_OUT_MUTE | val); 4785d41762aSTakashi Iwai } 4795d41762aSTakashi Iwai 4805d41762aSTakashi Iwai /* initialize the output path */ 4818e3679dcSTakashi Iwai for (i = path->depth - 1; i > 0; i--) { 4828e3679dcSTakashi Iwai nid = path->path[i - 1]; 4838e3679dcSTakashi Iwai unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid); 4845d41762aSTakashi Iwai } 4855d41762aSTakashi Iwai } 4865d41762aSTakashi Iwai 487c577b8a1SJoseph Chan 488c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 489c577b8a1SJoseph Chan { 490c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 491c577b8a1SJoseph Chan int i; 492c577b8a1SJoseph Chan 493e3d7a143STakashi Iwai for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) 4945d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.line_out_pins[i], 4955d41762aSTakashi Iwai PIN_OUT, &spec->out_path[i]); 496c577b8a1SJoseph Chan } 497c577b8a1SJoseph Chan 498c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 499c577b8a1SJoseph Chan { 500c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 501c577b8a1SJoseph Chan 5025d41762aSTakashi Iwai if (spec->hp_dac_nid) 5035d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5045d41762aSTakashi Iwai &spec->hp_path); 5055d41762aSTakashi Iwai else 5065d41762aSTakashi Iwai via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP, 5075d41762aSTakashi Iwai &spec->hp_dep_path); 50825eaba2fSLydia Wang } 509c577b8a1SJoseph Chan 5104a918ffeSTakashi Iwai static void via_auto_init_speaker_out(struct hda_codec *codec) 5114a918ffeSTakashi Iwai { 5124a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 5134a918ffeSTakashi Iwai 5144a918ffeSTakashi Iwai if (spec->autocfg.speaker_outs) 5154a918ffeSTakashi Iwai via_auto_init_output(codec, spec->autocfg.speaker_pins[0], 5164a918ffeSTakashi Iwai PIN_OUT, &spec->speaker_path); 5174a918ffeSTakashi Iwai } 5184a918ffeSTakashi Iwai 519f4a7828bSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin); 52032e0191dSClemens Ladisch 521c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 522c577b8a1SJoseph Chan { 523c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 5247b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 525096a8854STakashi Iwai hda_nid_t conn[HDA_MAX_CONNECTIONS]; 52632e0191dSClemens Ladisch unsigned int ctl; 527096a8854STakashi Iwai int i, num_conns; 528c577b8a1SJoseph Chan 529096a8854STakashi Iwai /* init ADCs */ 530096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 531096a8854STakashi Iwai snd_hda_codec_write(codec, spec->adc_nids[i], 0, 532096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 533096a8854STakashi Iwai AMP_IN_UNMUTE(0)); 534096a8854STakashi Iwai } 535096a8854STakashi Iwai 536096a8854STakashi Iwai /* init pins */ 5377b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 5387b315bb4STakashi Iwai hda_nid_t nid = cfg->inputs[i].pin; 539f4a7828bSTakashi Iwai if (spec->smart51_enabled && is_smart51_pins(codec, nid)) 54032e0191dSClemens Ladisch ctl = PIN_OUT; 54130649676SDavid Henningsson else if (cfg->inputs[i].type == AUTO_PIN_MIC) 54232e0191dSClemens Ladisch ctl = PIN_VREF50; 54332e0191dSClemens Ladisch else 54432e0191dSClemens Ladisch ctl = PIN_IN; 545c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 54632e0191dSClemens Ladisch AC_VERB_SET_PIN_WIDGET_CONTROL, ctl); 547c577b8a1SJoseph Chan } 548096a8854STakashi Iwai 549096a8854STakashi Iwai /* init input-src */ 550096a8854STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 551096a8854STakashi Iwai const struct hda_input_mux *imux = spec->input_mux; 552096a8854STakashi Iwai if (!imux || !spec->mux_nids[i]) 553096a8854STakashi Iwai continue; 554096a8854STakashi Iwai snd_hda_codec_write(codec, spec->mux_nids[i], 0, 555096a8854STakashi Iwai AC_VERB_SET_CONNECT_SEL, 556096a8854STakashi Iwai imux->items[spec->cur_mux[i]].index); 557096a8854STakashi Iwai } 558096a8854STakashi Iwai 559096a8854STakashi Iwai /* init aa-mixer */ 560096a8854STakashi Iwai if (!spec->aa_mix_nid) 561096a8854STakashi Iwai return; 562096a8854STakashi Iwai num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn, 563096a8854STakashi Iwai ARRAY_SIZE(conn)); 564096a8854STakashi Iwai for (i = 0; i < num_conns; i++) { 565096a8854STakashi Iwai unsigned int caps = get_wcaps(codec, conn[i]); 566096a8854STakashi Iwai if (get_wcaps_type(caps) == AC_WID_PIN) 567096a8854STakashi Iwai snd_hda_codec_write(codec, spec->aa_mix_nid, 0, 568096a8854STakashi Iwai AC_VERB_SET_AMP_GAIN_MUTE, 569096a8854STakashi Iwai AMP_IN_MUTE(i)); 570096a8854STakashi Iwai } 571c577b8a1SJoseph Chan } 572f5271101SLydia Wang 573f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid, 574f5271101SLydia Wang unsigned int *affected_parm) 575f5271101SLydia Wang { 576f5271101SLydia Wang unsigned parm; 577f5271101SLydia Wang unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid); 578f5271101SLydia Wang unsigned no_presence = (def_conf & AC_DEFCFG_MISC) 579f5271101SLydia Wang >> AC_DEFCFG_MISC_SHIFT 580f5271101SLydia Wang & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */ 5811564b287SLydia Wang struct via_spec *spec = codec->spec; 58224088a58STakashi Iwai unsigned present = 0; 58324088a58STakashi Iwai 58424088a58STakashi Iwai no_presence |= spec->no_pin_power_ctl; 58524088a58STakashi Iwai if (!no_presence) 58624088a58STakashi Iwai present = snd_hda_jack_detect(codec, nid); 587f4a7828bSTakashi Iwai if ((spec->smart51_enabled && is_smart51_pins(codec, nid)) 5881564b287SLydia Wang || ((no_presence || present) 5891564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 590f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 591f5271101SLydia Wang parm = AC_PWRST_D0; 592f5271101SLydia Wang } else 593f5271101SLydia Wang parm = AC_PWRST_D3; 594f5271101SLydia Wang 595f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 596f5271101SLydia Wang } 597f5271101SLydia Wang 59824088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol, 59924088a58STakashi Iwai struct snd_ctl_elem_info *uinfo) 60024088a58STakashi Iwai { 60124088a58STakashi Iwai static const char * const texts[] = { 60224088a58STakashi Iwai "Disabled", "Enabled" 60324088a58STakashi Iwai }; 60424088a58STakashi Iwai 60524088a58STakashi Iwai uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; 60624088a58STakashi Iwai uinfo->count = 1; 60724088a58STakashi Iwai uinfo->value.enumerated.items = 2; 60824088a58STakashi Iwai if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) 60924088a58STakashi Iwai uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1; 61024088a58STakashi Iwai strcpy(uinfo->value.enumerated.name, 61124088a58STakashi Iwai texts[uinfo->value.enumerated.item]); 61224088a58STakashi Iwai return 0; 61324088a58STakashi Iwai } 61424088a58STakashi Iwai 61524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol, 61624088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 61724088a58STakashi Iwai { 61824088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 61924088a58STakashi Iwai struct via_spec *spec = codec->spec; 62024088a58STakashi Iwai ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl; 62124088a58STakashi Iwai return 0; 62224088a58STakashi Iwai } 62324088a58STakashi Iwai 62424088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol, 62524088a58STakashi Iwai struct snd_ctl_elem_value *ucontrol) 62624088a58STakashi Iwai { 62724088a58STakashi Iwai struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 62824088a58STakashi Iwai struct via_spec *spec = codec->spec; 62924088a58STakashi Iwai unsigned int val = !ucontrol->value.enumerated.item[0]; 63024088a58STakashi Iwai 63124088a58STakashi Iwai if (val == spec->no_pin_power_ctl) 63224088a58STakashi Iwai return 0; 63324088a58STakashi Iwai spec->no_pin_power_ctl = val; 63424088a58STakashi Iwai set_widgets_power_state(codec); 63524088a58STakashi Iwai return 1; 63624088a58STakashi Iwai } 63724088a58STakashi Iwai 63824088a58STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = { 63924088a58STakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 64024088a58STakashi Iwai .name = "Dynamic Power-Control", 64124088a58STakashi Iwai .info = via_pin_power_ctl_info, 64224088a58STakashi Iwai .get = via_pin_power_ctl_get, 64324088a58STakashi Iwai .put = via_pin_power_ctl_put, 64424088a58STakashi Iwai }; 64524088a58STakashi Iwai 64624088a58STakashi Iwai 647c577b8a1SJoseph Chan /* 648c577b8a1SJoseph Chan * input MUX handling 649c577b8a1SJoseph Chan */ 650c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 651c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 652c577b8a1SJoseph Chan { 653c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 654c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 655c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 656c577b8a1SJoseph Chan } 657c577b8a1SJoseph Chan 658c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 659c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 660c577b8a1SJoseph Chan { 661c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 662c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 663c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 664c577b8a1SJoseph Chan 665c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 666c577b8a1SJoseph Chan return 0; 667c577b8a1SJoseph Chan } 668c577b8a1SJoseph Chan 669c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 670c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 671c577b8a1SJoseph Chan { 672c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 673c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 674c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 675bff5fbf5SLydia Wang int ret; 676c577b8a1SJoseph Chan 677337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 678337b9d02STakashi Iwai return -EINVAL; 679a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 680a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 681a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 682a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 683a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 684bff5fbf5SLydia Wang 685bff5fbf5SLydia Wang ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 686bff5fbf5SLydia Wang spec->mux_nids[adc_idx], 687bff5fbf5SLydia Wang &spec->cur_mux[adc_idx]); 688a80e6e3cSLydia Wang /* update jack power state */ 6893e95b9abSLydia Wang set_widgets_power_state(codec); 690a80e6e3cSLydia Wang 691bff5fbf5SLydia Wang return ret; 692c577b8a1SJoseph Chan } 693c577b8a1SJoseph Chan 6940aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 6950aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 6960aa62aefSHarald Welte { 6970aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 6980aa62aefSHarald Welte struct via_spec *spec = codec->spec; 6990aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 7000aa62aefSHarald Welte } 7010aa62aefSHarald Welte 7020aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 7030aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7040aa62aefSHarald Welte { 7050aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 706cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 707cdc1784dSLydia Wang 708ece8d043STakashi Iwai ucontrol->value.enumerated.item[0] = spec->hp_independent_mode; 709cdc1784dSLydia Wang return 0; 710cdc1784dSLydia Wang } 711cdc1784dSLydia Wang 7120aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 7130aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 7140aa62aefSHarald Welte { 7150aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 7160aa62aefSHarald Welte struct via_spec *spec = codec->spec; 7175b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 7180aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 719cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 720cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 721cdc1784dSLydia Wang ? 1 : 0; 722ece8d043STakashi Iwai snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 7230aa62aefSHarald Welte 724ce0e5a9eSLydia Wang /* update jack power state */ 7253e95b9abSLydia Wang set_widgets_power_state(codec); 7260aa62aefSHarald Welte return 0; 7270aa62aefSHarald Welte } 7280aa62aefSHarald Welte 729ece8d043STakashi Iwai static const struct snd_kcontrol_new via_hp_mixer = { 7300aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7310aa62aefSHarald Welte .name = "Independent HP", 7320aa62aefSHarald Welte .info = via_independent_hp_info, 7330aa62aefSHarald Welte .get = via_independent_hp_get, 7340aa62aefSHarald Welte .put = via_independent_hp_put, 7350aa62aefSHarald Welte }; 7360aa62aefSHarald Welte 7373d83e577STakashi Iwai static int via_hp_build(struct hda_codec *codec) 7385b0cb1d8SJaroslav Kysela { 7393d83e577STakashi Iwai struct via_spec *spec = codec->spec; 7405b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 7415b0cb1d8SJaroslav Kysela hda_nid_t nid; 7425b0cb1d8SJaroslav Kysela 7435b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 744ece8d043STakashi Iwai knew = via_clone_control(spec, &via_hp_mixer); 7453d83e577STakashi Iwai if (knew == NULL) 7463d83e577STakashi Iwai return -ENOMEM; 7473d83e577STakashi Iwai 7485b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 7495b0cb1d8SJaroslav Kysela knew->private_value = nid; 7505b0cb1d8SJaroslav Kysela 7515b0cb1d8SJaroslav Kysela return 0; 7525b0cb1d8SJaroslav Kysela } 7535b0cb1d8SJaroslav Kysela 7541564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 7551564b287SLydia Wang { 756e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 7571564b287SLydia Wang int i; 7581564b287SLydia Wang 759e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 760e3d7a143STakashi Iwai struct snd_kcontrol *ctl; 761e3d7a143STakashi Iwai struct snd_ctl_elem_id id; 7621564b287SLydia Wang memset(&id, 0, sizeof(id)); 7631564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 764e3d7a143STakashi Iwai sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]); 765525566cbSLydia Wang ctl = snd_hda_find_mixer_ctl(codec, id.name); 766525566cbSLydia Wang if (ctl) 767525566cbSLydia Wang snd_ctl_notify(codec->bus->card, 768525566cbSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, 769525566cbSLydia Wang &ctl->id); 7701564b287SLydia Wang } 7711564b287SLydia Wang } 7721564b287SLydia Wang 7731564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 7741564b287SLydia Wang { 7751564b287SLydia Wang struct via_spec *spec = codec->spec; 7761564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 777e3d7a143STakashi Iwai int i; 778e3d7a143STakashi Iwai 779e3d7a143STakashi Iwai /* check AA path's mute status */ 780e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 781e3d7a143STakashi Iwai if (spec->smart51_idxs[i] < 0) 782e3d7a143STakashi Iwai continue; 783e3d7a143STakashi Iwai snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, 784e3d7a143STakashi Iwai HDA_INPUT, spec->smart51_idxs[i], 7851564b287SLydia Wang HDA_AMP_MUTE, val); 7861564b287SLydia Wang } 7871564b287SLydia Wang } 788f4a7828bSTakashi Iwai 789e3d7a143STakashi Iwai static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin) 7901564b287SLydia Wang { 791f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 7927b315bb4STakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 7937b315bb4STakashi Iwai int i; 7947b315bb4STakashi Iwai 7957b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 796f4a7828bSTakashi Iwai unsigned int defcfg; 797f4a7828bSTakashi Iwai if (pin != cfg->inputs[i].pin) 798f4a7828bSTakashi Iwai continue; 799f4a7828bSTakashi Iwai if (cfg->inputs[i].type > AUTO_PIN_LINE_IN) 800f4a7828bSTakashi Iwai return false; 801f4a7828bSTakashi Iwai defcfg = snd_hda_codec_get_pincfg(codec, pin); 802f4a7828bSTakashi Iwai if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL) 803f4a7828bSTakashi Iwai return false; 804f4a7828bSTakashi Iwai return true; 8051564b287SLydia Wang } 806f4a7828bSTakashi Iwai return false; 8071564b287SLydia Wang } 8081564b287SLydia Wang 809e3d7a143STakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin) 810e3d7a143STakashi Iwai { 811e3d7a143STakashi Iwai struct via_spec *spec = codec->spec; 812e3d7a143STakashi Iwai int i; 813e3d7a143STakashi Iwai 814e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) 815e3d7a143STakashi Iwai if (spec->smart51_pins[i] == pin) 816e3d7a143STakashi Iwai return true; 817e3d7a143STakashi Iwai return false; 818e3d7a143STakashi Iwai } 819e3d7a143STakashi Iwai 8201564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 8211564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 8221564b287SLydia Wang { 8231564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 8241564b287SLydia Wang uinfo->count = 1; 8251564b287SLydia Wang uinfo->value.integer.min = 0; 8261564b287SLydia Wang uinfo->value.integer.max = 1; 8271564b287SLydia Wang return 0; 8281564b287SLydia Wang } 8291564b287SLydia Wang 8301564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 8311564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8321564b287SLydia Wang { 8331564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8341564b287SLydia Wang struct via_spec *spec = codec->spec; 8351564b287SLydia Wang int on = 1; 8361564b287SLydia Wang int i; 8371564b287SLydia Wang 838e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 839e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 840f4a7828bSTakashi Iwai unsigned int ctl; 841f4a7828bSTakashi Iwai ctl = snd_hda_codec_read(codec, nid, 0, 842f4a7828bSTakashi Iwai AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8437b315bb4STakashi Iwai if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN)) 8441564b287SLydia Wang on = 0; 8451564b287SLydia Wang } 8461564b287SLydia Wang *ucontrol->value.integer.value = on; 8471564b287SLydia Wang return 0; 8481564b287SLydia Wang } 8491564b287SLydia Wang 8501564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 8511564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 8521564b287SLydia Wang { 8531564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 8541564b287SLydia Wang struct via_spec *spec = codec->spec; 8551564b287SLydia Wang int out_in = *ucontrol->value.integer.value 8561564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 8571564b287SLydia Wang int i; 8581564b287SLydia Wang 859e3d7a143STakashi Iwai for (i = 0; i < spec->smart51_nums; i++) { 860e3d7a143STakashi Iwai hda_nid_t nid = spec->smart51_pins[i]; 8617b315bb4STakashi Iwai unsigned int parm; 8627b315bb4STakashi Iwai 8637b315bb4STakashi Iwai parm = snd_hda_codec_read(codec, nid, 0, 8641564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 8651564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 8661564b287SLydia Wang parm |= out_in; 8671564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 8681564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 8691564b287SLydia Wang parm); 8701564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 8711564b287SLydia Wang mute_aa_path(codec, 1); 8721564b287SLydia Wang notify_aa_path_ctls(codec); 8731564b287SLydia Wang } 8741564b287SLydia Wang } 8751564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 8763e95b9abSLydia Wang set_widgets_power_state(codec); 8771564b287SLydia Wang return 1; 8781564b287SLydia Wang } 8791564b287SLydia Wang 8805f4b36d6STakashi Iwai static const struct snd_kcontrol_new via_smart51_mixer = { 8811564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 8821564b287SLydia Wang .name = "Smart 5.1", 8831564b287SLydia Wang .count = 1, 8841564b287SLydia Wang .info = via_smart51_info, 8851564b287SLydia Wang .get = via_smart51_get, 8861564b287SLydia Wang .put = via_smart51_put, 8871564b287SLydia Wang }; 8881564b287SLydia Wang 889f4a7828bSTakashi Iwai static int via_smart51_build(struct hda_codec *codec) 8905b0cb1d8SJaroslav Kysela { 891f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 8925b0cb1d8SJaroslav Kysela 893e3d7a143STakashi Iwai if (!spec->smart51_nums) 894cb34c207SLydia Wang return 0; 895e3d7a143STakashi Iwai if (!via_clone_control(spec, &via_smart51_mixer)) 8965b0cb1d8SJaroslav Kysela return -ENOMEM; 8975b0cb1d8SJaroslav Kysela return 0; 8985b0cb1d8SJaroslav Kysela } 8995b0cb1d8SJaroslav Kysela 900f5271101SLydia Wang /* check AA path's mute status */ 901ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec) 902ada509ecSTakashi Iwai { 903ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 904ada509ecSTakashi Iwai const struct hda_amp_list *p; 905ada509ecSTakashi Iwai int i, ch, v; 906ada509ecSTakashi Iwai 907ada509ecSTakashi Iwai for (i = 0; i < spec->num_loopbacks; i++) { 908ada509ecSTakashi Iwai p = &spec->loopback_list[i]; 909ada509ecSTakashi Iwai for (ch = 0; ch < 2; ch++) { 910ada509ecSTakashi Iwai v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir, 911ada509ecSTakashi Iwai p->idx); 912ada509ecSTakashi Iwai if (!(v & HDA_AMP_MUTE) && v > 0) 913ada509ecSTakashi Iwai return false; 914f5271101SLydia Wang } 915f5271101SLydia Wang } 916ada509ecSTakashi Iwai return true; 917f5271101SLydia Wang } 918f5271101SLydia Wang 919f5271101SLydia Wang /* enter/exit analog low-current mode */ 920ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec) 921f5271101SLydia Wang { 922f5271101SLydia Wang struct via_spec *spec = codec->spec; 923ada509ecSTakashi Iwai bool enable; 924ada509ecSTakashi Iwai unsigned int verb, parm; 925f5271101SLydia Wang 926ada509ecSTakashi Iwai enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); 927f5271101SLydia Wang 928f5271101SLydia Wang /* decide low current mode's verb & parameter */ 929f5271101SLydia Wang switch (spec->codec_type) { 930f5271101SLydia Wang case VT1708B_8CH: 931f5271101SLydia Wang case VT1708B_4CH: 932f5271101SLydia Wang verb = 0xf70; 933f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 934f5271101SLydia Wang break; 935f5271101SLydia Wang case VT1708S: 936eb7188caSLydia Wang case VT1718S: 937f3db423dSLydia Wang case VT1716S: 938f5271101SLydia Wang verb = 0xf73; 939f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 940f5271101SLydia Wang break; 941f5271101SLydia Wang case VT1702: 942f5271101SLydia Wang verb = 0xf73; 943f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 944f5271101SLydia Wang break; 94525eaba2fSLydia Wang case VT2002P: 946ab6734e7SLydia Wang case VT1812: 94711890956SLydia Wang case VT1802: 94825eaba2fSLydia Wang verb = 0xf93; 94925eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 95025eaba2fSLydia Wang break; 951f5271101SLydia Wang default: 952f5271101SLydia Wang return; /* other codecs are not supported */ 953f5271101SLydia Wang } 954f5271101SLydia Wang /* send verb */ 955f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 956f5271101SLydia Wang } 957f5271101SLydia Wang 958c577b8a1SJoseph Chan /* 959c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 960c577b8a1SJoseph Chan */ 961096a8854STakashi Iwai static const struct hda_verb vt1708_init_verbs[] = { 962aa266fccSLydia Wang /* power down jack detect function */ 963aa266fccSLydia Wang {0x1, 0xf81, 0x1}, 964f7278fd0SJosepch Chan { } 965c577b8a1SJoseph Chan }; 966c577b8a1SJoseph Chan 967ada509ecSTakashi Iwai static void set_stream_active(struct hda_codec *codec, bool active) 9687eb56e84STakashi Iwai { 969ada509ecSTakashi Iwai struct via_spec *spec = codec->spec; 970ada509ecSTakashi Iwai 971ada509ecSTakashi Iwai if (active) 972ada509ecSTakashi Iwai spec->num_active_streams++; 973ada509ecSTakashi Iwai else 974ada509ecSTakashi Iwai spec->num_active_streams--; 975ada509ecSTakashi Iwai analog_low_current_mode(codec); 9767eb56e84STakashi Iwai } 9777eb56e84STakashi Iwai 978ece8d043STakashi Iwai static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo, 979c577b8a1SJoseph Chan struct hda_codec *codec, 980c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 981c577b8a1SJoseph Chan { 982c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 983ada509ecSTakashi Iwai int err; 984ece8d043STakashi Iwai 985ece8d043STakashi Iwai if (!spec->hp_independent_mode) 986ece8d043STakashi Iwai spec->multiout.hp_nid = spec->hp_dac_nid; 987ada509ecSTakashi Iwai set_stream_active(codec, true); 988ada509ecSTakashi Iwai err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 9899a08160bSTakashi Iwai hinfo); 990ada509ecSTakashi Iwai if (err < 0) { 991ada509ecSTakashi Iwai spec->multiout.hp_nid = 0; 992ada509ecSTakashi Iwai set_stream_active(codec, false); 993ada509ecSTakashi Iwai return err; 994ada509ecSTakashi Iwai } 995ada509ecSTakashi Iwai return 0; 996c577b8a1SJoseph Chan } 997c577b8a1SJoseph Chan 998ece8d043STakashi Iwai static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo, 9999af74210STakashi Iwai struct hda_codec *codec, 10009af74210STakashi Iwai struct snd_pcm_substream *substream) 10019af74210STakashi Iwai { 1002ece8d043STakashi Iwai struct via_spec *spec = codec->spec; 1003ece8d043STakashi Iwai 1004ece8d043STakashi Iwai spec->multiout.hp_nid = 0; 1005ada509ecSTakashi Iwai set_stream_active(codec, false); 10069af74210STakashi Iwai return 0; 10079af74210STakashi Iwai } 10089af74210STakashi Iwai 10097eb56e84STakashi Iwai static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo, 10107eb56e84STakashi Iwai struct hda_codec *codec, 10117eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10127eb56e84STakashi Iwai { 10137eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 10147eb56e84STakashi Iwai 1015ece8d043STakashi Iwai if (snd_BUG_ON(!spec->hp_dac_nid)) 10167eb56e84STakashi Iwai return -EINVAL; 1017ece8d043STakashi Iwai if (!spec->hp_independent_mode || spec->multiout.hp_nid) 1018ece8d043STakashi Iwai return -EBUSY; 1019ada509ecSTakashi Iwai set_stream_active(codec, true); 1020ece8d043STakashi Iwai return 0; 1021ece8d043STakashi Iwai } 1022ece8d043STakashi Iwai 1023ece8d043STakashi Iwai static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo, 1024ece8d043STakashi Iwai struct hda_codec *codec, 1025ece8d043STakashi Iwai struct snd_pcm_substream *substream) 1026ece8d043STakashi Iwai { 1027ada509ecSTakashi Iwai set_stream_active(codec, false); 10287eb56e84STakashi Iwai return 0; 10297eb56e84STakashi Iwai } 10307eb56e84STakashi Iwai 10317eb56e84STakashi Iwai static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 10327eb56e84STakashi Iwai struct hda_codec *codec, 10330aa62aefSHarald Welte unsigned int stream_tag, 10340aa62aefSHarald Welte unsigned int format, 10350aa62aefSHarald Welte struct snd_pcm_substream *substream) 10360aa62aefSHarald Welte { 10370aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10380aa62aefSHarald Welte 1039ece8d043STakashi Iwai snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1040ece8d043STakashi Iwai format, substream); 10417eb56e84STakashi Iwai vt1708_start_hp_work(spec); 10427eb56e84STakashi Iwai return 0; 10430aa62aefSHarald Welte } 10440aa62aefSHarald Welte 10457eb56e84STakashi Iwai static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo, 10460aa62aefSHarald Welte struct hda_codec *codec, 10470aa62aefSHarald Welte unsigned int stream_tag, 10480aa62aefSHarald Welte unsigned int format, 10490aa62aefSHarald Welte struct snd_pcm_substream *substream) 10500aa62aefSHarald Welte { 10510aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10520aa62aefSHarald Welte 1053ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1054ece8d043STakashi Iwai stream_tag, 0, format); 10551f2e99feSLydia Wang vt1708_start_hp_work(spec); 10560aa62aefSHarald Welte return 0; 10570aa62aefSHarald Welte } 10580aa62aefSHarald Welte 10590aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 10600aa62aefSHarald Welte struct hda_codec *codec, 10610aa62aefSHarald Welte struct snd_pcm_substream *substream) 10620aa62aefSHarald Welte { 10630aa62aefSHarald Welte struct via_spec *spec = codec->spec; 10640aa62aefSHarald Welte 1065ece8d043STakashi Iwai snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 10667eb56e84STakashi Iwai vt1708_stop_hp_work(spec); 10677eb56e84STakashi Iwai return 0; 10680aa62aefSHarald Welte } 10697eb56e84STakashi Iwai 10707eb56e84STakashi Iwai static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo, 10717eb56e84STakashi Iwai struct hda_codec *codec, 10727eb56e84STakashi Iwai struct snd_pcm_substream *substream) 10737eb56e84STakashi Iwai { 10747eb56e84STakashi Iwai struct via_spec *spec = codec->spec; 10757eb56e84STakashi Iwai 1076ece8d043STakashi Iwai snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 10771f2e99feSLydia Wang vt1708_stop_hp_work(spec); 10780aa62aefSHarald Welte return 0; 10790aa62aefSHarald Welte } 10800aa62aefSHarald Welte 1081c577b8a1SJoseph Chan /* 1082c577b8a1SJoseph Chan * Digital out 1083c577b8a1SJoseph Chan */ 1084c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1085c577b8a1SJoseph Chan struct hda_codec *codec, 1086c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1087c577b8a1SJoseph Chan { 1088c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1089c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1090c577b8a1SJoseph Chan } 1091c577b8a1SJoseph Chan 1092c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1093c577b8a1SJoseph Chan struct hda_codec *codec, 1094c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1095c577b8a1SJoseph Chan { 1096c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1097c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1098c577b8a1SJoseph Chan } 1099c577b8a1SJoseph Chan 11005691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 110198aa34c0SHarald Welte struct hda_codec *codec, 110298aa34c0SHarald Welte unsigned int stream_tag, 110398aa34c0SHarald Welte unsigned int format, 110498aa34c0SHarald Welte struct snd_pcm_substream *substream) 110598aa34c0SHarald Welte { 110698aa34c0SHarald Welte struct via_spec *spec = codec->spec; 11079da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 11089da29271STakashi Iwai stream_tag, format, substream); 11099da29271STakashi Iwai } 11105691ec7fSHarald Welte 11119da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 11129da29271STakashi Iwai struct hda_codec *codec, 11139da29271STakashi Iwai struct snd_pcm_substream *substream) 11149da29271STakashi Iwai { 11159da29271STakashi Iwai struct via_spec *spec = codec->spec; 11169da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 111798aa34c0SHarald Welte return 0; 111898aa34c0SHarald Welte } 111998aa34c0SHarald Welte 1120c577b8a1SJoseph Chan /* 1121c577b8a1SJoseph Chan * Analog capture 1122c577b8a1SJoseph Chan */ 1123c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1124c577b8a1SJoseph Chan struct hda_codec *codec, 1125c577b8a1SJoseph Chan unsigned int stream_tag, 1126c577b8a1SJoseph Chan unsigned int format, 1127c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1128c577b8a1SJoseph Chan { 1129c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1130c577b8a1SJoseph Chan 1131c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1132c577b8a1SJoseph Chan stream_tag, 0, format); 1133c577b8a1SJoseph Chan return 0; 1134c577b8a1SJoseph Chan } 1135c577b8a1SJoseph Chan 1136c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1137c577b8a1SJoseph Chan struct hda_codec *codec, 1138c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1139c577b8a1SJoseph Chan { 1140c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1141888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1142c577b8a1SJoseph Chan return 0; 1143c577b8a1SJoseph Chan } 1144c577b8a1SJoseph Chan 11459af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_playback = { 11467eb56e84STakashi Iwai .substreams = 1, 1147c577b8a1SJoseph Chan .channels_min = 2, 1148c577b8a1SJoseph Chan .channels_max = 8, 11499af74210STakashi Iwai /* NID is set in via_build_pcms */ 1150c577b8a1SJoseph Chan .ops = { 1151ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1152ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 11530aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 11540aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1155c577b8a1SJoseph Chan }, 1156c577b8a1SJoseph Chan }; 1157c577b8a1SJoseph Chan 11587eb56e84STakashi Iwai static const struct hda_pcm_stream via_pcm_hp_playback = { 11597eb56e84STakashi Iwai .substreams = 1, 11607eb56e84STakashi Iwai .channels_min = 2, 11617eb56e84STakashi Iwai .channels_max = 2, 11627eb56e84STakashi Iwai /* NID is set in via_build_pcms */ 11637eb56e84STakashi Iwai .ops = { 11647eb56e84STakashi Iwai .open = via_playback_hp_pcm_open, 1165ece8d043STakashi Iwai .close = via_playback_hp_pcm_close, 11667eb56e84STakashi Iwai .prepare = via_playback_hp_pcm_prepare, 11677eb56e84STakashi Iwai .cleanup = via_playback_hp_pcm_cleanup 11687eb56e84STakashi Iwai }, 11697eb56e84STakashi Iwai }; 11707eb56e84STakashi Iwai 117190dd48a1STakashi Iwai static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 11727eb56e84STakashi Iwai .substreams = 1, 1173bc9b5623STakashi Iwai .channels_min = 2, 1174bc9b5623STakashi Iwai .channels_max = 8, 11759af74210STakashi Iwai /* NID is set in via_build_pcms */ 1176bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1177bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1178bc9b5623STakashi Iwai * disable the 24bit format, so far. 1179bc9b5623STakashi Iwai */ 1180bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1181bc9b5623STakashi Iwai .ops = { 1182ece8d043STakashi Iwai .open = via_playback_multi_pcm_open, 1183ece8d043STakashi Iwai .close = via_playback_multi_pcm_close, 1184c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1185c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1186bc9b5623STakashi Iwai }, 1187bc9b5623STakashi Iwai }; 1188bc9b5623STakashi Iwai 11899af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_analog_capture = { 11907eb56e84STakashi Iwai .substreams = 1, /* will be changed in via_build_pcms() */ 1191c577b8a1SJoseph Chan .channels_min = 2, 1192c577b8a1SJoseph Chan .channels_max = 2, 11939af74210STakashi Iwai /* NID is set in via_build_pcms */ 1194c577b8a1SJoseph Chan .ops = { 1195c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1196c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1197c577b8a1SJoseph Chan }, 1198c577b8a1SJoseph Chan }; 1199c577b8a1SJoseph Chan 12009af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_playback = { 1201c577b8a1SJoseph Chan .substreams = 1, 1202c577b8a1SJoseph Chan .channels_min = 2, 1203c577b8a1SJoseph Chan .channels_max = 2, 1204c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1205c577b8a1SJoseph Chan .ops = { 1206c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 12076b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 12089da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 12099da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1210c577b8a1SJoseph Chan }, 1211c577b8a1SJoseph Chan }; 1212c577b8a1SJoseph Chan 12139af74210STakashi Iwai static const struct hda_pcm_stream via_pcm_digital_capture = { 1214c577b8a1SJoseph Chan .substreams = 1, 1215c577b8a1SJoseph Chan .channels_min = 2, 1216c577b8a1SJoseph Chan .channels_max = 2, 1217c577b8a1SJoseph Chan }; 1218c577b8a1SJoseph Chan 1219370bafbdSTakashi Iwai /* 1220370bafbdSTakashi Iwai * slave controls for virtual master 1221370bafbdSTakashi Iwai */ 1222370bafbdSTakashi Iwai static const char * const via_slave_vols[] = { 1223370bafbdSTakashi Iwai "Front Playback Volume", 1224370bafbdSTakashi Iwai "Surround Playback Volume", 1225370bafbdSTakashi Iwai "Center Playback Volume", 1226370bafbdSTakashi Iwai "LFE Playback Volume", 1227370bafbdSTakashi Iwai "Side Playback Volume", 1228370bafbdSTakashi Iwai "Headphone Playback Volume", 1229370bafbdSTakashi Iwai "Speaker Playback Volume", 1230370bafbdSTakashi Iwai NULL, 1231370bafbdSTakashi Iwai }; 1232370bafbdSTakashi Iwai 1233370bafbdSTakashi Iwai static const char * const via_slave_sws[] = { 1234370bafbdSTakashi Iwai "Front Playback Switch", 1235370bafbdSTakashi Iwai "Surround Playback Switch", 1236370bafbdSTakashi Iwai "Center Playback Switch", 1237370bafbdSTakashi Iwai "LFE Playback Switch", 1238370bafbdSTakashi Iwai "Side Playback Switch", 1239370bafbdSTakashi Iwai "Headphone Playback Switch", 1240370bafbdSTakashi Iwai "Speaker Playback Switch", 1241370bafbdSTakashi Iwai NULL, 1242370bafbdSTakashi Iwai }; 1243370bafbdSTakashi Iwai 1244c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1245c577b8a1SJoseph Chan { 1246c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 12475b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 12485b0cb1d8SJaroslav Kysela int err, i; 1249c577b8a1SJoseph Chan 125024088a58STakashi Iwai if (spec->set_widgets_power_state) 125124088a58STakashi Iwai if (!via_clone_control(spec, &via_pin_power_ctl_enum)) 125224088a58STakashi Iwai return -ENOMEM; 125324088a58STakashi Iwai 1254c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1255c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1256c577b8a1SJoseph Chan if (err < 0) 1257c577b8a1SJoseph Chan return err; 1258c577b8a1SJoseph Chan } 1259c577b8a1SJoseph Chan 1260c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1261c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 126274b654c9SStephen Warren spec->multiout.dig_out_nid, 1263c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1264c577b8a1SJoseph Chan if (err < 0) 1265c577b8a1SJoseph Chan return err; 12669a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 12679a08160bSTakashi Iwai &spec->multiout); 12689a08160bSTakashi Iwai if (err < 0) 12699a08160bSTakashi Iwai return err; 12709a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1271c577b8a1SJoseph Chan } 1272c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1273c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1274c577b8a1SJoseph Chan if (err < 0) 1275c577b8a1SJoseph Chan return err; 1276c577b8a1SJoseph Chan } 127717314379SLydia Wang 1278370bafbdSTakashi Iwai /* if we have no master control, let's create it */ 1279370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { 1280370bafbdSTakashi Iwai unsigned int vmaster_tlv[4]; 1281370bafbdSTakashi Iwai snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0], 1282370bafbdSTakashi Iwai HDA_OUTPUT, vmaster_tlv); 1283370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Volume", 1284370bafbdSTakashi Iwai vmaster_tlv, via_slave_vols); 1285370bafbdSTakashi Iwai if (err < 0) 1286370bafbdSTakashi Iwai return err; 1287370bafbdSTakashi Iwai } 1288370bafbdSTakashi Iwai if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) { 1289370bafbdSTakashi Iwai err = snd_hda_add_vmaster(codec, "Master Playback Switch", 1290370bafbdSTakashi Iwai NULL, via_slave_sws); 1291370bafbdSTakashi Iwai if (err < 0) 1292370bafbdSTakashi Iwai return err; 1293370bafbdSTakashi Iwai } 1294370bafbdSTakashi Iwai 12955b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 12965b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 12975b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 129821949f00STakashi Iwai err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]); 12995b0cb1d8SJaroslav Kysela if (err < 0) 13005b0cb1d8SJaroslav Kysela return err; 13015b0cb1d8SJaroslav Kysela } 13025b0cb1d8SJaroslav Kysela 130317314379SLydia Wang /* init power states */ 13043e95b9abSLydia Wang set_widgets_power_state(codec); 1305ada509ecSTakashi Iwai analog_low_current_mode(codec); 130617314379SLydia Wang 1307603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1308c577b8a1SJoseph Chan return 0; 1309c577b8a1SJoseph Chan } 1310c577b8a1SJoseph Chan 1311c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1312c577b8a1SJoseph Chan { 1313c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1314c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1315c577b8a1SJoseph Chan 1316c577b8a1SJoseph Chan codec->num_pcms = 1; 1317c577b8a1SJoseph Chan codec->pcm_info = info; 1318c577b8a1SJoseph Chan 131982673bc8STakashi Iwai snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog), 132082673bc8STakashi Iwai "%s Analog", codec->chip_name); 1321c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 13229af74210STakashi Iwai 13239af74210STakashi Iwai if (!spec->stream_analog_playback) 13249af74210STakashi Iwai spec->stream_analog_playback = &via_pcm_analog_playback; 1325377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 13269af74210STakashi Iwai *spec->stream_analog_playback; 1327377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1328377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1329c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1330c577b8a1SJoseph Chan spec->multiout.max_channels; 13319af74210STakashi Iwai 13329af74210STakashi Iwai if (!spec->stream_analog_capture) 13339af74210STakashi Iwai spec->stream_analog_capture = &via_pcm_analog_capture; 13349af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE] = 13359af74210STakashi Iwai *spec->stream_analog_capture; 13369af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 13379af74210STakashi Iwai info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 13389af74210STakashi Iwai spec->num_adc_nids; 1339c577b8a1SJoseph Chan 1340c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1341c577b8a1SJoseph Chan codec->num_pcms++; 1342c577b8a1SJoseph Chan info++; 134382673bc8STakashi Iwai snprintf(spec->stream_name_digital, 134482673bc8STakashi Iwai sizeof(spec->stream_name_digital), 134582673bc8STakashi Iwai "%s Digital", codec->chip_name); 1346c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 13477ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1348c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 13499af74210STakashi Iwai if (!spec->stream_digital_playback) 13509af74210STakashi Iwai spec->stream_digital_playback = 13519af74210STakashi Iwai &via_pcm_digital_playback; 1352c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 13539af74210STakashi Iwai *spec->stream_digital_playback; 1354c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1355c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1356c577b8a1SJoseph Chan } 1357c577b8a1SJoseph Chan if (spec->dig_in_nid) { 13589af74210STakashi Iwai if (!spec->stream_digital_capture) 13599af74210STakashi Iwai spec->stream_digital_capture = 13609af74210STakashi Iwai &via_pcm_digital_capture; 1361c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 13629af74210STakashi Iwai *spec->stream_digital_capture; 1363c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1364c577b8a1SJoseph Chan spec->dig_in_nid; 1365c577b8a1SJoseph Chan } 1366c577b8a1SJoseph Chan } 1367c577b8a1SJoseph Chan 1368ece8d043STakashi Iwai if (spec->hp_dac_nid) { 13697eb56e84STakashi Iwai codec->num_pcms++; 13707eb56e84STakashi Iwai info++; 13717eb56e84STakashi Iwai snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp), 13727eb56e84STakashi Iwai "%s HP", codec->chip_name); 13737eb56e84STakashi Iwai info->name = spec->stream_name_hp; 13747eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback; 13757eb56e84STakashi Iwai info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1376ece8d043STakashi Iwai spec->hp_dac_nid; 13777eb56e84STakashi Iwai } 1378c577b8a1SJoseph Chan return 0; 1379c577b8a1SJoseph Chan } 1380c577b8a1SJoseph Chan 1381c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1382c577b8a1SJoseph Chan { 1383c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1384c577b8a1SJoseph Chan 1385c577b8a1SJoseph Chan if (!spec) 1386c577b8a1SJoseph Chan return; 1387c577b8a1SJoseph Chan 1388603c4019STakashi Iwai via_free_kctls(codec); 13891f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1390c577b8a1SJoseph Chan kfree(codec->spec); 1391c577b8a1SJoseph Chan } 1392c577b8a1SJoseph Chan 139364be285bSTakashi Iwai /* mute/unmute outputs */ 139464be285bSTakashi Iwai static void toggle_output_mutes(struct hda_codec *codec, int num_pins, 139564be285bSTakashi Iwai hda_nid_t *pins, bool mute) 139664be285bSTakashi Iwai { 139764be285bSTakashi Iwai int i; 139864be285bSTakashi Iwai for (i = 0; i < num_pins; i++) 139964be285bSTakashi Iwai snd_hda_codec_write(codec, pins[i], 0, 140064be285bSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 140164be285bSTakashi Iwai mute ? 0 : PIN_OUT); 140264be285bSTakashi Iwai } 140364be285bSTakashi Iwai 14044a918ffeSTakashi Iwai /* mute internal speaker if line-out is plugged */ 14054a918ffeSTakashi Iwai static void via_line_automute(struct hda_codec *codec, int present) 14064a918ffeSTakashi Iwai { 14074a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 14084a918ffeSTakashi Iwai 14094a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs) 14104a918ffeSTakashi Iwai return; 14114a918ffeSTakashi Iwai if (!present) 14124a918ffeSTakashi Iwai present = snd_hda_jack_detect(codec, 14134a918ffeSTakashi Iwai spec->autocfg.line_out_pins[0]); 14144a918ffeSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.speaker_outs, 14154a918ffeSTakashi Iwai spec->autocfg.speaker_pins, 14164a918ffeSTakashi Iwai present); 14174a918ffeSTakashi Iwai } 14184a918ffeSTakashi Iwai 141969e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 142069e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 142169e52a80SHarald Welte { 14224a918ffeSTakashi Iwai int present = 0; 142369e52a80SHarald Welte struct via_spec *spec = codec->spec; 142469e52a80SHarald Welte 14254a918ffeSTakashi Iwai if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) { 1426d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 142764be285bSTakashi Iwai toggle_output_mutes(codec, spec->autocfg.line_outs, 142864be285bSTakashi Iwai spec->autocfg.line_out_pins, 142964be285bSTakashi Iwai present); 143069e52a80SHarald Welte } 14314a918ffeSTakashi Iwai via_line_automute(codec, present); 1432f3db423dSLydia Wang } 1433f3db423dSLydia Wang 143469e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 143569e52a80SHarald Welte { 143669e52a80SHarald Welte unsigned int gpio_data; 143769e52a80SHarald Welte unsigned int vol_counter; 143869e52a80SHarald Welte unsigned int vol; 143969e52a80SHarald Welte unsigned int master_vol; 144069e52a80SHarald Welte 144169e52a80SHarald Welte struct via_spec *spec = codec->spec; 144269e52a80SHarald Welte 144369e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 144469e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 144569e52a80SHarald Welte 144669e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 144769e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 144869e52a80SHarald Welte 144969e52a80SHarald Welte vol = vol_counter & 0x1F; 145069e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 145169e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 145269e52a80SHarald Welte AC_AMP_GET_INPUT); 145369e52a80SHarald Welte 145469e52a80SHarald Welte if (gpio_data == 0x02) { 145569e52a80SHarald Welte /* unmute line out */ 14563e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 14573e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 14583e0693e2STakashi Iwai PIN_OUT); 145969e52a80SHarald Welte if (vol_counter & 0x20) { 146069e52a80SHarald Welte /* decrease volume */ 146169e52a80SHarald Welte if (vol > master_vol) 146269e52a80SHarald Welte vol = master_vol; 146369e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 146469e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 146569e52a80SHarald Welte master_vol-vol); 146669e52a80SHarald Welte } else { 146769e52a80SHarald Welte /* increase volume */ 146869e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 146969e52a80SHarald Welte HDA_AMP_VOLMASK, 147069e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 147169e52a80SHarald Welte (master_vol+vol)); 147269e52a80SHarald Welte } 147369e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 147469e52a80SHarald Welte /* mute line out */ 14753e0693e2STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0, 14763e0693e2STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, 14773e0693e2STakashi Iwai 0); 147869e52a80SHarald Welte } 147969e52a80SHarald Welte } 148069e52a80SHarald Welte 148169e52a80SHarald Welte /* unsolicited event for jack sensing */ 148269e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 148369e52a80SHarald Welte unsigned int res) 148469e52a80SHarald Welte { 148569e52a80SHarald Welte res >>= 26; 1486ec7e7e42SLydia Wang 1487a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 14883e95b9abSLydia Wang set_widgets_power_state(codec); 1489ec7e7e42SLydia Wang 1490ec7e7e42SLydia Wang res &= ~VIA_JACK_EVENT; 1491ec7e7e42SLydia Wang 1492ec7e7e42SLydia Wang if (res == VIA_HP_EVENT) 1493ec7e7e42SLydia Wang via_hp_automute(codec); 1494ec7e7e42SLydia Wang else if (res == VIA_GPIO_EVENT) 1495ec7e7e42SLydia Wang via_gpio_control(codec); 14964a918ffeSTakashi Iwai else if (res == VIA_LINE_EVENT) 14974a918ffeSTakashi Iwai via_line_automute(codec, false); 149869e52a80SHarald Welte } 149969e52a80SHarald Welte 15001f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 15011f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 15021f2e99feSLydia Wang { 15031f2e99feSLydia Wang struct via_spec *spec = codec->spec; 15041f2e99feSLydia Wang vt1708_stop_hp_work(spec); 15051f2e99feSLydia Wang return 0; 15061f2e99feSLydia Wang } 15071f2e99feSLydia Wang #endif 15081f2e99feSLydia Wang 1509cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1510cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 1511cb53c626STakashi Iwai { 1512cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 1513cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 1514cb53c626STakashi Iwai } 1515cb53c626STakashi Iwai #endif 1516cb53c626STakashi Iwai 1517c577b8a1SJoseph Chan /* 1518c577b8a1SJoseph Chan */ 15195d41762aSTakashi Iwai 15205d41762aSTakashi Iwai static int via_init(struct hda_codec *codec); 15215d41762aSTakashi Iwai 152290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = { 1523c577b8a1SJoseph Chan .build_controls = via_build_controls, 1524c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 1525c577b8a1SJoseph Chan .init = via_init, 1526c577b8a1SJoseph Chan .free = via_free, 15274a918ffeSTakashi Iwai .unsol_event = via_unsol_event, 15281f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 15291f2e99feSLydia Wang .suspend = via_suspend, 15301f2e99feSLydia Wang #endif 1531cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 1532cb53c626STakashi Iwai .check_power_status = via_check_power_status, 1533cb53c626STakashi Iwai #endif 1534c577b8a1SJoseph Chan }; 1535c577b8a1SJoseph Chan 15364a79616dSTakashi Iwai static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac) 1537c577b8a1SJoseph Chan { 15384a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 15394a79616dSTakashi Iwai int i; 15404a79616dSTakashi Iwai 15414a79616dSTakashi Iwai for (i = 0; i < spec->multiout.num_dacs; i++) { 15424a79616dSTakashi Iwai if (spec->multiout.dac_nids[i] == dac) 15434a79616dSTakashi Iwai return false; 15444a79616dSTakashi Iwai } 1545ece8d043STakashi Iwai if (spec->hp_dac_nid == dac) 15464a79616dSTakashi Iwai return false; 15474a79616dSTakashi Iwai return true; 15484a79616dSTakashi Iwai } 15494a79616dSTakashi Iwai 15508e3679dcSTakashi Iwai static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid, 15514a79616dSTakashi Iwai hda_nid_t target_dac, struct nid_path *path, 15524a79616dSTakashi Iwai int depth, int wid_type) 15534a79616dSTakashi Iwai { 15544a79616dSTakashi Iwai hda_nid_t conn[8]; 15554a79616dSTakashi Iwai int i, nums; 15564a79616dSTakashi Iwai 15574a79616dSTakashi Iwai nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn)); 15584a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 15594a79616dSTakashi Iwai if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT) 15604a79616dSTakashi Iwai continue; 15614a79616dSTakashi Iwai if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) { 15628e3679dcSTakashi Iwai path->path[0] = conn[i]; 15638e3679dcSTakashi Iwai path->idx[0] = i; 15648e3679dcSTakashi Iwai path->depth = 1; 15654a79616dSTakashi Iwai return true; 15664a79616dSTakashi Iwai } 15674a79616dSTakashi Iwai } 15688e3679dcSTakashi Iwai if (depth >= MAX_NID_PATH_DEPTH) 15694a79616dSTakashi Iwai return false; 15704a79616dSTakashi Iwai for (i = 0; i < nums; i++) { 15714a79616dSTakashi Iwai unsigned int type; 15724a79616dSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, conn[i])); 15734a79616dSTakashi Iwai if (type == AC_WID_AUD_OUT || 15744a79616dSTakashi Iwai (wid_type != -1 && type != wid_type)) 15754a79616dSTakashi Iwai continue; 15768e3679dcSTakashi Iwai if (__parse_output_path(codec, conn[i], target_dac, 15774a79616dSTakashi Iwai path, depth + 1, AC_WID_AUD_SEL)) { 15788e3679dcSTakashi Iwai path->path[path->depth] = conn[i]; 15798e3679dcSTakashi Iwai path->idx[path->depth] = i; 15808e3679dcSTakashi Iwai path->depth++; 15814a79616dSTakashi Iwai return true; 15824a79616dSTakashi Iwai } 15834a79616dSTakashi Iwai } 15844a79616dSTakashi Iwai return false; 15854a79616dSTakashi Iwai } 15864a79616dSTakashi Iwai 15878e3679dcSTakashi Iwai static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid, 15888e3679dcSTakashi Iwai hda_nid_t target_dac, struct nid_path *path) 15898e3679dcSTakashi Iwai { 15908e3679dcSTakashi Iwai if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) { 15918e3679dcSTakashi Iwai path->path[path->depth] = nid; 15928e3679dcSTakashi Iwai path->depth++; 15938e3679dcSTakashi Iwai return true; 15948e3679dcSTakashi Iwai } 15958e3679dcSTakashi Iwai return false; 15968e3679dcSTakashi Iwai } 15978e3679dcSTakashi Iwai 15984a79616dSTakashi Iwai static int via_auto_fill_dac_nids(struct hda_codec *codec) 15994a79616dSTakashi Iwai { 16004a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 16014a79616dSTakashi Iwai const struct auto_pin_cfg *cfg = &spec->autocfg; 1602c577b8a1SJoseph Chan int i; 1603c577b8a1SJoseph Chan hda_nid_t nid; 1604c577b8a1SJoseph Chan 1605c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 16064a79616dSTakashi Iwai spec->multiout.num_dacs = cfg->line_outs; 16074a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 1608c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 16094a79616dSTakashi Iwai if (!nid) 16104a79616dSTakashi Iwai continue; 16118e3679dcSTakashi Iwai if (parse_output_path(codec, nid, 0, &spec->out_path[i])) 16128e3679dcSTakashi Iwai spec->private_dac_nids[i] = spec->out_path[i].path[0]; 1613c577b8a1SJoseph Chan } 1614c577b8a1SJoseph Chan return 0; 1615c577b8a1SJoseph Chan } 1616c577b8a1SJoseph Chan 16174a79616dSTakashi Iwai static int create_ch_ctls(struct hda_codec *codec, const char *pfx, 16184a79616dSTakashi Iwai hda_nid_t pin, hda_nid_t dac, int chs) 1619c577b8a1SJoseph Chan { 16204a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1621c577b8a1SJoseph Chan char name[32]; 16224a79616dSTakashi Iwai hda_nid_t nid; 16234a79616dSTakashi Iwai int err; 1624c577b8a1SJoseph Chan 16254a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 16264a79616dSTakashi Iwai nid = dac; 16274a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS) 16284a79616dSTakashi Iwai nid = pin; 16294a79616dSTakashi Iwai else 16304a79616dSTakashi Iwai nid = 0; 16314a79616dSTakashi Iwai if (nid) { 16324a79616dSTakashi Iwai sprintf(name, "%s Playback Volume", pfx); 1633c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1634*a00a5fadSLydia Wang HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 1635c577b8a1SJoseph Chan if (err < 0) 1636c577b8a1SJoseph Chan return err; 1637c577b8a1SJoseph Chan } 16384a79616dSTakashi Iwai 16394a79616dSTakashi Iwai if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE) 16404a79616dSTakashi Iwai nid = dac; 16414a79616dSTakashi Iwai else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE) 16424a79616dSTakashi Iwai nid = pin; 16434a79616dSTakashi Iwai else 16444a79616dSTakashi Iwai nid = 0; 16454a79616dSTakashi Iwai if (nid) { 16464a79616dSTakashi Iwai sprintf(name, "%s Playback Switch", pfx); 16474a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 16484a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT)); 16494a79616dSTakashi Iwai if (err < 0) 16504a79616dSTakashi Iwai return err; 16514a79616dSTakashi Iwai } 16524a79616dSTakashi Iwai return 0; 16534a79616dSTakashi Iwai } 16544a79616dSTakashi Iwai 1655f4a7828bSTakashi Iwai static void mangle_smart51(struct hda_codec *codec) 1656f4a7828bSTakashi Iwai { 1657f4a7828bSTakashi Iwai struct via_spec *spec = codec->spec; 1658f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 1659e3d7a143STakashi Iwai int i, nums = 0; 1660f4a7828bSTakashi Iwai 1661f4a7828bSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1662e3d7a143STakashi Iwai if (is_smart51_candidate(codec, cfg->inputs[i].pin)) 1663e3d7a143STakashi Iwai nums++; 1664e3d7a143STakashi Iwai } 1665e3d7a143STakashi Iwai if (cfg->line_outs + nums < 3) 1666e3d7a143STakashi Iwai return; 1667e3d7a143STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1668e3d7a143STakashi Iwai if (!is_smart51_candidate(codec, cfg->inputs[i].pin)) 1669f4a7828bSTakashi Iwai continue; 1670e3d7a143STakashi Iwai spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin; 1671f4a7828bSTakashi Iwai cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin; 1672f4a7828bSTakashi Iwai if (cfg->line_outs == 3) 1673f4a7828bSTakashi Iwai break; 1674f4a7828bSTakashi Iwai } 1675f4a7828bSTakashi Iwai } 1676f4a7828bSTakashi Iwai 16774a79616dSTakashi Iwai /* add playback controls from the parsed DAC table */ 16784a79616dSTakashi Iwai static int via_auto_create_multi_out_ctls(struct hda_codec *codec) 16794a79616dSTakashi Iwai { 16804a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1681f4a7828bSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 16824a79616dSTakashi Iwai static const char * const chname[4] = { 16834a79616dSTakashi Iwai "Front", "Surround", "C/LFE", "Side" 16844a79616dSTakashi Iwai }; 16854a79616dSTakashi Iwai int i, idx, err; 1686f4a7828bSTakashi Iwai int old_line_outs; 1687f4a7828bSTakashi Iwai 1688f4a7828bSTakashi Iwai /* check smart51 */ 1689f4a7828bSTakashi Iwai old_line_outs = cfg->line_outs; 1690f4a7828bSTakashi Iwai if (cfg->line_outs == 1) 1691f4a7828bSTakashi Iwai mangle_smart51(codec); 16924a79616dSTakashi Iwai 1693e3d7a143STakashi Iwai err = via_auto_fill_dac_nids(codec); 1694e3d7a143STakashi Iwai if (err < 0) 1695e3d7a143STakashi Iwai return err; 1696e3d7a143STakashi Iwai 16974a79616dSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 16984a79616dSTakashi Iwai hda_nid_t pin, dac; 16994a79616dSTakashi Iwai pin = cfg->line_out_pins[i]; 17004a79616dSTakashi Iwai dac = spec->multiout.dac_nids[i]; 17014a79616dSTakashi Iwai if (!pin || !dac) 17024a79616dSTakashi Iwai continue; 17030fe0adf8STakashi Iwai if (i == HDA_CLFE) { 17044a79616dSTakashi Iwai err = create_ch_ctls(codec, "Center", pin, dac, 1); 17054a79616dSTakashi Iwai if (err < 0) 17064a79616dSTakashi Iwai return err; 17074a79616dSTakashi Iwai err = create_ch_ctls(codec, "LFE", pin, dac, 2); 17084a79616dSTakashi Iwai if (err < 0) 17094a79616dSTakashi Iwai return err; 17104a79616dSTakashi Iwai } else { 17116aadf41dSTakashi Iwai const char *pfx = chname[i]; 17126aadf41dSTakashi Iwai if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT && 17136aadf41dSTakashi Iwai cfg->line_outs == 1) 17146aadf41dSTakashi Iwai pfx = "Speaker"; 17156aadf41dSTakashi Iwai err = create_ch_ctls(codec, pfx, pin, dac, 3); 17164a79616dSTakashi Iwai if (err < 0) 17174a79616dSTakashi Iwai return err; 17184a79616dSTakashi Iwai } 17194a79616dSTakashi Iwai } 17204a79616dSTakashi Iwai 17214a79616dSTakashi Iwai idx = get_connection_index(codec, spec->aa_mix_nid, 17224a79616dSTakashi Iwai spec->multiout.dac_nids[0]); 17234a79616dSTakashi Iwai if (idx >= 0) { 17244a79616dSTakashi Iwai /* add control to mixer */ 17254a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 17264a79616dSTakashi Iwai "PCM Playback Volume", 17274a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17284a79616dSTakashi Iwai idx, HDA_INPUT)); 17294a79616dSTakashi Iwai if (err < 0) 17304a79616dSTakashi Iwai return err; 17314a79616dSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 17324a79616dSTakashi Iwai "PCM Playback Switch", 17334a79616dSTakashi Iwai HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3, 17344a79616dSTakashi Iwai idx, HDA_INPUT)); 17354a79616dSTakashi Iwai if (err < 0) 17364a79616dSTakashi Iwai return err; 1737c577b8a1SJoseph Chan } 1738c577b8a1SJoseph Chan 1739f4a7828bSTakashi Iwai cfg->line_outs = old_line_outs; 1740f4a7828bSTakashi Iwai 1741c577b8a1SJoseph Chan return 0; 1742c577b8a1SJoseph Chan } 1743c577b8a1SJoseph Chan 17440aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 17450aa62aefSHarald Welte { 17460aa62aefSHarald Welte int i; 17470aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 1748ea734963STakashi Iwai static const char * const texts[] = { "OFF", "ON", NULL}; 17490aa62aefSHarald Welte 17500aa62aefSHarald Welte /* for hp mode select */ 175110a20af7STakashi Iwai for (i = 0; texts[i]; i++) 175210a20af7STakashi Iwai snd_hda_add_imux_item(imux, texts[i], i, NULL); 17530aa62aefSHarald Welte 17540aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 17550aa62aefSHarald Welte } 17560aa62aefSHarald Welte 17574a79616dSTakashi Iwai static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin) 1758c577b8a1SJoseph Chan { 17594a79616dSTakashi Iwai struct via_spec *spec = codec->spec; 1760c577b8a1SJoseph Chan int err; 1761c577b8a1SJoseph Chan 1762c577b8a1SJoseph Chan if (!pin) 1763c577b8a1SJoseph Chan return 0; 1764c577b8a1SJoseph Chan 17658e3679dcSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->hp_path)) { 17668e3679dcSTakashi Iwai spec->hp_dac_nid = spec->hp_path.path[0]; 17678e3679dcSTakashi Iwai spec->hp_independent_mode_index = spec->hp_path.idx[0]; 17680aa62aefSHarald Welte create_hp_imux(spec); 17694a79616dSTakashi Iwai } 17704a79616dSTakashi Iwai 1771ece8d043STakashi Iwai if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 17728e3679dcSTakashi Iwai &spec->hp_dep_path) && 1773ece8d043STakashi Iwai !spec->hp_dac_nid) 1774ece8d043STakashi Iwai return 0; 1775ece8d043STakashi Iwai 1776ece8d043STakashi Iwai 1777ece8d043STakashi Iwai err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3); 17784a79616dSTakashi Iwai if (err < 0) 17794a79616dSTakashi Iwai return err; 17800aa62aefSHarald Welte 1781c577b8a1SJoseph Chan return 0; 1782c577b8a1SJoseph Chan } 1783c577b8a1SJoseph Chan 17844a918ffeSTakashi Iwai static int via_auto_create_speaker_ctls(struct hda_codec *codec) 17854a918ffeSTakashi Iwai { 17864a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 17874a918ffeSTakashi Iwai hda_nid_t pin, dac; 17884a918ffeSTakashi Iwai 17894a918ffeSTakashi Iwai pin = spec->autocfg.speaker_pins[0]; 17904a918ffeSTakashi Iwai if (!spec->autocfg.speaker_outs || !pin) 17914a918ffeSTakashi Iwai return 0; 17924a918ffeSTakashi Iwai 17938e3679dcSTakashi Iwai if (parse_output_path(codec, pin, 0, &spec->speaker_path)) { 17948e3679dcSTakashi Iwai dac = spec->speaker_path.path[0]; 17954a918ffeSTakashi Iwai spec->multiout.extra_out_nid[0] = dac; 17964a918ffeSTakashi Iwai return create_ch_ctls(codec, "Speaker", pin, dac, 3); 17974a918ffeSTakashi Iwai } 17984a918ffeSTakashi Iwai if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT], 17998e3679dcSTakashi Iwai &spec->speaker_path)) 18008e3679dcSTakashi Iwai return create_ch_ctls(codec, "Speaker", pin, 0, 3); 18014a918ffeSTakashi Iwai 18024a918ffeSTakashi Iwai return 0; 18034a918ffeSTakashi Iwai } 18044a918ffeSTakashi Iwai 1805a766d0d7STakashi Iwai /* look for ADCs */ 1806a766d0d7STakashi Iwai static int via_fill_adcs(struct hda_codec *codec) 1807a766d0d7STakashi Iwai { 1808a766d0d7STakashi Iwai struct via_spec *spec = codec->spec; 1809a766d0d7STakashi Iwai hda_nid_t nid = codec->start_nid; 1810a766d0d7STakashi Iwai int i; 1811a766d0d7STakashi Iwai 1812a766d0d7STakashi Iwai for (i = 0; i < codec->num_nodes; i++, nid++) { 1813a766d0d7STakashi Iwai unsigned int wcaps = get_wcaps(codec, nid); 1814a766d0d7STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 1815a766d0d7STakashi Iwai continue; 1816a766d0d7STakashi Iwai if (wcaps & AC_WCAP_DIGITAL) 1817a766d0d7STakashi Iwai continue; 1818a766d0d7STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 1819a766d0d7STakashi Iwai continue; 1820a766d0d7STakashi Iwai if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids)) 1821a766d0d7STakashi Iwai return -ENOMEM; 1822a766d0d7STakashi Iwai spec->adc_nids[spec->num_adc_nids++] = nid; 1823a766d0d7STakashi Iwai } 1824a766d0d7STakashi Iwai return 0; 1825a766d0d7STakashi Iwai } 1826a766d0d7STakashi Iwai 1827a766d0d7STakashi Iwai static int get_mux_nids(struct hda_codec *codec); 1828a766d0d7STakashi Iwai 1829d7a99cceSTakashi Iwai static const struct snd_kcontrol_new via_input_src_ctl = { 1830d7a99cceSTakashi Iwai .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1831d7a99cceSTakashi Iwai /* The multiple "Capture Source" controls confuse alsamixer 1832d7a99cceSTakashi Iwai * So call somewhat different.. 1833d7a99cceSTakashi Iwai */ 1834d7a99cceSTakashi Iwai /* .name = "Capture Source", */ 1835d7a99cceSTakashi Iwai .name = "Input Source", 1836d7a99cceSTakashi Iwai .info = via_mux_enum_info, 1837d7a99cceSTakashi Iwai .get = via_mux_enum_get, 1838d7a99cceSTakashi Iwai .put = via_mux_enum_put, 1839d7a99cceSTakashi Iwai }; 1840d7a99cceSTakashi Iwai 184113af8e77STakashi Iwai static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx) 184213af8e77STakashi Iwai { 184313af8e77STakashi Iwai struct hda_amp_list *list; 184413af8e77STakashi Iwai 184513af8e77STakashi Iwai if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1) 184613af8e77STakashi Iwai return; 184713af8e77STakashi Iwai list = spec->loopback_list + spec->num_loopbacks; 184813af8e77STakashi Iwai list->nid = mix; 184913af8e77STakashi Iwai list->dir = HDA_INPUT; 185013af8e77STakashi Iwai list->idx = idx; 185113af8e77STakashi Iwai spec->num_loopbacks++; 185213af8e77STakashi Iwai spec->loopback.amplist = spec->loopback_list; 185313af8e77STakashi Iwai } 185413af8e77STakashi Iwai 1855c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 1856620e2b28STakashi Iwai static int via_auto_create_analog_input_ctls(struct hda_codec *codec, 1857620e2b28STakashi Iwai const struct auto_pin_cfg *cfg) 1858c577b8a1SJoseph Chan { 185910a20af7STakashi Iwai struct via_spec *spec = codec->spec; 18600aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 1861e3d7a143STakashi Iwai int i, j, err, idx, idx2, type, type_idx = 0; 1862a766d0d7STakashi Iwai hda_nid_t cap_nid; 1863a766d0d7STakashi Iwai hda_nid_t pin_idxs[8]; 1864a766d0d7STakashi Iwai int num_idxs; 1865a766d0d7STakashi Iwai 1866a766d0d7STakashi Iwai err = via_fill_adcs(codec); 1867a766d0d7STakashi Iwai if (err < 0) 1868a766d0d7STakashi Iwai return err; 1869a766d0d7STakashi Iwai err = get_mux_nids(codec); 1870a766d0d7STakashi Iwai if (err < 0) 1871a766d0d7STakashi Iwai return err; 1872a766d0d7STakashi Iwai cap_nid = spec->mux_nids[0]; 1873a766d0d7STakashi Iwai 1874a766d0d7STakashi Iwai num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs, 1875a766d0d7STakashi Iwai ARRAY_SIZE(pin_idxs)); 1876a766d0d7STakashi Iwai if (num_idxs <= 0) 1877a766d0d7STakashi Iwai return 0; 1878c577b8a1SJoseph Chan 1879c577b8a1SJoseph Chan /* for internal loopback recording select */ 1880f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) { 1881620e2b28STakashi Iwai if (pin_idxs[idx] == spec->aa_mix_nid) { 188210a20af7STakashi Iwai snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL); 1883f3268512STakashi Iwai break; 1884f3268512STakashi Iwai } 1885f3268512STakashi Iwai } 1886c577b8a1SJoseph Chan 18877b315bb4STakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 188810a20af7STakashi Iwai const char *label; 18897b315bb4STakashi Iwai type = cfg->inputs[i].type; 1890f3268512STakashi Iwai for (idx = 0; idx < num_idxs; idx++) 18917b315bb4STakashi Iwai if (pin_idxs[idx] == cfg->inputs[i].pin) 1892c577b8a1SJoseph Chan break; 1893f3268512STakashi Iwai if (idx >= num_idxs) 1894f3268512STakashi Iwai continue; 18957b315bb4STakashi Iwai if (i > 0 && type == cfg->inputs[i - 1].type) 18967b315bb4STakashi Iwai type_idx++; 18977b315bb4STakashi Iwai else 18987b315bb4STakashi Iwai type_idx = 0; 189910a20af7STakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 1900620e2b28STakashi Iwai idx2 = get_connection_index(codec, spec->aa_mix_nid, 1901620e2b28STakashi Iwai pin_idxs[idx]); 190213af8e77STakashi Iwai if (idx2 >= 0) { 190316922281SLydia Wang err = via_new_analog_input(spec, label, type_idx, 1904620e2b28STakashi Iwai idx2, spec->aa_mix_nid); 1905c577b8a1SJoseph Chan if (err < 0) 1906c577b8a1SJoseph Chan return err; 190713af8e77STakashi Iwai add_loopback_list(spec, spec->aa_mix_nid, idx2); 190813af8e77STakashi Iwai } 190910a20af7STakashi Iwai snd_hda_add_imux_item(imux, label, idx, NULL); 1910e3d7a143STakashi Iwai 1911e3d7a143STakashi Iwai /* remember the label for smart51 control */ 1912e3d7a143STakashi Iwai for (j = 0; j < spec->smart51_nums; j++) { 1913e3d7a143STakashi Iwai if (spec->smart51_pins[j] == cfg->inputs[i].pin) { 1914e3d7a143STakashi Iwai spec->smart51_idxs[j] = idx; 1915e3d7a143STakashi Iwai spec->smart51_labels[j] = label; 1916e3d7a143STakashi Iwai break; 1917e3d7a143STakashi Iwai } 1918e3d7a143STakashi Iwai } 1919c577b8a1SJoseph Chan } 1920d7a99cceSTakashi Iwai 1921d7a99cceSTakashi Iwai /* create capture mixer elements */ 1922d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 1923d7a99cceSTakashi Iwai hda_nid_t adc = spec->adc_nids[i]; 1924d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, 1925d7a99cceSTakashi Iwai "Capture Volume", i, 1926d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1927d7a99cceSTakashi Iwai HDA_INPUT)); 1928d7a99cceSTakashi Iwai if (err < 0) 1929d7a99cceSTakashi Iwai return err; 1930d7a99cceSTakashi Iwai err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE, 1931d7a99cceSTakashi Iwai "Capture Switch", i, 1932d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(adc, 3, 0, 1933d7a99cceSTakashi Iwai HDA_INPUT)); 1934d7a99cceSTakashi Iwai if (err < 0) 1935d7a99cceSTakashi Iwai return err; 1936d7a99cceSTakashi Iwai } 1937d7a99cceSTakashi Iwai 1938d7a99cceSTakashi Iwai /* input-source control */ 1939d7a99cceSTakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) 1940d7a99cceSTakashi Iwai if (!spec->mux_nids[i]) 1941d7a99cceSTakashi Iwai break; 1942d7a99cceSTakashi Iwai if (i) { 1943d7a99cceSTakashi Iwai struct snd_kcontrol_new *knew; 1944d7a99cceSTakashi Iwai knew = via_clone_control(spec, &via_input_src_ctl); 1945d7a99cceSTakashi Iwai if (!knew) 1946d7a99cceSTakashi Iwai return -ENOMEM; 1947d7a99cceSTakashi Iwai knew->count = i; 1948d7a99cceSTakashi Iwai } 1949d7a99cceSTakashi Iwai 1950d7a99cceSTakashi Iwai /* mic-boosts */ 1951d7a99cceSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 1952d7a99cceSTakashi Iwai hda_nid_t pin = cfg->inputs[i].pin; 1953d7a99cceSTakashi Iwai unsigned int caps; 1954d7a99cceSTakashi Iwai const char *label; 1955d7a99cceSTakashi Iwai char name[32]; 1956d7a99cceSTakashi Iwai 1957d7a99cceSTakashi Iwai if (cfg->inputs[i].type != AUTO_PIN_MIC) 1958d7a99cceSTakashi Iwai continue; 1959d7a99cceSTakashi Iwai caps = query_amp_caps(codec, pin, HDA_INPUT); 1960d7a99cceSTakashi Iwai if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS)) 1961d7a99cceSTakashi Iwai continue; 1962d7a99cceSTakashi Iwai label = hda_get_autocfg_input_label(codec, cfg, i); 196330f7c5d4STakashi Iwai snprintf(name, sizeof(name), "%s Boost Volume", label); 1964d7a99cceSTakashi Iwai err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 1965d7a99cceSTakashi Iwai HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT)); 1966d7a99cceSTakashi Iwai if (err < 0) 1967d7a99cceSTakashi Iwai return err; 1968d7a99cceSTakashi Iwai } 1969d7a99cceSTakashi Iwai 1970c577b8a1SJoseph Chan return 0; 1971c577b8a1SJoseph Chan } 1972c577b8a1SJoseph Chan 197376d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 197476d9b0ddSHarald Welte { 197576d9b0ddSHarald Welte unsigned int def_conf; 197676d9b0ddSHarald Welte unsigned char seqassoc; 197776d9b0ddSHarald Welte 19782f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 197976d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 198076d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 198182ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 198282ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 198376d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 19842f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 198576d9b0ddSHarald Welte } 198676d9b0ddSHarald Welte 198776d9b0ddSHarald Welte return; 198876d9b0ddSHarald Welte } 198976d9b0ddSHarald Welte 1990e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol, 19911f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 19921f2e99feSLydia Wang { 19931f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 19941f2e99feSLydia Wang struct via_spec *spec = codec->spec; 19951f2e99feSLydia Wang 19961f2e99feSLydia Wang if (spec->codec_type != VT1708) 19971f2e99feSLydia Wang return 0; 1998e06e5a29STakashi Iwai spec->vt1708_jack_detect = 19991f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 2000e06e5a29STakashi Iwai ucontrol->value.integer.value[0] = spec->vt1708_jack_detect; 20011f2e99feSLydia Wang return 0; 20021f2e99feSLydia Wang } 20031f2e99feSLydia Wang 2004e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol, 20051f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 20061f2e99feSLydia Wang { 20071f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 20081f2e99feSLydia Wang struct via_spec *spec = codec->spec; 20091f2e99feSLydia Wang int change; 20101f2e99feSLydia Wang 20111f2e99feSLydia Wang if (spec->codec_type != VT1708) 20121f2e99feSLydia Wang return 0; 2013e06e5a29STakashi Iwai spec->vt1708_jack_detect = ucontrol->value.integer.value[0]; 20141f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 2015e06e5a29STakashi Iwai == !spec->vt1708_jack_detect; 2016e06e5a29STakashi Iwai if (spec->vt1708_jack_detect) { 20171f2e99feSLydia Wang mute_aa_path(codec, 1); 20181f2e99feSLydia Wang notify_aa_path_ctls(codec); 20191f2e99feSLydia Wang } 20201f2e99feSLydia Wang return change; 20211f2e99feSLydia Wang } 20221f2e99feSLydia Wang 2023e06e5a29STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = { 20241f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 20251f2e99feSLydia Wang .name = "Jack Detect", 20261f2e99feSLydia Wang .count = 1, 20271f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 2028e06e5a29STakashi Iwai .get = vt1708_jack_detect_get, 2029e06e5a29STakashi Iwai .put = vt1708_jack_detect_put, 20301f2e99feSLydia Wang }; 20311f2e99feSLydia Wang 203212daef65STakashi Iwai static void fill_dig_outs(struct hda_codec *codec); 203312daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec); 203412daef65STakashi Iwai 203512daef65STakashi Iwai static int via_parse_auto_config(struct hda_codec *codec) 2036c577b8a1SJoseph Chan { 2037c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2038c577b8a1SJoseph Chan int err; 2039c577b8a1SJoseph Chan 2040c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2041c577b8a1SJoseph Chan if (err < 0) 2042c577b8a1SJoseph Chan return err; 2043c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 20447f0df88cSTakashi Iwai return -EINVAL; 2045c577b8a1SJoseph Chan 20464a79616dSTakashi Iwai err = via_auto_create_multi_out_ctls(codec); 2047c577b8a1SJoseph Chan if (err < 0) 2048c577b8a1SJoseph Chan return err; 20494a79616dSTakashi Iwai err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]); 2050c577b8a1SJoseph Chan if (err < 0) 2051c577b8a1SJoseph Chan return err; 20524a918ffeSTakashi Iwai err = via_auto_create_speaker_ctls(codec); 20534a918ffeSTakashi Iwai if (err < 0) 20544a918ffeSTakashi Iwai return err; 2055620e2b28STakashi Iwai err = via_auto_create_analog_input_ctls(codec, &spec->autocfg); 2056c577b8a1SJoseph Chan if (err < 0) 2057c577b8a1SJoseph Chan return err; 2058c577b8a1SJoseph Chan 2059c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2060c577b8a1SJoseph Chan 206112daef65STakashi Iwai fill_dig_outs(codec); 206212daef65STakashi Iwai fill_dig_in(codec); 2063c577b8a1SJoseph Chan 2064603c4019STakashi Iwai if (spec->kctls.list) 2065603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2066c577b8a1SJoseph Chan 2067096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs; 2068c577b8a1SJoseph Chan 20690aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 20700aa62aefSHarald Welte 2071ece8d043STakashi Iwai if (spec->hp_mux) { 2072ece8d043STakashi Iwai err = via_hp_build(codec); 2073ece8d043STakashi Iwai if (err < 0) 2074ece8d043STakashi Iwai return err; 2075ece8d043STakashi Iwai } 2076c577b8a1SJoseph Chan 2077f4a7828bSTakashi Iwai err = via_smart51_build(codec); 2078f4a7828bSTakashi Iwai if (err < 0) 2079f4a7828bSTakashi Iwai return err; 2080f4a7828bSTakashi Iwai 20815d41762aSTakashi Iwai /* assign slave outs */ 20825d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 20835d41762aSTakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 20845d41762aSTakashi Iwai 2085c577b8a1SJoseph Chan return 1; 2086c577b8a1SJoseph Chan } 2087c577b8a1SJoseph Chan 20885d41762aSTakashi Iwai static void via_auto_init_dig_outs(struct hda_codec *codec) 2089c577b8a1SJoseph Chan { 209025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 20915d41762aSTakashi Iwai if (spec->multiout.dig_out_nid) 20925d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT); 20935d41762aSTakashi Iwai if (spec->slave_dig_outs[0]) 20945d41762aSTakashi Iwai init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT); 20955d41762aSTakashi Iwai } 209625eaba2fSLydia Wang 20975d41762aSTakashi Iwai static void via_auto_init_dig_in(struct hda_codec *codec) 20985d41762aSTakashi Iwai { 20995d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 21005d41762aSTakashi Iwai if (!spec->dig_in_nid) 21015d41762aSTakashi Iwai return; 21025d41762aSTakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 21035d41762aSTakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 21045d41762aSTakashi Iwai } 21055d41762aSTakashi Iwai 21064a918ffeSTakashi Iwai /* initialize the unsolicited events */ 21074a918ffeSTakashi Iwai static void via_auto_init_unsol_event(struct hda_codec *codec) 21084a918ffeSTakashi Iwai { 21094a918ffeSTakashi Iwai struct via_spec *spec = codec->spec; 21104a918ffeSTakashi Iwai struct auto_pin_cfg *cfg = &spec->autocfg; 21114a918ffeSTakashi Iwai unsigned int ev; 21124a918ffeSTakashi Iwai int i; 21134a918ffeSTakashi Iwai 21144a918ffeSTakashi Iwai if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0])) 21154a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->hp_pins[0], 0, 21164a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21174a918ffeSTakashi Iwai AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT); 21184a918ffeSTakashi Iwai 21194a918ffeSTakashi Iwai if (cfg->speaker_pins[0]) 21204a918ffeSTakashi Iwai ev = VIA_LINE_EVENT; 21214a918ffeSTakashi Iwai else 21224a918ffeSTakashi Iwai ev = 0; 21234a918ffeSTakashi Iwai for (i = 0; i < cfg->line_outs; i++) { 21244a918ffeSTakashi Iwai if (cfg->line_out_pins[i] && 21254a918ffeSTakashi Iwai is_jack_detectable(codec, cfg->line_out_pins[i])) 21264a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->line_out_pins[0], 0, 21274a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21284a918ffeSTakashi Iwai AC_USRSP_EN | ev | VIA_JACK_EVENT); 21294a918ffeSTakashi Iwai } 21304a918ffeSTakashi Iwai 21314a918ffeSTakashi Iwai for (i = 0; i < cfg->num_inputs; i++) { 21324a918ffeSTakashi Iwai if (is_jack_detectable(codec, cfg->inputs[i].pin)) 21334a918ffeSTakashi Iwai snd_hda_codec_write(codec, cfg->inputs[i].pin, 0, 21344a918ffeSTakashi Iwai AC_VERB_SET_UNSOLICITED_ENABLE, 21354a918ffeSTakashi Iwai AC_USRSP_EN | VIA_JACK_EVENT); 21364a918ffeSTakashi Iwai } 21374a918ffeSTakashi Iwai } 21384a918ffeSTakashi Iwai 21395d41762aSTakashi Iwai static int via_init(struct hda_codec *codec) 21405d41762aSTakashi Iwai { 21415d41762aSTakashi Iwai struct via_spec *spec = codec->spec; 21425d41762aSTakashi Iwai int i; 21435d41762aSTakashi Iwai 21445d41762aSTakashi Iwai for (i = 0; i < spec->num_iverbs; i++) 21455d41762aSTakashi Iwai snd_hda_sequence_write(codec, spec->init_verbs[i]); 21465d41762aSTakashi Iwai 2147c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2148c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 21494a918ffeSTakashi Iwai via_auto_init_speaker_out(codec); 2150c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 21515d41762aSTakashi Iwai via_auto_init_dig_outs(codec); 21525d41762aSTakashi Iwai via_auto_init_dig_in(codec); 215311890956SLydia Wang 21544a918ffeSTakashi Iwai via_auto_init_unsol_event(codec); 21554a918ffeSTakashi Iwai 215625eaba2fSLydia Wang via_hp_automute(codec); 21574a918ffeSTakashi Iwai via_line_automute(codec, false); 215825eaba2fSLydia Wang 2159c577b8a1SJoseph Chan return 0; 2160c577b8a1SJoseph Chan } 2161c577b8a1SJoseph Chan 21621f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 21631f2e99feSLydia Wang { 21641f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 21651f2e99feSLydia Wang vt1708_hp_work.work); 21661f2e99feSLydia Wang if (spec->codec_type != VT1708) 21671f2e99feSLydia Wang return; 21681f2e99feSLydia Wang /* if jack state toggled */ 21691f2e99feSLydia Wang if (spec->vt1708_hp_present 2170d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 21711f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 21721f2e99feSLydia Wang via_hp_automute(spec->codec); 21731f2e99feSLydia Wang } 21741f2e99feSLydia Wang vt1708_start_hp_work(spec); 21751f2e99feSLydia Wang } 21761f2e99feSLydia Wang 2177337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2178337b9d02STakashi Iwai { 2179337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2180337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2181337b9d02STakashi Iwai unsigned int type; 2182337b9d02STakashi Iwai int i, n; 2183337b9d02STakashi Iwai 2184337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2185337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2186337b9d02STakashi Iwai while (nid) { 2187a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 21881c55d521STakashi Iwai if (type == AC_WID_PIN) 21891c55d521STakashi Iwai break; 2190337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2191337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2192337b9d02STakashi Iwai if (n <= 0) 2193337b9d02STakashi Iwai break; 2194337b9d02STakashi Iwai if (n > 1) { 2195337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2196337b9d02STakashi Iwai break; 2197337b9d02STakashi Iwai } 2198337b9d02STakashi Iwai nid = conn[0]; 2199337b9d02STakashi Iwai } 2200337b9d02STakashi Iwai } 22011c55d521STakashi Iwai return 0; 2202337b9d02STakashi Iwai } 2203337b9d02STakashi Iwai 2204c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2205c577b8a1SJoseph Chan { 2206c577b8a1SJoseph Chan struct via_spec *spec; 2207c577b8a1SJoseph Chan int err; 2208c577b8a1SJoseph Chan 2209c577b8a1SJoseph Chan /* create a codec specific record */ 22105b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2211c577b8a1SJoseph Chan if (spec == NULL) 2212c577b8a1SJoseph Chan return -ENOMEM; 2213c577b8a1SJoseph Chan 2214620e2b28STakashi Iwai spec->aa_mix_nid = 0x17; 2215620e2b28STakashi Iwai 221612daef65STakashi Iwai /* Add HP and CD pin config connect bit re-config action */ 221712daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 221812daef65STakashi Iwai vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 221912daef65STakashi Iwai 2220c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 222112daef65STakashi Iwai err = via_parse_auto_config(codec); 2222c577b8a1SJoseph Chan if (err < 0) { 2223c577b8a1SJoseph Chan via_free(codec); 2224c577b8a1SJoseph Chan return err; 2225c577b8a1SJoseph Chan } 2226c577b8a1SJoseph Chan 222712daef65STakashi Iwai /* add jack detect on/off control */ 222812daef65STakashi Iwai if (!via_clone_control(spec, &vt1708_jack_detect_ctl)) 222912daef65STakashi Iwai return -ENOMEM; 223012daef65STakashi Iwai 2231bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2232bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2233bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2234c577b8a1SJoseph Chan 2235c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2236c577b8a1SJoseph Chan 22371f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2238c577b8a1SJoseph Chan return 0; 2239c577b8a1SJoseph Chan } 2240c577b8a1SJoseph Chan 2241c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 2242c577b8a1SJoseph Chan { 2243c577b8a1SJoseph Chan struct via_spec *spec; 2244c577b8a1SJoseph Chan int err; 2245c577b8a1SJoseph Chan 2246c577b8a1SJoseph Chan /* create a codec specific record */ 22475b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2248c577b8a1SJoseph Chan if (spec == NULL) 2249c577b8a1SJoseph Chan return -ENOMEM; 2250c577b8a1SJoseph Chan 2251620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2252620e2b28STakashi Iwai 225312daef65STakashi Iwai err = via_parse_auto_config(codec); 2254c577b8a1SJoseph Chan if (err < 0) { 2255c577b8a1SJoseph Chan via_free(codec); 2256c577b8a1SJoseph Chan return err; 2257c577b8a1SJoseph Chan } 2258c577b8a1SJoseph Chan 2259c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2260c577b8a1SJoseph Chan 2261c577b8a1SJoseph Chan return 0; 2262c577b8a1SJoseph Chan } 2263c577b8a1SJoseph Chan /* 2264c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2265c577b8a1SJoseph Chan */ 2266c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 2267c577b8a1SJoseph Chan { 2268c577b8a1SJoseph Chan struct via_spec *spec; 2269c577b8a1SJoseph Chan int err; 2270c577b8a1SJoseph Chan 2271c577b8a1SJoseph Chan /* create a codec specific record */ 22725b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2273c577b8a1SJoseph Chan if (spec == NULL) 2274c577b8a1SJoseph Chan return -ENOMEM; 2275c577b8a1SJoseph Chan 2276620e2b28STakashi Iwai spec->aa_mix_nid = 0x18; 2277620e2b28STakashi Iwai 227812daef65STakashi Iwai err = via_parse_auto_config(codec); 2279c577b8a1SJoseph Chan if (err < 0) { 2280c577b8a1SJoseph Chan via_free(codec); 2281c577b8a1SJoseph Chan return err; 2282c577b8a1SJoseph Chan } 2283c577b8a1SJoseph Chan 2284c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2285c577b8a1SJoseph Chan 2286f7278fd0SJosepch Chan return 0; 2287f7278fd0SJosepch Chan } 2288f7278fd0SJosepch Chan 22893e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec) 22903e95b9abSLydia Wang { 22913e95b9abSLydia Wang struct via_spec *spec = codec->spec; 22923e95b9abSLydia Wang int imux_is_smixer; 22933e95b9abSLydia Wang unsigned int parm; 22943e95b9abSLydia Wang int is_8ch = 0; 2295bc92df7fSLydia Wang if ((spec->codec_type != VT1708B_4CH) && 2296bc92df7fSLydia Wang (codec->vendor_id != 0x11064397)) 22973e95b9abSLydia Wang is_8ch = 1; 22983e95b9abSLydia Wang 22993e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 23003e95b9abSLydia Wang imux_is_smixer = 23013e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 23023e95b9abSLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0)); 23033e95b9abSLydia Wang /* inputs */ 23043e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 23053e95b9abSLydia Wang parm = AC_PWRST_D3; 23063e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 23073e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 23083e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 23093e95b9abSLydia Wang if (imux_is_smixer) 23103e95b9abSLydia Wang parm = AC_PWRST_D0; 23113e95b9abSLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 23123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 23133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 23143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 23153e95b9abSLydia Wang 23163e95b9abSLydia Wang /* outputs */ 23173e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 23183e95b9abSLydia Wang parm = AC_PWRST_D3; 23193e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 23203e95b9abSLydia Wang if (spec->smart51_enabled) 23213e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 23223e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 23233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 23243e95b9abSLydia Wang 23253e95b9abSLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 23263e95b9abSLydia Wang if (is_8ch) { 23273e95b9abSLydia Wang parm = AC_PWRST_D3; 23283e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 23293e95b9abSLydia Wang if (spec->smart51_enabled) 23303e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 23313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 23323e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 23333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x24, 0, 23343e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2335bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397) { 2336bc92df7fSLydia Wang /* PW7(23h), SW2(27h), AOW2(25h) */ 2337bc92df7fSLydia Wang parm = AC_PWRST_D3; 2338bc92df7fSLydia Wang set_pin_power_state(codec, 0x23, &parm); 2339bc92df7fSLydia Wang if (spec->smart51_enabled) 2340bc92df7fSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 2341bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 2342bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2343bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2344bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 23453e95b9abSLydia Wang } 23463e95b9abSLydia Wang 23473e95b9abSLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 23483e95b9abSLydia Wang parm = AC_PWRST_D3; 23493e95b9abSLydia Wang /* force to D0 for internal Speaker */ 23503e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 23513e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 23523e95b9abSLydia Wang if (is_8ch) 23533e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 23543e95b9abSLydia Wang 23553e95b9abSLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 23563e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 23573e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 23583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 23593e95b9abSLydia Wang if (is_8ch) { 23603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 23613e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 23623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, 23633e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 2364bc92df7fSLydia Wang } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode) 2365bc92df7fSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 2366bc92df7fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 23673e95b9abSLydia Wang } 23683e95b9abSLydia Wang 2369518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 2370f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 2371f7278fd0SJosepch Chan { 2372f7278fd0SJosepch Chan struct via_spec *spec; 2373f7278fd0SJosepch Chan int err; 2374f7278fd0SJosepch Chan 2375518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 2376518bf3baSLydia Wang return patch_vt1708S(codec); 2377f7278fd0SJosepch Chan /* create a codec specific record */ 23785b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2379f7278fd0SJosepch Chan if (spec == NULL) 2380f7278fd0SJosepch Chan return -ENOMEM; 2381f7278fd0SJosepch Chan 2382620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2383620e2b28STakashi Iwai 2384f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 238512daef65STakashi Iwai err = via_parse_auto_config(codec); 2386f7278fd0SJosepch Chan if (err < 0) { 2387f7278fd0SJosepch Chan via_free(codec); 2388f7278fd0SJosepch Chan return err; 2389f7278fd0SJosepch Chan } 2390f7278fd0SJosepch Chan 2391f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2392f7278fd0SJosepch Chan 23933e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 23943e95b9abSLydia Wang 2395f7278fd0SJosepch Chan return 0; 2396f7278fd0SJosepch Chan } 2397f7278fd0SJosepch Chan 2398f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 2399f7278fd0SJosepch Chan { 2400f7278fd0SJosepch Chan struct via_spec *spec; 2401f7278fd0SJosepch Chan int err; 2402f7278fd0SJosepch Chan 2403f7278fd0SJosepch Chan /* create a codec specific record */ 24045b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2405f7278fd0SJosepch Chan if (spec == NULL) 2406f7278fd0SJosepch Chan return -ENOMEM; 2407f7278fd0SJosepch Chan 2408f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 240912daef65STakashi Iwai err = via_parse_auto_config(codec); 2410f7278fd0SJosepch Chan if (err < 0) { 2411f7278fd0SJosepch Chan via_free(codec); 2412f7278fd0SJosepch Chan return err; 2413f7278fd0SJosepch Chan } 2414f7278fd0SJosepch Chan 2415f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 2416f7278fd0SJosepch Chan 24173e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 24183e95b9abSLydia Wang 2419c577b8a1SJoseph Chan return 0; 2420c577b8a1SJoseph Chan } 2421c577b8a1SJoseph Chan 2422d949cac1SHarald Welte /* Patch for VT1708S */ 2423096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = { 2424d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 2425d7426329SHarald Welte {0x1, 0xf98, 0x1}, 2426bc7e7e5cSLydia Wang /* don't bybass mixer */ 2427bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 2428d949cac1SHarald Welte { } 2429d949cac1SHarald Welte }; 2430d949cac1SHarald Welte 24319da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 24329da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 24339da29271STakashi Iwai { 24349da29271STakashi Iwai struct via_spec *spec = codec->spec; 24359da29271STakashi Iwai int i; 24369da29271STakashi Iwai 24379da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 24389da29271STakashi Iwai hda_nid_t nid; 24399da29271STakashi Iwai int conn; 24409da29271STakashi Iwai 24419da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 24429da29271STakashi Iwai if (!nid) 24439da29271STakashi Iwai continue; 24449da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 24459da29271STakashi Iwai if (conn < 1) 24469da29271STakashi Iwai continue; 24479da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 24489da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 24499da29271STakashi Iwai else { 24509da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 24519da29271STakashi Iwai break; /* at most two dig outs */ 24529da29271STakashi Iwai } 24539da29271STakashi Iwai } 24549da29271STakashi Iwai } 24559da29271STakashi Iwai 245612daef65STakashi Iwai static void fill_dig_in(struct hda_codec *codec) 2457d949cac1SHarald Welte { 2458d949cac1SHarald Welte struct via_spec *spec = codec->spec; 245912daef65STakashi Iwai hda_nid_t dig_nid; 246012daef65STakashi Iwai int i, err; 2461d949cac1SHarald Welte 246212daef65STakashi Iwai if (!spec->autocfg.dig_in_pin) 246312daef65STakashi Iwai return; 2464d949cac1SHarald Welte 246512daef65STakashi Iwai dig_nid = codec->start_nid; 246612daef65STakashi Iwai for (i = 0; i < codec->num_nodes; i++, dig_nid++) { 246712daef65STakashi Iwai unsigned int wcaps = get_wcaps(codec, dig_nid); 246812daef65STakashi Iwai if (get_wcaps_type(wcaps) != AC_WID_AUD_IN) 246912daef65STakashi Iwai continue; 247012daef65STakashi Iwai if (!(wcaps & AC_WCAP_DIGITAL)) 247112daef65STakashi Iwai continue; 247212daef65STakashi Iwai if (!(wcaps & AC_WCAP_CONN_LIST)) 247312daef65STakashi Iwai continue; 247412daef65STakashi Iwai err = get_connection_index(codec, dig_nid, 247512daef65STakashi Iwai spec->autocfg.dig_in_pin); 247612daef65STakashi Iwai if (err >= 0) { 247712daef65STakashi Iwai spec->dig_in_nid = dig_nid; 247812daef65STakashi Iwai break; 247912daef65STakashi Iwai } 248012daef65STakashi Iwai } 2481d949cac1SHarald Welte } 2482d949cac1SHarald Welte 24836369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 24846369bcfcSLydia Wang int offset, int num_steps, int step_size) 24856369bcfcSLydia Wang { 24866369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 24876369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 24886369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 24896369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 24906369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 24916369bcfcSLydia Wang } 24926369bcfcSLydia Wang 2493d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 2494d949cac1SHarald Welte { 2495d949cac1SHarald Welte struct via_spec *spec; 2496d949cac1SHarald Welte int err; 2497d949cac1SHarald Welte 2498d949cac1SHarald Welte /* create a codec specific record */ 24995b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2500d949cac1SHarald Welte if (spec == NULL) 2501d949cac1SHarald Welte return -ENOMEM; 2502d949cac1SHarald Welte 2503620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2504d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2505d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2506620e2b28STakashi Iwai 2507d949cac1SHarald Welte /* automatic parse from the BIOS config */ 250812daef65STakashi Iwai err = via_parse_auto_config(codec); 2509d949cac1SHarald Welte if (err < 0) { 2510d949cac1SHarald Welte via_free(codec); 2511d949cac1SHarald Welte return err; 2512d949cac1SHarald Welte } 2513d949cac1SHarald Welte 2514096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs; 2515d949cac1SHarald Welte 2516d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2517d949cac1SHarald Welte 2518518bf3baSLydia Wang /* correct names for VT1708BCE */ 2519518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 2520518bf3baSLydia Wang kfree(codec->chip_name); 2521518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 2522518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 2523518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 2524518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2525970f630fSLydia Wang } 2526bc92df7fSLydia Wang /* correct names for VT1705 */ 2527bc92df7fSLydia Wang if (codec->vendor_id == 0x11064397) { 2528bc92df7fSLydia Wang kfree(codec->chip_name); 2529bc92df7fSLydia Wang codec->chip_name = kstrdup("VT1705", GFP_KERNEL); 2530bc92df7fSLydia Wang snprintf(codec->bus->card->mixername, 2531bc92df7fSLydia Wang sizeof(codec->bus->card->mixername), 2532bc92df7fSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 2533bc92df7fSLydia Wang } 25343e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1708B; 2535d949cac1SHarald Welte return 0; 2536d949cac1SHarald Welte } 2537d949cac1SHarald Welte 2538d949cac1SHarald Welte /* Patch for VT1702 */ 2539d949cac1SHarald Welte 2540096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = { 2541bc7e7e5cSLydia Wang /* mixer enable */ 2542bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 2543bc7e7e5cSLydia Wang /* GPIO 0~2 */ 2544bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 2545d949cac1SHarald Welte { } 2546d949cac1SHarald Welte }; 2547d949cac1SHarald Welte 25483e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec) 25493e95b9abSLydia Wang { 25503e95b9abSLydia Wang int imux_is_smixer = 25513e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 25523e95b9abSLydia Wang unsigned int parm; 25533e95b9abSLydia Wang /* inputs */ 25543e95b9abSLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 25553e95b9abSLydia Wang parm = AC_PWRST_D3; 25563e95b9abSLydia Wang set_pin_power_state(codec, 0x14, &parm); 25573e95b9abSLydia Wang set_pin_power_state(codec, 0x15, &parm); 25583e95b9abSLydia Wang set_pin_power_state(codec, 0x18, &parm); 25593e95b9abSLydia Wang if (imux_is_smixer) 25603e95b9abSLydia Wang parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */ 25613e95b9abSLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 25623e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 25633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm); 25643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 25653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm); 25663e95b9abSLydia Wang 25673e95b9abSLydia Wang /* outputs */ 25683e95b9abSLydia Wang /* PW 3/4 (16h/17h) */ 25693e95b9abSLydia Wang parm = AC_PWRST_D3; 25703e95b9abSLydia Wang set_pin_power_state(codec, 0x17, &parm); 25713e95b9abSLydia Wang set_pin_power_state(codec, 0x16, &parm); 25723e95b9abSLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 25733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 25743e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 25753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 25763e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 25773e95b9abSLydia Wang } 25783e95b9abSLydia Wang 2579d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 2580d949cac1SHarald Welte { 2581d949cac1SHarald Welte struct via_spec *spec; 2582d949cac1SHarald Welte int err; 2583d949cac1SHarald Welte 2584d949cac1SHarald Welte /* create a codec specific record */ 25855b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2586d949cac1SHarald Welte if (spec == NULL) 2587d949cac1SHarald Welte return -ENOMEM; 2588d949cac1SHarald Welte 2589620e2b28STakashi Iwai spec->aa_mix_nid = 0x1a; 2590620e2b28STakashi Iwai 259112daef65STakashi Iwai /* limit AA path volume to 0 dB */ 259212daef65STakashi Iwai snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 259312daef65STakashi Iwai (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 259412daef65STakashi Iwai (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 259512daef65STakashi Iwai (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 259612daef65STakashi Iwai (1 << AC_AMPCAP_MUTE_SHIFT)); 259712daef65STakashi Iwai 2598d949cac1SHarald Welte /* automatic parse from the BIOS config */ 259912daef65STakashi Iwai err = via_parse_auto_config(codec); 2600d949cac1SHarald Welte if (err < 0) { 2601d949cac1SHarald Welte via_free(codec); 2602d949cac1SHarald Welte return err; 2603d949cac1SHarald Welte } 2604d949cac1SHarald Welte 2605096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs; 2606d949cac1SHarald Welte 2607d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 2608d949cac1SHarald Welte 26093e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1702; 2610d949cac1SHarald Welte return 0; 2611d949cac1SHarald Welte } 2612d949cac1SHarald Welte 2613eb7188caSLydia Wang /* Patch for VT1718S */ 2614eb7188caSLydia Wang 2615096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = { 26164ab2d53aSLydia Wang /* Enable MW0 adjust Gain 5 */ 26174ab2d53aSLydia Wang {0x1, 0xfb2, 0x10}, 2618eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 2619eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 26205d41762aSTakashi Iwai 2621eb7188caSLydia Wang { } 2622eb7188caSLydia Wang }; 2623eb7188caSLydia Wang 26243e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec) 26253e95b9abSLydia Wang { 26263e95b9abSLydia Wang struct via_spec *spec = codec->spec; 26273e95b9abSLydia Wang int imux_is_smixer; 26283e95b9abSLydia Wang unsigned int parm; 26293e95b9abSLydia Wang /* MUX6 (1eh) = stereo mixer */ 26303e95b9abSLydia Wang imux_is_smixer = 26313e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 26323e95b9abSLydia Wang /* inputs */ 26333e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 26343e95b9abSLydia Wang parm = AC_PWRST_D3; 26353e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 26363e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 26373e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 26383e95b9abSLydia Wang if (imux_is_smixer) 26393e95b9abSLydia Wang parm = AC_PWRST_D0; 26403e95b9abSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 26413e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 26423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 26433e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 26443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 26453e95b9abSLydia Wang 26463e95b9abSLydia Wang /* outputs */ 26473e95b9abSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 26483e95b9abSLydia Wang parm = AC_PWRST_D3; 26493e95b9abSLydia Wang set_pin_power_state(codec, 0x27, &parm); 26503e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm); 26513e95b9abSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm); 26523e95b9abSLydia Wang 26533e95b9abSLydia Wang /* PW2 (26h), AOW2 (ah) */ 26543e95b9abSLydia Wang parm = AC_PWRST_D3; 26553e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 26563e95b9abSLydia Wang if (spec->smart51_enabled) 26573e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 26583e95b9abSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm); 26593e95b9abSLydia Wang 26603e95b9abSLydia Wang /* PW0 (24h), AOW0 (8h) */ 26613e95b9abSLydia Wang parm = AC_PWRST_D3; 26623e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 26633e95b9abSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 26643e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 26653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 26663e95b9abSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 26673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 26683e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 26693e95b9abSLydia Wang 26703e95b9abSLydia Wang /* PW1 (25h), AOW1 (9h) */ 26713e95b9abSLydia Wang parm = AC_PWRST_D3; 26723e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 26733e95b9abSLydia Wang if (spec->smart51_enabled) 26743e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 26753e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm); 26763e95b9abSLydia Wang 26773e95b9abSLydia Wang if (spec->hp_independent_mode) { 26783e95b9abSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 26793e95b9abSLydia Wang parm = AC_PWRST_D3; 26803e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 26813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 26823e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 26833e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 26843e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 26853e95b9abSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 26863e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 26873e95b9abSLydia Wang } 26883e95b9abSLydia Wang } 26893e95b9abSLydia Wang 2690eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 2691eb7188caSLydia Wang { 2692eb7188caSLydia Wang struct via_spec *spec; 2693eb7188caSLydia Wang int err; 2694eb7188caSLydia Wang 2695eb7188caSLydia Wang /* create a codec specific record */ 26965b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2697eb7188caSLydia Wang if (spec == NULL) 2698eb7188caSLydia Wang return -ENOMEM; 2699eb7188caSLydia Wang 2700620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 2701d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 2702d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 2703620e2b28STakashi Iwai 2704eb7188caSLydia Wang /* automatic parse from the BIOS config */ 270512daef65STakashi Iwai err = via_parse_auto_config(codec); 2706eb7188caSLydia Wang if (err < 0) { 2707eb7188caSLydia Wang via_free(codec); 2708eb7188caSLydia Wang return err; 2709eb7188caSLydia Wang } 2710eb7188caSLydia Wang 2711096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs; 2712eb7188caSLydia Wang 2713eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 2714eb7188caSLydia Wang 27153e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1718S; 27163e95b9abSLydia Wang 2717eb7188caSLydia Wang return 0; 2718eb7188caSLydia Wang } 2719f3db423dSLydia Wang 2720f3db423dSLydia Wang /* Patch for VT1716S */ 2721f3db423dSLydia Wang 2722f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 2723f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 2724f3db423dSLydia Wang { 2725f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 2726f3db423dSLydia Wang uinfo->count = 1; 2727f3db423dSLydia Wang uinfo->value.integer.min = 0; 2728f3db423dSLydia Wang uinfo->value.integer.max = 1; 2729f3db423dSLydia Wang return 0; 2730f3db423dSLydia Wang } 2731f3db423dSLydia Wang 2732f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 2733f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2734f3db423dSLydia Wang { 2735f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2736f3db423dSLydia Wang int index = 0; 2737f3db423dSLydia Wang 2738f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 2739f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 2740f3db423dSLydia Wang if (index != -1) 2741f3db423dSLydia Wang *ucontrol->value.integer.value = index; 2742f3db423dSLydia Wang 2743f3db423dSLydia Wang return 0; 2744f3db423dSLydia Wang } 2745f3db423dSLydia Wang 2746f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 2747f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 2748f3db423dSLydia Wang { 2749f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 2750f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2751f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 2752f3db423dSLydia Wang 2753f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 2754f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 2755f3db423dSLydia Wang spec->dmic_enabled = index; 27563e95b9abSLydia Wang set_widgets_power_state(codec); 2757f3db423dSLydia Wang return 1; 2758f3db423dSLydia Wang } 2759f3db423dSLydia Wang 276090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 2761f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 2762f3db423dSLydia Wang { 2763f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2764f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 27655b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 2766f3db423dSLydia Wang .count = 1, 2767f3db423dSLydia Wang .info = vt1716s_dmic_info, 2768f3db423dSLydia Wang .get = vt1716s_dmic_get, 2769f3db423dSLydia Wang .put = vt1716s_dmic_put, 2770f3db423dSLydia Wang }, 2771f3db423dSLydia Wang {} /* end */ 2772f3db423dSLydia Wang }; 2773f3db423dSLydia Wang 2774f3db423dSLydia Wang 2775f3db423dSLydia Wang /* mono-out mixer elements */ 277690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 2777f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 2778f3db423dSLydia Wang { } /* end */ 2779f3db423dSLydia Wang }; 2780f3db423dSLydia Wang 2781096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = { 2782f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 2783f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 2784f3db423dSLydia Wang /* don't bybass mixer */ 2785f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 2786f3db423dSLydia Wang /* Enable mono output */ 2787f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 2788f3db423dSLydia Wang { } 2789f3db423dSLydia Wang }; 2790f3db423dSLydia Wang 27913e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec) 27923e95b9abSLydia Wang { 27933e95b9abSLydia Wang struct via_spec *spec = codec->spec; 27943e95b9abSLydia Wang int imux_is_smixer; 27953e95b9abSLydia Wang unsigned int parm; 27963e95b9abSLydia Wang unsigned int mono_out, present; 27973e95b9abSLydia Wang /* SW0 (17h) = stereo mixer */ 27983e95b9abSLydia Wang imux_is_smixer = 27993e95b9abSLydia Wang (snd_hda_codec_read(codec, 0x17, 0, 28003e95b9abSLydia Wang AC_VERB_GET_CONNECT_SEL, 0x00) == 5); 28013e95b9abSLydia Wang /* inputs */ 28023e95b9abSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 28033e95b9abSLydia Wang parm = AC_PWRST_D3; 28043e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28053e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28063e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28073e95b9abSLydia Wang if (imux_is_smixer) 28083e95b9abSLydia Wang parm = AC_PWRST_D0; 28093e95b9abSLydia Wang /* SW0 (17h), AIW0(13h) */ 28103e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm); 28113e95b9abSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm); 28123e95b9abSLydia Wang 28133e95b9abSLydia Wang parm = AC_PWRST_D3; 28143e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28153e95b9abSLydia Wang /* PW11 (22h) */ 28163e95b9abSLydia Wang if (spec->dmic_enabled) 28173e95b9abSLydia Wang set_pin_power_state(codec, 0x22, &parm); 28183e95b9abSLydia Wang else 28193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x22, 0, 28203e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 28213e95b9abSLydia Wang 28223e95b9abSLydia Wang /* SW2(26h), AIW1(14h) */ 28233e95b9abSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm); 28243e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm); 28253e95b9abSLydia Wang 28263e95b9abSLydia Wang /* outputs */ 28273e95b9abSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 28283e95b9abSLydia Wang parm = AC_PWRST_D3; 28293e95b9abSLydia Wang set_pin_power_state(codec, 0x19, &parm); 28303e95b9abSLydia Wang /* Smart 5.1 PW2(1bh) */ 28313e95b9abSLydia Wang if (spec->smart51_enabled) 28323e95b9abSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 28333e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 28343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 28353e95b9abSLydia Wang 28363e95b9abSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 28373e95b9abSLydia Wang parm = AC_PWRST_D3; 28383e95b9abSLydia Wang set_pin_power_state(codec, 0x23, &parm); 28393e95b9abSLydia Wang /* Smart 5.1 PW1(1ah) */ 28403e95b9abSLydia Wang if (spec->smart51_enabled) 28413e95b9abSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 28423e95b9abSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm); 28433e95b9abSLydia Wang 28443e95b9abSLydia Wang /* Smart 5.1 PW5(1eh) */ 28453e95b9abSLydia Wang if (spec->smart51_enabled) 28463e95b9abSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 28473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm); 28483e95b9abSLydia Wang 28493e95b9abSLydia Wang /* Mono out */ 28503e95b9abSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 28513e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1c); 28523e95b9abSLydia Wang 28533e95b9abSLydia Wang if (present) 28543e95b9abSLydia Wang mono_out = 0; 28553e95b9abSLydia Wang else { 28563e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x1d); 28573e95b9abSLydia Wang if (!spec->hp_independent_mode && present) 28583e95b9abSLydia Wang mono_out = 0; 28593e95b9abSLydia Wang else 28603e95b9abSLydia Wang mono_out = 1; 28613e95b9abSLydia Wang } 28623e95b9abSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 28633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm); 28643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm); 28653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm); 28663e95b9abSLydia Wang 28673e95b9abSLydia Wang /* PW 3/4 (1ch/1dh) */ 28683e95b9abSLydia Wang parm = AC_PWRST_D3; 28693e95b9abSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 28703e95b9abSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 28713e95b9abSLydia Wang /* HP Independent Mode, power on AOW3 */ 28723e95b9abSLydia Wang if (spec->hp_independent_mode) 28733e95b9abSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 28743e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 28753e95b9abSLydia Wang 28763e95b9abSLydia Wang /* force to D0 for internal Speaker */ 28773e95b9abSLydia Wang /* MW0 (16h), AOW0 (10h) */ 28783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 28793e95b9abSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 28803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 28813e95b9abSLydia Wang mono_out ? AC_PWRST_D0 : parm); 28823e95b9abSLydia Wang } 28833e95b9abSLydia Wang 2884f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 2885f3db423dSLydia Wang { 2886f3db423dSLydia Wang struct via_spec *spec; 2887f3db423dSLydia Wang int err; 2888f3db423dSLydia Wang 2889f3db423dSLydia Wang /* create a codec specific record */ 28905b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2891f3db423dSLydia Wang if (spec == NULL) 2892f3db423dSLydia Wang return -ENOMEM; 2893f3db423dSLydia Wang 2894620e2b28STakashi Iwai spec->aa_mix_nid = 0x16; 2895d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1a, 0, 3, 40); 2896d7a99cceSTakashi Iwai override_mic_boost(codec, 0x1e, 0, 3, 40); 2897620e2b28STakashi Iwai 2898f3db423dSLydia Wang /* automatic parse from the BIOS config */ 289912daef65STakashi Iwai err = via_parse_auto_config(codec); 2900f3db423dSLydia Wang if (err < 0) { 2901f3db423dSLydia Wang via_free(codec); 2902f3db423dSLydia Wang return err; 2903f3db423dSLydia Wang } 2904f3db423dSLydia Wang 2905096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs; 2906f3db423dSLydia Wang 2907f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 2908f3db423dSLydia Wang spec->num_mixers++; 2909f3db423dSLydia Wang 2910f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 2911f3db423dSLydia Wang 2912f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 2913f3db423dSLydia Wang 29143e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1716S; 2915f3db423dSLydia Wang return 0; 2916f3db423dSLydia Wang } 291725eaba2fSLydia Wang 291825eaba2fSLydia Wang /* for vt2002P */ 291925eaba2fSLydia Wang 2920096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = { 2921eadb9a80SLydia Wang /* Class-D speaker related verbs */ 2922eadb9a80SLydia Wang {0x1, 0xfe0, 0x4}, 2923eadb9a80SLydia Wang {0x1, 0xfe9, 0x80}, 2924eadb9a80SLydia Wang {0x1, 0xfe2, 0x22}, 292525eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 292625eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 292725eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 292825eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 292925eaba2fSLydia Wang { } 293025eaba2fSLydia Wang }; 29314a918ffeSTakashi Iwai 2932096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = { 293311890956SLydia Wang /* Enable Boost Volume backdoor */ 293411890956SLydia Wang {0x1, 0xfb9, 0x24}, 293511890956SLydia Wang /* Enable AOW0 to MW9 */ 293611890956SLydia Wang {0x1, 0xfb8, 0x88}, 293711890956SLydia Wang { } 293811890956SLydia Wang }; 293925eaba2fSLydia Wang 29403e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec) 29413e95b9abSLydia Wang { 29423e95b9abSLydia Wang struct via_spec *spec = codec->spec; 29433e95b9abSLydia Wang int imux_is_smixer; 29443e95b9abSLydia Wang unsigned int parm; 29453e95b9abSLydia Wang unsigned int present; 29463e95b9abSLydia Wang /* MUX9 (1eh) = stereo mixer */ 29473e95b9abSLydia Wang imux_is_smixer = 29483e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 29493e95b9abSLydia Wang /* inputs */ 29503e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 29513e95b9abSLydia Wang parm = AC_PWRST_D3; 29523e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 29533e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 29543e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 29553e95b9abSLydia Wang parm = AC_PWRST_D0; 29563e95b9abSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 29573e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 29583e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 29593e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 29603e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 29613e95b9abSLydia Wang 29623e95b9abSLydia Wang /* outputs */ 29633e95b9abSLydia Wang /* AOW0 (8h)*/ 29643e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm); 29653e95b9abSLydia Wang 296611890956SLydia Wang if (spec->codec_type == VT1802) { 296711890956SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 296811890956SLydia Wang parm = AC_PWRST_D3; 296911890956SLydia Wang set_pin_power_state(codec, 0x28, &parm); 297011890956SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 297111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 297211890956SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 297311890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 297411890956SLydia Wang } else { 29753e95b9abSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 29763e95b9abSLydia Wang parm = AC_PWRST_D3; 29773e95b9abSLydia Wang set_pin_power_state(codec, 0x26, &parm); 29783e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 29793e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29803e95b9abSLydia Wang snd_hda_codec_write(codec, 0x37, 0, 29813e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 298211890956SLydia Wang } 29833e95b9abSLydia Wang 298411890956SLydia Wang if (spec->codec_type == VT1802) { 298511890956SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 298611890956SLydia Wang parm = AC_PWRST_D3; 298711890956SLydia Wang set_pin_power_state(codec, 0x25, &parm); 298811890956SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 298911890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 299011890956SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 299111890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 299211890956SLydia Wang } else { 29933e95b9abSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 29943e95b9abSLydia Wang parm = AC_PWRST_D3; 29953e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 29963e95b9abSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 29973e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 29983e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 29993e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 300011890956SLydia Wang } 30013e95b9abSLydia Wang 30023e95b9abSLydia Wang if (spec->hp_independent_mode) 30033e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 30043e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 30053e95b9abSLydia Wang 30063e95b9abSLydia Wang /* Class-D */ 30073e95b9abSLydia Wang /* PW0 (24h), MW0(18h/14h), MUX0(34h) */ 30083e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 30093e95b9abSLydia Wang 30103e95b9abSLydia Wang parm = AC_PWRST_D3; 30113e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 30123e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 301311890956SLydia Wang if (spec->codec_type == VT1802) 301411890956SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 301511890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 301611890956SLydia Wang else 30173e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, 30183e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm); 30203e95b9abSLydia Wang 30213e95b9abSLydia Wang /* Mono Out */ 30223e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x26); 30233e95b9abSLydia Wang 30243e95b9abSLydia Wang parm = present ? AC_PWRST_D3 : AC_PWRST_D0; 302511890956SLydia Wang if (spec->codec_type == VT1802) { 302611890956SLydia Wang /* PW15 (33h), MW8(1ch), MUX8(3ch) */ 302711890956SLydia Wang snd_hda_codec_write(codec, 0x33, 0, 302811890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 302911890956SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 303011890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 303111890956SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 303211890956SLydia Wang AC_VERB_SET_POWER_STATE, parm); 303311890956SLydia Wang } else { 30343e95b9abSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 30353e95b9abSLydia Wang snd_hda_codec_write(codec, 0x31, 0, 30363e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30373e95b9abSLydia Wang snd_hda_codec_write(codec, 0x17, 0, 30383e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 30393e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3b, 0, 30403e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, parm); 304111890956SLydia Wang } 30423e95b9abSLydia Wang /* MW9 (21h) */ 30433e95b9abSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 30443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 30453e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 30463e95b9abSLydia Wang else 30473e95b9abSLydia Wang snd_hda_codec_write(codec, 0x21, 0, 30483e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 30493e95b9abSLydia Wang } 305025eaba2fSLydia Wang 305125eaba2fSLydia Wang /* patch for vt2002P */ 305225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 305325eaba2fSLydia Wang { 305425eaba2fSLydia Wang struct via_spec *spec; 305525eaba2fSLydia Wang int err; 305625eaba2fSLydia Wang 305725eaba2fSLydia Wang /* create a codec specific record */ 30585b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 305925eaba2fSLydia Wang if (spec == NULL) 306025eaba2fSLydia Wang return -ENOMEM; 306125eaba2fSLydia Wang 3062620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3063d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3064d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3065620e2b28STakashi Iwai 306625eaba2fSLydia Wang /* automatic parse from the BIOS config */ 306712daef65STakashi Iwai err = via_parse_auto_config(codec); 306825eaba2fSLydia Wang if (err < 0) { 306925eaba2fSLydia Wang via_free(codec); 307025eaba2fSLydia Wang return err; 307125eaba2fSLydia Wang } 307225eaba2fSLydia Wang 307311890956SLydia Wang if (spec->codec_type == VT1802) 30744a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs; 307511890956SLydia Wang else 30764a918ffeSTakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs; 307711890956SLydia Wang 307825eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 307925eaba2fSLydia Wang 30803e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt2002P; 308125eaba2fSLydia Wang return 0; 308225eaba2fSLydia Wang } 3083ab6734e7SLydia Wang 3084ab6734e7SLydia Wang /* for vt1812 */ 3085ab6734e7SLydia Wang 3086096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = { 3087ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 3088ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 3089ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 3090ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 3091ab6734e7SLydia Wang { } 3092ab6734e7SLydia Wang }; 3093ab6734e7SLydia Wang 30943e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec) 30953e95b9abSLydia Wang { 30963e95b9abSLydia Wang struct via_spec *spec = codec->spec; 30973e95b9abSLydia Wang int imux_is_smixer = 30983e95b9abSLydia Wang snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 30993e95b9abSLydia Wang unsigned int parm; 31003e95b9abSLydia Wang unsigned int present; 31013e95b9abSLydia Wang /* MUX10 (1eh) = stereo mixer */ 31023e95b9abSLydia Wang imux_is_smixer = 31033e95b9abSLydia Wang snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 31043e95b9abSLydia Wang /* inputs */ 31053e95b9abSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 31063e95b9abSLydia Wang parm = AC_PWRST_D3; 31073e95b9abSLydia Wang set_pin_power_state(codec, 0x29, &parm); 31083e95b9abSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 31093e95b9abSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 31103e95b9abSLydia Wang parm = AC_PWRST_D0; 31113e95b9abSLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 31123e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm); 31133e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm); 31143e95b9abSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm); 31153e95b9abSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm); 31163e95b9abSLydia Wang 31173e95b9abSLydia Wang /* outputs */ 31183e95b9abSLydia Wang /* AOW0 (8h)*/ 31193e95b9abSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 31203e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31213e95b9abSLydia Wang 31223e95b9abSLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 31233e95b9abSLydia Wang parm = AC_PWRST_D3; 31243e95b9abSLydia Wang set_pin_power_state(codec, 0x28, &parm); 31253e95b9abSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm); 31263e95b9abSLydia Wang snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm); 31273e95b9abSLydia Wang 31283e95b9abSLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 31293e95b9abSLydia Wang parm = AC_PWRST_D3; 31303e95b9abSLydia Wang set_pin_power_state(codec, 0x25, &parm); 31313e95b9abSLydia Wang snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm); 31323e95b9abSLydia Wang snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm); 31333e95b9abSLydia Wang if (spec->hp_independent_mode) 31343e95b9abSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 31353e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31363e95b9abSLydia Wang 31373e95b9abSLydia Wang /* Internal Speaker */ 31383e95b9abSLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 31393e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x25); 31403e95b9abSLydia Wang 31413e95b9abSLydia Wang parm = AC_PWRST_D3; 31423e95b9abSLydia Wang set_pin_power_state(codec, 0x24, &parm); 31433e95b9abSLydia Wang if (present) { 31443e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 31453e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 31463e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 31473e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 31483e95b9abSLydia Wang } else { 31493e95b9abSLydia Wang snd_hda_codec_write(codec, 0x14, 0, 31503e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31513e95b9abSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 31523e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31533e95b9abSLydia Wang } 31543e95b9abSLydia Wang 31553e95b9abSLydia Wang 31563e95b9abSLydia Wang /* Mono Out */ 31573e95b9abSLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 31583e95b9abSLydia Wang present = snd_hda_jack_detect(codec, 0x28); 31593e95b9abSLydia Wang 31603e95b9abSLydia Wang parm = AC_PWRST_D3; 31613e95b9abSLydia Wang set_pin_power_state(codec, 0x31, &parm); 31623e95b9abSLydia Wang if (present) { 31633e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 31643e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 31653e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 31663e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 31673e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 31683e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 31693e95b9abSLydia Wang } else { 31703e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 31713e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31723e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 31733e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31743e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 31753e95b9abSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 31763e95b9abSLydia Wang } 31773e95b9abSLydia Wang 31783e95b9abSLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 31793e95b9abSLydia Wang parm = AC_PWRST_D3; 31803e95b9abSLydia Wang set_pin_power_state(codec, 0x33, &parm); 31813e95b9abSLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm); 31823e95b9abSLydia Wang snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm); 31833e95b9abSLydia Wang 31843e95b9abSLydia Wang } 3185ab6734e7SLydia Wang 3186ab6734e7SLydia Wang /* patch for vt1812 */ 3187ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 3188ab6734e7SLydia Wang { 3189ab6734e7SLydia Wang struct via_spec *spec; 3190ab6734e7SLydia Wang int err; 3191ab6734e7SLydia Wang 3192ab6734e7SLydia Wang /* create a codec specific record */ 31935b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3194ab6734e7SLydia Wang if (spec == NULL) 3195ab6734e7SLydia Wang return -ENOMEM; 3196ab6734e7SLydia Wang 3197620e2b28STakashi Iwai spec->aa_mix_nid = 0x21; 3198d7a99cceSTakashi Iwai override_mic_boost(codec, 0x2b, 0, 3, 40); 3199d7a99cceSTakashi Iwai override_mic_boost(codec, 0x29, 0, 3, 40); 3200620e2b28STakashi Iwai 3201ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 320212daef65STakashi Iwai err = via_parse_auto_config(codec); 3203ab6734e7SLydia Wang if (err < 0) { 3204ab6734e7SLydia Wang via_free(codec); 3205ab6734e7SLydia Wang return err; 3206ab6734e7SLydia Wang } 3207ab6734e7SLydia Wang 3208096a8854STakashi Iwai spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs; 3209ab6734e7SLydia Wang 3210ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 3211ab6734e7SLydia Wang 32123e95b9abSLydia Wang spec->set_widgets_power_state = set_widgets_power_state_vt1812; 3213ab6734e7SLydia Wang return 0; 3214ab6734e7SLydia Wang } 3215ab6734e7SLydia Wang 3216c577b8a1SJoseph Chan /* 3217c577b8a1SJoseph Chan * patch entries 3218c577b8a1SJoseph Chan */ 321990dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = { 32203218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 32213218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 32223218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 32233218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 32243218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 3225f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 32263218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 3227f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 32283218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 3229f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 32303218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 3231f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 32323218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 3233f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 32343218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 3235f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 32363218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 3237f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 32383218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 3239f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 32403218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 3241f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 32423218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 3243f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 32443218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 3245f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 32463218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 3247f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 32483218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 3249f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 32503218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 3251f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 32523218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 3253f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 32543218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 3255f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 32563218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 3257d949cac1SHarald Welte .patch = patch_vt1708S}, 32583218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 3259d949cac1SHarald Welte .patch = patch_vt1708S}, 32603218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 3261d949cac1SHarald Welte .patch = patch_vt1708S}, 32623218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 3263d949cac1SHarald Welte .patch = patch_vt1708S}, 3264bc92df7fSLydia Wang { .id = 0x11064397, .name = "VT1705", 3265d949cac1SHarald Welte .patch = patch_vt1708S}, 32663218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 3267d949cac1SHarald Welte .patch = patch_vt1708S}, 32683218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 3269d949cac1SHarald Welte .patch = patch_vt1708S}, 32703218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 3271d949cac1SHarald Welte .patch = patch_vt1708S}, 32723218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 3273d949cac1SHarald Welte .patch = patch_vt1702}, 32743218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 3275d949cac1SHarald Welte .patch = patch_vt1702}, 32763218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 3277d949cac1SHarald Welte .patch = patch_vt1702}, 32783218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 3279d949cac1SHarald Welte .patch = patch_vt1702}, 32803218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 3281d949cac1SHarald Welte .patch = patch_vt1702}, 32823218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 3283d949cac1SHarald Welte .patch = patch_vt1702}, 32843218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 3285d949cac1SHarald Welte .patch = patch_vt1702}, 32863218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 3287d949cac1SHarald Welte .patch = patch_vt1702}, 3288eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 3289eb7188caSLydia Wang .patch = patch_vt1718S}, 3290eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 3291eb7188caSLydia Wang .patch = patch_vt1718S}, 3292bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 3293bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3294bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 3295bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 3296f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 3297f3db423dSLydia Wang .patch = patch_vt1716S}, 3298f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 3299f3db423dSLydia Wang .patch = patch_vt1716S}, 330025eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 330125eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 3302ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 330336dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 330436dd5c4aSLydia Wang .patch = patch_vt1708S}, 330511890956SLydia Wang { .id = 0x11060446, .name = "VT1802", 330611890956SLydia Wang .patch = patch_vt2002P}, 330711890956SLydia Wang { .id = 0x11068446, .name = "VT1802", 330811890956SLydia Wang .patch = patch_vt2002P}, 3309c577b8a1SJoseph Chan {} /* terminator */ 3310c577b8a1SJoseph Chan }; 33111289e9e8STakashi Iwai 33121289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 33131289e9e8STakashi Iwai 33141289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 33151289e9e8STakashi Iwai .preset = snd_hda_preset_via, 33161289e9e8STakashi Iwai .owner = THIS_MODULE, 33171289e9e8STakashi Iwai }; 33181289e9e8STakashi Iwai 33191289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 33201289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 33211289e9e8STakashi Iwai 33221289e9e8STakashi Iwai static int __init patch_via_init(void) 33231289e9e8STakashi Iwai { 33241289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 33251289e9e8STakashi Iwai } 33261289e9e8STakashi Iwai 33271289e9e8STakashi Iwai static void __exit patch_via_exit(void) 33281289e9e8STakashi Iwai { 33291289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 33301289e9e8STakashi Iwai } 33311289e9e8STakashi Iwai 33321289e9e8STakashi Iwai module_init(patch_via_init) 33331289e9e8STakashi Iwai module_exit(patch_via_exit) 3334