1c577b8a1SJoseph Chan /* 2c577b8a1SJoseph Chan * Universal Interface for Intel High Definition Audio Codec 3c577b8a1SJoseph Chan * 48e86597fSLydia Wang * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec 5c577b8a1SJoseph Chan * 68e86597fSLydia Wang * (C) 2006-2009 VIA Technology, Inc. 78e86597fSLydia Wang * (C) 2006-2008 Takashi Iwai <tiwai@suse.de> 8c577b8a1SJoseph Chan * 9c577b8a1SJoseph Chan * This driver is free software; you can redistribute it and/or modify 10c577b8a1SJoseph Chan * it under the terms of the GNU General Public License as published by 11c577b8a1SJoseph Chan * the Free Software Foundation; either version 2 of the License, or 12c577b8a1SJoseph Chan * (at your option) any later version. 13c577b8a1SJoseph Chan * 14c577b8a1SJoseph Chan * This driver is distributed in the hope that it will be useful, 15c577b8a1SJoseph Chan * but WITHOUT ANY WARRANTY; without even the implied warranty of 16c577b8a1SJoseph Chan * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17c577b8a1SJoseph Chan * GNU General Public License for more details. 18c577b8a1SJoseph Chan * 19c577b8a1SJoseph Chan * You should have received a copy of the GNU General Public License 20c577b8a1SJoseph Chan * along with this program; if not, write to the Free Software 21c577b8a1SJoseph Chan * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22c577b8a1SJoseph Chan */ 23c577b8a1SJoseph Chan 24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */ 25c577b8a1SJoseph Chan /* */ 26c577b8a1SJoseph Chan /* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */ 27c577b8a1SJoseph Chan /* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */ 28c577b8a1SJoseph Chan /* 2006-08-02 Lydia Wang Add support to VT1709 codec */ 29c577b8a1SJoseph Chan /* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */ 30f7278fd0SJosepch Chan /* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */ 31f7278fd0SJosepch Chan /* 2007-09-17 Lydia Wang Add VT1708B codec support */ 3276d9b0ddSHarald Welte /* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */ 33fb4cb772SHarald Welte /* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */ 34d949cac1SHarald Welte /* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */ 3569e52a80SHarald Welte /* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */ 360aa62aefSHarald Welte /* 2008-04-09 Lydia Wang Add Independent HP feature */ 3798aa34c0SHarald Welte /* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */ 38d7426329SHarald Welte /* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */ 398e86597fSLydia Wang /* 2009-02-16 Logan Li Add support for VT1718S */ 408e86597fSLydia Wang /* 2009-03-13 Logan Li Add support for VT1716S */ 418e86597fSLydia Wang /* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */ 428e86597fSLydia Wang /* 2009-07-08 Lydia Wang Add support for VT2002P */ 438e86597fSLydia Wang /* 2009-07-21 Lydia Wang Add support for VT1812 */ 4436dd5c4aSLydia Wang /* 2009-09-19 Lydia Wang Add support for VT1818S */ 45c577b8a1SJoseph Chan /* */ 46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 47c577b8a1SJoseph Chan 48c577b8a1SJoseph Chan 49c577b8a1SJoseph Chan #include <linux/init.h> 50c577b8a1SJoseph Chan #include <linux/delay.h> 51c577b8a1SJoseph Chan #include <linux/slab.h> 52c577b8a1SJoseph Chan #include <sound/core.h> 530aa62aefSHarald Welte #include <sound/asoundef.h> 54c577b8a1SJoseph Chan #include "hda_codec.h" 55c577b8a1SJoseph Chan #include "hda_local.h" 56c577b8a1SJoseph Chan 575b0cb1d8SJaroslav Kysela #define NID_MAPPING (-1) 585b0cb1d8SJaroslav Kysela 59c577b8a1SJoseph Chan /* amp values */ 60c577b8a1SJoseph Chan #define AMP_VAL_IDX_SHIFT 19 61c577b8a1SJoseph Chan #define AMP_VAL_IDX_MASK (0x0f<<19) 62c577b8a1SJoseph Chan 63c577b8a1SJoseph Chan /* Pin Widget NID */ 64c577b8a1SJoseph Chan #define VT1708_HP_NID 0x13 65c577b8a1SJoseph Chan #define VT1708_DIGOUT_NID 0x14 66c577b8a1SJoseph Chan #define VT1708_DIGIN_NID 0x16 67f7278fd0SJosepch Chan #define VT1708_DIGIN_PIN 0x26 6876d9b0ddSHarald Welte #define VT1708_HP_PIN_NID 0x20 6976d9b0ddSHarald Welte #define VT1708_CD_PIN_NID 0x24 70c577b8a1SJoseph Chan 71c577b8a1SJoseph Chan #define VT1709_HP_DAC_NID 0x28 72c577b8a1SJoseph Chan #define VT1709_DIGOUT_NID 0x13 73c577b8a1SJoseph Chan #define VT1709_DIGIN_NID 0x17 74f7278fd0SJosepch Chan #define VT1709_DIGIN_PIN 0x25 75f7278fd0SJosepch Chan 76f7278fd0SJosepch Chan #define VT1708B_HP_NID 0x25 77f7278fd0SJosepch Chan #define VT1708B_DIGOUT_NID 0x12 78f7278fd0SJosepch Chan #define VT1708B_DIGIN_NID 0x15 79f7278fd0SJosepch Chan #define VT1708B_DIGIN_PIN 0x21 80c577b8a1SJoseph Chan 81d949cac1SHarald Welte #define VT1708S_HP_NID 0x25 82d949cac1SHarald Welte #define VT1708S_DIGOUT_NID 0x12 83d949cac1SHarald Welte 84d949cac1SHarald Welte #define VT1702_HP_NID 0x17 85d949cac1SHarald Welte #define VT1702_DIGOUT_NID 0x11 86d949cac1SHarald Welte 87d7426329SHarald Welte enum VIA_HDA_CODEC { 88d7426329SHarald Welte UNKNOWN = -1, 89d7426329SHarald Welte VT1708, 90d7426329SHarald Welte VT1709_10CH, 91d7426329SHarald Welte VT1709_6CH, 92d7426329SHarald Welte VT1708B_8CH, 93d7426329SHarald Welte VT1708B_4CH, 94d7426329SHarald Welte VT1708S, 95518bf3baSLydia Wang VT1708BCE, 96d7426329SHarald Welte VT1702, 97eb7188caSLydia Wang VT1718S, 98f3db423dSLydia Wang VT1716S, 9925eaba2fSLydia Wang VT2002P, 100ab6734e7SLydia Wang VT1812, 101d7426329SHarald Welte CODEC_TYPES, 102d7426329SHarald Welte }; 103d7426329SHarald Welte 1041f2e99feSLydia Wang struct via_spec { 1051f2e99feSLydia Wang /* codec parameterization */ 106f3db423dSLydia Wang struct snd_kcontrol_new *mixers[6]; 1071f2e99feSLydia Wang unsigned int num_mixers; 1081f2e99feSLydia Wang 1091f2e99feSLydia Wang struct hda_verb *init_verbs[5]; 1101f2e99feSLydia Wang unsigned int num_iverbs; 1111f2e99feSLydia Wang 1121f2e99feSLydia Wang char *stream_name_analog; 1131f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_playback; 1141f2e99feSLydia Wang struct hda_pcm_stream *stream_analog_capture; 1151f2e99feSLydia Wang 1161f2e99feSLydia Wang char *stream_name_digital; 1171f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_playback; 1181f2e99feSLydia Wang struct hda_pcm_stream *stream_digital_capture; 1191f2e99feSLydia Wang 1201f2e99feSLydia Wang /* playback */ 1211f2e99feSLydia Wang struct hda_multi_out multiout; 1221f2e99feSLydia Wang hda_nid_t slave_dig_outs[2]; 1231f2e99feSLydia Wang 1241f2e99feSLydia Wang /* capture */ 1251f2e99feSLydia Wang unsigned int num_adc_nids; 1261f2e99feSLydia Wang hda_nid_t *adc_nids; 1271f2e99feSLydia Wang hda_nid_t mux_nids[3]; 1281f2e99feSLydia Wang hda_nid_t dig_in_nid; 1291f2e99feSLydia Wang hda_nid_t dig_in_pin; 1301f2e99feSLydia Wang 1311f2e99feSLydia Wang /* capture source */ 1321f2e99feSLydia Wang const struct hda_input_mux *input_mux; 1331f2e99feSLydia Wang unsigned int cur_mux[3]; 1341f2e99feSLydia Wang 1351f2e99feSLydia Wang /* PCM information */ 1361f2e99feSLydia Wang struct hda_pcm pcm_rec[3]; 1371f2e99feSLydia Wang 1381f2e99feSLydia Wang /* dynamic controls, init_verbs and input_mux */ 1391f2e99feSLydia Wang struct auto_pin_cfg autocfg; 1401f2e99feSLydia Wang struct snd_array kctls; 1411f2e99feSLydia Wang struct hda_input_mux private_imux[2]; 1421f2e99feSLydia Wang hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; 1431f2e99feSLydia Wang 1441f2e99feSLydia Wang /* HP mode source */ 1451f2e99feSLydia Wang const struct hda_input_mux *hp_mux; 1461f2e99feSLydia Wang unsigned int hp_independent_mode; 1471f2e99feSLydia Wang unsigned int hp_independent_mode_index; 1481f2e99feSLydia Wang unsigned int smart51_enabled; 149f3db423dSLydia Wang unsigned int dmic_enabled; 1501f2e99feSLydia Wang enum VIA_HDA_CODEC codec_type; 1511f2e99feSLydia Wang 1521f2e99feSLydia Wang /* work to check hp jack state */ 1531f2e99feSLydia Wang struct hda_codec *codec; 1541f2e99feSLydia Wang struct delayed_work vt1708_hp_work; 1551f2e99feSLydia Wang int vt1708_jack_detectect; 1561f2e99feSLydia Wang int vt1708_hp_present; 1571f2e99feSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 1581f2e99feSLydia Wang struct hda_loopback_check loopback; 1591f2e99feSLydia Wang #endif 1601f2e99feSLydia Wang }; 1611f2e99feSLydia Wang 1625b0cb1d8SJaroslav Kysela static struct via_spec * via_new_spec(struct hda_codec *codec) 1635b0cb1d8SJaroslav Kysela { 1645b0cb1d8SJaroslav Kysela struct via_spec *spec; 1655b0cb1d8SJaroslav Kysela 1665b0cb1d8SJaroslav Kysela spec = kzalloc(sizeof(*spec), GFP_KERNEL); 1675b0cb1d8SJaroslav Kysela if (spec == NULL) 1685b0cb1d8SJaroslav Kysela return NULL; 1695b0cb1d8SJaroslav Kysela 1705b0cb1d8SJaroslav Kysela codec->spec = spec; 1715b0cb1d8SJaroslav Kysela spec->codec = codec; 1725b0cb1d8SJaroslav Kysela return spec; 1735b0cb1d8SJaroslav Kysela } 1745b0cb1d8SJaroslav Kysela 175744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) 176d7426329SHarald Welte { 177744ff5f4SLydia Wang u32 vendor_id = codec->vendor_id; 178d7426329SHarald Welte u16 ven_id = vendor_id >> 16; 179d7426329SHarald Welte u16 dev_id = vendor_id & 0xffff; 180d7426329SHarald Welte enum VIA_HDA_CODEC codec_type; 181d7426329SHarald Welte 182d7426329SHarald Welte /* get codec type */ 183d7426329SHarald Welte if (ven_id != 0x1106) 184d7426329SHarald Welte codec_type = UNKNOWN; 185d7426329SHarald Welte else if (dev_id >= 0x1708 && dev_id <= 0x170b) 186d7426329SHarald Welte codec_type = VT1708; 187d7426329SHarald Welte else if (dev_id >= 0xe710 && dev_id <= 0xe713) 188d7426329SHarald Welte codec_type = VT1709_10CH; 189d7426329SHarald Welte else if (dev_id >= 0xe714 && dev_id <= 0xe717) 190d7426329SHarald Welte codec_type = VT1709_6CH; 191518bf3baSLydia Wang else if (dev_id >= 0xe720 && dev_id <= 0xe723) { 192d7426329SHarald Welte codec_type = VT1708B_8CH; 193518bf3baSLydia Wang if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7) 194518bf3baSLydia Wang codec_type = VT1708BCE; 195518bf3baSLydia Wang } else if (dev_id >= 0xe724 && dev_id <= 0xe727) 196d7426329SHarald Welte codec_type = VT1708B_4CH; 197d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x397 198d7426329SHarald Welte && (dev_id >> 12) < 8) 199d7426329SHarald Welte codec_type = VT1708S; 200d7426329SHarald Welte else if ((dev_id & 0xfff) == 0x398 201d7426329SHarald Welte && (dev_id >> 12) < 8) 202d7426329SHarald Welte codec_type = VT1702; 203eb7188caSLydia Wang else if ((dev_id & 0xfff) == 0x428 204eb7188caSLydia Wang && (dev_id >> 12) < 8) 205eb7188caSLydia Wang codec_type = VT1718S; 206f3db423dSLydia Wang else if (dev_id == 0x0433 || dev_id == 0xa721) 207f3db423dSLydia Wang codec_type = VT1716S; 208bb3c6bfcSLydia Wang else if (dev_id == 0x0441 || dev_id == 0x4441) 209bb3c6bfcSLydia Wang codec_type = VT1718S; 21025eaba2fSLydia Wang else if (dev_id == 0x0438 || dev_id == 0x4438) 21125eaba2fSLydia Wang codec_type = VT2002P; 212ab6734e7SLydia Wang else if (dev_id == 0x0448) 213ab6734e7SLydia Wang codec_type = VT1812; 21436dd5c4aSLydia Wang else if (dev_id == 0x0440) 21536dd5c4aSLydia Wang codec_type = VT1708S; 216d7426329SHarald Welte else 217d7426329SHarald Welte codec_type = UNKNOWN; 218d7426329SHarald Welte return codec_type; 219d7426329SHarald Welte }; 220d7426329SHarald Welte 22169e52a80SHarald Welte #define VIA_HP_EVENT 0x01 22269e52a80SHarald Welte #define VIA_GPIO_EVENT 0x02 223a34df19aSLydia Wang #define VIA_JACK_EVENT 0x04 224f3db423dSLydia Wang #define VIA_MONO_EVENT 0x08 22525eaba2fSLydia Wang #define VIA_SPEAKER_EVENT 0x10 22625eaba2fSLydia Wang #define VIA_BIND_HP_EVENT 0x20 22769e52a80SHarald Welte 228c577b8a1SJoseph Chan enum { 229c577b8a1SJoseph Chan VIA_CTL_WIDGET_VOL, 230c577b8a1SJoseph Chan VIA_CTL_WIDGET_MUTE, 231f5271101SLydia Wang VIA_CTL_WIDGET_ANALOG_MUTE, 23225eaba2fSLydia Wang VIA_CTL_WIDGET_BIND_PIN_MUTE, 233c577b8a1SJoseph Chan }; 234c577b8a1SJoseph Chan 235c577b8a1SJoseph Chan enum { 236eb14a46cSHarald Welte AUTO_SEQ_FRONT = 0, 237c577b8a1SJoseph Chan AUTO_SEQ_SURROUND, 238c577b8a1SJoseph Chan AUTO_SEQ_CENLFE, 239c577b8a1SJoseph Chan AUTO_SEQ_SIDE 240c577b8a1SJoseph Chan }; 241c577b8a1SJoseph Chan 242f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); 243f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec); 2441f2e99feSLydia Wang static int is_aa_path_mute(struct hda_codec *codec); 2451f2e99feSLydia Wang 2461f2e99feSLydia Wang static void vt1708_start_hp_work(struct via_spec *spec) 2471f2e99feSLydia Wang { 2481f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2491f2e99feSLydia Wang return; 2501f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2511f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2521f2e99feSLydia Wang if (!delayed_work_pending(&spec->vt1708_hp_work)) 2531f2e99feSLydia Wang schedule_delayed_work(&spec->vt1708_hp_work, 2541f2e99feSLydia Wang msecs_to_jiffies(100)); 2551f2e99feSLydia Wang } 2561f2e99feSLydia Wang 2571f2e99feSLydia Wang static void vt1708_stop_hp_work(struct via_spec *spec) 2581f2e99feSLydia Wang { 2591f2e99feSLydia Wang if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) 2601f2e99feSLydia Wang return; 2611f2e99feSLydia Wang if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 2621f2e99feSLydia Wang && !is_aa_path_mute(spec->codec)) 2631f2e99feSLydia Wang return; 2641f2e99feSLydia Wang snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 2651f2e99feSLydia Wang !spec->vt1708_jack_detectect); 2661f2e99feSLydia Wang cancel_delayed_work(&spec->vt1708_hp_work); 2671f2e99feSLydia Wang flush_scheduled_work(); 2681f2e99feSLydia Wang } 269f5271101SLydia Wang 27025eaba2fSLydia Wang 271f5271101SLydia Wang static int analog_input_switch_put(struct snd_kcontrol *kcontrol, 272f5271101SLydia Wang struct snd_ctl_elem_value *ucontrol) 273f5271101SLydia Wang { 274f5271101SLydia Wang int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); 275f5271101SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 276f5271101SLydia Wang 277f5271101SLydia Wang set_jack_power_state(codec); 278f5271101SLydia Wang analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); 2791f2e99feSLydia Wang if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { 2801f2e99feSLydia Wang if (is_aa_path_mute(codec)) 2811f2e99feSLydia Wang vt1708_start_hp_work(codec->spec); 2821f2e99feSLydia Wang else 2831f2e99feSLydia Wang vt1708_stop_hp_work(codec->spec); 2841f2e99feSLydia Wang } 285f5271101SLydia Wang return change; 286f5271101SLydia Wang } 287f5271101SLydia Wang 288f5271101SLydia Wang /* modify .put = snd_hda_mixer_amp_switch_put */ 289f5271101SLydia Wang #define ANALOG_INPUT_MUTE \ 290f5271101SLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 291f5271101SLydia Wang .name = NULL, \ 292f5271101SLydia Wang .index = 0, \ 293f5271101SLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 294f5271101SLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 295f5271101SLydia Wang .put = analog_input_switch_put, \ 296f5271101SLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 297f5271101SLydia Wang 29825eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec); 29925eaba2fSLydia Wang 30025eaba2fSLydia Wang static int bind_pin_switch_put(struct snd_kcontrol *kcontrol, 30125eaba2fSLydia Wang struct snd_ctl_elem_value *ucontrol) 30225eaba2fSLydia Wang { 30325eaba2fSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 30425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 30525eaba2fSLydia Wang int i; 30625eaba2fSLydia Wang int change = 0; 30725eaba2fSLydia Wang 30825eaba2fSLydia Wang long *valp = ucontrol->value.integer.value; 30925eaba2fSLydia Wang int lmute, rmute; 31025eaba2fSLydia Wang if (strstr(kcontrol->id.name, "Switch") == NULL) { 31125eaba2fSLydia Wang snd_printd("Invalid control!\n"); 31225eaba2fSLydia Wang return change; 31325eaba2fSLydia Wang } 31425eaba2fSLydia Wang change = snd_hda_mixer_amp_switch_put(kcontrol, 31525eaba2fSLydia Wang ucontrol); 31625eaba2fSLydia Wang /* Get mute value */ 31725eaba2fSLydia Wang lmute = *valp ? 0 : HDA_AMP_MUTE; 31825eaba2fSLydia Wang valp++; 31925eaba2fSLydia Wang rmute = *valp ? 0 : HDA_AMP_MUTE; 32025eaba2fSLydia Wang 32125eaba2fSLydia Wang /* Set hp pins */ 32225eaba2fSLydia Wang if (!spec->hp_independent_mode) { 32325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 32425eaba2fSLydia Wang snd_hda_codec_amp_update( 32525eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 32625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 32725eaba2fSLydia Wang lmute); 32825eaba2fSLydia Wang snd_hda_codec_amp_update( 32925eaba2fSLydia Wang codec, spec->autocfg.hp_pins[i], 33025eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 33125eaba2fSLydia Wang rmute); 33225eaba2fSLydia Wang } 33325eaba2fSLydia Wang } 33425eaba2fSLydia Wang 33525eaba2fSLydia Wang if (!lmute && !rmute) { 33625eaba2fSLydia Wang /* Line Outs */ 33725eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 33825eaba2fSLydia Wang snd_hda_codec_amp_stereo( 33925eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 34025eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 34125eaba2fSLydia Wang /* Speakers */ 34225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 34325eaba2fSLydia Wang snd_hda_codec_amp_stereo( 34425eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], 34525eaba2fSLydia Wang HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 34625eaba2fSLydia Wang /* unmute */ 34725eaba2fSLydia Wang via_hp_bind_automute(codec); 34825eaba2fSLydia Wang 34925eaba2fSLydia Wang } else { 35025eaba2fSLydia Wang if (lmute) { 35125eaba2fSLydia Wang /* Mute all left channels */ 35225eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 35325eaba2fSLydia Wang snd_hda_codec_amp_update( 35425eaba2fSLydia Wang codec, 35525eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 35625eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 35725eaba2fSLydia Wang lmute); 35825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 35925eaba2fSLydia Wang snd_hda_codec_amp_update( 36025eaba2fSLydia Wang codec, 36125eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 36225eaba2fSLydia Wang 0, HDA_OUTPUT, 0, HDA_AMP_MUTE, 36325eaba2fSLydia Wang lmute); 36425eaba2fSLydia Wang } 36525eaba2fSLydia Wang if (rmute) { 36625eaba2fSLydia Wang /* mute all right channels */ 36725eaba2fSLydia Wang for (i = 1; i < spec->autocfg.line_outs; i++) 36825eaba2fSLydia Wang snd_hda_codec_amp_update( 36925eaba2fSLydia Wang codec, 37025eaba2fSLydia Wang spec->autocfg.line_out_pins[i], 37125eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37225eaba2fSLydia Wang rmute); 37325eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 37425eaba2fSLydia Wang snd_hda_codec_amp_update( 37525eaba2fSLydia Wang codec, 37625eaba2fSLydia Wang spec->autocfg.speaker_pins[i], 37725eaba2fSLydia Wang 1, HDA_OUTPUT, 0, HDA_AMP_MUTE, 37825eaba2fSLydia Wang rmute); 37925eaba2fSLydia Wang } 38025eaba2fSLydia Wang } 38125eaba2fSLydia Wang return change; 38225eaba2fSLydia Wang } 38325eaba2fSLydia Wang 38425eaba2fSLydia Wang #define BIND_PIN_MUTE \ 38525eaba2fSLydia Wang { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 38625eaba2fSLydia Wang .name = NULL, \ 38725eaba2fSLydia Wang .index = 0, \ 38825eaba2fSLydia Wang .info = snd_hda_mixer_amp_switch_info, \ 38925eaba2fSLydia Wang .get = snd_hda_mixer_amp_switch_get, \ 39025eaba2fSLydia Wang .put = bind_pin_switch_put, \ 39125eaba2fSLydia Wang .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) } 39225eaba2fSLydia Wang 39371eb7dccSLydia Wang static struct snd_kcontrol_new via_control_templates[] = { 394c577b8a1SJoseph Chan HDA_CODEC_VOLUME(NULL, 0, 0, 0), 395c577b8a1SJoseph Chan HDA_CODEC_MUTE(NULL, 0, 0, 0), 396f5271101SLydia Wang ANALOG_INPUT_MUTE, 39725eaba2fSLydia Wang BIND_PIN_MUTE, 398c577b8a1SJoseph Chan }; 399c577b8a1SJoseph Chan 400c577b8a1SJoseph Chan static hda_nid_t vt1708_adc_nids[2] = { 401c577b8a1SJoseph Chan /* ADC1-2 */ 402c577b8a1SJoseph Chan 0x15, 0x27 403c577b8a1SJoseph Chan }; 404c577b8a1SJoseph Chan 405c577b8a1SJoseph Chan static hda_nid_t vt1709_adc_nids[3] = { 406c577b8a1SJoseph Chan /* ADC1-2 */ 407c577b8a1SJoseph Chan 0x14, 0x15, 0x16 408c577b8a1SJoseph Chan }; 409c577b8a1SJoseph Chan 410f7278fd0SJosepch Chan static hda_nid_t vt1708B_adc_nids[2] = { 411f7278fd0SJosepch Chan /* ADC1-2 */ 412f7278fd0SJosepch Chan 0x13, 0x14 413f7278fd0SJosepch Chan }; 414f7278fd0SJosepch Chan 415d949cac1SHarald Welte static hda_nid_t vt1708S_adc_nids[2] = { 416d949cac1SHarald Welte /* ADC1-2 */ 417d949cac1SHarald Welte 0x13, 0x14 418d949cac1SHarald Welte }; 419d949cac1SHarald Welte 420d949cac1SHarald Welte static hda_nid_t vt1702_adc_nids[3] = { 421d949cac1SHarald Welte /* ADC1-2 */ 422d949cac1SHarald Welte 0x12, 0x20, 0x1F 423d949cac1SHarald Welte }; 424d949cac1SHarald Welte 425eb7188caSLydia Wang static hda_nid_t vt1718S_adc_nids[2] = { 426eb7188caSLydia Wang /* ADC1-2 */ 427eb7188caSLydia Wang 0x10, 0x11 428eb7188caSLydia Wang }; 429eb7188caSLydia Wang 430f3db423dSLydia Wang static hda_nid_t vt1716S_adc_nids[2] = { 431f3db423dSLydia Wang /* ADC1-2 */ 432f3db423dSLydia Wang 0x13, 0x14 433f3db423dSLydia Wang }; 434f3db423dSLydia Wang 43525eaba2fSLydia Wang static hda_nid_t vt2002P_adc_nids[2] = { 43625eaba2fSLydia Wang /* ADC1-2 */ 43725eaba2fSLydia Wang 0x10, 0x11 43825eaba2fSLydia Wang }; 43925eaba2fSLydia Wang 440ab6734e7SLydia Wang static hda_nid_t vt1812_adc_nids[2] = { 441ab6734e7SLydia Wang /* ADC1-2 */ 442ab6734e7SLydia Wang 0x10, 0x11 443ab6734e7SLydia Wang }; 444ab6734e7SLydia Wang 445ab6734e7SLydia Wang 446c577b8a1SJoseph Chan /* add dynamic controls */ 447c577b8a1SJoseph Chan static int via_add_control(struct via_spec *spec, int type, const char *name, 448c577b8a1SJoseph Chan unsigned long val) 449c577b8a1SJoseph Chan { 450c577b8a1SJoseph Chan struct snd_kcontrol_new *knew; 451c577b8a1SJoseph Chan 452603c4019STakashi Iwai snd_array_init(&spec->kctls, sizeof(*knew), 32); 453603c4019STakashi Iwai knew = snd_array_new(&spec->kctls); 454c577b8a1SJoseph Chan if (!knew) 455c577b8a1SJoseph Chan return -ENOMEM; 45671eb7dccSLydia Wang *knew = via_control_templates[type]; 457c577b8a1SJoseph Chan knew->name = kstrdup(name, GFP_KERNEL); 458c577b8a1SJoseph Chan if (!knew->name) 459c577b8a1SJoseph Chan return -ENOMEM; 4604d02d1b6SJaroslav Kysela if (get_amp_nid_(val)) 461*5e26dfd0SJaroslav Kysela knew->subdevice = HDA_SUBDEV_AMP_FLAG; 462c577b8a1SJoseph Chan knew->private_value = val; 463c577b8a1SJoseph Chan return 0; 464c577b8a1SJoseph Chan } 465c577b8a1SJoseph Chan 4665b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec, 4675b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *tmpl) 4685b0cb1d8SJaroslav Kysela { 4695b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 4705b0cb1d8SJaroslav Kysela 4715b0cb1d8SJaroslav Kysela snd_array_init(&spec->kctls, sizeof(*knew), 32); 4725b0cb1d8SJaroslav Kysela knew = snd_array_new(&spec->kctls); 4735b0cb1d8SJaroslav Kysela if (!knew) 4745b0cb1d8SJaroslav Kysela return NULL; 4755b0cb1d8SJaroslav Kysela *knew = *tmpl; 4765b0cb1d8SJaroslav Kysela knew->name = kstrdup(tmpl->name, GFP_KERNEL); 4775b0cb1d8SJaroslav Kysela if (!knew->name) 4785b0cb1d8SJaroslav Kysela return NULL; 4795b0cb1d8SJaroslav Kysela return 0; 4805b0cb1d8SJaroslav Kysela } 4815b0cb1d8SJaroslav Kysela 482603c4019STakashi Iwai static void via_free_kctls(struct hda_codec *codec) 483603c4019STakashi Iwai { 484603c4019STakashi Iwai struct via_spec *spec = codec->spec; 485603c4019STakashi Iwai 486603c4019STakashi Iwai if (spec->kctls.list) { 487603c4019STakashi Iwai struct snd_kcontrol_new *kctl = spec->kctls.list; 488603c4019STakashi Iwai int i; 489603c4019STakashi Iwai for (i = 0; i < spec->kctls.used; i++) 490603c4019STakashi Iwai kfree(kctl[i].name); 491603c4019STakashi Iwai } 492603c4019STakashi Iwai snd_array_free(&spec->kctls); 493603c4019STakashi Iwai } 494603c4019STakashi Iwai 495c577b8a1SJoseph Chan /* create input playback/capture controls for the given pin */ 4969510e8ddSLydia Wang static int via_new_analog_input(struct via_spec *spec, const char *ctlname, 4979510e8ddSLydia Wang int idx, int mix_nid) 498c577b8a1SJoseph Chan { 499c577b8a1SJoseph Chan char name[32]; 500c577b8a1SJoseph Chan int err; 501c577b8a1SJoseph Chan 502c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", ctlname); 503c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 504c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 505c577b8a1SJoseph Chan if (err < 0) 506c577b8a1SJoseph Chan return err; 507c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", ctlname); 508f5271101SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, 509c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT)); 510c577b8a1SJoseph Chan if (err < 0) 511c577b8a1SJoseph Chan return err; 512c577b8a1SJoseph Chan return 0; 513c577b8a1SJoseph Chan } 514c577b8a1SJoseph Chan 515c577b8a1SJoseph Chan static void via_auto_set_output_and_unmute(struct hda_codec *codec, 516c577b8a1SJoseph Chan hda_nid_t nid, int pin_type, 517c577b8a1SJoseph Chan int dac_idx) 518c577b8a1SJoseph Chan { 519c577b8a1SJoseph Chan /* set as output */ 520c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 521c577b8a1SJoseph Chan pin_type); 522c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, 523c577b8a1SJoseph Chan AMP_OUT_UNMUTE); 524d3a11e60STakashi Iwai if (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD) 525d3a11e60STakashi Iwai snd_hda_codec_write(codec, nid, 0, 526d3a11e60STakashi Iwai AC_VERB_SET_EAPD_BTLENABLE, 0x02); 527c577b8a1SJoseph Chan } 528c577b8a1SJoseph Chan 529c577b8a1SJoseph Chan 530c577b8a1SJoseph Chan static void via_auto_init_multi_out(struct hda_codec *codec) 531c577b8a1SJoseph Chan { 532c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 533c577b8a1SJoseph Chan int i; 534c577b8a1SJoseph Chan 535c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 536c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.line_out_pins[i]; 537c577b8a1SJoseph Chan if (nid) 538c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, nid, PIN_OUT, i); 539c577b8a1SJoseph Chan } 540c577b8a1SJoseph Chan } 541c577b8a1SJoseph Chan 542c577b8a1SJoseph Chan static void via_auto_init_hp_out(struct hda_codec *codec) 543c577b8a1SJoseph Chan { 544c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 545c577b8a1SJoseph Chan hda_nid_t pin; 54625eaba2fSLydia Wang int i; 547c577b8a1SJoseph Chan 54825eaba2fSLydia Wang for (i = 0; i < spec->autocfg.hp_outs; i++) { 54925eaba2fSLydia Wang pin = spec->autocfg.hp_pins[i]; 550c577b8a1SJoseph Chan if (pin) /* connect to front */ 551c577b8a1SJoseph Chan via_auto_set_output_and_unmute(codec, pin, PIN_HP, 0); 552c577b8a1SJoseph Chan } 55325eaba2fSLydia Wang } 554c577b8a1SJoseph Chan 555c577b8a1SJoseph Chan static void via_auto_init_analog_input(struct hda_codec *codec) 556c577b8a1SJoseph Chan { 557c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 558c577b8a1SJoseph Chan int i; 559c577b8a1SJoseph Chan 560c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 561c577b8a1SJoseph Chan hda_nid_t nid = spec->autocfg.input_pins[i]; 562c577b8a1SJoseph Chan 563c577b8a1SJoseph Chan snd_hda_codec_write(codec, nid, 0, 564c577b8a1SJoseph Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 565c577b8a1SJoseph Chan (i <= AUTO_PIN_FRONT_MIC ? 566c577b8a1SJoseph Chan PIN_VREF50 : PIN_IN)); 567c577b8a1SJoseph Chan 568c577b8a1SJoseph Chan } 569c577b8a1SJoseph Chan } 570f5271101SLydia Wang 5711564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin); 5721564b287SLydia 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 */ 581d56757abSTakashi Iwai unsigned present = snd_hda_jack_detect(codec, nid); 5821564b287SLydia Wang struct via_spec *spec = codec->spec; 5831564b287SLydia Wang if ((spec->smart51_enabled && is_smart51_pins(spec, nid)) 5841564b287SLydia Wang || ((no_presence || present) 5851564b287SLydia Wang && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) { 586f5271101SLydia Wang *affected_parm = AC_PWRST_D0; /* if it's connected */ 587f5271101SLydia Wang parm = AC_PWRST_D0; 588f5271101SLydia Wang } else 589f5271101SLydia Wang parm = AC_PWRST_D3; 590f5271101SLydia Wang 591f5271101SLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm); 592f5271101SLydia Wang } 593f5271101SLydia Wang 594f5271101SLydia Wang static void set_jack_power_state(struct hda_codec *codec) 595f5271101SLydia Wang { 596f5271101SLydia Wang struct via_spec *spec = codec->spec; 597f5271101SLydia Wang int imux_is_smixer; 598f5271101SLydia Wang unsigned int parm; 599f5271101SLydia Wang 600f5271101SLydia Wang if (spec->codec_type == VT1702) { 601f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 602f5271101SLydia Wang codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 603f5271101SLydia Wang /* inputs */ 604f5271101SLydia Wang /* PW 1/2/5 (14h/15h/18h) */ 605f5271101SLydia Wang parm = AC_PWRST_D3; 606f5271101SLydia Wang set_pin_power_state(codec, 0x14, &parm); 607f5271101SLydia Wang set_pin_power_state(codec, 0x15, &parm); 608f5271101SLydia Wang set_pin_power_state(codec, 0x18, &parm); 609f5271101SLydia Wang if (imux_is_smixer) 610f5271101SLydia Wang parm = AC_PWRST_D0; /* SW0 = stereo mixer (idx 3) */ 611f5271101SLydia Wang /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */ 612f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 613f5271101SLydia Wang parm); 614f5271101SLydia Wang snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, 615f5271101SLydia Wang parm); 616f5271101SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 617f5271101SLydia Wang parm); 618f5271101SLydia Wang snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, 619f5271101SLydia Wang parm); 620f5271101SLydia Wang 621f5271101SLydia Wang /* outputs */ 622f5271101SLydia Wang /* PW 3/4 (16h/17h) */ 623f5271101SLydia Wang parm = AC_PWRST_D3; 624f5271101SLydia Wang set_pin_power_state(codec, 0x16, &parm); 625f5271101SLydia Wang set_pin_power_state(codec, 0x17, &parm); 626f5271101SLydia Wang /* MW0 (1ah), AOW 0/1 (10h/1dh) */ 627f5271101SLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 628f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 629f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 630f5271101SLydia Wang parm); 631f5271101SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, 632f5271101SLydia Wang parm); 633f5271101SLydia Wang } else if (spec->codec_type == VT1708B_8CH 634f5271101SLydia Wang || spec->codec_type == VT1708B_4CH 635f5271101SLydia Wang || spec->codec_type == VT1708S) { 636f5271101SLydia Wang /* SW0 (17h) = stereo mixer */ 637f5271101SLydia Wang int is_8ch = spec->codec_type != VT1708B_4CH; 638f5271101SLydia Wang imux_is_smixer = snd_hda_codec_read( 639f5271101SLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) 640f5271101SLydia Wang == ((spec->codec_type == VT1708S) ? 5 : 0); 641f5271101SLydia Wang /* inputs */ 642f5271101SLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 643f5271101SLydia Wang parm = AC_PWRST_D3; 644f5271101SLydia Wang set_pin_power_state(codec, 0x1a, &parm); 645f5271101SLydia Wang set_pin_power_state(codec, 0x1b, &parm); 646f5271101SLydia Wang set_pin_power_state(codec, 0x1e, &parm); 647f5271101SLydia Wang if (imux_is_smixer) 648f5271101SLydia Wang parm = AC_PWRST_D0; 649f5271101SLydia Wang /* SW0 (17h), AIW 0/1 (13h/14h) */ 650f5271101SLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 651f5271101SLydia Wang parm); 652f5271101SLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 653f5271101SLydia Wang parm); 654f5271101SLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 655f5271101SLydia Wang parm); 656f5271101SLydia Wang 657f5271101SLydia Wang /* outputs */ 658f5271101SLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 659f5271101SLydia Wang parm = AC_PWRST_D3; 660f5271101SLydia Wang set_pin_power_state(codec, 0x19, &parm); 661f5271101SLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 662f5271101SLydia Wang parm); 663f5271101SLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 664f5271101SLydia Wang parm); 665f5271101SLydia Wang 666f5271101SLydia Wang /* PW6 (22h), SW2 (26h), AOW2 (24h) */ 667f5271101SLydia Wang if (is_8ch) { 668f5271101SLydia Wang parm = AC_PWRST_D3; 669f5271101SLydia Wang set_pin_power_state(codec, 0x22, &parm); 670f5271101SLydia Wang snd_hda_codec_write(codec, 0x26, 0, 671f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 672f5271101SLydia Wang snd_hda_codec_write(codec, 0x24, 0, 673f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 674f5271101SLydia Wang } 675f5271101SLydia Wang 676f5271101SLydia Wang /* PW 3/4/7 (1ch/1dh/23h) */ 677f5271101SLydia Wang parm = AC_PWRST_D3; 678f5271101SLydia Wang /* force to D0 for internal Speaker */ 679f5271101SLydia Wang set_pin_power_state(codec, 0x1c, &parm); 680f5271101SLydia Wang set_pin_power_state(codec, 0x1d, &parm); 681f5271101SLydia Wang if (is_8ch) 682f5271101SLydia Wang set_pin_power_state(codec, 0x23, &parm); 683f5271101SLydia Wang /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */ 684f5271101SLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 685f5271101SLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 686f5271101SLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 687f5271101SLydia Wang parm); 688f5271101SLydia Wang if (is_8ch) { 689f5271101SLydia Wang snd_hda_codec_write(codec, 0x25, 0, 690f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 691f5271101SLydia Wang snd_hda_codec_write(codec, 0x27, 0, 692f5271101SLydia Wang AC_VERB_SET_POWER_STATE, parm); 693f5271101SLydia Wang } 694eb7188caSLydia Wang } else if (spec->codec_type == VT1718S) { 695eb7188caSLydia Wang /* MUX6 (1eh) = stereo mixer */ 696eb7188caSLydia Wang imux_is_smixer = snd_hda_codec_read( 697eb7188caSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 698eb7188caSLydia Wang /* inputs */ 699eb7188caSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 700eb7188caSLydia Wang parm = AC_PWRST_D3; 701eb7188caSLydia Wang set_pin_power_state(codec, 0x29, &parm); 702eb7188caSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 703eb7188caSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 704eb7188caSLydia Wang if (imux_is_smixer) 705eb7188caSLydia Wang parm = AC_PWRST_D0; 706eb7188caSLydia Wang /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */ 707eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, 708eb7188caSLydia Wang parm); 709eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, 710eb7188caSLydia Wang parm); 711eb7188caSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 712eb7188caSLydia Wang parm); 713eb7188caSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 714eb7188caSLydia Wang parm); 715eb7188caSLydia Wang 716eb7188caSLydia Wang /* outputs */ 717eb7188caSLydia Wang /* PW3 (27h), MW2 (1ah), AOW3 (bh) */ 718eb7188caSLydia Wang parm = AC_PWRST_D3; 719eb7188caSLydia Wang set_pin_power_state(codec, 0x27, &parm); 720eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, 721eb7188caSLydia Wang parm); 722eb7188caSLydia Wang snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, 723eb7188caSLydia Wang parm); 724eb7188caSLydia Wang 725eb7188caSLydia Wang /* PW2 (26h), AOW2 (ah) */ 726eb7188caSLydia Wang parm = AC_PWRST_D3; 727eb7188caSLydia Wang set_pin_power_state(codec, 0x26, &parm); 728eb7188caSLydia Wang snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, 729eb7188caSLydia Wang parm); 730eb7188caSLydia Wang 731eb7188caSLydia Wang /* PW0/1 (24h/25h) */ 732eb7188caSLydia Wang parm = AC_PWRST_D3; 733eb7188caSLydia Wang set_pin_power_state(codec, 0x24, &parm); 734eb7188caSLydia Wang set_pin_power_state(codec, 0x25, &parm); 735eb7188caSLydia Wang if (!spec->hp_independent_mode) /* check for redirected HP */ 736eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 737eb7188caSLydia Wang snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, 738eb7188caSLydia Wang parm); 739eb7188caSLydia Wang snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, 740eb7188caSLydia Wang parm); 741eb7188caSLydia Wang /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */ 742eb7188caSLydia Wang snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE, 743eb7188caSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 744eb7188caSLydia Wang if (spec->hp_independent_mode) { 745eb7188caSLydia Wang /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */ 746eb7188caSLydia Wang parm = AC_PWRST_D3; 747eb7188caSLydia Wang set_pin_power_state(codec, 0x28, &parm); 748eb7188caSLydia Wang snd_hda_codec_write(codec, 0x1b, 0, 749eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 750eb7188caSLydia Wang snd_hda_codec_write(codec, 0x34, 0, 751eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 752eb7188caSLydia Wang snd_hda_codec_write(codec, 0xc, 0, 753eb7188caSLydia Wang AC_VERB_SET_POWER_STATE, parm); 754eb7188caSLydia Wang } 755f3db423dSLydia Wang } else if (spec->codec_type == VT1716S) { 756f3db423dSLydia Wang unsigned int mono_out, present; 757f3db423dSLydia Wang /* SW0 (17h) = stereo mixer */ 758f3db423dSLydia Wang imux_is_smixer = snd_hda_codec_read( 759f3db423dSLydia Wang codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 760f3db423dSLydia Wang /* inputs */ 761f3db423dSLydia Wang /* PW 1/2/5 (1ah/1bh/1eh) */ 762f3db423dSLydia Wang parm = AC_PWRST_D3; 763f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 764f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 765f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 766f3db423dSLydia Wang if (imux_is_smixer) 767f3db423dSLydia Wang parm = AC_PWRST_D0; 768f3db423dSLydia Wang /* SW0 (17h), AIW0(13h) */ 769f3db423dSLydia Wang snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, 770f3db423dSLydia Wang parm); 771f3db423dSLydia Wang snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, 772f3db423dSLydia Wang parm); 773f3db423dSLydia Wang 774f3db423dSLydia Wang parm = AC_PWRST_D3; 775f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 776f3db423dSLydia Wang /* PW11 (22h) */ 777f3db423dSLydia Wang if (spec->dmic_enabled) 778f3db423dSLydia Wang set_pin_power_state(codec, 0x22, &parm); 779f3db423dSLydia Wang else 780f3db423dSLydia Wang snd_hda_codec_write( 781f3db423dSLydia Wang codec, 0x22, 0, 782f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 783f3db423dSLydia Wang 784f3db423dSLydia Wang /* SW2(26h), AIW1(14h) */ 785f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, 786f3db423dSLydia Wang parm); 787f3db423dSLydia Wang snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, 788f3db423dSLydia Wang parm); 789f3db423dSLydia Wang 790f3db423dSLydia Wang /* outputs */ 791f3db423dSLydia Wang /* PW0 (19h), SW1 (18h), AOW1 (11h) */ 792f3db423dSLydia Wang parm = AC_PWRST_D3; 793f3db423dSLydia Wang set_pin_power_state(codec, 0x19, &parm); 794f3db423dSLydia Wang /* Smart 5.1 PW2(1bh) */ 795f3db423dSLydia Wang if (spec->smart51_enabled) 796f3db423dSLydia Wang set_pin_power_state(codec, 0x1b, &parm); 797f3db423dSLydia Wang snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, 798f3db423dSLydia Wang parm); 799f3db423dSLydia Wang snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, 800f3db423dSLydia Wang parm); 801f3db423dSLydia Wang 802f3db423dSLydia Wang /* PW7 (23h), SW3 (27h), AOW3 (25h) */ 803f3db423dSLydia Wang parm = AC_PWRST_D3; 804f3db423dSLydia Wang set_pin_power_state(codec, 0x23, &parm); 805f3db423dSLydia Wang /* Smart 5.1 PW1(1ah) */ 806f3db423dSLydia Wang if (spec->smart51_enabled) 807f3db423dSLydia Wang set_pin_power_state(codec, 0x1a, &parm); 808f3db423dSLydia Wang snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, 809f3db423dSLydia Wang parm); 810f3db423dSLydia Wang 811f3db423dSLydia Wang /* Smart 5.1 PW5(1eh) */ 812f3db423dSLydia Wang if (spec->smart51_enabled) 813f3db423dSLydia Wang set_pin_power_state(codec, 0x1e, &parm); 814f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, 815f3db423dSLydia Wang parm); 816f3db423dSLydia Wang 817f3db423dSLydia Wang /* Mono out */ 818f3db423dSLydia Wang /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/ 819d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x1c); 820f3db423dSLydia Wang if (present) 821f3db423dSLydia Wang mono_out = 0; 822f3db423dSLydia Wang else { 823d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x1d); 824f3db423dSLydia Wang if (!spec->hp_independent_mode && present) 825f3db423dSLydia Wang mono_out = 0; 826f3db423dSLydia Wang else 827f3db423dSLydia Wang mono_out = 1; 828f3db423dSLydia Wang } 829f3db423dSLydia Wang parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3; 830f3db423dSLydia Wang snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, 831f3db423dSLydia Wang parm); 832f3db423dSLydia Wang snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, 833f3db423dSLydia Wang parm); 834f3db423dSLydia Wang snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, 835f3db423dSLydia Wang parm); 836f3db423dSLydia Wang 837f3db423dSLydia Wang /* PW 3/4 (1ch/1dh) */ 838f3db423dSLydia Wang parm = AC_PWRST_D3; 839f3db423dSLydia Wang set_pin_power_state(codec, 0x1c, &parm); 840f3db423dSLydia Wang set_pin_power_state(codec, 0x1d, &parm); 841f3db423dSLydia Wang /* HP Independent Mode, power on AOW3 */ 842f3db423dSLydia Wang if (spec->hp_independent_mode) 843f3db423dSLydia Wang snd_hda_codec_write(codec, 0x25, 0, 844f3db423dSLydia Wang AC_VERB_SET_POWER_STATE, parm); 845f3db423dSLydia Wang 846f3db423dSLydia Wang /* force to D0 for internal Speaker */ 847f3db423dSLydia Wang /* MW0 (16h), AOW0 (10h) */ 848f3db423dSLydia Wang snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE, 849f3db423dSLydia Wang imux_is_smixer ? AC_PWRST_D0 : parm); 850f3db423dSLydia Wang snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, 851f3db423dSLydia Wang mono_out ? AC_PWRST_D0 : parm); 85225eaba2fSLydia Wang } else if (spec->codec_type == VT2002P) { 85325eaba2fSLydia Wang unsigned int present; 85425eaba2fSLydia Wang /* MUX9 (1eh) = stereo mixer */ 85525eaba2fSLydia Wang imux_is_smixer = snd_hda_codec_read( 85625eaba2fSLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3; 85725eaba2fSLydia Wang /* inputs */ 85825eaba2fSLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 85925eaba2fSLydia Wang parm = AC_PWRST_D3; 86025eaba2fSLydia Wang set_pin_power_state(codec, 0x29, &parm); 86125eaba2fSLydia Wang set_pin_power_state(codec, 0x2a, &parm); 86225eaba2fSLydia Wang set_pin_power_state(codec, 0x2b, &parm); 86325eaba2fSLydia Wang if (imux_is_smixer) 86425eaba2fSLydia Wang parm = AC_PWRST_D0; 86525eaba2fSLydia Wang /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */ 86625eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 86725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 86825eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 86925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 87025eaba2fSLydia Wang snd_hda_codec_write(codec, 0x10, 0, 87125eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 87225eaba2fSLydia Wang snd_hda_codec_write(codec, 0x11, 0, 87325eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 87425eaba2fSLydia Wang 87525eaba2fSLydia Wang /* outputs */ 87625eaba2fSLydia Wang /* AOW0 (8h)*/ 87725eaba2fSLydia Wang snd_hda_codec_write(codec, 0x8, 0, 87825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 87925eaba2fSLydia Wang 88025eaba2fSLydia Wang /* PW4 (26h), MW4 (1ch), MUX4(37h) */ 88125eaba2fSLydia Wang parm = AC_PWRST_D3; 88225eaba2fSLydia Wang set_pin_power_state(codec, 0x26, &parm); 88325eaba2fSLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 88425eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 88525eaba2fSLydia Wang snd_hda_codec_write(codec, 0x37, 88625eaba2fSLydia Wang 0, AC_VERB_SET_POWER_STATE, parm); 88725eaba2fSLydia Wang 88825eaba2fSLydia Wang /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */ 88925eaba2fSLydia Wang parm = AC_PWRST_D3; 89025eaba2fSLydia Wang set_pin_power_state(codec, 0x25, &parm); 89125eaba2fSLydia Wang snd_hda_codec_write(codec, 0x19, 0, 89225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 89325eaba2fSLydia Wang snd_hda_codec_write(codec, 0x35, 0, 89425eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 89525eaba2fSLydia Wang if (spec->hp_independent_mode) { 89625eaba2fSLydia Wang snd_hda_codec_write(codec, 0x9, 0, 89725eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, parm); 89825eaba2fSLydia Wang } 89925eaba2fSLydia Wang 90025eaba2fSLydia Wang /* Class-D */ 90125eaba2fSLydia Wang /* PW0 (24h), MW0(18h), MUX0(34h) */ 902d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x25); 90325eaba2fSLydia Wang parm = AC_PWRST_D3; 90425eaba2fSLydia Wang set_pin_power_state(codec, 0x24, &parm); 90525eaba2fSLydia Wang if (present) { 90625eaba2fSLydia Wang snd_hda_codec_write( 90725eaba2fSLydia Wang codec, 0x18, 0, 90825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 90925eaba2fSLydia Wang snd_hda_codec_write( 91025eaba2fSLydia Wang codec, 0x34, 0, 91125eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 91225eaba2fSLydia Wang } else { 91325eaba2fSLydia Wang snd_hda_codec_write( 91425eaba2fSLydia Wang codec, 0x18, 0, 91525eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 91625eaba2fSLydia Wang snd_hda_codec_write( 91725eaba2fSLydia Wang codec, 0x34, 0, 91825eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 91925eaba2fSLydia Wang } 92025eaba2fSLydia Wang 92125eaba2fSLydia Wang /* Mono Out */ 92225eaba2fSLydia Wang /* PW15 (31h), MW8(17h), MUX8(3bh) */ 923d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x26); 92425eaba2fSLydia Wang parm = AC_PWRST_D3; 92525eaba2fSLydia Wang set_pin_power_state(codec, 0x31, &parm); 92625eaba2fSLydia Wang if (present) { 92725eaba2fSLydia Wang snd_hda_codec_write( 92825eaba2fSLydia Wang codec, 0x17, 0, 92925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 93025eaba2fSLydia Wang snd_hda_codec_write( 93125eaba2fSLydia Wang codec, 0x3b, 0, 93225eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 93325eaba2fSLydia Wang } else { 93425eaba2fSLydia Wang snd_hda_codec_write( 93525eaba2fSLydia Wang codec, 0x17, 0, 93625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 93725eaba2fSLydia Wang snd_hda_codec_write( 93825eaba2fSLydia Wang codec, 0x3b, 0, 93925eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 94025eaba2fSLydia Wang } 94125eaba2fSLydia Wang 94225eaba2fSLydia Wang /* MW9 (21h) */ 94325eaba2fSLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 94425eaba2fSLydia Wang snd_hda_codec_write( 94525eaba2fSLydia Wang codec, 0x21, 0, 94625eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 94725eaba2fSLydia Wang else 94825eaba2fSLydia Wang snd_hda_codec_write( 94925eaba2fSLydia Wang codec, 0x21, 0, 95025eaba2fSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 951ab6734e7SLydia Wang } else if (spec->codec_type == VT1812) { 952ab6734e7SLydia Wang unsigned int present; 953ab6734e7SLydia Wang /* MUX10 (1eh) = stereo mixer */ 954ab6734e7SLydia Wang imux_is_smixer = snd_hda_codec_read( 955ab6734e7SLydia Wang codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5; 956ab6734e7SLydia Wang /* inputs */ 957ab6734e7SLydia Wang /* PW 5/6/7 (29h/2ah/2bh) */ 958ab6734e7SLydia Wang parm = AC_PWRST_D3; 959ab6734e7SLydia Wang set_pin_power_state(codec, 0x29, &parm); 960ab6734e7SLydia Wang set_pin_power_state(codec, 0x2a, &parm); 961ab6734e7SLydia Wang set_pin_power_state(codec, 0x2b, &parm); 962ab6734e7SLydia Wang if (imux_is_smixer) 963ab6734e7SLydia Wang parm = AC_PWRST_D0; 964ab6734e7SLydia Wang /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */ 965ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1e, 0, 966ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 967ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1f, 0, 968ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 969ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x10, 0, 970ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 971ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x11, 0, 972ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 973ab6734e7SLydia Wang 974ab6734e7SLydia Wang /* outputs */ 975ab6734e7SLydia Wang /* AOW0 (8h)*/ 976ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x8, 0, 977ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 978ab6734e7SLydia Wang 979ab6734e7SLydia Wang /* PW4 (28h), MW4 (18h), MUX4(38h) */ 980ab6734e7SLydia Wang parm = AC_PWRST_D3; 981ab6734e7SLydia Wang set_pin_power_state(codec, 0x28, &parm); 982ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x18, 0, 983ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 984ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x38, 0, 985ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 986ab6734e7SLydia Wang 987ab6734e7SLydia Wang /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */ 988ab6734e7SLydia Wang parm = AC_PWRST_D3; 989ab6734e7SLydia Wang set_pin_power_state(codec, 0x25, &parm); 990ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x15, 0, 991ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 992ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x35, 0, 993ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 994ab6734e7SLydia Wang if (spec->hp_independent_mode) { 995ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x9, 0, 996ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 997ab6734e7SLydia Wang } 998ab6734e7SLydia Wang 999ab6734e7SLydia Wang /* Internal Speaker */ 1000ab6734e7SLydia Wang /* PW0 (24h), MW0(14h), MUX0(34h) */ 1001d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x25); 1002ab6734e7SLydia Wang parm = AC_PWRST_D3; 1003ab6734e7SLydia Wang set_pin_power_state(codec, 0x24, &parm); 1004ab6734e7SLydia Wang if (present) { 1005ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 1006ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1007ab6734e7SLydia Wang AC_PWRST_D3); 1008ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 1009ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1010ab6734e7SLydia Wang AC_PWRST_D3); 1011ab6734e7SLydia Wang } else { 1012ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x14, 0, 1013ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1014ab6734e7SLydia Wang AC_PWRST_D0); 1015ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x34, 0, 1016ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1017ab6734e7SLydia Wang AC_PWRST_D0); 1018ab6734e7SLydia Wang } 1019ab6734e7SLydia Wang /* Mono Out */ 1020ab6734e7SLydia Wang /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */ 1021d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, 0x28); 1022ab6734e7SLydia Wang parm = AC_PWRST_D3; 1023ab6734e7SLydia Wang set_pin_power_state(codec, 0x31, &parm); 1024ab6734e7SLydia Wang if (present) { 1025ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 1026ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1027ab6734e7SLydia Wang AC_PWRST_D3); 1028ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1029ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1030ab6734e7SLydia Wang AC_PWRST_D3); 1031ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1032ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1033ab6734e7SLydia Wang AC_PWRST_D3); 1034ab6734e7SLydia Wang } else { 1035ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1c, 0, 1036ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1037ab6734e7SLydia Wang AC_PWRST_D0); 1038ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3c, 0, 1039ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1040ab6734e7SLydia Wang AC_PWRST_D0); 1041ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3e, 0, 1042ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, 1043ab6734e7SLydia Wang AC_PWRST_D0); 1044ab6734e7SLydia Wang } 1045ab6734e7SLydia Wang 1046ab6734e7SLydia Wang /* PW15 (33h), MW15 (1dh), MUX15(3dh) */ 1047ab6734e7SLydia Wang parm = AC_PWRST_D3; 1048ab6734e7SLydia Wang set_pin_power_state(codec, 0x33, &parm); 1049ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x1d, 0, 1050ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1051ab6734e7SLydia Wang snd_hda_codec_write(codec, 0x3d, 0, 1052ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, parm); 1053ab6734e7SLydia Wang 1054ab6734e7SLydia Wang /* MW9 (21h) */ 1055ab6734e7SLydia Wang if (imux_is_smixer || !is_aa_path_mute(codec)) 1056ab6734e7SLydia Wang snd_hda_codec_write( 1057ab6734e7SLydia Wang codec, 0x21, 0, 1058ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1059ab6734e7SLydia Wang else 1060ab6734e7SLydia Wang snd_hda_codec_write( 1061ab6734e7SLydia Wang codec, 0x21, 0, 1062ab6734e7SLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D3); 1063f5271101SLydia Wang } 1064f5271101SLydia Wang } 1065f5271101SLydia Wang 1066c577b8a1SJoseph Chan /* 1067c577b8a1SJoseph Chan * input MUX handling 1068c577b8a1SJoseph Chan */ 1069c577b8a1SJoseph Chan static int via_mux_enum_info(struct snd_kcontrol *kcontrol, 1070c577b8a1SJoseph Chan struct snd_ctl_elem_info *uinfo) 1071c577b8a1SJoseph Chan { 1072c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1073c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1074c577b8a1SJoseph Chan return snd_hda_input_mux_info(spec->input_mux, uinfo); 1075c577b8a1SJoseph Chan } 1076c577b8a1SJoseph Chan 1077c577b8a1SJoseph Chan static int via_mux_enum_get(struct snd_kcontrol *kcontrol, 1078c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1079c577b8a1SJoseph Chan { 1080c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1081c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1082c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1083c577b8a1SJoseph Chan 1084c577b8a1SJoseph Chan ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx]; 1085c577b8a1SJoseph Chan return 0; 1086c577b8a1SJoseph Chan } 1087c577b8a1SJoseph Chan 1088c577b8a1SJoseph Chan static int via_mux_enum_put(struct snd_kcontrol *kcontrol, 1089c577b8a1SJoseph Chan struct snd_ctl_elem_value *ucontrol) 1090c577b8a1SJoseph Chan { 1091c577b8a1SJoseph Chan struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 1092c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1093c577b8a1SJoseph Chan unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); 1094c577b8a1SJoseph Chan 1095337b9d02STakashi Iwai if (!spec->mux_nids[adc_idx]) 1096337b9d02STakashi Iwai return -EINVAL; 1097a80e6e3cSLydia Wang /* switch to D0 beofre change index */ 1098a80e6e3cSLydia Wang if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0, 1099a80e6e3cSLydia Wang AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0) 1100a80e6e3cSLydia Wang snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0, 1101a80e6e3cSLydia Wang AC_VERB_SET_POWER_STATE, AC_PWRST_D0); 1102a80e6e3cSLydia Wang /* update jack power state */ 1103a80e6e3cSLydia Wang set_jack_power_state(codec); 1104a80e6e3cSLydia Wang 1105c577b8a1SJoseph Chan return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol, 1106337b9d02STakashi Iwai spec->mux_nids[adc_idx], 1107c577b8a1SJoseph Chan &spec->cur_mux[adc_idx]); 1108c577b8a1SJoseph Chan } 1109c577b8a1SJoseph Chan 11100aa62aefSHarald Welte static int via_independent_hp_info(struct snd_kcontrol *kcontrol, 11110aa62aefSHarald Welte struct snd_ctl_elem_info *uinfo) 11120aa62aefSHarald Welte { 11130aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11140aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11150aa62aefSHarald Welte return snd_hda_input_mux_info(spec->hp_mux, uinfo); 11160aa62aefSHarald Welte } 11170aa62aefSHarald Welte 11180aa62aefSHarald Welte static int via_independent_hp_get(struct snd_kcontrol *kcontrol, 11190aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 11200aa62aefSHarald Welte { 11210aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11225b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 1123eb7188caSLydia Wang unsigned int pinsel; 1124eb7188caSLydia Wang 1125eb7188caSLydia Wang /* use !! to translate conn sel 2 for VT1718S */ 1126eb7188caSLydia Wang pinsel = !!snd_hda_codec_read(codec, nid, 0, 11270aa62aefSHarald Welte AC_VERB_GET_CONNECT_SEL, 11280aa62aefSHarald Welte 0x00); 11290aa62aefSHarald Welte ucontrol->value.enumerated.item[0] = pinsel; 11300aa62aefSHarald Welte 11310aa62aefSHarald Welte return 0; 11320aa62aefSHarald Welte } 11330aa62aefSHarald Welte 11340713efebSLydia Wang static void activate_ctl(struct hda_codec *codec, const char *name, int active) 11350713efebSLydia Wang { 11360713efebSLydia Wang struct snd_kcontrol *ctl = snd_hda_find_mixer_ctl(codec, name); 11370713efebSLydia Wang if (ctl) { 11380713efebSLydia Wang ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11390713efebSLydia Wang ctl->vd[0].access |= active 11400713efebSLydia Wang ? 0 : SNDRV_CTL_ELEM_ACCESS_INACTIVE; 11410713efebSLydia Wang snd_ctl_notify(codec->bus->card, 11420713efebSLydia Wang SNDRV_CTL_EVENT_MASK_VALUE, &ctl->id); 11430713efebSLydia Wang } 11440713efebSLydia Wang } 11450713efebSLydia Wang 11465b0cb1d8SJaroslav Kysela static hda_nid_t side_mute_channel(struct via_spec *spec) 11475b0cb1d8SJaroslav Kysela { 11485b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 11495b0cb1d8SJaroslav Kysela case VT1708: return 0x1b; 11505b0cb1d8SJaroslav Kysela case VT1709_10CH: return 0x29; 11515b0cb1d8SJaroslav Kysela case VT1708B_8CH: /* fall thru */ 11525b0cb1d8SJaroslav Kysela case VT1708S: return 0x27; 11535b0cb1d8SJaroslav Kysela default: return 0; 11545b0cb1d8SJaroslav Kysela } 11555b0cb1d8SJaroslav Kysela } 11565b0cb1d8SJaroslav Kysela 1157cdc1784dSLydia Wang static int update_side_mute_status(struct hda_codec *codec) 1158cdc1784dSLydia Wang { 1159cdc1784dSLydia Wang /* mute side channel */ 1160cdc1784dSLydia Wang struct via_spec *spec = codec->spec; 1161cdc1784dSLydia Wang unsigned int parm = spec->hp_independent_mode 1162cdc1784dSLydia Wang ? AMP_OUT_MUTE : AMP_OUT_UNMUTE; 11635b0cb1d8SJaroslav Kysela hda_nid_t sw3 = side_mute_channel(spec); 1164cdc1784dSLydia Wang 1165cdc1784dSLydia Wang if (sw3) 1166cdc1784dSLydia Wang snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE, 1167cdc1784dSLydia Wang parm); 1168cdc1784dSLydia Wang return 0; 1169cdc1784dSLydia Wang } 1170cdc1784dSLydia Wang 11710aa62aefSHarald Welte static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 11720aa62aefSHarald Welte struct snd_ctl_elem_value *ucontrol) 11730aa62aefSHarald Welte { 11740aa62aefSHarald Welte struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 11750aa62aefSHarald Welte struct via_spec *spec = codec->spec; 11765b0cb1d8SJaroslav Kysela hda_nid_t nid = kcontrol->private_value; 11770aa62aefSHarald Welte unsigned int pinsel = ucontrol->value.enumerated.item[0]; 1178cdc1784dSLydia Wang /* Get Independent Mode index of headphone pin widget */ 1179cdc1784dSLydia Wang spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel 1180cdc1784dSLydia Wang ? 1 : 0; 1181cdc1784dSLydia Wang snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel); 11820aa62aefSHarald Welte 1183cdc1784dSLydia Wang if (spec->multiout.hp_nid && spec->multiout.hp_nid 1184cdc1784dSLydia Wang != spec->multiout.dac_nids[HDA_FRONT]) 1185cdc1784dSLydia Wang snd_hda_codec_setup_stream(codec, spec->multiout.hp_nid, 11860aa62aefSHarald Welte 0, 0, 0); 11870aa62aefSHarald Welte 1188cdc1784dSLydia Wang update_side_mute_status(codec); 11890713efebSLydia Wang /* update HP volume/swtich active state */ 11900713efebSLydia Wang if (spec->codec_type == VT1708S 1191eb7188caSLydia Wang || spec->codec_type == VT1702 1192f3db423dSLydia Wang || spec->codec_type == VT1718S 119325eaba2fSLydia Wang || spec->codec_type == VT1716S 1194ab6734e7SLydia Wang || spec->codec_type == VT2002P 1195ab6734e7SLydia Wang || spec->codec_type == VT1812) { 11960713efebSLydia Wang activate_ctl(codec, "Headphone Playback Volume", 11970713efebSLydia Wang spec->hp_independent_mode); 11980713efebSLydia Wang activate_ctl(codec, "Headphone Playback Switch", 11990713efebSLydia Wang spec->hp_independent_mode); 12000713efebSLydia Wang } 12010aa62aefSHarald Welte return 0; 12020aa62aefSHarald Welte } 12030aa62aefSHarald Welte 12045b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_hp_mixer[2] = { 12050aa62aefSHarald Welte { 12060aa62aefSHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 12070aa62aefSHarald Welte .name = "Independent HP", 12080aa62aefSHarald Welte .info = via_independent_hp_info, 12090aa62aefSHarald Welte .get = via_independent_hp_get, 12100aa62aefSHarald Welte .put = via_independent_hp_put, 12110aa62aefSHarald Welte }, 12125b0cb1d8SJaroslav Kysela { 12135b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 12145b0cb1d8SJaroslav Kysela .name = "Independent HP", 12155b0cb1d8SJaroslav Kysela }, 12160aa62aefSHarald Welte }; 12170aa62aefSHarald Welte 12185b0cb1d8SJaroslav Kysela static int via_hp_build(struct via_spec *spec) 12195b0cb1d8SJaroslav Kysela { 12205b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 12215b0cb1d8SJaroslav Kysela hda_nid_t nid; 12225b0cb1d8SJaroslav Kysela 12235b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[0]); 12245b0cb1d8SJaroslav Kysela if (knew == NULL) 12255b0cb1d8SJaroslav Kysela return -ENOMEM; 12265b0cb1d8SJaroslav Kysela 12275b0cb1d8SJaroslav Kysela switch (spec->codec_type) { 12285b0cb1d8SJaroslav Kysela case VT1718S: 12295b0cb1d8SJaroslav Kysela nid = 0x34; 12305b0cb1d8SJaroslav Kysela break; 12315b0cb1d8SJaroslav Kysela case VT2002P: 12325b0cb1d8SJaroslav Kysela nid = 0x35; 12335b0cb1d8SJaroslav Kysela break; 12345b0cb1d8SJaroslav Kysela case VT1812: 12355b0cb1d8SJaroslav Kysela nid = 0x3d; 12365b0cb1d8SJaroslav Kysela break; 12375b0cb1d8SJaroslav Kysela default: 12385b0cb1d8SJaroslav Kysela nid = spec->autocfg.hp_pins[0]; 12395b0cb1d8SJaroslav Kysela break; 12405b0cb1d8SJaroslav Kysela } 12415b0cb1d8SJaroslav Kysela 12425b0cb1d8SJaroslav Kysela knew->subdevice = HDA_SUBDEV_NID_FLAG | nid; 12435b0cb1d8SJaroslav Kysela knew->private_value = nid; 12445b0cb1d8SJaroslav Kysela 12455b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_hp_mixer[1]); 12465b0cb1d8SJaroslav Kysela if (knew == NULL) 12475b0cb1d8SJaroslav Kysela return -ENOMEM; 12485b0cb1d8SJaroslav Kysela knew->subdevice = side_mute_channel(spec); 12495b0cb1d8SJaroslav Kysela 12505b0cb1d8SJaroslav Kysela return 0; 12515b0cb1d8SJaroslav Kysela } 12525b0cb1d8SJaroslav Kysela 12531564b287SLydia Wang static void notify_aa_path_ctls(struct hda_codec *codec) 12541564b287SLydia Wang { 12551564b287SLydia Wang int i; 12561564b287SLydia Wang struct snd_ctl_elem_id id; 12571564b287SLydia Wang const char *labels[] = {"Mic", "Front Mic", "Line"}; 12581564b287SLydia Wang 12591564b287SLydia Wang memset(&id, 0, sizeof(id)); 12601564b287SLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 12611564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(labels); i++) { 12621564b287SLydia Wang sprintf(id.name, "%s Playback Volume", labels[i]); 12631564b287SLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 12641564b287SLydia Wang &id); 12651564b287SLydia Wang } 12661564b287SLydia Wang } 12671564b287SLydia Wang 12681564b287SLydia Wang static void mute_aa_path(struct hda_codec *codec, int mute) 12691564b287SLydia Wang { 12701564b287SLydia Wang struct via_spec *spec = codec->spec; 12711564b287SLydia Wang hda_nid_t nid_mixer; 12721564b287SLydia Wang int start_idx; 12731564b287SLydia Wang int end_idx; 12741564b287SLydia Wang int i; 12751564b287SLydia Wang /* get nid of MW0 and start & end index */ 12761564b287SLydia Wang switch (spec->codec_type) { 12771564b287SLydia Wang case VT1708: 12781564b287SLydia Wang nid_mixer = 0x17; 12791564b287SLydia Wang start_idx = 2; 12801564b287SLydia Wang end_idx = 4; 12811564b287SLydia Wang break; 12821564b287SLydia Wang case VT1709_10CH: 12831564b287SLydia Wang case VT1709_6CH: 12841564b287SLydia Wang nid_mixer = 0x18; 12851564b287SLydia Wang start_idx = 2; 12861564b287SLydia Wang end_idx = 4; 12871564b287SLydia Wang break; 12881564b287SLydia Wang case VT1708B_8CH: 12891564b287SLydia Wang case VT1708B_4CH: 12901564b287SLydia Wang case VT1708S: 1291f3db423dSLydia Wang case VT1716S: 12921564b287SLydia Wang nid_mixer = 0x16; 12931564b287SLydia Wang start_idx = 2; 12941564b287SLydia Wang end_idx = 4; 12951564b287SLydia Wang break; 12961564b287SLydia Wang default: 12971564b287SLydia Wang return; 12981564b287SLydia Wang } 12991564b287SLydia Wang /* check AA path's mute status */ 13001564b287SLydia Wang for (i = start_idx; i <= end_idx; i++) { 13011564b287SLydia Wang int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE; 13021564b287SLydia Wang snd_hda_codec_amp_stereo(codec, nid_mixer, HDA_INPUT, i, 13031564b287SLydia Wang HDA_AMP_MUTE, val); 13041564b287SLydia Wang } 13051564b287SLydia Wang } 13061564b287SLydia Wang static int is_smart51_pins(struct via_spec *spec, hda_nid_t pin) 13071564b287SLydia Wang { 13081564b287SLydia Wang int res = 0; 13091564b287SLydia Wang int index; 13101564b287SLydia Wang for (index = AUTO_PIN_MIC; index < AUTO_PIN_FRONT_LINE; index++) { 13111564b287SLydia Wang if (pin == spec->autocfg.input_pins[index]) { 13121564b287SLydia Wang res = 1; 13131564b287SLydia Wang break; 13141564b287SLydia Wang } 13151564b287SLydia Wang } 13161564b287SLydia Wang return res; 13171564b287SLydia Wang } 13181564b287SLydia Wang 13191564b287SLydia Wang static int via_smart51_info(struct snd_kcontrol *kcontrol, 13201564b287SLydia Wang struct snd_ctl_elem_info *uinfo) 13211564b287SLydia Wang { 13221564b287SLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 13231564b287SLydia Wang uinfo->count = 1; 13241564b287SLydia Wang uinfo->value.integer.min = 0; 13251564b287SLydia Wang uinfo->value.integer.max = 1; 13261564b287SLydia Wang return 0; 13271564b287SLydia Wang } 13281564b287SLydia Wang 13291564b287SLydia Wang static int via_smart51_get(struct snd_kcontrol *kcontrol, 13301564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 13311564b287SLydia Wang { 13321564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 13331564b287SLydia Wang struct via_spec *spec = codec->spec; 13341564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 13351564b287SLydia Wang int on = 1; 13361564b287SLydia Wang int i; 13371564b287SLydia Wang 13381564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 13391564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 13401564b287SLydia Wang if (nid) { 13411564b287SLydia Wang int ctl = 13421564b287SLydia Wang snd_hda_codec_read(codec, nid, 0, 13431564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 13441564b287SLydia Wang 0); 13451564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1346eb7188caSLydia Wang && spec->hp_independent_mode 1347eb7188caSLydia Wang && spec->codec_type != VT1718S) 13481564b287SLydia Wang continue; /* ignore FMic for independent HP */ 13491564b287SLydia Wang if (ctl & AC_PINCTL_IN_EN 13501564b287SLydia Wang && !(ctl & AC_PINCTL_OUT_EN)) 13511564b287SLydia Wang on = 0; 13521564b287SLydia Wang } 13531564b287SLydia Wang } 13541564b287SLydia Wang *ucontrol->value.integer.value = on; 13551564b287SLydia Wang return 0; 13561564b287SLydia Wang } 13571564b287SLydia Wang 13581564b287SLydia Wang static int via_smart51_put(struct snd_kcontrol *kcontrol, 13591564b287SLydia Wang struct snd_ctl_elem_value *ucontrol) 13601564b287SLydia Wang { 13611564b287SLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 13621564b287SLydia Wang struct via_spec *spec = codec->spec; 13631564b287SLydia Wang int out_in = *ucontrol->value.integer.value 13641564b287SLydia Wang ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN; 13651564b287SLydia Wang int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 13661564b287SLydia Wang int i; 13671564b287SLydia Wang 13681564b287SLydia Wang for (i = 0; i < ARRAY_SIZE(index); i++) { 13691564b287SLydia Wang hda_nid_t nid = spec->autocfg.input_pins[index[i]]; 13701564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC 1371eb7188caSLydia Wang && spec->hp_independent_mode 1372eb7188caSLydia Wang && spec->codec_type != VT1718S) 13731564b287SLydia Wang continue; /* don't retask FMic for independent HP */ 13741564b287SLydia Wang if (nid) { 13751564b287SLydia Wang unsigned int parm = snd_hda_codec_read( 13761564b287SLydia Wang codec, nid, 0, 13771564b287SLydia Wang AC_VERB_GET_PIN_WIDGET_CONTROL, 0); 13781564b287SLydia Wang parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN); 13791564b287SLydia Wang parm |= out_in; 13801564b287SLydia Wang snd_hda_codec_write(codec, nid, 0, 13811564b287SLydia Wang AC_VERB_SET_PIN_WIDGET_CONTROL, 13821564b287SLydia Wang parm); 13831564b287SLydia Wang if (out_in == AC_PINCTL_OUT_EN) { 13841564b287SLydia Wang mute_aa_path(codec, 1); 13851564b287SLydia Wang notify_aa_path_ctls(codec); 13861564b287SLydia Wang } 1387eb7188caSLydia Wang if (spec->codec_type == VT1718S) 1388eb7188caSLydia Wang snd_hda_codec_amp_stereo( 1389eb7188caSLydia Wang codec, nid, HDA_OUTPUT, 0, HDA_AMP_MUTE, 1390eb7188caSLydia Wang HDA_AMP_UNMUTE); 13911564b287SLydia Wang } 13921564b287SLydia Wang if (i == AUTO_PIN_FRONT_MIC) { 1393f3db423dSLydia Wang if (spec->codec_type == VT1708S 1394f3db423dSLydia Wang || spec->codec_type == VT1716S) { 13951564b287SLydia Wang /* input = index 1 (AOW3) */ 13961564b287SLydia Wang snd_hda_codec_write( 13971564b287SLydia Wang codec, nid, 0, 13981564b287SLydia Wang AC_VERB_SET_CONNECT_SEL, 1); 13991564b287SLydia Wang snd_hda_codec_amp_stereo( 14001564b287SLydia Wang codec, nid, HDA_OUTPUT, 14011564b287SLydia Wang 0, HDA_AMP_MUTE, HDA_AMP_UNMUTE); 14021564b287SLydia Wang } 14031564b287SLydia Wang } 14041564b287SLydia Wang } 14051564b287SLydia Wang spec->smart51_enabled = *ucontrol->value.integer.value; 14061564b287SLydia Wang set_jack_power_state(codec); 14071564b287SLydia Wang return 1; 14081564b287SLydia Wang } 14091564b287SLydia Wang 14105b0cb1d8SJaroslav Kysela static struct snd_kcontrol_new via_smart51_mixer[2] = { 14111564b287SLydia Wang { 14121564b287SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 14131564b287SLydia Wang .name = "Smart 5.1", 14141564b287SLydia Wang .count = 1, 14151564b287SLydia Wang .info = via_smart51_info, 14161564b287SLydia Wang .get = via_smart51_get, 14171564b287SLydia Wang .put = via_smart51_put, 14181564b287SLydia Wang }, 14195b0cb1d8SJaroslav Kysela { 14205b0cb1d8SJaroslav Kysela .iface = NID_MAPPING, 14215b0cb1d8SJaroslav Kysela .name = "Smart 5.1", 14225b0cb1d8SJaroslav Kysela } 14231564b287SLydia Wang }; 14241564b287SLydia Wang 14255b0cb1d8SJaroslav Kysela static int via_smart51_build(struct via_spec *spec) 14265b0cb1d8SJaroslav Kysela { 14275b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 14285b0cb1d8SJaroslav Kysela int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE }; 14295b0cb1d8SJaroslav Kysela hda_nid_t nid; 14305b0cb1d8SJaroslav Kysela int i; 14315b0cb1d8SJaroslav Kysela 14325b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[0]); 14335b0cb1d8SJaroslav Kysela if (knew == NULL) 14345b0cb1d8SJaroslav Kysela return -ENOMEM; 14355b0cb1d8SJaroslav Kysela 14365b0cb1d8SJaroslav Kysela for (i = 0; i < ARRAY_SIZE(index); i++) { 14375b0cb1d8SJaroslav Kysela nid = spec->autocfg.input_pins[index[i]]; 14385b0cb1d8SJaroslav Kysela if (nid) { 14395b0cb1d8SJaroslav Kysela knew = via_clone_control(spec, &via_smart51_mixer[1]); 14405b0cb1d8SJaroslav Kysela if (knew == NULL) 14415b0cb1d8SJaroslav Kysela return -ENOMEM; 14425b0cb1d8SJaroslav Kysela knew->subdevice = nid; 14435b0cb1d8SJaroslav Kysela } 14445b0cb1d8SJaroslav Kysela } 14455b0cb1d8SJaroslav Kysela 14465b0cb1d8SJaroslav Kysela return 0; 14475b0cb1d8SJaroslav Kysela } 14485b0cb1d8SJaroslav Kysela 1449c577b8a1SJoseph Chan /* capture mixer elements */ 1450c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1708_capture_mixer[] = { 1451c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT), 1452c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_INPUT), 1453c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x27, 0x0, HDA_INPUT), 1454c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x27, 0x0, HDA_INPUT), 1455c577b8a1SJoseph Chan { 1456c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 1457c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 1458c577b8a1SJoseph Chan * So call somewhat different.. 1459c577b8a1SJoseph Chan */ 1460c577b8a1SJoseph Chan /* .name = "Capture Source", */ 1461c577b8a1SJoseph Chan .name = "Input Source", 1462c577b8a1SJoseph Chan .count = 1, 1463c577b8a1SJoseph Chan .info = via_mux_enum_info, 1464c577b8a1SJoseph Chan .get = via_mux_enum_get, 1465c577b8a1SJoseph Chan .put = via_mux_enum_put, 1466c577b8a1SJoseph Chan }, 1467c577b8a1SJoseph Chan { } /* end */ 1468c577b8a1SJoseph Chan }; 1469f5271101SLydia Wang 1470f5271101SLydia Wang /* check AA path's mute statue */ 1471f5271101SLydia Wang static int is_aa_path_mute(struct hda_codec *codec) 1472f5271101SLydia Wang { 1473f5271101SLydia Wang int mute = 1; 1474f5271101SLydia Wang hda_nid_t nid_mixer; 1475f5271101SLydia Wang int start_idx; 1476f5271101SLydia Wang int end_idx; 1477f5271101SLydia Wang int i; 1478f5271101SLydia Wang struct via_spec *spec = codec->spec; 1479f5271101SLydia Wang /* get nid of MW0 and start & end index */ 1480f5271101SLydia Wang switch (spec->codec_type) { 1481f5271101SLydia Wang case VT1708B_8CH: 1482f5271101SLydia Wang case VT1708B_4CH: 1483f5271101SLydia Wang case VT1708S: 1484f3db423dSLydia Wang case VT1716S: 1485f5271101SLydia Wang nid_mixer = 0x16; 1486f5271101SLydia Wang start_idx = 2; 1487f5271101SLydia Wang end_idx = 4; 1488f5271101SLydia Wang break; 1489f5271101SLydia Wang case VT1702: 1490f5271101SLydia Wang nid_mixer = 0x1a; 1491f5271101SLydia Wang start_idx = 1; 1492f5271101SLydia Wang end_idx = 3; 1493f5271101SLydia Wang break; 1494eb7188caSLydia Wang case VT1718S: 1495eb7188caSLydia Wang nid_mixer = 0x21; 1496eb7188caSLydia Wang start_idx = 1; 1497eb7188caSLydia Wang end_idx = 3; 1498eb7188caSLydia Wang break; 149925eaba2fSLydia Wang case VT2002P: 1500ab6734e7SLydia Wang case VT1812: 150125eaba2fSLydia Wang nid_mixer = 0x21; 150225eaba2fSLydia Wang start_idx = 0; 150325eaba2fSLydia Wang end_idx = 2; 150425eaba2fSLydia Wang break; 1505f5271101SLydia Wang default: 1506f5271101SLydia Wang return 0; 1507f5271101SLydia Wang } 1508f5271101SLydia Wang /* check AA path's mute status */ 1509f5271101SLydia Wang for (i = start_idx; i <= end_idx; i++) { 1510f5271101SLydia Wang unsigned int con_list = snd_hda_codec_read( 1511f5271101SLydia Wang codec, nid_mixer, 0, AC_VERB_GET_CONNECT_LIST, i/4*4); 1512f5271101SLydia Wang int shift = 8 * (i % 4); 1513f5271101SLydia Wang hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift; 1514f5271101SLydia Wang unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin); 1515f5271101SLydia Wang if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) { 1516f5271101SLydia Wang /* check mute status while the pin is connected */ 1517f5271101SLydia Wang int mute_l = snd_hda_codec_amp_read(codec, nid_mixer, 0, 1518f5271101SLydia Wang HDA_INPUT, i) >> 7; 1519f5271101SLydia Wang int mute_r = snd_hda_codec_amp_read(codec, nid_mixer, 1, 1520f5271101SLydia Wang HDA_INPUT, i) >> 7; 1521f5271101SLydia Wang if (!mute_l || !mute_r) { 1522f5271101SLydia Wang mute = 0; 1523f5271101SLydia Wang break; 1524f5271101SLydia Wang } 1525f5271101SLydia Wang } 1526f5271101SLydia Wang } 1527f5271101SLydia Wang return mute; 1528f5271101SLydia Wang } 1529f5271101SLydia Wang 1530f5271101SLydia Wang /* enter/exit analog low-current mode */ 1531f5271101SLydia Wang static void analog_low_current_mode(struct hda_codec *codec, int stream_idle) 1532f5271101SLydia Wang { 1533f5271101SLydia Wang struct via_spec *spec = codec->spec; 1534f5271101SLydia Wang static int saved_stream_idle = 1; /* saved stream idle status */ 1535f5271101SLydia Wang int enable = is_aa_path_mute(codec); 1536f5271101SLydia Wang unsigned int verb = 0; 1537f5271101SLydia Wang unsigned int parm = 0; 1538f5271101SLydia Wang 1539f5271101SLydia Wang if (stream_idle == -1) /* stream status did not change */ 1540f5271101SLydia Wang enable = enable && saved_stream_idle; 1541f5271101SLydia Wang else { 1542f5271101SLydia Wang enable = enable && stream_idle; 1543f5271101SLydia Wang saved_stream_idle = stream_idle; 1544f5271101SLydia Wang } 1545f5271101SLydia Wang 1546f5271101SLydia Wang /* decide low current mode's verb & parameter */ 1547f5271101SLydia Wang switch (spec->codec_type) { 1548f5271101SLydia Wang case VT1708B_8CH: 1549f5271101SLydia Wang case VT1708B_4CH: 1550f5271101SLydia Wang verb = 0xf70; 1551f5271101SLydia Wang parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */ 1552f5271101SLydia Wang break; 1553f5271101SLydia Wang case VT1708S: 1554eb7188caSLydia Wang case VT1718S: 1555f3db423dSLydia Wang case VT1716S: 1556f5271101SLydia Wang verb = 0xf73; 1557f5271101SLydia Wang parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */ 1558f5271101SLydia Wang break; 1559f5271101SLydia Wang case VT1702: 1560f5271101SLydia Wang verb = 0xf73; 1561f5271101SLydia Wang parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */ 1562f5271101SLydia Wang break; 156325eaba2fSLydia Wang case VT2002P: 1564ab6734e7SLydia Wang case VT1812: 156525eaba2fSLydia Wang verb = 0xf93; 156625eaba2fSLydia Wang parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */ 156725eaba2fSLydia Wang break; 1568f5271101SLydia Wang default: 1569f5271101SLydia Wang return; /* other codecs are not supported */ 1570f5271101SLydia Wang } 1571f5271101SLydia Wang /* send verb */ 1572f5271101SLydia Wang snd_hda_codec_write(codec, codec->afg, 0, verb, parm); 1573f5271101SLydia Wang } 1574f5271101SLydia Wang 1575c577b8a1SJoseph Chan /* 1576c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 1577c577b8a1SJoseph Chan */ 1578c577b8a1SJoseph Chan static struct hda_verb vt1708_volume_init_verbs[] = { 1579c577b8a1SJoseph Chan /* 1580c577b8a1SJoseph Chan * Unmute ADC0-1 and set the default input to mic-in 1581c577b8a1SJoseph Chan */ 1582c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1583c577b8a1SJoseph Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1584c577b8a1SJoseph Chan 1585c577b8a1SJoseph Chan 1586f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 1587c577b8a1SJoseph Chan * mixer widget 1588c577b8a1SJoseph Chan */ 1589c577b8a1SJoseph Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 1590f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 1591f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 1592f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 1593f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 1594f7278fd0SJosepch Chan {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 1595c577b8a1SJoseph Chan 1596c577b8a1SJoseph Chan /* 1597c577b8a1SJoseph Chan * Set up output mixers (0x19 - 0x1b) 1598c577b8a1SJoseph Chan */ 1599c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 1600c577b8a1SJoseph Chan {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1601c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1602c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 1603c577b8a1SJoseph Chan 1604bfdc675aSLydia Wang /* Setup default input MW0 to PW4 */ 1605bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 1606c577b8a1SJoseph Chan /* PW9 Output enable */ 1607c577b8a1SJoseph Chan {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 1608f7278fd0SJosepch Chan { } 1609c577b8a1SJoseph Chan }; 1610c577b8a1SJoseph Chan 1611c577b8a1SJoseph Chan static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, 1612c577b8a1SJoseph Chan struct hda_codec *codec, 1613c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1614c577b8a1SJoseph Chan { 1615c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 161617314379SLydia Wang int idle = substream->pstr->substream_opened == 1 161717314379SLydia Wang && substream->ref_count == 0; 161817314379SLydia Wang analog_low_current_mode(codec, idle); 16199a08160bSTakashi Iwai return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 16209a08160bSTakashi Iwai hinfo); 1621c577b8a1SJoseph Chan } 1622c577b8a1SJoseph Chan 16230aa62aefSHarald Welte static void playback_multi_pcm_prep_0(struct hda_codec *codec, 16240aa62aefSHarald Welte unsigned int stream_tag, 16250aa62aefSHarald Welte unsigned int format, 16260aa62aefSHarald Welte struct snd_pcm_substream *substream) 16270aa62aefSHarald Welte { 16280aa62aefSHarald Welte struct via_spec *spec = codec->spec; 16290aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 16300aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 16310aa62aefSHarald Welte int chs = substream->runtime->channels; 16320aa62aefSHarald Welte int i; 16330aa62aefSHarald Welte 16340aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 16350aa62aefSHarald Welte if (mout->dig_out_nid && mout->dig_out_used != HDA_DIG_EXCLUSIVE) { 16360aa62aefSHarald Welte if (chs == 2 && 16370aa62aefSHarald Welte snd_hda_is_supported_format(codec, mout->dig_out_nid, 16380aa62aefSHarald Welte format) && 16390aa62aefSHarald Welte !(codec->spdif_status & IEC958_AES0_NONAUDIO)) { 16400aa62aefSHarald Welte mout->dig_out_used = HDA_DIG_ANALOG_DUP; 16410aa62aefSHarald Welte /* turn off SPDIF once; otherwise the IEC958 bits won't 16420aa62aefSHarald Welte * be updated */ 16430aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 16440aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 16450aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 16460aa62aefSHarald Welte codec->spdif_ctls & 16470aa62aefSHarald Welte ~AC_DIG1_ENABLE & 0xff); 16480aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16490aa62aefSHarald Welte stream_tag, 0, format); 16500aa62aefSHarald Welte /* turn on again (if needed) */ 16510aa62aefSHarald Welte if (codec->spdif_ctls & AC_DIG1_ENABLE) 16520aa62aefSHarald Welte snd_hda_codec_write(codec, mout->dig_out_nid, 0, 16530aa62aefSHarald Welte AC_VERB_SET_DIGI_CONVERT_1, 16540aa62aefSHarald Welte codec->spdif_ctls & 0xff); 16550aa62aefSHarald Welte } else { 16560aa62aefSHarald Welte mout->dig_out_used = 0; 16570aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 16580aa62aefSHarald Welte 0, 0, 0); 16590aa62aefSHarald Welte } 16600aa62aefSHarald Welte } 16610aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 16620aa62aefSHarald Welte 16630aa62aefSHarald Welte /* front */ 16640aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[HDA_FRONT], stream_tag, 16650aa62aefSHarald Welte 0, format); 16660aa62aefSHarald Welte 1667eb7188caSLydia Wang if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] 1668eb7188caSLydia Wang && !spec->hp_independent_mode) 16690aa62aefSHarald Welte /* headphone out will just decode front left/right (stereo) */ 16700aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, stream_tag, 16710aa62aefSHarald Welte 0, format); 16720aa62aefSHarald Welte 16730aa62aefSHarald Welte /* extra outputs copied from front */ 16740aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 16750aa62aefSHarald Welte if (mout->extra_out_nid[i]) 16760aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 16770aa62aefSHarald Welte mout->extra_out_nid[i], 16780aa62aefSHarald Welte stream_tag, 0, format); 16790aa62aefSHarald Welte 16800aa62aefSHarald Welte /* surrounds */ 16810aa62aefSHarald Welte for (i = 1; i < mout->num_dacs; i++) { 16820aa62aefSHarald Welte if (chs >= (i + 1) * 2) /* independent out */ 16830aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 16840aa62aefSHarald Welte i * 2, format); 16850aa62aefSHarald Welte else /* copy front */ 16860aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], stream_tag, 16870aa62aefSHarald Welte 0, format); 16880aa62aefSHarald Welte } 16890aa62aefSHarald Welte } 16900aa62aefSHarald Welte 16910aa62aefSHarald Welte static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, 16920aa62aefSHarald Welte struct hda_codec *codec, 16930aa62aefSHarald Welte unsigned int stream_tag, 16940aa62aefSHarald Welte unsigned int format, 16950aa62aefSHarald Welte struct snd_pcm_substream *substream) 16960aa62aefSHarald Welte { 16970aa62aefSHarald Welte struct via_spec *spec = codec->spec; 16980aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 16990aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 17000aa62aefSHarald Welte 17010aa62aefSHarald Welte if (substream->number == 0) 17020aa62aefSHarald Welte playback_multi_pcm_prep_0(codec, stream_tag, format, 17030aa62aefSHarald Welte substream); 17040aa62aefSHarald Welte else { 17050aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 17060aa62aefSHarald Welte spec->hp_independent_mode) 17070aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17080aa62aefSHarald Welte stream_tag, 0, format); 17090aa62aefSHarald Welte } 17101f2e99feSLydia Wang vt1708_start_hp_work(spec); 17110aa62aefSHarald Welte return 0; 17120aa62aefSHarald Welte } 17130aa62aefSHarald Welte 17140aa62aefSHarald Welte static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, 17150aa62aefSHarald Welte struct hda_codec *codec, 17160aa62aefSHarald Welte struct snd_pcm_substream *substream) 17170aa62aefSHarald Welte { 17180aa62aefSHarald Welte struct via_spec *spec = codec->spec; 17190aa62aefSHarald Welte struct hda_multi_out *mout = &spec->multiout; 17200aa62aefSHarald Welte hda_nid_t *nids = mout->dac_nids; 17210aa62aefSHarald Welte int i; 17220aa62aefSHarald Welte 17230aa62aefSHarald Welte if (substream->number == 0) { 17240aa62aefSHarald Welte for (i = 0; i < mout->num_dacs; i++) 17250aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, nids[i], 0, 0, 0); 17260aa62aefSHarald Welte 17270aa62aefSHarald Welte if (mout->hp_nid && !spec->hp_independent_mode) 17280aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17290aa62aefSHarald Welte 0, 0, 0); 17300aa62aefSHarald Welte 17310aa62aefSHarald Welte for (i = 0; i < ARRAY_SIZE(mout->extra_out_nid); i++) 17320aa62aefSHarald Welte if (mout->extra_out_nid[i]) 17330aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, 17340aa62aefSHarald Welte mout->extra_out_nid[i], 17350aa62aefSHarald Welte 0, 0, 0); 17360aa62aefSHarald Welte mutex_lock(&codec->spdif_mutex); 17370aa62aefSHarald Welte if (mout->dig_out_nid && 17380aa62aefSHarald Welte mout->dig_out_used == HDA_DIG_ANALOG_DUP) { 17390aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->dig_out_nid, 17400aa62aefSHarald Welte 0, 0, 0); 17410aa62aefSHarald Welte mout->dig_out_used = 0; 17420aa62aefSHarald Welte } 17430aa62aefSHarald Welte mutex_unlock(&codec->spdif_mutex); 17440aa62aefSHarald Welte } else { 17450aa62aefSHarald Welte if (mout->hp_nid && mout->hp_nid != nids[HDA_FRONT] && 17460aa62aefSHarald Welte spec->hp_independent_mode) 17470aa62aefSHarald Welte snd_hda_codec_setup_stream(codec, mout->hp_nid, 17480aa62aefSHarald Welte 0, 0, 0); 17490aa62aefSHarald Welte } 17501f2e99feSLydia Wang vt1708_stop_hp_work(spec); 17510aa62aefSHarald Welte return 0; 17520aa62aefSHarald Welte } 17530aa62aefSHarald Welte 1754c577b8a1SJoseph Chan /* 1755c577b8a1SJoseph Chan * Digital out 1756c577b8a1SJoseph Chan */ 1757c577b8a1SJoseph Chan static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo, 1758c577b8a1SJoseph Chan struct hda_codec *codec, 1759c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1760c577b8a1SJoseph Chan { 1761c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1762c577b8a1SJoseph Chan return snd_hda_multi_out_dig_open(codec, &spec->multiout); 1763c577b8a1SJoseph Chan } 1764c577b8a1SJoseph Chan 1765c577b8a1SJoseph Chan static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo, 1766c577b8a1SJoseph Chan struct hda_codec *codec, 1767c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1768c577b8a1SJoseph Chan { 1769c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1770c577b8a1SJoseph Chan return snd_hda_multi_out_dig_close(codec, &spec->multiout); 1771c577b8a1SJoseph Chan } 1772c577b8a1SJoseph Chan 17735691ec7fSHarald Welte static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo, 177498aa34c0SHarald Welte struct hda_codec *codec, 177598aa34c0SHarald Welte unsigned int stream_tag, 177698aa34c0SHarald Welte unsigned int format, 177798aa34c0SHarald Welte struct snd_pcm_substream *substream) 177898aa34c0SHarald Welte { 177998aa34c0SHarald Welte struct via_spec *spec = codec->spec; 17809da29271STakashi Iwai return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, 17819da29271STakashi Iwai stream_tag, format, substream); 17829da29271STakashi Iwai } 17835691ec7fSHarald Welte 17849da29271STakashi Iwai static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, 17859da29271STakashi Iwai struct hda_codec *codec, 17869da29271STakashi Iwai struct snd_pcm_substream *substream) 17879da29271STakashi Iwai { 17889da29271STakashi Iwai struct via_spec *spec = codec->spec; 17899da29271STakashi Iwai snd_hda_multi_out_dig_cleanup(codec, &spec->multiout); 179098aa34c0SHarald Welte return 0; 179198aa34c0SHarald Welte } 179298aa34c0SHarald Welte 1793c577b8a1SJoseph Chan /* 1794c577b8a1SJoseph Chan * Analog capture 1795c577b8a1SJoseph Chan */ 1796c577b8a1SJoseph Chan static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo, 1797c577b8a1SJoseph Chan struct hda_codec *codec, 1798c577b8a1SJoseph Chan unsigned int stream_tag, 1799c577b8a1SJoseph Chan unsigned int format, 1800c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1801c577b8a1SJoseph Chan { 1802c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1803c577b8a1SJoseph Chan 1804c577b8a1SJoseph Chan snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number], 1805c577b8a1SJoseph Chan stream_tag, 0, format); 1806c577b8a1SJoseph Chan return 0; 1807c577b8a1SJoseph Chan } 1808c577b8a1SJoseph Chan 1809c577b8a1SJoseph Chan static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo, 1810c577b8a1SJoseph Chan struct hda_codec *codec, 1811c577b8a1SJoseph Chan struct snd_pcm_substream *substream) 1812c577b8a1SJoseph Chan { 1813c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1814888afa15STakashi Iwai snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]); 1815c577b8a1SJoseph Chan return 0; 1816c577b8a1SJoseph Chan } 1817c577b8a1SJoseph Chan 1818c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_playback = { 18190aa62aefSHarald Welte .substreams = 2, 1820c577b8a1SJoseph Chan .channels_min = 2, 1821c577b8a1SJoseph Chan .channels_max = 8, 1822c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 1823c577b8a1SJoseph Chan .ops = { 1824c577b8a1SJoseph Chan .open = via_playback_pcm_open, 18250aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 18260aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 1827c577b8a1SJoseph Chan }, 1828c577b8a1SJoseph Chan }; 1829c577b8a1SJoseph Chan 1830bc9b5623STakashi Iwai static struct hda_pcm_stream vt1708_pcm_analog_s16_playback = { 1831c873cc25SLydia Wang .substreams = 2, 1832bc9b5623STakashi Iwai .channels_min = 2, 1833bc9b5623STakashi Iwai .channels_max = 8, 1834bc9b5623STakashi Iwai .nid = 0x10, /* NID to query formats and rates */ 1835bc9b5623STakashi Iwai /* We got noisy outputs on the right channel on VT1708 when 1836bc9b5623STakashi Iwai * 24bit samples are used. Until any workaround is found, 1837bc9b5623STakashi Iwai * disable the 24bit format, so far. 1838bc9b5623STakashi Iwai */ 1839bc9b5623STakashi Iwai .formats = SNDRV_PCM_FMTBIT_S16_LE, 1840bc9b5623STakashi Iwai .ops = { 1841bc9b5623STakashi Iwai .open = via_playback_pcm_open, 1842c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 1843c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup 1844bc9b5623STakashi Iwai }, 1845bc9b5623STakashi Iwai }; 1846bc9b5623STakashi Iwai 1847c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_analog_capture = { 1848c577b8a1SJoseph Chan .substreams = 2, 1849c577b8a1SJoseph Chan .channels_min = 2, 1850c577b8a1SJoseph Chan .channels_max = 2, 1851c577b8a1SJoseph Chan .nid = 0x15, /* NID to query formats and rates */ 1852c577b8a1SJoseph Chan .ops = { 1853c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 1854c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 1855c577b8a1SJoseph Chan }, 1856c577b8a1SJoseph Chan }; 1857c577b8a1SJoseph Chan 1858c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_playback = { 1859c577b8a1SJoseph Chan .substreams = 1, 1860c577b8a1SJoseph Chan .channels_min = 2, 1861c577b8a1SJoseph Chan .channels_max = 2, 1862c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 1863c577b8a1SJoseph Chan .ops = { 1864c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 18656b97eb45STakashi Iwai .close = via_dig_playback_pcm_close, 18669da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 18679da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 1868c577b8a1SJoseph Chan }, 1869c577b8a1SJoseph Chan }; 1870c577b8a1SJoseph Chan 1871c577b8a1SJoseph Chan static struct hda_pcm_stream vt1708_pcm_digital_capture = { 1872c577b8a1SJoseph Chan .substreams = 1, 1873c577b8a1SJoseph Chan .channels_min = 2, 1874c577b8a1SJoseph Chan .channels_max = 2, 1875c577b8a1SJoseph Chan }; 1876c577b8a1SJoseph Chan 1877c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec) 1878c577b8a1SJoseph Chan { 1879c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 18805b0cb1d8SJaroslav Kysela struct snd_kcontrol *kctl; 18815b0cb1d8SJaroslav Kysela struct snd_kcontrol_new *knew; 18825b0cb1d8SJaroslav Kysela int err, i; 1883c577b8a1SJoseph Chan 1884c577b8a1SJoseph Chan for (i = 0; i < spec->num_mixers; i++) { 1885c577b8a1SJoseph Chan err = snd_hda_add_new_ctls(codec, spec->mixers[i]); 1886c577b8a1SJoseph Chan if (err < 0) 1887c577b8a1SJoseph Chan return err; 1888c577b8a1SJoseph Chan } 1889c577b8a1SJoseph Chan 1890c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1891c577b8a1SJoseph Chan err = snd_hda_create_spdif_out_ctls(codec, 1892c577b8a1SJoseph Chan spec->multiout.dig_out_nid); 1893c577b8a1SJoseph Chan if (err < 0) 1894c577b8a1SJoseph Chan return err; 18959a08160bSTakashi Iwai err = snd_hda_create_spdif_share_sw(codec, 18969a08160bSTakashi Iwai &spec->multiout); 18979a08160bSTakashi Iwai if (err < 0) 18989a08160bSTakashi Iwai return err; 18999a08160bSTakashi Iwai spec->multiout.share_spdif = 1; 1900c577b8a1SJoseph Chan } 1901c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1902c577b8a1SJoseph Chan err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid); 1903c577b8a1SJoseph Chan if (err < 0) 1904c577b8a1SJoseph Chan return err; 1905c577b8a1SJoseph Chan } 190617314379SLydia Wang 19075b0cb1d8SJaroslav Kysela /* assign Capture Source enums to NID */ 19085b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, "Input Source"); 19095b0cb1d8SJaroslav Kysela for (i = 0; kctl && i < kctl->count; i++) { 19105b0cb1d8SJaroslav Kysela err = snd_hda_add_nids(codec, kctl, i, spec->mux_nids, 19115b0cb1d8SJaroslav Kysela spec->input_mux->num_items); 19125b0cb1d8SJaroslav Kysela if (err < 0) 19135b0cb1d8SJaroslav Kysela return err; 19145b0cb1d8SJaroslav Kysela } 19155b0cb1d8SJaroslav Kysela 19165b0cb1d8SJaroslav Kysela /* other nid->control mapping */ 19175b0cb1d8SJaroslav Kysela for (i = 0; i < spec->num_mixers; i++) { 19185b0cb1d8SJaroslav Kysela for (knew = spec->mixers[i]; knew->name; knew++) { 19195b0cb1d8SJaroslav Kysela if (knew->iface != NID_MAPPING) 19205b0cb1d8SJaroslav Kysela continue; 19215b0cb1d8SJaroslav Kysela kctl = snd_hda_find_mixer_ctl(codec, knew->name); 19225b0cb1d8SJaroslav Kysela if (kctl == NULL) 19235b0cb1d8SJaroslav Kysela continue; 19245b0cb1d8SJaroslav Kysela err = snd_hda_add_nid(codec, kctl, 0, 19255b0cb1d8SJaroslav Kysela knew->subdevice); 19265b0cb1d8SJaroslav Kysela } 19275b0cb1d8SJaroslav Kysela } 19285b0cb1d8SJaroslav Kysela 192917314379SLydia Wang /* init power states */ 193017314379SLydia Wang set_jack_power_state(codec); 193117314379SLydia Wang analog_low_current_mode(codec, 1); 193217314379SLydia Wang 1933603c4019STakashi Iwai via_free_kctls(codec); /* no longer needed */ 1934c577b8a1SJoseph Chan return 0; 1935c577b8a1SJoseph Chan } 1936c577b8a1SJoseph Chan 1937c577b8a1SJoseph Chan static int via_build_pcms(struct hda_codec *codec) 1938c577b8a1SJoseph Chan { 1939c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1940c577b8a1SJoseph Chan struct hda_pcm *info = spec->pcm_rec; 1941c577b8a1SJoseph Chan 1942c577b8a1SJoseph Chan codec->num_pcms = 1; 1943c577b8a1SJoseph Chan codec->pcm_info = info; 1944c577b8a1SJoseph Chan 1945c577b8a1SJoseph Chan info->name = spec->stream_name_analog; 1946377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1947377ff31aSLydia Wang *(spec->stream_analog_playback); 1948377ff31aSLydia Wang info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1949377ff31aSLydia Wang spec->multiout.dac_nids[0]; 1950c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = *(spec->stream_analog_capture); 1951c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0]; 1952c577b8a1SJoseph Chan 1953c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = 1954c577b8a1SJoseph Chan spec->multiout.max_channels; 1955c577b8a1SJoseph Chan 1956c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid || spec->dig_in_nid) { 1957c577b8a1SJoseph Chan codec->num_pcms++; 1958c577b8a1SJoseph Chan info++; 1959c577b8a1SJoseph Chan info->name = spec->stream_name_digital; 19607ba72ba1STakashi Iwai info->pcm_type = HDA_PCM_TYPE_SPDIF; 1961c577b8a1SJoseph Chan if (spec->multiout.dig_out_nid) { 1962c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK] = 1963c577b8a1SJoseph Chan *(spec->stream_digital_playback); 1964c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = 1965c577b8a1SJoseph Chan spec->multiout.dig_out_nid; 1966c577b8a1SJoseph Chan } 1967c577b8a1SJoseph Chan if (spec->dig_in_nid) { 1968c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE] = 1969c577b8a1SJoseph Chan *(spec->stream_digital_capture); 1970c577b8a1SJoseph Chan info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = 1971c577b8a1SJoseph Chan spec->dig_in_nid; 1972c577b8a1SJoseph Chan } 1973c577b8a1SJoseph Chan } 1974c577b8a1SJoseph Chan 1975c577b8a1SJoseph Chan return 0; 1976c577b8a1SJoseph Chan } 1977c577b8a1SJoseph Chan 1978c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec) 1979c577b8a1SJoseph Chan { 1980c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 1981c577b8a1SJoseph Chan 1982c577b8a1SJoseph Chan if (!spec) 1983c577b8a1SJoseph Chan return; 1984c577b8a1SJoseph Chan 1985603c4019STakashi Iwai via_free_kctls(codec); 19861f2e99feSLydia Wang vt1708_stop_hp_work(spec); 1987c577b8a1SJoseph Chan kfree(codec->spec); 1988c577b8a1SJoseph Chan } 1989c577b8a1SJoseph Chan 199069e52a80SHarald Welte /* mute internal speaker if HP is plugged */ 199169e52a80SHarald Welte static void via_hp_automute(struct hda_codec *codec) 199269e52a80SHarald Welte { 1993dcf34c8cSLydia Wang unsigned int present = 0; 199469e52a80SHarald Welte struct via_spec *spec = codec->spec; 199569e52a80SHarald Welte 1996d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 1997dcf34c8cSLydia Wang 1998dcf34c8cSLydia Wang if (!spec->hp_independent_mode) { 1999dcf34c8cSLydia Wang struct snd_ctl_elem_id id; 2000dcf34c8cSLydia Wang /* auto mute */ 2001dcf34c8cSLydia Wang snd_hda_codec_amp_stereo( 2002dcf34c8cSLydia Wang codec, spec->autocfg.line_out_pins[0], HDA_OUTPUT, 0, 2003dcf34c8cSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 2004dcf34c8cSLydia Wang /* notify change */ 2005dcf34c8cSLydia Wang memset(&id, 0, sizeof(id)); 2006dcf34c8cSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 2007dcf34c8cSLydia Wang strcpy(id.name, "Front Playback Switch"); 2008dcf34c8cSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 2009dcf34c8cSLydia Wang &id); 2010dcf34c8cSLydia Wang } 201169e52a80SHarald Welte } 201269e52a80SHarald Welte 2013f3db423dSLydia Wang /* mute mono out if HP or Line out is plugged */ 2014f3db423dSLydia Wang static void via_mono_automute(struct hda_codec *codec) 2015f3db423dSLydia Wang { 2016f3db423dSLydia Wang unsigned int hp_present, lineout_present; 2017f3db423dSLydia Wang struct via_spec *spec = codec->spec; 2018f3db423dSLydia Wang 2019f3db423dSLydia Wang if (spec->codec_type != VT1716S) 2020f3db423dSLydia Wang return; 2021f3db423dSLydia Wang 2022d56757abSTakashi Iwai lineout_present = snd_hda_jack_detect(codec, 2023d56757abSTakashi Iwai spec->autocfg.line_out_pins[0]); 2024f3db423dSLydia Wang 2025f3db423dSLydia Wang /* Mute Mono Out if Line Out is plugged */ 2026f3db423dSLydia Wang if (lineout_present) { 2027f3db423dSLydia Wang snd_hda_codec_amp_stereo( 2028f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, HDA_AMP_MUTE); 2029f3db423dSLydia Wang return; 2030f3db423dSLydia Wang } 2031f3db423dSLydia Wang 2032d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 2033f3db423dSLydia Wang 2034f3db423dSLydia Wang if (!spec->hp_independent_mode) 2035f3db423dSLydia Wang snd_hda_codec_amp_stereo( 2036f3db423dSLydia Wang codec, 0x2A, HDA_OUTPUT, 0, HDA_AMP_MUTE, 2037f3db423dSLydia Wang hp_present ? HDA_AMP_MUTE : 0); 2038f3db423dSLydia Wang } 2039f3db423dSLydia Wang 204069e52a80SHarald Welte static void via_gpio_control(struct hda_codec *codec) 204169e52a80SHarald Welte { 204269e52a80SHarald Welte unsigned int gpio_data; 204369e52a80SHarald Welte unsigned int vol_counter; 204469e52a80SHarald Welte unsigned int vol; 204569e52a80SHarald Welte unsigned int master_vol; 204669e52a80SHarald Welte 204769e52a80SHarald Welte struct via_spec *spec = codec->spec; 204869e52a80SHarald Welte 204969e52a80SHarald Welte gpio_data = snd_hda_codec_read(codec, codec->afg, 0, 205069e52a80SHarald Welte AC_VERB_GET_GPIO_DATA, 0) & 0x03; 205169e52a80SHarald Welte 205269e52a80SHarald Welte vol_counter = (snd_hda_codec_read(codec, codec->afg, 0, 205369e52a80SHarald Welte 0xF84, 0) & 0x3F0000) >> 16; 205469e52a80SHarald Welte 205569e52a80SHarald Welte vol = vol_counter & 0x1F; 205669e52a80SHarald Welte master_vol = snd_hda_codec_read(codec, 0x1A, 0, 205769e52a80SHarald Welte AC_VERB_GET_AMP_GAIN_MUTE, 205869e52a80SHarald Welte AC_AMP_GET_INPUT); 205969e52a80SHarald Welte 206069e52a80SHarald Welte if (gpio_data == 0x02) { 206169e52a80SHarald Welte /* unmute line out */ 206269e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, spec->autocfg.line_out_pins[0], 206369e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 0); 206469e52a80SHarald Welte 206569e52a80SHarald Welte if (vol_counter & 0x20) { 206669e52a80SHarald Welte /* decrease volume */ 206769e52a80SHarald Welte if (vol > master_vol) 206869e52a80SHarald Welte vol = master_vol; 206969e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 207069e52a80SHarald Welte 0, HDA_AMP_VOLMASK, 207169e52a80SHarald Welte master_vol-vol); 207269e52a80SHarald Welte } else { 207369e52a80SHarald Welte /* increase volume */ 207469e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0, 207569e52a80SHarald Welte HDA_AMP_VOLMASK, 207669e52a80SHarald Welte ((master_vol+vol) > 0x2A) ? 0x2A : 207769e52a80SHarald Welte (master_vol+vol)); 207869e52a80SHarald Welte } 207969e52a80SHarald Welte } else if (!(gpio_data & 0x02)) { 208069e52a80SHarald Welte /* mute line out */ 208169e52a80SHarald Welte snd_hda_codec_amp_stereo(codec, 208269e52a80SHarald Welte spec->autocfg.line_out_pins[0], 208369e52a80SHarald Welte HDA_OUTPUT, 0, HDA_AMP_MUTE, 208469e52a80SHarald Welte HDA_AMP_MUTE); 208569e52a80SHarald Welte } 208669e52a80SHarald Welte } 208769e52a80SHarald Welte 208825eaba2fSLydia Wang /* mute Internal-Speaker if HP is plugged */ 208925eaba2fSLydia Wang static void via_speaker_automute(struct hda_codec *codec) 209025eaba2fSLydia Wang { 209125eaba2fSLydia Wang unsigned int hp_present; 209225eaba2fSLydia Wang struct via_spec *spec = codec->spec; 209325eaba2fSLydia Wang 2094ab6734e7SLydia Wang if (spec->codec_type != VT2002P && spec->codec_type != VT1812) 209525eaba2fSLydia Wang return; 209625eaba2fSLydia Wang 2097d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 209825eaba2fSLydia Wang 209925eaba2fSLydia Wang if (!spec->hp_independent_mode) { 210025eaba2fSLydia Wang struct snd_ctl_elem_id id; 210125eaba2fSLydia Wang snd_hda_codec_amp_stereo( 210225eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[0], HDA_OUTPUT, 0, 210325eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 210425eaba2fSLydia Wang /* notify change */ 210525eaba2fSLydia Wang memset(&id, 0, sizeof(id)); 210625eaba2fSLydia Wang id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; 210725eaba2fSLydia Wang strcpy(id.name, "Speaker Playback Switch"); 210825eaba2fSLydia Wang snd_ctl_notify(codec->bus->card, SNDRV_CTL_EVENT_MASK_VALUE, 210925eaba2fSLydia Wang &id); 211025eaba2fSLydia Wang } 211125eaba2fSLydia Wang } 211225eaba2fSLydia Wang 211325eaba2fSLydia Wang /* mute line-out and internal speaker if HP is plugged */ 211425eaba2fSLydia Wang static void via_hp_bind_automute(struct hda_codec *codec) 211525eaba2fSLydia Wang { 211601a1796bSakpm@linux-foundation.org /* use long instead of int below just to avoid an internal compiler 211701a1796bSakpm@linux-foundation.org * error with gcc 4.0.x 211801a1796bSakpm@linux-foundation.org */ 211901a1796bSakpm@linux-foundation.org unsigned long hp_present, present = 0; 212025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 212125eaba2fSLydia Wang int i; 212225eaba2fSLydia Wang 212325eaba2fSLydia Wang if (!spec->autocfg.hp_pins[0] || !spec->autocfg.line_out_pins[0]) 212425eaba2fSLydia Wang return; 212525eaba2fSLydia Wang 2126d56757abSTakashi Iwai hp_present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]); 212725eaba2fSLydia Wang 2128d56757abSTakashi Iwai present = snd_hda_jack_detect(codec, spec->autocfg.line_out_pins[0]); 212925eaba2fSLydia Wang 213025eaba2fSLydia Wang if (!spec->hp_independent_mode) { 213125eaba2fSLydia Wang /* Mute Line-Outs */ 213225eaba2fSLydia Wang for (i = 0; i < spec->autocfg.line_outs; i++) 213325eaba2fSLydia Wang snd_hda_codec_amp_stereo( 213425eaba2fSLydia Wang codec, spec->autocfg.line_out_pins[i], 213525eaba2fSLydia Wang HDA_OUTPUT, 0, 213625eaba2fSLydia Wang HDA_AMP_MUTE, hp_present ? HDA_AMP_MUTE : 0); 213725eaba2fSLydia Wang if (hp_present) 213825eaba2fSLydia Wang present = hp_present; 213925eaba2fSLydia Wang } 214025eaba2fSLydia Wang /* Speakers */ 214125eaba2fSLydia Wang for (i = 0; i < spec->autocfg.speaker_outs; i++) 214225eaba2fSLydia Wang snd_hda_codec_amp_stereo( 214325eaba2fSLydia Wang codec, spec->autocfg.speaker_pins[i], HDA_OUTPUT, 0, 214425eaba2fSLydia Wang HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); 214525eaba2fSLydia Wang } 214625eaba2fSLydia Wang 214725eaba2fSLydia Wang 214869e52a80SHarald Welte /* unsolicited event for jack sensing */ 214969e52a80SHarald Welte static void via_unsol_event(struct hda_codec *codec, 215069e52a80SHarald Welte unsigned int res) 215169e52a80SHarald Welte { 215269e52a80SHarald Welte res >>= 26; 2153a34df19aSLydia Wang if (res & VIA_HP_EVENT) 215469e52a80SHarald Welte via_hp_automute(codec); 2155a34df19aSLydia Wang if (res & VIA_GPIO_EVENT) 215669e52a80SHarald Welte via_gpio_control(codec); 2157a34df19aSLydia Wang if (res & VIA_JACK_EVENT) 2158a34df19aSLydia Wang set_jack_power_state(codec); 2159f3db423dSLydia Wang if (res & VIA_MONO_EVENT) 2160f3db423dSLydia Wang via_mono_automute(codec); 216125eaba2fSLydia Wang if (res & VIA_SPEAKER_EVENT) 216225eaba2fSLydia Wang via_speaker_automute(codec); 216325eaba2fSLydia Wang if (res & VIA_BIND_HP_EVENT) 216425eaba2fSLydia Wang via_hp_bind_automute(codec); 216569e52a80SHarald Welte } 216669e52a80SHarald Welte 2167c577b8a1SJoseph Chan static int via_init(struct hda_codec *codec) 2168c577b8a1SJoseph Chan { 2169c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 217069e52a80SHarald Welte int i; 217169e52a80SHarald Welte for (i = 0; i < spec->num_iverbs; i++) 217269e52a80SHarald Welte snd_hda_sequence_write(codec, spec->init_verbs[i]); 217369e52a80SHarald Welte 2174518bf3baSLydia Wang spec->codec_type = get_codec_type(codec); 2175518bf3baSLydia Wang if (spec->codec_type == VT1708BCE) 2176518bf3baSLydia Wang spec->codec_type = VT1708S; /* VT1708BCE & VT1708S are almost 2177518bf3baSLydia Wang same */ 2178f7278fd0SJosepch Chan /* Lydia Add for EAPD enable */ 2179f7278fd0SJosepch Chan if (!spec->dig_in_nid) { /* No Digital In connection */ 218055d1d6c1STakashi Iwai if (spec->dig_in_pin) { 218155d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2182f7278fd0SJosepch Chan AC_VERB_SET_PIN_WIDGET_CONTROL, 218312b74c80STakashi Iwai PIN_OUT); 218455d1d6c1STakashi Iwai snd_hda_codec_write(codec, spec->dig_in_pin, 0, 2185f7278fd0SJosepch Chan AC_VERB_SET_EAPD_BTLENABLE, 0x02); 2186f7278fd0SJosepch Chan } 218712b74c80STakashi Iwai } else /* enable SPDIF-input pin */ 218812b74c80STakashi Iwai snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0, 218912b74c80STakashi Iwai AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN); 2190f7278fd0SJosepch Chan 21919da29271STakashi Iwai /* assign slave outs */ 21929da29271STakashi Iwai if (spec->slave_dig_outs[0]) 21939da29271STakashi Iwai codec->slave_dig_outs = spec->slave_dig_outs; 21945691ec7fSHarald Welte 2195c577b8a1SJoseph Chan return 0; 2196c577b8a1SJoseph Chan } 2197c577b8a1SJoseph Chan 21981f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 21991f2e99feSLydia Wang static int via_suspend(struct hda_codec *codec, pm_message_t state) 22001f2e99feSLydia Wang { 22011f2e99feSLydia Wang struct via_spec *spec = codec->spec; 22021f2e99feSLydia Wang vt1708_stop_hp_work(spec); 22031f2e99feSLydia Wang return 0; 22041f2e99feSLydia Wang } 22051f2e99feSLydia Wang #endif 22061f2e99feSLydia Wang 2207cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2208cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) 2209cb53c626STakashi Iwai { 2210cb53c626STakashi Iwai struct via_spec *spec = codec->spec; 2211cb53c626STakashi Iwai return snd_hda_check_amp_list_power(codec, &spec->loopback, nid); 2212cb53c626STakashi Iwai } 2213cb53c626STakashi Iwai #endif 2214cb53c626STakashi Iwai 2215c577b8a1SJoseph Chan /* 2216c577b8a1SJoseph Chan */ 2217c577b8a1SJoseph Chan static struct hda_codec_ops via_patch_ops = { 2218c577b8a1SJoseph Chan .build_controls = via_build_controls, 2219c577b8a1SJoseph Chan .build_pcms = via_build_pcms, 2220c577b8a1SJoseph Chan .init = via_init, 2221c577b8a1SJoseph Chan .free = via_free, 22221f2e99feSLydia Wang #ifdef SND_HDA_NEEDS_RESUME 22231f2e99feSLydia Wang .suspend = via_suspend, 22241f2e99feSLydia Wang #endif 2225cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2226cb53c626STakashi Iwai .check_power_status = via_check_power_status, 2227cb53c626STakashi Iwai #endif 2228c577b8a1SJoseph Chan }; 2229c577b8a1SJoseph Chan 2230c577b8a1SJoseph Chan /* fill in the dac_nids table from the parsed pin configuration */ 2231c577b8a1SJoseph Chan static int vt1708_auto_fill_dac_nids(struct via_spec *spec, 2232c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2233c577b8a1SJoseph Chan { 2234c577b8a1SJoseph Chan int i; 2235c577b8a1SJoseph Chan hda_nid_t nid; 2236c577b8a1SJoseph Chan 2237c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; 2238c577b8a1SJoseph Chan 2239c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2240c577b8a1SJoseph Chan 2241c577b8a1SJoseph Chan for (i = 0; i < 4; i++) { 2242c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2243c577b8a1SJoseph Chan if (nid) { 2244c577b8a1SJoseph Chan /* config dac list */ 2245c577b8a1SJoseph Chan switch (i) { 2246c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2247c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2248c577b8a1SJoseph Chan break; 2249c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2250c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2251c577b8a1SJoseph Chan break; 2252c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2253fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2254c577b8a1SJoseph Chan break; 2255c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2256fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x13; 2257c577b8a1SJoseph Chan break; 2258c577b8a1SJoseph Chan } 2259c577b8a1SJoseph Chan } 2260c577b8a1SJoseph Chan } 2261c577b8a1SJoseph Chan 2262c577b8a1SJoseph Chan return 0; 2263c577b8a1SJoseph Chan } 2264c577b8a1SJoseph Chan 2265c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2266c577b8a1SJoseph Chan static int vt1708_auto_create_multi_out_ctls(struct via_spec *spec, 2267c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2268c577b8a1SJoseph Chan { 2269c577b8a1SJoseph Chan char name[32]; 2270c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 22719645c203SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x17, 0x19, 0x1a, 0x1b}; 2272c577b8a1SJoseph Chan int i, err; 2273c577b8a1SJoseph Chan 2274c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2275c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2276c577b8a1SJoseph Chan 2277c577b8a1SJoseph Chan if (!nid) 2278c577b8a1SJoseph Chan continue; 2279c577b8a1SJoseph Chan 22809645c203SLydia Wang nid_vol = nid_vols[i]; 2281c577b8a1SJoseph Chan 2282c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2283c577b8a1SJoseph Chan /* Center/LFE */ 2284c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2285c577b8a1SJoseph Chan "Center Playback Volume", 2286f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2287f7278fd0SJosepch Chan HDA_OUTPUT)); 2288c577b8a1SJoseph Chan if (err < 0) 2289c577b8a1SJoseph Chan return err; 2290c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2291c577b8a1SJoseph Chan "LFE Playback Volume", 2292f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2293f7278fd0SJosepch Chan HDA_OUTPUT)); 2294c577b8a1SJoseph Chan if (err < 0) 2295c577b8a1SJoseph Chan return err; 2296c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2297c577b8a1SJoseph Chan "Center Playback Switch", 2298f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2299f7278fd0SJosepch Chan HDA_OUTPUT)); 2300c577b8a1SJoseph Chan if (err < 0) 2301c577b8a1SJoseph Chan return err; 2302c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2303c577b8a1SJoseph Chan "LFE Playback Switch", 2304f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2305f7278fd0SJosepch Chan HDA_OUTPUT)); 2306c577b8a1SJoseph Chan if (err < 0) 2307c577b8a1SJoseph Chan return err; 2308c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 2309c577b8a1SJoseph Chan /* add control to mixer index 0 */ 2310c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2311c577b8a1SJoseph Chan "Master Front Playback Volume", 23129645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2313f7278fd0SJosepch Chan HDA_INPUT)); 2314c577b8a1SJoseph Chan if (err < 0) 2315c577b8a1SJoseph Chan return err; 2316c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2317c577b8a1SJoseph Chan "Master Front Playback Switch", 23189645c203SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2319f7278fd0SJosepch Chan HDA_INPUT)); 2320c577b8a1SJoseph Chan if (err < 0) 2321c577b8a1SJoseph Chan return err; 2322c577b8a1SJoseph Chan 2323c577b8a1SJoseph Chan /* add control to PW3 */ 2324c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2325c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2326f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2327f7278fd0SJosepch Chan HDA_OUTPUT)); 2328c577b8a1SJoseph Chan if (err < 0) 2329c577b8a1SJoseph Chan return err; 2330c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2331c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2332f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2333f7278fd0SJosepch Chan HDA_OUTPUT)); 2334c577b8a1SJoseph Chan if (err < 0) 2335c577b8a1SJoseph Chan return err; 2336c577b8a1SJoseph Chan } else { 2337c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2338c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2339f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2340f7278fd0SJosepch Chan HDA_OUTPUT)); 2341c577b8a1SJoseph Chan if (err < 0) 2342c577b8a1SJoseph Chan return err; 2343c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2344c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2345f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2346f7278fd0SJosepch Chan HDA_OUTPUT)); 2347c577b8a1SJoseph Chan if (err < 0) 2348c577b8a1SJoseph Chan return err; 2349c577b8a1SJoseph Chan } 2350c577b8a1SJoseph Chan } 2351c577b8a1SJoseph Chan 2352c577b8a1SJoseph Chan return 0; 2353c577b8a1SJoseph Chan } 2354c577b8a1SJoseph Chan 23550aa62aefSHarald Welte static void create_hp_imux(struct via_spec *spec) 23560aa62aefSHarald Welte { 23570aa62aefSHarald Welte int i; 23580aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[1]; 23590aa62aefSHarald Welte static const char *texts[] = { "OFF", "ON", NULL}; 23600aa62aefSHarald Welte 23610aa62aefSHarald Welte /* for hp mode select */ 23620aa62aefSHarald Welte i = 0; 23630aa62aefSHarald Welte while (texts[i] != NULL) { 23640aa62aefSHarald Welte imux->items[imux->num_items].label = texts[i]; 23650aa62aefSHarald Welte imux->items[imux->num_items].index = i; 23660aa62aefSHarald Welte imux->num_items++; 23670aa62aefSHarald Welte i++; 23680aa62aefSHarald Welte } 23690aa62aefSHarald Welte 23700aa62aefSHarald Welte spec->hp_mux = &spec->private_imux[1]; 23710aa62aefSHarald Welte } 23720aa62aefSHarald Welte 2373c577b8a1SJoseph Chan static int vt1708_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2374c577b8a1SJoseph Chan { 2375c577b8a1SJoseph Chan int err; 2376c577b8a1SJoseph Chan 2377c577b8a1SJoseph Chan if (!pin) 2378c577b8a1SJoseph Chan return 0; 2379c577b8a1SJoseph Chan 2380c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1708_HP_NID; /* AOW3 */ 2381cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2382c577b8a1SJoseph Chan 2383c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2384c577b8a1SJoseph Chan "Headphone Playback Volume", 2385c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2386c577b8a1SJoseph Chan if (err < 0) 2387c577b8a1SJoseph Chan return err; 2388c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2389c577b8a1SJoseph Chan "Headphone Playback Switch", 2390c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2391c577b8a1SJoseph Chan if (err < 0) 2392c577b8a1SJoseph Chan return err; 2393c577b8a1SJoseph Chan 23940aa62aefSHarald Welte create_hp_imux(spec); 23950aa62aefSHarald Welte 2396c577b8a1SJoseph Chan return 0; 2397c577b8a1SJoseph Chan } 2398c577b8a1SJoseph Chan 2399c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 2400c577b8a1SJoseph Chan static int vt1708_auto_create_analog_input_ctls(struct via_spec *spec, 2401c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2402c577b8a1SJoseph Chan { 2403c577b8a1SJoseph Chan static char *labels[] = { 2404c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 2405c577b8a1SJoseph Chan }; 24060aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 2407c577b8a1SJoseph Chan int i, err, idx = 0; 2408c577b8a1SJoseph Chan 2409c577b8a1SJoseph Chan /* for internal loopback recording select */ 2410c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 2411c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2412c577b8a1SJoseph Chan imux->num_items++; 2413c577b8a1SJoseph Chan 2414c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 2415c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 2416c577b8a1SJoseph Chan continue; 2417c577b8a1SJoseph Chan 2418c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 2419c577b8a1SJoseph Chan case 0x1d: /* Mic */ 2420c577b8a1SJoseph Chan idx = 2; 2421c577b8a1SJoseph Chan break; 2422c577b8a1SJoseph Chan 2423c577b8a1SJoseph Chan case 0x1e: /* Line In */ 2424c577b8a1SJoseph Chan idx = 3; 2425c577b8a1SJoseph Chan break; 2426c577b8a1SJoseph Chan 2427c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 2428c577b8a1SJoseph Chan idx = 4; 2429c577b8a1SJoseph Chan break; 2430c577b8a1SJoseph Chan 2431c577b8a1SJoseph Chan case 0x24: /* CD */ 2432c577b8a1SJoseph Chan idx = 1; 2433c577b8a1SJoseph Chan break; 2434c577b8a1SJoseph Chan } 24359510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x17); 2436c577b8a1SJoseph Chan if (err < 0) 2437c577b8a1SJoseph Chan return err; 2438c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 2439c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 2440c577b8a1SJoseph Chan imux->num_items++; 2441c577b8a1SJoseph Chan } 2442c577b8a1SJoseph Chan return 0; 2443c577b8a1SJoseph Chan } 2444c577b8a1SJoseph Chan 2445cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2446cb53c626STakashi Iwai static struct hda_amp_list vt1708_loopbacks[] = { 2447cb53c626STakashi Iwai { 0x17, HDA_INPUT, 1 }, 2448cb53c626STakashi Iwai { 0x17, HDA_INPUT, 2 }, 2449cb53c626STakashi Iwai { 0x17, HDA_INPUT, 3 }, 2450cb53c626STakashi Iwai { 0x17, HDA_INPUT, 4 }, 2451cb53c626STakashi Iwai { } /* end */ 2452cb53c626STakashi Iwai }; 2453cb53c626STakashi Iwai #endif 2454cb53c626STakashi Iwai 245576d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) 245676d9b0ddSHarald Welte { 245776d9b0ddSHarald Welte unsigned int def_conf; 245876d9b0ddSHarald Welte unsigned char seqassoc; 245976d9b0ddSHarald Welte 24602f334f92STakashi Iwai def_conf = snd_hda_codec_get_pincfg(codec, nid); 246176d9b0ddSHarald Welte seqassoc = (unsigned char) get_defcfg_association(def_conf); 246276d9b0ddSHarald Welte seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf); 246382ef9e45SLydia Wang if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE 246482ef9e45SLydia Wang && (seqassoc == 0xf0 || seqassoc == 0xff)) { 246576d9b0ddSHarald Welte def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30)); 24662f334f92STakashi Iwai snd_hda_codec_set_pincfg(codec, nid, def_conf); 246776d9b0ddSHarald Welte } 246876d9b0ddSHarald Welte 246976d9b0ddSHarald Welte return; 247076d9b0ddSHarald Welte } 247176d9b0ddSHarald Welte 24721f2e99feSLydia Wang static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, 24731f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 24741f2e99feSLydia Wang { 24751f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24761f2e99feSLydia Wang struct via_spec *spec = codec->spec; 24771f2e99feSLydia Wang 24781f2e99feSLydia Wang if (spec->codec_type != VT1708) 24791f2e99feSLydia Wang return 0; 24801f2e99feSLydia Wang spec->vt1708_jack_detectect = 24811f2e99feSLydia Wang !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); 24821f2e99feSLydia Wang ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; 24831f2e99feSLydia Wang return 0; 24841f2e99feSLydia Wang } 24851f2e99feSLydia Wang 24861f2e99feSLydia Wang static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, 24871f2e99feSLydia Wang struct snd_ctl_elem_value *ucontrol) 24881f2e99feSLydia Wang { 24891f2e99feSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 24901f2e99feSLydia Wang struct via_spec *spec = codec->spec; 24911f2e99feSLydia Wang int change; 24921f2e99feSLydia Wang 24931f2e99feSLydia Wang if (spec->codec_type != VT1708) 24941f2e99feSLydia Wang return 0; 24951f2e99feSLydia Wang spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; 24961f2e99feSLydia Wang change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) 24971f2e99feSLydia Wang == !spec->vt1708_jack_detectect; 24981f2e99feSLydia Wang if (spec->vt1708_jack_detectect) { 24991f2e99feSLydia Wang mute_aa_path(codec, 1); 25001f2e99feSLydia Wang notify_aa_path_ctls(codec); 25011f2e99feSLydia Wang } 25021f2e99feSLydia Wang return change; 25031f2e99feSLydia Wang } 25041f2e99feSLydia Wang 25051f2e99feSLydia Wang static struct snd_kcontrol_new vt1708_jack_detectect[] = { 25061f2e99feSLydia Wang { 25071f2e99feSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 25081f2e99feSLydia Wang .name = "Jack Detect", 25091f2e99feSLydia Wang .count = 1, 25101f2e99feSLydia Wang .info = snd_ctl_boolean_mono_info, 25111f2e99feSLydia Wang .get = vt1708_jack_detectect_get, 25121f2e99feSLydia Wang .put = vt1708_jack_detectect_put, 25131f2e99feSLydia Wang }, 25141f2e99feSLydia Wang {} /* end */ 25151f2e99feSLydia Wang }; 25161f2e99feSLydia Wang 2517c577b8a1SJoseph Chan static int vt1708_parse_auto_config(struct hda_codec *codec) 2518c577b8a1SJoseph Chan { 2519c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 2520c577b8a1SJoseph Chan int err; 2521c577b8a1SJoseph Chan 252276d9b0ddSHarald Welte /* Add HP and CD pin config connect bit re-config action */ 252376d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID); 252476d9b0ddSHarald Welte vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID); 252576d9b0ddSHarald Welte 2526c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 2527c577b8a1SJoseph Chan if (err < 0) 2528c577b8a1SJoseph Chan return err; 2529c577b8a1SJoseph Chan err = vt1708_auto_fill_dac_nids(spec, &spec->autocfg); 2530c577b8a1SJoseph Chan if (err < 0) 2531c577b8a1SJoseph Chan return err; 2532c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 2533c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 2534c577b8a1SJoseph Chan 2535c577b8a1SJoseph Chan err = vt1708_auto_create_multi_out_ctls(spec, &spec->autocfg); 2536c577b8a1SJoseph Chan if (err < 0) 2537c577b8a1SJoseph Chan return err; 2538c577b8a1SJoseph Chan err = vt1708_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 2539c577b8a1SJoseph Chan if (err < 0) 2540c577b8a1SJoseph Chan return err; 2541c577b8a1SJoseph Chan err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); 2542c577b8a1SJoseph Chan if (err < 0) 2543c577b8a1SJoseph Chan return err; 25441f2e99feSLydia Wang /* add jack detect on/off control */ 25451f2e99feSLydia Wang err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); 25461f2e99feSLydia Wang if (err < 0) 25471f2e99feSLydia Wang return err; 2548c577b8a1SJoseph Chan 2549c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 2550c577b8a1SJoseph Chan 25510852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 2552c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1708_DIGOUT_NID; 255355d1d6c1STakashi Iwai spec->dig_in_pin = VT1708_DIGIN_PIN; 2554c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 2555c577b8a1SJoseph Chan spec->dig_in_nid = VT1708_DIGIN_NID; 2556c577b8a1SJoseph Chan 2557603c4019STakashi Iwai if (spec->kctls.list) 2558603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 2559c577b8a1SJoseph Chan 256069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708_volume_init_verbs; 2561c577b8a1SJoseph Chan 25620aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 25630aa62aefSHarald Welte 2564f8fdd495SHarald Welte if (spec->hp_mux) 25655b0cb1d8SJaroslav Kysela via_hp_build(spec); 2566c577b8a1SJoseph Chan 25675b0cb1d8SJaroslav Kysela via_smart51_build(spec); 2568c577b8a1SJoseph Chan return 1; 2569c577b8a1SJoseph Chan } 2570c577b8a1SJoseph Chan 2571c577b8a1SJoseph Chan /* init callback for auto-configuration model -- overriding the default init */ 2572c577b8a1SJoseph Chan static int via_auto_init(struct hda_codec *codec) 2573c577b8a1SJoseph Chan { 257425eaba2fSLydia Wang struct via_spec *spec = codec->spec; 257525eaba2fSLydia Wang 2576c577b8a1SJoseph Chan via_init(codec); 2577c577b8a1SJoseph Chan via_auto_init_multi_out(codec); 2578c577b8a1SJoseph Chan via_auto_init_hp_out(codec); 2579c577b8a1SJoseph Chan via_auto_init_analog_input(codec); 2580ab6734e7SLydia Wang if (spec->codec_type == VT2002P || spec->codec_type == VT1812) { 258125eaba2fSLydia Wang via_hp_bind_automute(codec); 258225eaba2fSLydia Wang } else { 258325eaba2fSLydia Wang via_hp_automute(codec); 258425eaba2fSLydia Wang via_speaker_automute(codec); 258525eaba2fSLydia Wang } 258625eaba2fSLydia Wang 2587c577b8a1SJoseph Chan return 0; 2588c577b8a1SJoseph Chan } 2589c577b8a1SJoseph Chan 25901f2e99feSLydia Wang static void vt1708_update_hp_jack_state(struct work_struct *work) 25911f2e99feSLydia Wang { 25921f2e99feSLydia Wang struct via_spec *spec = container_of(work, struct via_spec, 25931f2e99feSLydia Wang vt1708_hp_work.work); 25941f2e99feSLydia Wang if (spec->codec_type != VT1708) 25951f2e99feSLydia Wang return; 25961f2e99feSLydia Wang /* if jack state toggled */ 25971f2e99feSLydia Wang if (spec->vt1708_hp_present 2598d56757abSTakashi Iwai != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) { 25991f2e99feSLydia Wang spec->vt1708_hp_present ^= 1; 26001f2e99feSLydia Wang via_hp_automute(spec->codec); 26011f2e99feSLydia Wang } 26021f2e99feSLydia Wang vt1708_start_hp_work(spec); 26031f2e99feSLydia Wang } 26041f2e99feSLydia Wang 2605337b9d02STakashi Iwai static int get_mux_nids(struct hda_codec *codec) 2606337b9d02STakashi Iwai { 2607337b9d02STakashi Iwai struct via_spec *spec = codec->spec; 2608337b9d02STakashi Iwai hda_nid_t nid, conn[8]; 2609337b9d02STakashi Iwai unsigned int type; 2610337b9d02STakashi Iwai int i, n; 2611337b9d02STakashi Iwai 2612337b9d02STakashi Iwai for (i = 0; i < spec->num_adc_nids; i++) { 2613337b9d02STakashi Iwai nid = spec->adc_nids[i]; 2614337b9d02STakashi Iwai while (nid) { 2615a22d543aSTakashi Iwai type = get_wcaps_type(get_wcaps(codec, nid)); 26161c55d521STakashi Iwai if (type == AC_WID_PIN) 26171c55d521STakashi Iwai break; 2618337b9d02STakashi Iwai n = snd_hda_get_connections(codec, nid, conn, 2619337b9d02STakashi Iwai ARRAY_SIZE(conn)); 2620337b9d02STakashi Iwai if (n <= 0) 2621337b9d02STakashi Iwai break; 2622337b9d02STakashi Iwai if (n > 1) { 2623337b9d02STakashi Iwai spec->mux_nids[i] = nid; 2624337b9d02STakashi Iwai break; 2625337b9d02STakashi Iwai } 2626337b9d02STakashi Iwai nid = conn[0]; 2627337b9d02STakashi Iwai } 2628337b9d02STakashi Iwai } 26291c55d521STakashi Iwai return 0; 2630337b9d02STakashi Iwai } 2631337b9d02STakashi Iwai 2632c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec) 2633c577b8a1SJoseph Chan { 2634c577b8a1SJoseph Chan struct via_spec *spec; 2635c577b8a1SJoseph Chan int err; 2636c577b8a1SJoseph Chan 2637c577b8a1SJoseph Chan /* create a codec specific record */ 26385b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 2639c577b8a1SJoseph Chan if (spec == NULL) 2640c577b8a1SJoseph Chan return -ENOMEM; 2641c577b8a1SJoseph Chan 2642c577b8a1SJoseph Chan /* automatic parse from the BIOS config */ 2643c577b8a1SJoseph Chan err = vt1708_parse_auto_config(codec); 2644c577b8a1SJoseph Chan if (err < 0) { 2645c577b8a1SJoseph Chan via_free(codec); 2646c577b8a1SJoseph Chan return err; 2647c577b8a1SJoseph Chan } else if (!err) { 2648c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 2649c577b8a1SJoseph Chan "from BIOS. Using genenic mode...\n"); 2650c577b8a1SJoseph Chan } 2651c577b8a1SJoseph Chan 2652c577b8a1SJoseph Chan 2653c577b8a1SJoseph Chan spec->stream_name_analog = "VT1708 Analog"; 2654c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1708_pcm_analog_playback; 2655bc9b5623STakashi Iwai /* disable 32bit format on VT1708 */ 2656bc9b5623STakashi Iwai if (codec->vendor_id == 0x11061708) 2657bc9b5623STakashi Iwai spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback; 2658c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1708_pcm_analog_capture; 2659c577b8a1SJoseph Chan 2660c577b8a1SJoseph Chan spec->stream_name_digital = "VT1708 Digital"; 2661c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1708_pcm_digital_playback; 2662c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1708_pcm_digital_capture; 2663c577b8a1SJoseph Chan 2664c577b8a1SJoseph Chan 2665c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 2666c577b8a1SJoseph Chan spec->adc_nids = vt1708_adc_nids; 2667c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1708_adc_nids); 26680f67a611STakashi Iwai get_mux_nids(codec); 2669c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1708_capture_mixer; 2670c577b8a1SJoseph Chan spec->num_mixers++; 2671c577b8a1SJoseph Chan } 2672c577b8a1SJoseph Chan 2673c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 2674c577b8a1SJoseph Chan 2675c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 2676cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 2677cb53c626STakashi Iwai spec->loopback.amplist = vt1708_loopbacks; 2678cb53c626STakashi Iwai #endif 26791f2e99feSLydia Wang INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); 2680c577b8a1SJoseph Chan return 0; 2681c577b8a1SJoseph Chan } 2682c577b8a1SJoseph Chan 2683c577b8a1SJoseph Chan /* capture mixer elements */ 2684c577b8a1SJoseph Chan static struct snd_kcontrol_new vt1709_capture_mixer[] = { 2685c577b8a1SJoseph Chan HDA_CODEC_VOLUME("Capture Volume", 0x14, 0x0, HDA_INPUT), 2686c577b8a1SJoseph Chan HDA_CODEC_MUTE("Capture Switch", 0x14, 0x0, HDA_INPUT), 2687c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x15, 0x0, HDA_INPUT), 2688c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x15, 0x0, HDA_INPUT), 2689c577b8a1SJoseph Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x16, 0x0, HDA_INPUT), 2690c577b8a1SJoseph Chan HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x16, 0x0, HDA_INPUT), 2691c577b8a1SJoseph Chan { 2692c577b8a1SJoseph Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 2693c577b8a1SJoseph Chan /* The multiple "Capture Source" controls confuse alsamixer 2694c577b8a1SJoseph Chan * So call somewhat different.. 2695c577b8a1SJoseph Chan */ 2696c577b8a1SJoseph Chan /* .name = "Capture Source", */ 2697c577b8a1SJoseph Chan .name = "Input Source", 2698c577b8a1SJoseph Chan .count = 1, 2699c577b8a1SJoseph Chan .info = via_mux_enum_info, 2700c577b8a1SJoseph Chan .get = via_mux_enum_get, 2701c577b8a1SJoseph Chan .put = via_mux_enum_put, 2702c577b8a1SJoseph Chan }, 2703c577b8a1SJoseph Chan { } /* end */ 2704c577b8a1SJoseph Chan }; 2705c577b8a1SJoseph Chan 270669e52a80SHarald Welte static struct hda_verb vt1709_uniwill_init_verbs[] = { 2707a34df19aSLydia Wang {0x20, AC_VERB_SET_UNSOLICITED_ENABLE, 2708a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 270969e52a80SHarald Welte { } 271069e52a80SHarald Welte }; 271169e52a80SHarald Welte 2712c577b8a1SJoseph Chan /* 2713c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 2714c577b8a1SJoseph Chan */ 2715c577b8a1SJoseph Chan static struct hda_verb vt1709_10ch_volume_init_verbs[] = { 2716c577b8a1SJoseph Chan /* 2717c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 2718c577b8a1SJoseph Chan */ 2719c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2720c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2721c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2722c577b8a1SJoseph Chan 2723c577b8a1SJoseph Chan 2724f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 2725c577b8a1SJoseph Chan * mixer widget 2726c577b8a1SJoseph Chan */ 2727c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 2728f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 2729f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 2730f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 2731f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 2732f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 2733c577b8a1SJoseph Chan 2734c577b8a1SJoseph Chan /* 2735c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 2736c577b8a1SJoseph Chan */ 2737c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 2738c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2739c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2740c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2741c577b8a1SJoseph Chan 2742c577b8a1SJoseph Chan /* 2743c577b8a1SJoseph Chan * Unmute PW3 and PW4 2744c577b8a1SJoseph Chan */ 2745c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2746c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 2747c577b8a1SJoseph Chan 2748bfdc675aSLydia Wang /* Set input of PW4 as MW0 */ 2749bfdc675aSLydia Wang {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 2750c577b8a1SJoseph Chan /* PW9 Output enable */ 2751c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 2752c577b8a1SJoseph Chan { } 2753c577b8a1SJoseph Chan }; 2754c577b8a1SJoseph Chan 2755c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_10ch_pcm_analog_playback = { 2756c577b8a1SJoseph Chan .substreams = 1, 2757c577b8a1SJoseph Chan .channels_min = 2, 2758c577b8a1SJoseph Chan .channels_max = 10, 2759c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2760c577b8a1SJoseph Chan .ops = { 2761c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2762c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2763c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2764c577b8a1SJoseph Chan }, 2765c577b8a1SJoseph Chan }; 2766c577b8a1SJoseph Chan 2767c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_6ch_pcm_analog_playback = { 2768c577b8a1SJoseph Chan .substreams = 1, 2769c577b8a1SJoseph Chan .channels_min = 2, 2770c577b8a1SJoseph Chan .channels_max = 6, 2771c577b8a1SJoseph Chan .nid = 0x10, /* NID to query formats and rates */ 2772c577b8a1SJoseph Chan .ops = { 2773c577b8a1SJoseph Chan .open = via_playback_pcm_open, 2774c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 2775c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 2776c577b8a1SJoseph Chan }, 2777c577b8a1SJoseph Chan }; 2778c577b8a1SJoseph Chan 2779c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_analog_capture = { 2780c577b8a1SJoseph Chan .substreams = 2, 2781c577b8a1SJoseph Chan .channels_min = 2, 2782c577b8a1SJoseph Chan .channels_max = 2, 2783c577b8a1SJoseph Chan .nid = 0x14, /* NID to query formats and rates */ 2784c577b8a1SJoseph Chan .ops = { 2785c577b8a1SJoseph Chan .prepare = via_capture_pcm_prepare, 2786c577b8a1SJoseph Chan .cleanup = via_capture_pcm_cleanup 2787c577b8a1SJoseph Chan }, 2788c577b8a1SJoseph Chan }; 2789c577b8a1SJoseph Chan 2790c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_playback = { 2791c577b8a1SJoseph Chan .substreams = 1, 2792c577b8a1SJoseph Chan .channels_min = 2, 2793c577b8a1SJoseph Chan .channels_max = 2, 2794c577b8a1SJoseph Chan /* NID is set in via_build_pcms */ 2795c577b8a1SJoseph Chan .ops = { 2796c577b8a1SJoseph Chan .open = via_dig_playback_pcm_open, 2797c577b8a1SJoseph Chan .close = via_dig_playback_pcm_close 2798c577b8a1SJoseph Chan }, 2799c577b8a1SJoseph Chan }; 2800c577b8a1SJoseph Chan 2801c577b8a1SJoseph Chan static struct hda_pcm_stream vt1709_pcm_digital_capture = { 2802c577b8a1SJoseph Chan .substreams = 1, 2803c577b8a1SJoseph Chan .channels_min = 2, 2804c577b8a1SJoseph Chan .channels_max = 2, 2805c577b8a1SJoseph Chan }; 2806c577b8a1SJoseph Chan 2807c577b8a1SJoseph Chan static int vt1709_auto_fill_dac_nids(struct via_spec *spec, 2808c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2809c577b8a1SJoseph Chan { 2810c577b8a1SJoseph Chan int i; 2811c577b8a1SJoseph Chan hda_nid_t nid; 2812c577b8a1SJoseph Chan 2813c577b8a1SJoseph Chan if (cfg->line_outs == 4) /* 10 channels */ 2814c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs+1; /* AOW0~AOW4 */ 2815c577b8a1SJoseph Chan else if (cfg->line_outs == 3) /* 6 channels */ 2816c577b8a1SJoseph Chan spec->multiout.num_dacs = cfg->line_outs; /* AOW0~AOW2 */ 2817c577b8a1SJoseph Chan 2818c577b8a1SJoseph Chan spec->multiout.dac_nids = spec->private_dac_nids; 2819c577b8a1SJoseph Chan 2820c577b8a1SJoseph Chan if (cfg->line_outs == 4) { /* 10 channels */ 2821c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2822c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2823c577b8a1SJoseph Chan if (nid) { 2824c577b8a1SJoseph Chan /* config dac list */ 2825c577b8a1SJoseph Chan switch (i) { 2826c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2827c577b8a1SJoseph Chan /* AOW0 */ 2828c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2829c577b8a1SJoseph Chan break; 2830c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2831c577b8a1SJoseph Chan /* AOW2 */ 2832c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2833c577b8a1SJoseph Chan break; 2834c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2835c577b8a1SJoseph Chan /* AOW3 */ 2836fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 2837c577b8a1SJoseph Chan break; 2838c577b8a1SJoseph Chan case AUTO_SEQ_SIDE: 2839c577b8a1SJoseph Chan /* AOW1 */ 2840fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x27; 2841c577b8a1SJoseph Chan break; 2842c577b8a1SJoseph Chan default: 2843c577b8a1SJoseph Chan break; 2844c577b8a1SJoseph Chan } 2845c577b8a1SJoseph Chan } 2846c577b8a1SJoseph Chan } 2847c577b8a1SJoseph Chan spec->multiout.dac_nids[cfg->line_outs] = 0x28; /* AOW4 */ 2848c577b8a1SJoseph Chan 2849c577b8a1SJoseph Chan } else if (cfg->line_outs == 3) { /* 6 channels */ 2850c577b8a1SJoseph Chan for (i = 0; i < cfg->line_outs; i++) { 2851c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2852c577b8a1SJoseph Chan if (nid) { 2853c577b8a1SJoseph Chan /* config dac list */ 2854c577b8a1SJoseph Chan switch (i) { 2855c577b8a1SJoseph Chan case AUTO_SEQ_FRONT: 2856c577b8a1SJoseph Chan /* AOW0 */ 2857c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x10; 2858c577b8a1SJoseph Chan break; 2859c577b8a1SJoseph Chan case AUTO_SEQ_CENLFE: 2860c577b8a1SJoseph Chan /* AOW2 */ 2861c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x12; 2862c577b8a1SJoseph Chan break; 2863c577b8a1SJoseph Chan case AUTO_SEQ_SURROUND: 2864c577b8a1SJoseph Chan /* AOW1 */ 2865c577b8a1SJoseph Chan spec->multiout.dac_nids[i] = 0x11; 2866c577b8a1SJoseph Chan break; 2867c577b8a1SJoseph Chan default: 2868c577b8a1SJoseph Chan break; 2869c577b8a1SJoseph Chan } 2870c577b8a1SJoseph Chan } 2871c577b8a1SJoseph Chan } 2872c577b8a1SJoseph Chan } 2873c577b8a1SJoseph Chan 2874c577b8a1SJoseph Chan return 0; 2875c577b8a1SJoseph Chan } 2876c577b8a1SJoseph Chan 2877c577b8a1SJoseph Chan /* add playback controls from the parsed DAC table */ 2878c577b8a1SJoseph Chan static int vt1709_auto_create_multi_out_ctls(struct via_spec *spec, 2879c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 2880c577b8a1SJoseph Chan { 2881c577b8a1SJoseph Chan char name[32]; 2882c577b8a1SJoseph Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 28834483a2f5SLydia Wang hda_nid_t nid, nid_vol, nid_vols[] = {0x18, 0x1a, 0x1b, 0x29}; 2884c577b8a1SJoseph Chan int i, err; 2885c577b8a1SJoseph Chan 2886c577b8a1SJoseph Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 2887c577b8a1SJoseph Chan nid = cfg->line_out_pins[i]; 2888c577b8a1SJoseph Chan 2889c577b8a1SJoseph Chan if (!nid) 2890c577b8a1SJoseph Chan continue; 2891c577b8a1SJoseph Chan 28924483a2f5SLydia Wang nid_vol = nid_vols[i]; 28934483a2f5SLydia Wang 2894c577b8a1SJoseph Chan if (i == AUTO_SEQ_CENLFE) { 2895c577b8a1SJoseph Chan /* Center/LFE */ 2896c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2897c577b8a1SJoseph Chan "Center Playback Volume", 28984483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2899f7278fd0SJosepch Chan HDA_OUTPUT)); 2900c577b8a1SJoseph Chan if (err < 0) 2901c577b8a1SJoseph Chan return err; 2902c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2903c577b8a1SJoseph Chan "LFE Playback Volume", 29044483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2905f7278fd0SJosepch Chan HDA_OUTPUT)); 2906c577b8a1SJoseph Chan if (err < 0) 2907c577b8a1SJoseph Chan return err; 2908c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2909c577b8a1SJoseph Chan "Center Playback Switch", 29104483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 2911f7278fd0SJosepch Chan HDA_OUTPUT)); 2912c577b8a1SJoseph Chan if (err < 0) 2913c577b8a1SJoseph Chan return err; 2914c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2915c577b8a1SJoseph Chan "LFE Playback Switch", 29164483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 2917f7278fd0SJosepch Chan HDA_OUTPUT)); 2918c577b8a1SJoseph Chan if (err < 0) 2919c577b8a1SJoseph Chan return err; 2920c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_FRONT) { 29214483a2f5SLydia Wang /* ADD control to mixer index 0 */ 2922c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2923c577b8a1SJoseph Chan "Master Front Playback Volume", 29244483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2925f7278fd0SJosepch Chan HDA_INPUT)); 2926c577b8a1SJoseph Chan if (err < 0) 2927c577b8a1SJoseph Chan return err; 2928c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2929c577b8a1SJoseph Chan "Master Front Playback Switch", 29304483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2931f7278fd0SJosepch Chan HDA_INPUT)); 2932c577b8a1SJoseph Chan if (err < 0) 2933c577b8a1SJoseph Chan return err; 2934c577b8a1SJoseph Chan 2935c577b8a1SJoseph Chan /* add control to PW3 */ 2936c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2937c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 2938f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2939f7278fd0SJosepch Chan HDA_OUTPUT)); 2940c577b8a1SJoseph Chan if (err < 0) 2941c577b8a1SJoseph Chan return err; 2942c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2943c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 2944f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 2945f7278fd0SJosepch Chan HDA_OUTPUT)); 2946c577b8a1SJoseph Chan if (err < 0) 2947c577b8a1SJoseph Chan return err; 2948c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SURROUND) { 2949c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2950c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 29514483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2952f7278fd0SJosepch Chan HDA_OUTPUT)); 2953c577b8a1SJoseph Chan if (err < 0) 2954c577b8a1SJoseph Chan return err; 2955c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2956c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 29574483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2958f7278fd0SJosepch Chan HDA_OUTPUT)); 2959c577b8a1SJoseph Chan if (err < 0) 2960c577b8a1SJoseph Chan return err; 2961c577b8a1SJoseph Chan } else if (i == AUTO_SEQ_SIDE) { 2962c577b8a1SJoseph Chan sprintf(name, "%s Playback Volume", chname[i]); 2963c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 29644483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2965f7278fd0SJosepch Chan HDA_OUTPUT)); 2966c577b8a1SJoseph Chan if (err < 0) 2967c577b8a1SJoseph Chan return err; 2968c577b8a1SJoseph Chan sprintf(name, "%s Playback Switch", chname[i]); 2969c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 29704483a2f5SLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 2971f7278fd0SJosepch Chan HDA_OUTPUT)); 2972c577b8a1SJoseph Chan if (err < 0) 2973c577b8a1SJoseph Chan return err; 2974c577b8a1SJoseph Chan } 2975c577b8a1SJoseph Chan } 2976c577b8a1SJoseph Chan 2977c577b8a1SJoseph Chan return 0; 2978c577b8a1SJoseph Chan } 2979c577b8a1SJoseph Chan 2980c577b8a1SJoseph Chan static int vt1709_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 2981c577b8a1SJoseph Chan { 2982c577b8a1SJoseph Chan int err; 2983c577b8a1SJoseph Chan 2984c577b8a1SJoseph Chan if (!pin) 2985c577b8a1SJoseph Chan return 0; 2986c577b8a1SJoseph Chan 2987c577b8a1SJoseph Chan if (spec->multiout.num_dacs == 5) /* 10 channels */ 2988c577b8a1SJoseph Chan spec->multiout.hp_nid = VT1709_HP_DAC_NID; 2989c577b8a1SJoseph Chan else if (spec->multiout.num_dacs == 3) /* 6 channels */ 2990c577b8a1SJoseph Chan spec->multiout.hp_nid = 0; 2991cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 2992c577b8a1SJoseph Chan 2993c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 2994c577b8a1SJoseph Chan "Headphone Playback Volume", 2995c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 2996c577b8a1SJoseph Chan if (err < 0) 2997c577b8a1SJoseph Chan return err; 2998c577b8a1SJoseph Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 2999c577b8a1SJoseph Chan "Headphone Playback Switch", 3000c577b8a1SJoseph Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3001c577b8a1SJoseph Chan if (err < 0) 3002c577b8a1SJoseph Chan return err; 3003c577b8a1SJoseph Chan 3004c577b8a1SJoseph Chan return 0; 3005c577b8a1SJoseph Chan } 3006c577b8a1SJoseph Chan 3007c577b8a1SJoseph Chan /* create playback/capture controls for input pins */ 3008c577b8a1SJoseph Chan static int vt1709_auto_create_analog_input_ctls(struct via_spec *spec, 3009c577b8a1SJoseph Chan const struct auto_pin_cfg *cfg) 3010c577b8a1SJoseph Chan { 3011c577b8a1SJoseph Chan static char *labels[] = { 3012c577b8a1SJoseph Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3013c577b8a1SJoseph Chan }; 30140aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3015c577b8a1SJoseph Chan int i, err, idx = 0; 3016c577b8a1SJoseph Chan 3017c577b8a1SJoseph Chan /* for internal loopback recording select */ 3018c577b8a1SJoseph Chan imux->items[imux->num_items].label = "Stereo Mixer"; 3019c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 3020c577b8a1SJoseph Chan imux->num_items++; 3021c577b8a1SJoseph Chan 3022c577b8a1SJoseph Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 3023c577b8a1SJoseph Chan if (!cfg->input_pins[i]) 3024c577b8a1SJoseph Chan continue; 3025c577b8a1SJoseph Chan 3026c577b8a1SJoseph Chan switch (cfg->input_pins[i]) { 3027c577b8a1SJoseph Chan case 0x1d: /* Mic */ 3028c577b8a1SJoseph Chan idx = 2; 3029c577b8a1SJoseph Chan break; 3030c577b8a1SJoseph Chan 3031c577b8a1SJoseph Chan case 0x1e: /* Line In */ 3032c577b8a1SJoseph Chan idx = 3; 3033c577b8a1SJoseph Chan break; 3034c577b8a1SJoseph Chan 3035c577b8a1SJoseph Chan case 0x21: /* Front Mic */ 3036c577b8a1SJoseph Chan idx = 4; 3037c577b8a1SJoseph Chan break; 3038c577b8a1SJoseph Chan 3039c577b8a1SJoseph Chan case 0x23: /* CD */ 3040c577b8a1SJoseph Chan idx = 1; 3041c577b8a1SJoseph Chan break; 3042c577b8a1SJoseph Chan } 30439510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x18); 3044c577b8a1SJoseph Chan if (err < 0) 3045c577b8a1SJoseph Chan return err; 3046c577b8a1SJoseph Chan imux->items[imux->num_items].label = labels[i]; 3047c577b8a1SJoseph Chan imux->items[imux->num_items].index = idx; 3048c577b8a1SJoseph Chan imux->num_items++; 3049c577b8a1SJoseph Chan } 3050c577b8a1SJoseph Chan return 0; 3051c577b8a1SJoseph Chan } 3052c577b8a1SJoseph Chan 3053c577b8a1SJoseph Chan static int vt1709_parse_auto_config(struct hda_codec *codec) 3054c577b8a1SJoseph Chan { 3055c577b8a1SJoseph Chan struct via_spec *spec = codec->spec; 3056c577b8a1SJoseph Chan int err; 3057c577b8a1SJoseph Chan 3058c577b8a1SJoseph Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3059c577b8a1SJoseph Chan if (err < 0) 3060c577b8a1SJoseph Chan return err; 3061c577b8a1SJoseph Chan err = vt1709_auto_fill_dac_nids(spec, &spec->autocfg); 3062c577b8a1SJoseph Chan if (err < 0) 3063c577b8a1SJoseph Chan return err; 3064c577b8a1SJoseph Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3065c577b8a1SJoseph Chan return 0; /* can't find valid BIOS pin config */ 3066c577b8a1SJoseph Chan 3067c577b8a1SJoseph Chan err = vt1709_auto_create_multi_out_ctls(spec, &spec->autocfg); 3068c577b8a1SJoseph Chan if (err < 0) 3069c577b8a1SJoseph Chan return err; 3070c577b8a1SJoseph Chan err = vt1709_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3071c577b8a1SJoseph Chan if (err < 0) 3072c577b8a1SJoseph Chan return err; 3073c577b8a1SJoseph Chan err = vt1709_auto_create_analog_input_ctls(spec, &spec->autocfg); 3074c577b8a1SJoseph Chan if (err < 0) 3075c577b8a1SJoseph Chan return err; 3076c577b8a1SJoseph Chan 3077c577b8a1SJoseph Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3078c577b8a1SJoseph Chan 30790852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3080c577b8a1SJoseph Chan spec->multiout.dig_out_nid = VT1709_DIGOUT_NID; 308155d1d6c1STakashi Iwai spec->dig_in_pin = VT1709_DIGIN_PIN; 3082c577b8a1SJoseph Chan if (spec->autocfg.dig_in_pin) 3083c577b8a1SJoseph Chan spec->dig_in_nid = VT1709_DIGIN_NID; 3084c577b8a1SJoseph Chan 3085603c4019STakashi Iwai if (spec->kctls.list) 3086603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3087c577b8a1SJoseph Chan 30880aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 3089c577b8a1SJoseph Chan 3090f8fdd495SHarald Welte if (spec->hp_mux) 30915b0cb1d8SJaroslav Kysela via_hp_build(spec); 3092f8fdd495SHarald Welte 30935b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3094c577b8a1SJoseph Chan return 1; 3095c577b8a1SJoseph Chan } 3096c577b8a1SJoseph Chan 3097cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3098cb53c626STakashi Iwai static struct hda_amp_list vt1709_loopbacks[] = { 3099cb53c626STakashi Iwai { 0x18, HDA_INPUT, 1 }, 3100cb53c626STakashi Iwai { 0x18, HDA_INPUT, 2 }, 3101cb53c626STakashi Iwai { 0x18, HDA_INPUT, 3 }, 3102cb53c626STakashi Iwai { 0x18, HDA_INPUT, 4 }, 3103cb53c626STakashi Iwai { } /* end */ 3104cb53c626STakashi Iwai }; 3105cb53c626STakashi Iwai #endif 3106cb53c626STakashi Iwai 3107c577b8a1SJoseph Chan static int patch_vt1709_10ch(struct hda_codec *codec) 3108c577b8a1SJoseph Chan { 3109c577b8a1SJoseph Chan struct via_spec *spec; 3110c577b8a1SJoseph Chan int err; 3111c577b8a1SJoseph Chan 3112c577b8a1SJoseph Chan /* create a codec specific record */ 31135b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3114c577b8a1SJoseph Chan if (spec == NULL) 3115c577b8a1SJoseph Chan return -ENOMEM; 3116c577b8a1SJoseph Chan 3117c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3118c577b8a1SJoseph Chan if (err < 0) { 3119c577b8a1SJoseph Chan via_free(codec); 3120c577b8a1SJoseph Chan return err; 3121c577b8a1SJoseph Chan } else if (!err) { 3122c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3123c577b8a1SJoseph Chan "Using genenic mode...\n"); 3124c577b8a1SJoseph Chan } 3125c577b8a1SJoseph Chan 312669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_10ch_volume_init_verbs; 312769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3128c577b8a1SJoseph Chan 3129c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3130c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_10ch_pcm_analog_playback; 3131c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3132c577b8a1SJoseph Chan 3133c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3134c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3135c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3136c577b8a1SJoseph Chan 3137c577b8a1SJoseph Chan 3138c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3139c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3140c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3141337b9d02STakashi Iwai get_mux_nids(codec); 3142c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3143c577b8a1SJoseph Chan spec->num_mixers++; 3144c577b8a1SJoseph Chan } 3145c577b8a1SJoseph Chan 3146c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3147c577b8a1SJoseph Chan 3148c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 314969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3150cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3151cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3152cb53c626STakashi Iwai #endif 3153c577b8a1SJoseph Chan 3154c577b8a1SJoseph Chan return 0; 3155c577b8a1SJoseph Chan } 3156c577b8a1SJoseph Chan /* 3157c577b8a1SJoseph Chan * generic initialization of ADC, input mixers and output mixers 3158c577b8a1SJoseph Chan */ 3159c577b8a1SJoseph Chan static struct hda_verb vt1709_6ch_volume_init_verbs[] = { 3160c577b8a1SJoseph Chan /* 3161c577b8a1SJoseph Chan * Unmute ADC0-2 and set the default input to mic-in 3162c577b8a1SJoseph Chan */ 3163c577b8a1SJoseph Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3164c577b8a1SJoseph Chan {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3165c577b8a1SJoseph Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3166c577b8a1SJoseph Chan 3167c577b8a1SJoseph Chan 3168c577b8a1SJoseph Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3169c577b8a1SJoseph Chan * mixer widget 3170c577b8a1SJoseph Chan */ 3171c577b8a1SJoseph Chan /* Amp Indices: AOW0=0, CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3172c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3173c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3174c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3175c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3176c577b8a1SJoseph Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3177c577b8a1SJoseph Chan 3178c577b8a1SJoseph Chan /* 3179c577b8a1SJoseph Chan * Set up output selector (0x1a, 0x1b, 0x29) 3180c577b8a1SJoseph Chan */ 3181c577b8a1SJoseph Chan /* set vol=0 to output mixers */ 3182c577b8a1SJoseph Chan {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3183c577b8a1SJoseph Chan {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3184c577b8a1SJoseph Chan {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3185c577b8a1SJoseph Chan 3186c577b8a1SJoseph Chan /* 3187c577b8a1SJoseph Chan * Unmute PW3 and PW4 3188c577b8a1SJoseph Chan */ 3189c577b8a1SJoseph Chan {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3190c577b8a1SJoseph Chan {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3191c577b8a1SJoseph Chan 3192c577b8a1SJoseph Chan /* Set input of PW4 as MW0 */ 3193c577b8a1SJoseph Chan {0x20, AC_VERB_SET_CONNECT_SEL, 0}, 3194c577b8a1SJoseph Chan /* PW9 Output enable */ 3195c577b8a1SJoseph Chan {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3196c577b8a1SJoseph Chan { } 3197c577b8a1SJoseph Chan }; 3198c577b8a1SJoseph Chan 3199c577b8a1SJoseph Chan static int patch_vt1709_6ch(struct hda_codec *codec) 3200c577b8a1SJoseph Chan { 3201c577b8a1SJoseph Chan struct via_spec *spec; 3202c577b8a1SJoseph Chan int err; 3203c577b8a1SJoseph Chan 3204c577b8a1SJoseph Chan /* create a codec specific record */ 32055b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3206c577b8a1SJoseph Chan if (spec == NULL) 3207c577b8a1SJoseph Chan return -ENOMEM; 3208c577b8a1SJoseph Chan 3209c577b8a1SJoseph Chan err = vt1709_parse_auto_config(codec); 3210c577b8a1SJoseph Chan if (err < 0) { 3211c577b8a1SJoseph Chan via_free(codec); 3212c577b8a1SJoseph Chan return err; 3213c577b8a1SJoseph Chan } else if (!err) { 3214c577b8a1SJoseph Chan printk(KERN_INFO "hda_codec: Cannot set up configuration. " 3215c577b8a1SJoseph Chan "Using genenic mode...\n"); 3216c577b8a1SJoseph Chan } 3217c577b8a1SJoseph Chan 321869e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_6ch_volume_init_verbs; 321969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1709_uniwill_init_verbs; 3220c577b8a1SJoseph Chan 3221c577b8a1SJoseph Chan spec->stream_name_analog = "VT1709 Analog"; 3222c577b8a1SJoseph Chan spec->stream_analog_playback = &vt1709_6ch_pcm_analog_playback; 3223c577b8a1SJoseph Chan spec->stream_analog_capture = &vt1709_pcm_analog_capture; 3224c577b8a1SJoseph Chan 3225c577b8a1SJoseph Chan spec->stream_name_digital = "VT1709 Digital"; 3226c577b8a1SJoseph Chan spec->stream_digital_playback = &vt1709_pcm_digital_playback; 3227c577b8a1SJoseph Chan spec->stream_digital_capture = &vt1709_pcm_digital_capture; 3228c577b8a1SJoseph Chan 3229c577b8a1SJoseph Chan 3230c577b8a1SJoseph Chan if (!spec->adc_nids && spec->input_mux) { 3231c577b8a1SJoseph Chan spec->adc_nids = vt1709_adc_nids; 3232c577b8a1SJoseph Chan spec->num_adc_nids = ARRAY_SIZE(vt1709_adc_nids); 3233337b9d02STakashi Iwai get_mux_nids(codec); 3234c577b8a1SJoseph Chan spec->mixers[spec->num_mixers] = vt1709_capture_mixer; 3235c577b8a1SJoseph Chan spec->num_mixers++; 3236c577b8a1SJoseph Chan } 3237c577b8a1SJoseph Chan 3238c577b8a1SJoseph Chan codec->patch_ops = via_patch_ops; 3239c577b8a1SJoseph Chan 3240c577b8a1SJoseph Chan codec->patch_ops.init = via_auto_init; 324169e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3242cb53c626STakashi Iwai #ifdef CONFIG_SND_HDA_POWER_SAVE 3243cb53c626STakashi Iwai spec->loopback.amplist = vt1709_loopbacks; 3244cb53c626STakashi Iwai #endif 3245f7278fd0SJosepch Chan return 0; 3246f7278fd0SJosepch Chan } 3247f7278fd0SJosepch Chan 3248f7278fd0SJosepch Chan /* capture mixer elements */ 3249f7278fd0SJosepch Chan static struct snd_kcontrol_new vt1708B_capture_mixer[] = { 3250f7278fd0SJosepch Chan HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3251f7278fd0SJosepch Chan HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3252f7278fd0SJosepch Chan HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3253f7278fd0SJosepch Chan HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 3254f7278fd0SJosepch Chan { 3255f7278fd0SJosepch Chan .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3256f7278fd0SJosepch Chan /* The multiple "Capture Source" controls confuse alsamixer 3257f7278fd0SJosepch Chan * So call somewhat different.. 3258f7278fd0SJosepch Chan */ 3259f7278fd0SJosepch Chan /* .name = "Capture Source", */ 3260f7278fd0SJosepch Chan .name = "Input Source", 3261f7278fd0SJosepch Chan .count = 1, 3262f7278fd0SJosepch Chan .info = via_mux_enum_info, 3263f7278fd0SJosepch Chan .get = via_mux_enum_get, 3264f7278fd0SJosepch Chan .put = via_mux_enum_put, 3265f7278fd0SJosepch Chan }, 3266f7278fd0SJosepch Chan { } /* end */ 3267f7278fd0SJosepch Chan }; 3268f7278fd0SJosepch Chan /* 3269f7278fd0SJosepch Chan * generic initialization of ADC, input mixers and output mixers 3270f7278fd0SJosepch Chan */ 3271f7278fd0SJosepch Chan static struct hda_verb vt1708B_8ch_volume_init_verbs[] = { 3272f7278fd0SJosepch Chan /* 3273f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3274f7278fd0SJosepch Chan */ 3275f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3276f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3277f7278fd0SJosepch Chan 3278f7278fd0SJosepch Chan 3279f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3280f7278fd0SJosepch Chan * mixer widget 3281f7278fd0SJosepch Chan */ 3282f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3283f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3284f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3285f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3286f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3287f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3288f7278fd0SJosepch Chan 3289f7278fd0SJosepch Chan /* 3290f7278fd0SJosepch Chan * Set up output mixers 3291f7278fd0SJosepch Chan */ 3292f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3293f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3294f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3295f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3296f7278fd0SJosepch Chan 3297f7278fd0SJosepch Chan /* Setup default input to PW4 */ 3298bfdc675aSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0}, 3299f7278fd0SJosepch Chan /* PW9 Output enable */ 3300f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3301f7278fd0SJosepch Chan /* PW10 Input enable */ 3302f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3303f7278fd0SJosepch Chan { } 3304f7278fd0SJosepch Chan }; 3305f7278fd0SJosepch Chan 3306f7278fd0SJosepch Chan static struct hda_verb vt1708B_4ch_volume_init_verbs[] = { 3307f7278fd0SJosepch Chan /* 3308f7278fd0SJosepch Chan * Unmute ADC0-1 and set the default input to mic-in 3309f7278fd0SJosepch Chan */ 3310f7278fd0SJosepch Chan {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3311f7278fd0SJosepch Chan {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3312f7278fd0SJosepch Chan 3313f7278fd0SJosepch Chan 3314f7278fd0SJosepch Chan /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 3315f7278fd0SJosepch Chan * mixer widget 3316f7278fd0SJosepch Chan */ 3317f7278fd0SJosepch Chan /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3318f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3319f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3320f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3321f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3322f7278fd0SJosepch Chan {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3323f7278fd0SJosepch Chan 3324f7278fd0SJosepch Chan /* 3325f7278fd0SJosepch Chan * Set up output mixers 3326f7278fd0SJosepch Chan */ 3327f7278fd0SJosepch Chan /* set vol=0 to output mixers */ 3328f7278fd0SJosepch Chan {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3329f7278fd0SJosepch Chan {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3330f7278fd0SJosepch Chan {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, 3331f7278fd0SJosepch Chan 3332f7278fd0SJosepch Chan /* Setup default input of PW4 to MW0 */ 3333f7278fd0SJosepch Chan {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 3334f7278fd0SJosepch Chan /* PW9 Output enable */ 3335f7278fd0SJosepch Chan {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3336f7278fd0SJosepch Chan /* PW10 Input enable */ 3337f7278fd0SJosepch Chan {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20}, 3338f7278fd0SJosepch Chan { } 3339f7278fd0SJosepch Chan }; 3340f7278fd0SJosepch Chan 334169e52a80SHarald Welte static struct hda_verb vt1708B_uniwill_init_verbs[] = { 3342a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3343a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3344a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3345a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3346a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3347a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3348a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3349a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3350a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 335169e52a80SHarald Welte { } 335269e52a80SHarald Welte }; 335369e52a80SHarald Welte 335417314379SLydia Wang static int via_pcm_open_close(struct hda_pcm_stream *hinfo, 335517314379SLydia Wang struct hda_codec *codec, 335617314379SLydia Wang struct snd_pcm_substream *substream) 335717314379SLydia Wang { 335817314379SLydia Wang int idle = substream->pstr->substream_opened == 1 335917314379SLydia Wang && substream->ref_count == 0; 336017314379SLydia Wang 336117314379SLydia Wang analog_low_current_mode(codec, idle); 336217314379SLydia Wang return 0; 336317314379SLydia Wang } 336417314379SLydia Wang 3365f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_8ch_pcm_analog_playback = { 33660aa62aefSHarald Welte .substreams = 2, 3367f7278fd0SJosepch Chan .channels_min = 2, 3368f7278fd0SJosepch Chan .channels_max = 8, 3369f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3370f7278fd0SJosepch Chan .ops = { 3371f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33720aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 337317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 337417314379SLydia Wang .close = via_pcm_open_close 3375f7278fd0SJosepch Chan }, 3376f7278fd0SJosepch Chan }; 3377f7278fd0SJosepch Chan 3378f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_4ch_pcm_analog_playback = { 33790aa62aefSHarald Welte .substreams = 2, 3380f7278fd0SJosepch Chan .channels_min = 2, 3381f7278fd0SJosepch Chan .channels_max = 4, 3382f7278fd0SJosepch Chan .nid = 0x10, /* NID to query formats and rates */ 3383f7278fd0SJosepch Chan .ops = { 3384f7278fd0SJosepch Chan .open = via_playback_pcm_open, 33850aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 33860aa62aefSHarald Welte .cleanup = via_playback_multi_pcm_cleanup 3387f7278fd0SJosepch Chan }, 3388f7278fd0SJosepch Chan }; 3389f7278fd0SJosepch Chan 3390f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_analog_capture = { 3391f7278fd0SJosepch Chan .substreams = 2, 3392f7278fd0SJosepch Chan .channels_min = 2, 3393f7278fd0SJosepch Chan .channels_max = 2, 3394f7278fd0SJosepch Chan .nid = 0x13, /* NID to query formats and rates */ 3395f7278fd0SJosepch Chan .ops = { 339617314379SLydia Wang .open = via_pcm_open_close, 3397f7278fd0SJosepch Chan .prepare = via_capture_pcm_prepare, 339817314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 339917314379SLydia Wang .close = via_pcm_open_close 3400f7278fd0SJosepch Chan }, 3401f7278fd0SJosepch Chan }; 3402f7278fd0SJosepch Chan 3403f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_playback = { 3404f7278fd0SJosepch Chan .substreams = 1, 3405f7278fd0SJosepch Chan .channels_min = 2, 3406f7278fd0SJosepch Chan .channels_max = 2, 3407f7278fd0SJosepch Chan /* NID is set in via_build_pcms */ 3408f7278fd0SJosepch Chan .ops = { 3409f7278fd0SJosepch Chan .open = via_dig_playback_pcm_open, 3410f7278fd0SJosepch Chan .close = via_dig_playback_pcm_close, 34119da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 34129da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3413f7278fd0SJosepch Chan }, 3414f7278fd0SJosepch Chan }; 3415f7278fd0SJosepch Chan 3416f7278fd0SJosepch Chan static struct hda_pcm_stream vt1708B_pcm_digital_capture = { 3417f7278fd0SJosepch Chan .substreams = 1, 3418f7278fd0SJosepch Chan .channels_min = 2, 3419f7278fd0SJosepch Chan .channels_max = 2, 3420f7278fd0SJosepch Chan }; 3421f7278fd0SJosepch Chan 3422f7278fd0SJosepch Chan /* fill in the dac_nids table from the parsed pin configuration */ 3423f7278fd0SJosepch Chan static int vt1708B_auto_fill_dac_nids(struct via_spec *spec, 3424f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3425f7278fd0SJosepch Chan { 3426f7278fd0SJosepch Chan int i; 3427f7278fd0SJosepch Chan hda_nid_t nid; 3428f7278fd0SJosepch Chan 3429f7278fd0SJosepch Chan spec->multiout.num_dacs = cfg->line_outs; 3430f7278fd0SJosepch Chan 3431f7278fd0SJosepch Chan spec->multiout.dac_nids = spec->private_dac_nids; 3432f7278fd0SJosepch Chan 3433f7278fd0SJosepch Chan for (i = 0; i < 4; i++) { 3434f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3435f7278fd0SJosepch Chan if (nid) { 3436f7278fd0SJosepch Chan /* config dac list */ 3437f7278fd0SJosepch Chan switch (i) { 3438f7278fd0SJosepch Chan case AUTO_SEQ_FRONT: 3439f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x10; 3440f7278fd0SJosepch Chan break; 3441f7278fd0SJosepch Chan case AUTO_SEQ_CENLFE: 3442f7278fd0SJosepch Chan spec->multiout.dac_nids[i] = 0x24; 3443f7278fd0SJosepch Chan break; 3444f7278fd0SJosepch Chan case AUTO_SEQ_SURROUND: 3445fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3446f7278fd0SJosepch Chan break; 3447f7278fd0SJosepch Chan case AUTO_SEQ_SIDE: 3448fb4cb772SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3449f7278fd0SJosepch Chan break; 3450f7278fd0SJosepch Chan } 3451f7278fd0SJosepch Chan } 3452f7278fd0SJosepch Chan } 3453f7278fd0SJosepch Chan 3454f7278fd0SJosepch Chan return 0; 3455f7278fd0SJosepch Chan } 3456f7278fd0SJosepch Chan 3457f7278fd0SJosepch Chan /* add playback controls from the parsed DAC table */ 3458f7278fd0SJosepch Chan static int vt1708B_auto_create_multi_out_ctls(struct via_spec *spec, 3459f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3460f7278fd0SJosepch Chan { 3461f7278fd0SJosepch Chan char name[32]; 3462f7278fd0SJosepch Chan static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3463fb4cb772SHarald Welte hda_nid_t nid_vols[] = {0x16, 0x18, 0x26, 0x27}; 3464f7278fd0SJosepch Chan hda_nid_t nid, nid_vol = 0; 3465f7278fd0SJosepch Chan int i, err; 3466f7278fd0SJosepch Chan 3467f7278fd0SJosepch Chan for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3468f7278fd0SJosepch Chan nid = cfg->line_out_pins[i]; 3469f7278fd0SJosepch Chan 3470f7278fd0SJosepch Chan if (!nid) 3471f7278fd0SJosepch Chan continue; 3472f7278fd0SJosepch Chan 3473f7278fd0SJosepch Chan nid_vol = nid_vols[i]; 3474f7278fd0SJosepch Chan 3475f7278fd0SJosepch Chan if (i == AUTO_SEQ_CENLFE) { 3476f7278fd0SJosepch Chan /* Center/LFE */ 3477f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3478f7278fd0SJosepch Chan "Center Playback Volume", 3479f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3480f7278fd0SJosepch Chan HDA_OUTPUT)); 3481f7278fd0SJosepch Chan if (err < 0) 3482f7278fd0SJosepch Chan return err; 3483f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3484f7278fd0SJosepch Chan "LFE Playback Volume", 3485f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3486f7278fd0SJosepch Chan HDA_OUTPUT)); 3487f7278fd0SJosepch Chan if (err < 0) 3488f7278fd0SJosepch Chan return err; 3489f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3490f7278fd0SJosepch Chan "Center Playback Switch", 3491f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3492f7278fd0SJosepch Chan HDA_OUTPUT)); 3493f7278fd0SJosepch Chan if (err < 0) 3494f7278fd0SJosepch Chan return err; 3495f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3496f7278fd0SJosepch Chan "LFE Playback Switch", 3497f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3498f7278fd0SJosepch Chan HDA_OUTPUT)); 3499f7278fd0SJosepch Chan if (err < 0) 3500f7278fd0SJosepch Chan return err; 3501f7278fd0SJosepch Chan } else if (i == AUTO_SEQ_FRONT) { 3502f7278fd0SJosepch Chan /* add control to mixer index 0 */ 3503f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3504f7278fd0SJosepch Chan "Master Front Playback Volume", 3505f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3506f7278fd0SJosepch Chan HDA_INPUT)); 3507f7278fd0SJosepch Chan if (err < 0) 3508f7278fd0SJosepch Chan return err; 3509f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3510f7278fd0SJosepch Chan "Master Front Playback Switch", 3511f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3512f7278fd0SJosepch Chan HDA_INPUT)); 3513f7278fd0SJosepch Chan if (err < 0) 3514f7278fd0SJosepch Chan return err; 3515f7278fd0SJosepch Chan 3516f7278fd0SJosepch Chan /* add control to PW3 */ 3517f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3518f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3519f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3520f7278fd0SJosepch Chan HDA_OUTPUT)); 3521f7278fd0SJosepch Chan if (err < 0) 3522f7278fd0SJosepch Chan return err; 3523f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3524f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3525f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid, 3, 0, 3526f7278fd0SJosepch Chan HDA_OUTPUT)); 3527f7278fd0SJosepch Chan if (err < 0) 3528f7278fd0SJosepch Chan return err; 3529f7278fd0SJosepch Chan } else { 3530f7278fd0SJosepch Chan sprintf(name, "%s Playback Volume", chname[i]); 3531f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3532f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3533f7278fd0SJosepch Chan HDA_OUTPUT)); 3534f7278fd0SJosepch Chan if (err < 0) 3535f7278fd0SJosepch Chan return err; 3536f7278fd0SJosepch Chan sprintf(name, "%s Playback Switch", chname[i]); 3537f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3538f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3539f7278fd0SJosepch Chan HDA_OUTPUT)); 3540f7278fd0SJosepch Chan if (err < 0) 3541f7278fd0SJosepch Chan return err; 3542f7278fd0SJosepch Chan } 3543f7278fd0SJosepch Chan } 3544f7278fd0SJosepch Chan 3545f7278fd0SJosepch Chan return 0; 3546f7278fd0SJosepch Chan } 3547f7278fd0SJosepch Chan 3548f7278fd0SJosepch Chan static int vt1708B_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 3549f7278fd0SJosepch Chan { 3550f7278fd0SJosepch Chan int err; 3551f7278fd0SJosepch Chan 3552f7278fd0SJosepch Chan if (!pin) 3553f7278fd0SJosepch Chan return 0; 3554f7278fd0SJosepch Chan 3555f7278fd0SJosepch Chan spec->multiout.hp_nid = VT1708B_HP_NID; /* AOW3 */ 3556cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 3557f7278fd0SJosepch Chan 3558f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3559f7278fd0SJosepch Chan "Headphone Playback Volume", 3560f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3561f7278fd0SJosepch Chan if (err < 0) 3562f7278fd0SJosepch Chan return err; 3563f7278fd0SJosepch Chan err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3564f7278fd0SJosepch Chan "Headphone Playback Switch", 3565f7278fd0SJosepch Chan HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 3566f7278fd0SJosepch Chan if (err < 0) 3567f7278fd0SJosepch Chan return err; 3568f7278fd0SJosepch Chan 35690aa62aefSHarald Welte create_hp_imux(spec); 35700aa62aefSHarald Welte 3571f7278fd0SJosepch Chan return 0; 3572f7278fd0SJosepch Chan } 3573f7278fd0SJosepch Chan 3574f7278fd0SJosepch Chan /* create playback/capture controls for input pins */ 3575f7278fd0SJosepch Chan static int vt1708B_auto_create_analog_input_ctls(struct via_spec *spec, 3576f7278fd0SJosepch Chan const struct auto_pin_cfg *cfg) 3577f7278fd0SJosepch Chan { 3578f7278fd0SJosepch Chan static char *labels[] = { 3579f7278fd0SJosepch Chan "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 3580f7278fd0SJosepch Chan }; 35810aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 3582f7278fd0SJosepch Chan int i, err, idx = 0; 3583f7278fd0SJosepch Chan 3584f7278fd0SJosepch Chan /* for internal loopback recording select */ 3585f7278fd0SJosepch Chan imux->items[imux->num_items].label = "Stereo Mixer"; 3586f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3587f7278fd0SJosepch Chan imux->num_items++; 3588f7278fd0SJosepch Chan 3589f7278fd0SJosepch Chan for (i = 0; i < AUTO_PIN_LAST; i++) { 3590f7278fd0SJosepch Chan if (!cfg->input_pins[i]) 3591f7278fd0SJosepch Chan continue; 3592f7278fd0SJosepch Chan 3593f7278fd0SJosepch Chan switch (cfg->input_pins[i]) { 3594f7278fd0SJosepch Chan case 0x1a: /* Mic */ 3595f7278fd0SJosepch Chan idx = 2; 3596f7278fd0SJosepch Chan break; 3597f7278fd0SJosepch Chan 3598f7278fd0SJosepch Chan case 0x1b: /* Line In */ 3599f7278fd0SJosepch Chan idx = 3; 3600f7278fd0SJosepch Chan break; 3601f7278fd0SJosepch Chan 3602f7278fd0SJosepch Chan case 0x1e: /* Front Mic */ 3603f7278fd0SJosepch Chan idx = 4; 3604f7278fd0SJosepch Chan break; 3605f7278fd0SJosepch Chan 3606f7278fd0SJosepch Chan case 0x1f: /* CD */ 3607f7278fd0SJosepch Chan idx = 1; 3608f7278fd0SJosepch Chan break; 3609f7278fd0SJosepch Chan } 36109510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 3611f7278fd0SJosepch Chan if (err < 0) 3612f7278fd0SJosepch Chan return err; 3613f7278fd0SJosepch Chan imux->items[imux->num_items].label = labels[i]; 3614f7278fd0SJosepch Chan imux->items[imux->num_items].index = idx; 3615f7278fd0SJosepch Chan imux->num_items++; 3616f7278fd0SJosepch Chan } 3617f7278fd0SJosepch Chan return 0; 3618f7278fd0SJosepch Chan } 3619f7278fd0SJosepch Chan 3620f7278fd0SJosepch Chan static int vt1708B_parse_auto_config(struct hda_codec *codec) 3621f7278fd0SJosepch Chan { 3622f7278fd0SJosepch Chan struct via_spec *spec = codec->spec; 3623f7278fd0SJosepch Chan int err; 3624f7278fd0SJosepch Chan 3625f7278fd0SJosepch Chan err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 3626f7278fd0SJosepch Chan if (err < 0) 3627f7278fd0SJosepch Chan return err; 3628f7278fd0SJosepch Chan err = vt1708B_auto_fill_dac_nids(spec, &spec->autocfg); 3629f7278fd0SJosepch Chan if (err < 0) 3630f7278fd0SJosepch Chan return err; 3631f7278fd0SJosepch Chan if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 3632f7278fd0SJosepch Chan return 0; /* can't find valid BIOS pin config */ 3633f7278fd0SJosepch Chan 3634f7278fd0SJosepch Chan err = vt1708B_auto_create_multi_out_ctls(spec, &spec->autocfg); 3635f7278fd0SJosepch Chan if (err < 0) 3636f7278fd0SJosepch Chan return err; 3637f7278fd0SJosepch Chan err = vt1708B_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 3638f7278fd0SJosepch Chan if (err < 0) 3639f7278fd0SJosepch Chan return err; 3640f7278fd0SJosepch Chan err = vt1708B_auto_create_analog_input_ctls(spec, &spec->autocfg); 3641f7278fd0SJosepch Chan if (err < 0) 3642f7278fd0SJosepch Chan return err; 3643f7278fd0SJosepch Chan 3644f7278fd0SJosepch Chan spec->multiout.max_channels = spec->multiout.num_dacs * 2; 3645f7278fd0SJosepch Chan 36460852d7a6STakashi Iwai if (spec->autocfg.dig_outs) 3647f7278fd0SJosepch Chan spec->multiout.dig_out_nid = VT1708B_DIGOUT_NID; 364855d1d6c1STakashi Iwai spec->dig_in_pin = VT1708B_DIGIN_PIN; 3649f7278fd0SJosepch Chan if (spec->autocfg.dig_in_pin) 3650f7278fd0SJosepch Chan spec->dig_in_nid = VT1708B_DIGIN_NID; 3651f7278fd0SJosepch Chan 3652603c4019STakashi Iwai if (spec->kctls.list) 3653603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 3654f7278fd0SJosepch Chan 36550aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 36560aa62aefSHarald Welte 3657f8fdd495SHarald Welte if (spec->hp_mux) 36585b0cb1d8SJaroslav Kysela via_hp_build(spec); 3659f7278fd0SJosepch Chan 36605b0cb1d8SJaroslav Kysela via_smart51_build(spec); 3661f7278fd0SJosepch Chan return 1; 3662f7278fd0SJosepch Chan } 3663f7278fd0SJosepch Chan 3664f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3665f7278fd0SJosepch Chan static struct hda_amp_list vt1708B_loopbacks[] = { 3666f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 1 }, 3667f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 2 }, 3668f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 3 }, 3669f7278fd0SJosepch Chan { 0x16, HDA_INPUT, 4 }, 3670f7278fd0SJosepch Chan { } /* end */ 3671f7278fd0SJosepch Chan }; 3672f7278fd0SJosepch Chan #endif 3673518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec); 3674f7278fd0SJosepch Chan static int patch_vt1708B_8ch(struct hda_codec *codec) 3675f7278fd0SJosepch Chan { 3676f7278fd0SJosepch Chan struct via_spec *spec; 3677f7278fd0SJosepch Chan int err; 3678f7278fd0SJosepch Chan 3679518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) 3680518bf3baSLydia Wang return patch_vt1708S(codec); 3681f7278fd0SJosepch Chan /* create a codec specific record */ 36825b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3683f7278fd0SJosepch Chan if (spec == NULL) 3684f7278fd0SJosepch Chan return -ENOMEM; 3685f7278fd0SJosepch Chan 3686f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3687f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3688f7278fd0SJosepch Chan if (err < 0) { 3689f7278fd0SJosepch Chan via_free(codec); 3690f7278fd0SJosepch Chan return err; 3691f7278fd0SJosepch Chan } else if (!err) { 3692f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3693f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3694f7278fd0SJosepch Chan } 3695f7278fd0SJosepch Chan 369669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_8ch_volume_init_verbs; 369769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3698f7278fd0SJosepch Chan 3699f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3700f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_8ch_pcm_analog_playback; 3701f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3702f7278fd0SJosepch Chan 3703f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3704f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3705f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3706f7278fd0SJosepch Chan 3707f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3708f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3709f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3710337b9d02STakashi Iwai get_mux_nids(codec); 3711f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3712f7278fd0SJosepch Chan spec->num_mixers++; 3713f7278fd0SJosepch Chan } 3714f7278fd0SJosepch Chan 3715f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3716f7278fd0SJosepch Chan 3717f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 371869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3719f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3720f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3721f7278fd0SJosepch Chan #endif 3722f7278fd0SJosepch Chan 3723f7278fd0SJosepch Chan return 0; 3724f7278fd0SJosepch Chan } 3725f7278fd0SJosepch Chan 3726f7278fd0SJosepch Chan static int patch_vt1708B_4ch(struct hda_codec *codec) 3727f7278fd0SJosepch Chan { 3728f7278fd0SJosepch Chan struct via_spec *spec; 3729f7278fd0SJosepch Chan int err; 3730f7278fd0SJosepch Chan 3731f7278fd0SJosepch Chan /* create a codec specific record */ 37325b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 3733f7278fd0SJosepch Chan if (spec == NULL) 3734f7278fd0SJosepch Chan return -ENOMEM; 3735f7278fd0SJosepch Chan 3736f7278fd0SJosepch Chan /* automatic parse from the BIOS config */ 3737f7278fd0SJosepch Chan err = vt1708B_parse_auto_config(codec); 3738f7278fd0SJosepch Chan if (err < 0) { 3739f7278fd0SJosepch Chan via_free(codec); 3740f7278fd0SJosepch Chan return err; 3741f7278fd0SJosepch Chan } else if (!err) { 3742f7278fd0SJosepch Chan printk(KERN_INFO "hda_codec: Cannot set up configuration " 3743f7278fd0SJosepch Chan "from BIOS. Using genenic mode...\n"); 3744f7278fd0SJosepch Chan } 3745f7278fd0SJosepch Chan 374669e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_4ch_volume_init_verbs; 374769e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708B_uniwill_init_verbs; 3748f7278fd0SJosepch Chan 3749f7278fd0SJosepch Chan spec->stream_name_analog = "VT1708B Analog"; 3750f7278fd0SJosepch Chan spec->stream_analog_playback = &vt1708B_4ch_pcm_analog_playback; 3751f7278fd0SJosepch Chan spec->stream_analog_capture = &vt1708B_pcm_analog_capture; 3752f7278fd0SJosepch Chan 3753f7278fd0SJosepch Chan spec->stream_name_digital = "VT1708B Digital"; 3754f7278fd0SJosepch Chan spec->stream_digital_playback = &vt1708B_pcm_digital_playback; 3755f7278fd0SJosepch Chan spec->stream_digital_capture = &vt1708B_pcm_digital_capture; 3756f7278fd0SJosepch Chan 3757f7278fd0SJosepch Chan if (!spec->adc_nids && spec->input_mux) { 3758f7278fd0SJosepch Chan spec->adc_nids = vt1708B_adc_nids; 3759f7278fd0SJosepch Chan spec->num_adc_nids = ARRAY_SIZE(vt1708B_adc_nids); 3760337b9d02STakashi Iwai get_mux_nids(codec); 3761f7278fd0SJosepch Chan spec->mixers[spec->num_mixers] = vt1708B_capture_mixer; 3762f7278fd0SJosepch Chan spec->num_mixers++; 3763f7278fd0SJosepch Chan } 3764f7278fd0SJosepch Chan 3765f7278fd0SJosepch Chan codec->patch_ops = via_patch_ops; 3766f7278fd0SJosepch Chan 3767f7278fd0SJosepch Chan codec->patch_ops.init = via_auto_init; 376869e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 3769f7278fd0SJosepch Chan #ifdef CONFIG_SND_HDA_POWER_SAVE 3770f7278fd0SJosepch Chan spec->loopback.amplist = vt1708B_loopbacks; 3771f7278fd0SJosepch Chan #endif 3772c577b8a1SJoseph Chan 3773c577b8a1SJoseph Chan return 0; 3774c577b8a1SJoseph Chan } 3775c577b8a1SJoseph Chan 3776d949cac1SHarald Welte /* Patch for VT1708S */ 3777d949cac1SHarald Welte 3778d949cac1SHarald Welte /* capture mixer elements */ 3779d949cac1SHarald Welte static struct snd_kcontrol_new vt1708S_capture_mixer[] = { 3780d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 3781d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 3782d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 3783d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 37846369bcfcSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 37856369bcfcSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 37866369bcfcSLydia Wang HDA_INPUT), 3787d949cac1SHarald Welte { 3788d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 3789d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 3790d949cac1SHarald Welte * So call somewhat different.. 3791d949cac1SHarald Welte */ 3792d949cac1SHarald Welte /* .name = "Capture Source", */ 3793d949cac1SHarald Welte .name = "Input Source", 3794d949cac1SHarald Welte .count = 1, 3795d949cac1SHarald Welte .info = via_mux_enum_info, 3796d949cac1SHarald Welte .get = via_mux_enum_get, 3797d949cac1SHarald Welte .put = via_mux_enum_put, 3798d949cac1SHarald Welte }, 3799d949cac1SHarald Welte { } /* end */ 3800d949cac1SHarald Welte }; 3801d949cac1SHarald Welte 3802d949cac1SHarald Welte static struct hda_verb vt1708S_volume_init_verbs[] = { 3803d949cac1SHarald Welte /* Unmute ADC0-1 and set the default input to mic-in */ 3804d949cac1SHarald Welte {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3805d949cac1SHarald Welte {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3806d949cac1SHarald Welte 3807d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the 3808d949cac1SHarald Welte * analog-loopback mixer widget */ 3809d949cac1SHarald Welte /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 3810d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 3811d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 3812d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 3813d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 3814d949cac1SHarald Welte {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, 3815d949cac1SHarald Welte 3816d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 3817d949cac1SHarald Welte {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 38185691ec7fSHarald Welte /* PW9, PW10 Output enable */ 3819d949cac1SHarald Welte {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 38205691ec7fSHarald Welte {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 3821d7426329SHarald Welte /* Enable Mic Boost Volume backdoor */ 3822d7426329SHarald Welte {0x1, 0xf98, 0x1}, 3823bc7e7e5cSLydia Wang /* don't bybass mixer */ 3824bc7e7e5cSLydia Wang {0x1, 0xf88, 0xc0}, 3825d949cac1SHarald Welte { } 3826d949cac1SHarald Welte }; 3827d949cac1SHarald Welte 382869e52a80SHarald Welte static struct hda_verb vt1708S_uniwill_init_verbs[] = { 3829a34df19aSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 3830a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 3831a34df19aSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3832a34df19aSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3833a34df19aSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3834a34df19aSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3835a34df19aSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3836a34df19aSLydia Wang {0x22, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 3837a34df19aSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 383869e52a80SHarald Welte { } 383969e52a80SHarald Welte }; 384069e52a80SHarald Welte 3841d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_playback = { 3842d949cac1SHarald Welte .substreams = 2, 3843d949cac1SHarald Welte .channels_min = 2, 3844d949cac1SHarald Welte .channels_max = 8, 3845d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 3846d949cac1SHarald Welte .ops = { 3847d949cac1SHarald Welte .open = via_playback_pcm_open, 3848c873cc25SLydia Wang .prepare = via_playback_multi_pcm_prepare, 3849c873cc25SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 385017314379SLydia Wang .close = via_pcm_open_close 3851d949cac1SHarald Welte }, 3852d949cac1SHarald Welte }; 3853d949cac1SHarald Welte 3854d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_analog_capture = { 3855d949cac1SHarald Welte .substreams = 2, 3856d949cac1SHarald Welte .channels_min = 2, 3857d949cac1SHarald Welte .channels_max = 2, 3858d949cac1SHarald Welte .nid = 0x13, /* NID to query formats and rates */ 3859d949cac1SHarald Welte .ops = { 386017314379SLydia Wang .open = via_pcm_open_close, 3861d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 386217314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 386317314379SLydia Wang .close = via_pcm_open_close 3864d949cac1SHarald Welte }, 3865d949cac1SHarald Welte }; 3866d949cac1SHarald Welte 3867d949cac1SHarald Welte static struct hda_pcm_stream vt1708S_pcm_digital_playback = { 38689da29271STakashi Iwai .substreams = 1, 3869d949cac1SHarald Welte .channels_min = 2, 3870d949cac1SHarald Welte .channels_max = 2, 3871d949cac1SHarald Welte /* NID is set in via_build_pcms */ 3872d949cac1SHarald Welte .ops = { 3873d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 3874d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 38759da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 38769da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 3877d949cac1SHarald Welte }, 3878d949cac1SHarald Welte }; 3879d949cac1SHarald Welte 3880d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 3881d949cac1SHarald Welte static int vt1708S_auto_fill_dac_nids(struct via_spec *spec, 3882d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3883d949cac1SHarald Welte { 3884d949cac1SHarald Welte int i; 3885d949cac1SHarald Welte hda_nid_t nid; 3886d949cac1SHarald Welte 3887d949cac1SHarald Welte spec->multiout.num_dacs = cfg->line_outs; 3888d949cac1SHarald Welte 3889d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 3890d949cac1SHarald Welte 3891d949cac1SHarald Welte for (i = 0; i < 4; i++) { 3892d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3893d949cac1SHarald Welte if (nid) { 3894d949cac1SHarald Welte /* config dac list */ 3895d949cac1SHarald Welte switch (i) { 3896d949cac1SHarald Welte case AUTO_SEQ_FRONT: 3897d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x10; 3898d949cac1SHarald Welte break; 3899d949cac1SHarald Welte case AUTO_SEQ_CENLFE: 3900d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x24; 3901d949cac1SHarald Welte break; 3902d949cac1SHarald Welte case AUTO_SEQ_SURROUND: 3903d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x11; 3904d949cac1SHarald Welte break; 3905d949cac1SHarald Welte case AUTO_SEQ_SIDE: 3906d949cac1SHarald Welte spec->multiout.dac_nids[i] = 0x25; 3907d949cac1SHarald Welte break; 3908d949cac1SHarald Welte } 3909d949cac1SHarald Welte } 3910d949cac1SHarald Welte } 3911d949cac1SHarald Welte 3912d949cac1SHarald Welte return 0; 3913d949cac1SHarald Welte } 3914d949cac1SHarald Welte 3915d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 3916d949cac1SHarald Welte static int vt1708S_auto_create_multi_out_ctls(struct via_spec *spec, 3917d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 3918d949cac1SHarald Welte { 3919d949cac1SHarald Welte char name[32]; 3920d949cac1SHarald Welte static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 3921d949cac1SHarald Welte hda_nid_t nid_vols[] = {0x10, 0x11, 0x24, 0x25}; 3922d949cac1SHarald Welte hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x26, 0x27}; 3923d949cac1SHarald Welte hda_nid_t nid, nid_vol, nid_mute; 3924d949cac1SHarald Welte int i, err; 3925d949cac1SHarald Welte 3926d949cac1SHarald Welte for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 3927d949cac1SHarald Welte nid = cfg->line_out_pins[i]; 3928d949cac1SHarald Welte 3929d949cac1SHarald Welte if (!nid) 3930d949cac1SHarald Welte continue; 3931d949cac1SHarald Welte 3932d949cac1SHarald Welte nid_vol = nid_vols[i]; 3933d949cac1SHarald Welte nid_mute = nid_mutes[i]; 3934d949cac1SHarald Welte 3935d949cac1SHarald Welte if (i == AUTO_SEQ_CENLFE) { 3936d949cac1SHarald Welte /* Center/LFE */ 3937d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3938d949cac1SHarald Welte "Center Playback Volume", 3939d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 3940d949cac1SHarald Welte HDA_OUTPUT)); 3941d949cac1SHarald Welte if (err < 0) 3942d949cac1SHarald Welte return err; 3943d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3944d949cac1SHarald Welte "LFE Playback Volume", 3945d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 3946d949cac1SHarald Welte HDA_OUTPUT)); 3947d949cac1SHarald Welte if (err < 0) 3948d949cac1SHarald Welte return err; 3949d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3950d949cac1SHarald Welte "Center Playback Switch", 3951d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3952d949cac1SHarald Welte 1, 0, 3953d949cac1SHarald Welte HDA_OUTPUT)); 3954d949cac1SHarald Welte if (err < 0) 3955d949cac1SHarald Welte return err; 3956d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3957d949cac1SHarald Welte "LFE Playback Switch", 3958d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3959d949cac1SHarald Welte 2, 0, 3960d949cac1SHarald Welte HDA_OUTPUT)); 3961d949cac1SHarald Welte if (err < 0) 3962d949cac1SHarald Welte return err; 3963d949cac1SHarald Welte } else if (i == AUTO_SEQ_FRONT) { 3964d949cac1SHarald Welte /* add control to mixer index 0 */ 3965d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 3966d949cac1SHarald Welte "Master Front Playback Volume", 3967d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3968d949cac1SHarald Welte HDA_INPUT)); 3969d949cac1SHarald Welte if (err < 0) 3970d949cac1SHarald Welte return err; 3971d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 3972d949cac1SHarald Welte "Master Front Playback Switch", 3973d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, 3974d949cac1SHarald Welte HDA_INPUT)); 3975d949cac1SHarald Welte if (err < 0) 3976d949cac1SHarald Welte return err; 3977d949cac1SHarald Welte 3978d949cac1SHarald Welte /* Front */ 3979d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3980d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3981d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3982d949cac1SHarald Welte HDA_OUTPUT)); 3983d949cac1SHarald Welte if (err < 0) 3984d949cac1SHarald Welte return err; 3985d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 3986d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 3987d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 3988d949cac1SHarald Welte 3, 0, 3989d949cac1SHarald Welte HDA_OUTPUT)); 3990d949cac1SHarald Welte if (err < 0) 3991d949cac1SHarald Welte return err; 3992d949cac1SHarald Welte } else { 3993d949cac1SHarald Welte sprintf(name, "%s Playback Volume", chname[i]); 3994d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name, 3995d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, 3996d949cac1SHarald Welte HDA_OUTPUT)); 3997d949cac1SHarald Welte if (err < 0) 3998d949cac1SHarald Welte return err; 3999d949cac1SHarald Welte sprintf(name, "%s Playback Switch", chname[i]); 4000d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name, 4001d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(nid_mute, 4002d949cac1SHarald Welte 3, 0, 4003d949cac1SHarald Welte HDA_OUTPUT)); 4004d949cac1SHarald Welte if (err < 0) 4005d949cac1SHarald Welte return err; 4006d949cac1SHarald Welte } 4007d949cac1SHarald Welte } 4008d949cac1SHarald Welte 4009d949cac1SHarald Welte return 0; 4010d949cac1SHarald Welte } 4011d949cac1SHarald Welte 4012d949cac1SHarald Welte static int vt1708S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4013d949cac1SHarald Welte { 4014d949cac1SHarald Welte int err; 4015d949cac1SHarald Welte 4016d949cac1SHarald Welte if (!pin) 4017d949cac1SHarald Welte return 0; 4018d949cac1SHarald Welte 4019d949cac1SHarald Welte spec->multiout.hp_nid = VT1708S_HP_NID; /* AOW3 */ 4020cdc1784dSLydia Wang spec->hp_independent_mode_index = 1; 4021d949cac1SHarald Welte 4022d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4023d949cac1SHarald Welte "Headphone Playback Volume", 4024d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 4025d949cac1SHarald Welte if (err < 0) 4026d949cac1SHarald Welte return err; 4027d949cac1SHarald Welte 4028d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4029d949cac1SHarald Welte "Headphone Playback Switch", 4030d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4031d949cac1SHarald Welte if (err < 0) 4032d949cac1SHarald Welte return err; 4033d949cac1SHarald Welte 40340aa62aefSHarald Welte create_hp_imux(spec); 40350aa62aefSHarald Welte 4036d949cac1SHarald Welte return 0; 4037d949cac1SHarald Welte } 4038d949cac1SHarald Welte 4039d949cac1SHarald Welte /* create playback/capture controls for input pins */ 4040d949cac1SHarald Welte static int vt1708S_auto_create_analog_input_ctls(struct via_spec *spec, 4041d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4042d949cac1SHarald Welte { 4043d949cac1SHarald Welte static char *labels[] = { 4044d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4045d949cac1SHarald Welte }; 40460aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 4047d949cac1SHarald Welte int i, err, idx = 0; 4048d949cac1SHarald Welte 4049d949cac1SHarald Welte /* for internal loopback recording select */ 4050d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 4051d949cac1SHarald Welte imux->items[imux->num_items].index = 5; 4052d949cac1SHarald Welte imux->num_items++; 4053d949cac1SHarald Welte 4054d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 4055d949cac1SHarald Welte if (!cfg->input_pins[i]) 4056d949cac1SHarald Welte continue; 4057d949cac1SHarald Welte 4058d949cac1SHarald Welte switch (cfg->input_pins[i]) { 4059d949cac1SHarald Welte case 0x1a: /* Mic */ 4060d949cac1SHarald Welte idx = 2; 4061d949cac1SHarald Welte break; 4062d949cac1SHarald Welte 4063d949cac1SHarald Welte case 0x1b: /* Line In */ 4064d949cac1SHarald Welte idx = 3; 4065d949cac1SHarald Welte break; 4066d949cac1SHarald Welte 4067d949cac1SHarald Welte case 0x1e: /* Front Mic */ 4068d949cac1SHarald Welte idx = 4; 4069d949cac1SHarald Welte break; 4070d949cac1SHarald Welte 4071d949cac1SHarald Welte case 0x1f: /* CD */ 4072d949cac1SHarald Welte idx = 1; 4073d949cac1SHarald Welte break; 4074d949cac1SHarald Welte } 40759510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 4076d949cac1SHarald Welte if (err < 0) 4077d949cac1SHarald Welte return err; 4078d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 4079d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 4080d949cac1SHarald Welte imux->num_items++; 4081d949cac1SHarald Welte } 4082d949cac1SHarald Welte return 0; 4083d949cac1SHarald Welte } 4084d949cac1SHarald Welte 40859da29271STakashi Iwai /* fill out digital output widgets; one for master and one for slave outputs */ 40869da29271STakashi Iwai static void fill_dig_outs(struct hda_codec *codec) 40879da29271STakashi Iwai { 40889da29271STakashi Iwai struct via_spec *spec = codec->spec; 40899da29271STakashi Iwai int i; 40909da29271STakashi Iwai 40919da29271STakashi Iwai for (i = 0; i < spec->autocfg.dig_outs; i++) { 40929da29271STakashi Iwai hda_nid_t nid; 40939da29271STakashi Iwai int conn; 40949da29271STakashi Iwai 40959da29271STakashi Iwai nid = spec->autocfg.dig_out_pins[i]; 40969da29271STakashi Iwai if (!nid) 40979da29271STakashi Iwai continue; 40989da29271STakashi Iwai conn = snd_hda_get_connections(codec, nid, &nid, 1); 40999da29271STakashi Iwai if (conn < 1) 41009da29271STakashi Iwai continue; 41019da29271STakashi Iwai if (!spec->multiout.dig_out_nid) 41029da29271STakashi Iwai spec->multiout.dig_out_nid = nid; 41039da29271STakashi Iwai else { 41049da29271STakashi Iwai spec->slave_dig_outs[0] = nid; 41059da29271STakashi Iwai break; /* at most two dig outs */ 41069da29271STakashi Iwai } 41079da29271STakashi Iwai } 41089da29271STakashi Iwai } 41099da29271STakashi Iwai 4110d949cac1SHarald Welte static int vt1708S_parse_auto_config(struct hda_codec *codec) 4111d949cac1SHarald Welte { 4112d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4113d949cac1SHarald Welte int err; 4114d949cac1SHarald Welte 41159da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4116d949cac1SHarald Welte if (err < 0) 4117d949cac1SHarald Welte return err; 4118d949cac1SHarald Welte err = vt1708S_auto_fill_dac_nids(spec, &spec->autocfg); 4119d949cac1SHarald Welte if (err < 0) 4120d949cac1SHarald Welte return err; 4121d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4122d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4123d949cac1SHarald Welte 4124d949cac1SHarald Welte err = vt1708S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4125d949cac1SHarald Welte if (err < 0) 4126d949cac1SHarald Welte return err; 4127d949cac1SHarald Welte err = vt1708S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4128d949cac1SHarald Welte if (err < 0) 4129d949cac1SHarald Welte return err; 4130d949cac1SHarald Welte err = vt1708S_auto_create_analog_input_ctls(spec, &spec->autocfg); 4131d949cac1SHarald Welte if (err < 0) 4132d949cac1SHarald Welte return err; 4133d949cac1SHarald Welte 4134d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4135d949cac1SHarald Welte 41369da29271STakashi Iwai fill_dig_outs(codec); 413798aa34c0SHarald Welte 4138603c4019STakashi Iwai if (spec->kctls.list) 4139603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4140d949cac1SHarald Welte 41410aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 41420aa62aefSHarald Welte 4143f8fdd495SHarald Welte if (spec->hp_mux) 41445b0cb1d8SJaroslav Kysela via_hp_build(spec); 4145d949cac1SHarald Welte 41465b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4147d949cac1SHarald Welte return 1; 4148d949cac1SHarald Welte } 4149d949cac1SHarald Welte 4150d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4151d949cac1SHarald Welte static struct hda_amp_list vt1708S_loopbacks[] = { 4152d949cac1SHarald Welte { 0x16, HDA_INPUT, 1 }, 4153d949cac1SHarald Welte { 0x16, HDA_INPUT, 2 }, 4154d949cac1SHarald Welte { 0x16, HDA_INPUT, 3 }, 4155d949cac1SHarald Welte { 0x16, HDA_INPUT, 4 }, 4156d949cac1SHarald Welte { } /* end */ 4157d949cac1SHarald Welte }; 4158d949cac1SHarald Welte #endif 4159d949cac1SHarald Welte 41606369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin, 41616369bcfcSLydia Wang int offset, int num_steps, int step_size) 41626369bcfcSLydia Wang { 41636369bcfcSLydia Wang snd_hda_override_amp_caps(codec, pin, HDA_INPUT, 41646369bcfcSLydia Wang (offset << AC_AMPCAP_OFFSET_SHIFT) | 41656369bcfcSLydia Wang (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) | 41666369bcfcSLydia Wang (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) | 41676369bcfcSLydia Wang (0 << AC_AMPCAP_MUTE_SHIFT)); 41686369bcfcSLydia Wang } 41696369bcfcSLydia Wang 4170d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec) 4171d949cac1SHarald Welte { 4172d949cac1SHarald Welte struct via_spec *spec; 4173d949cac1SHarald Welte int err; 4174d949cac1SHarald Welte 4175d949cac1SHarald Welte /* create a codec specific record */ 41765b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4177d949cac1SHarald Welte if (spec == NULL) 4178d949cac1SHarald Welte return -ENOMEM; 4179d949cac1SHarald Welte 4180d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4181d949cac1SHarald Welte err = vt1708S_parse_auto_config(codec); 4182d949cac1SHarald Welte if (err < 0) { 4183d949cac1SHarald Welte via_free(codec); 4184d949cac1SHarald Welte return err; 4185d949cac1SHarald Welte } else if (!err) { 4186d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4187d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4188d949cac1SHarald Welte } 4189d949cac1SHarald Welte 419069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_volume_init_verbs; 419169e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1708S_uniwill_init_verbs; 4192d949cac1SHarald Welte 419336dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 419436dd5c4aSLydia Wang spec->stream_name_analog = "VT1818S Analog"; 419536dd5c4aSLydia Wang else 4196d949cac1SHarald Welte spec->stream_name_analog = "VT1708S Analog"; 4197d949cac1SHarald Welte spec->stream_analog_playback = &vt1708S_pcm_analog_playback; 4198d949cac1SHarald Welte spec->stream_analog_capture = &vt1708S_pcm_analog_capture; 4199d949cac1SHarald Welte 420036dd5c4aSLydia Wang if (codec->vendor_id == 0x11060440) 420136dd5c4aSLydia Wang spec->stream_name_digital = "VT1818S Digital"; 420236dd5c4aSLydia Wang else 4203d949cac1SHarald Welte spec->stream_name_digital = "VT1708S Digital"; 4204d949cac1SHarald Welte spec->stream_digital_playback = &vt1708S_pcm_digital_playback; 4205d949cac1SHarald Welte 4206d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4207d949cac1SHarald Welte spec->adc_nids = vt1708S_adc_nids; 4208d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1708S_adc_nids); 4209337b9d02STakashi Iwai get_mux_nids(codec); 42106369bcfcSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 42116369bcfcSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 4212d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1708S_capture_mixer; 4213d949cac1SHarald Welte spec->num_mixers++; 4214d949cac1SHarald Welte } 4215d949cac1SHarald Welte 4216d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4217d949cac1SHarald Welte 4218d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 421969e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4220d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4221d949cac1SHarald Welte spec->loopback.amplist = vt1708S_loopbacks; 4222d949cac1SHarald Welte #endif 4223d949cac1SHarald Welte 4224518bf3baSLydia Wang /* correct names for VT1708BCE */ 4225518bf3baSLydia Wang if (get_codec_type(codec) == VT1708BCE) { 4226518bf3baSLydia Wang kfree(codec->chip_name); 4227518bf3baSLydia Wang codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL); 4228518bf3baSLydia Wang snprintf(codec->bus->card->mixername, 4229518bf3baSLydia Wang sizeof(codec->bus->card->mixername), 4230518bf3baSLydia Wang "%s %s", codec->vendor_name, codec->chip_name); 4231518bf3baSLydia Wang spec->stream_name_analog = "VT1708BCE Analog"; 4232518bf3baSLydia Wang spec->stream_name_digital = "VT1708BCE Digital"; 4233518bf3baSLydia Wang } 4234d949cac1SHarald Welte return 0; 4235d949cac1SHarald Welte } 4236d949cac1SHarald Welte 4237d949cac1SHarald Welte /* Patch for VT1702 */ 4238d949cac1SHarald Welte 4239d949cac1SHarald Welte /* capture mixer elements */ 4240d949cac1SHarald Welte static struct snd_kcontrol_new vt1702_capture_mixer[] = { 4241d949cac1SHarald Welte HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_INPUT), 4242d949cac1SHarald Welte HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_INPUT), 4243d949cac1SHarald Welte HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x20, 0x0, HDA_INPUT), 4244d949cac1SHarald Welte HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x20, 0x0, HDA_INPUT), 4245d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x1F, 0x0, HDA_INPUT), 4246d949cac1SHarald Welte HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x1F, 0x0, HDA_INPUT), 4247d949cac1SHarald Welte HDA_CODEC_VOLUME("Digital Mic Boost Capture Volume", 0x1E, 0x0, 4248d949cac1SHarald Welte HDA_INPUT), 4249d949cac1SHarald Welte { 4250d949cac1SHarald Welte .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4251d949cac1SHarald Welte /* The multiple "Capture Source" controls confuse alsamixer 4252d949cac1SHarald Welte * So call somewhat different.. 4253d949cac1SHarald Welte */ 4254d949cac1SHarald Welte /* .name = "Capture Source", */ 4255d949cac1SHarald Welte .name = "Input Source", 4256d949cac1SHarald Welte .count = 1, 4257d949cac1SHarald Welte .info = via_mux_enum_info, 4258d949cac1SHarald Welte .get = via_mux_enum_get, 4259d949cac1SHarald Welte .put = via_mux_enum_put, 4260d949cac1SHarald Welte }, 4261d949cac1SHarald Welte { } /* end */ 4262d949cac1SHarald Welte }; 4263d949cac1SHarald Welte 4264d949cac1SHarald Welte static struct hda_verb vt1702_volume_init_verbs[] = { 4265d949cac1SHarald Welte /* 4266d949cac1SHarald Welte * Unmute ADC0-1 and set the default input to mic-in 4267d949cac1SHarald Welte */ 4268d949cac1SHarald Welte {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4269d949cac1SHarald Welte {0x1F, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4270d949cac1SHarald Welte {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4271d949cac1SHarald Welte 4272d949cac1SHarald Welte 4273d949cac1SHarald Welte /* Unmute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4274d949cac1SHarald Welte * mixer widget 4275d949cac1SHarald Welte */ 4276d949cac1SHarald Welte /* Amp Indices: Mic1 = 1, Line = 1, Mic2 = 3 */ 4277d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4278d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4279d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(2)}, 4280d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3)}, 4281d949cac1SHarald Welte {0x1A, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 4282d949cac1SHarald Welte 4283d949cac1SHarald Welte /* Setup default input of PW4 to MW0 */ 4284d949cac1SHarald Welte {0x17, AC_VERB_SET_CONNECT_SEL, 0x1}, 4285d949cac1SHarald Welte /* PW6 PW7 Output enable */ 4286d949cac1SHarald Welte {0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4287d949cac1SHarald Welte {0x1C, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 4288bc7e7e5cSLydia Wang /* mixer enable */ 4289bc7e7e5cSLydia Wang {0x1, 0xF88, 0x3}, 4290bc7e7e5cSLydia Wang /* GPIO 0~2 */ 4291bc7e7e5cSLydia Wang {0x1, 0xF82, 0x3F}, 4292d949cac1SHarald Welte { } 4293d949cac1SHarald Welte }; 4294d949cac1SHarald Welte 429569e52a80SHarald Welte static struct hda_verb vt1702_uniwill_init_verbs[] = { 4296a34df19aSLydia Wang {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, 4297a34df19aSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4298a34df19aSLydia Wang {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4299a34df19aSLydia Wang {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4300a34df19aSLydia Wang {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4301a34df19aSLydia Wang {0x18, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 430269e52a80SHarald Welte { } 430369e52a80SHarald Welte }; 430469e52a80SHarald Welte 4305d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_playback = { 43060aa62aefSHarald Welte .substreams = 2, 4307d949cac1SHarald Welte .channels_min = 2, 4308d949cac1SHarald Welte .channels_max = 2, 4309d949cac1SHarald Welte .nid = 0x10, /* NID to query formats and rates */ 4310d949cac1SHarald Welte .ops = { 4311d949cac1SHarald Welte .open = via_playback_pcm_open, 43120aa62aefSHarald Welte .prepare = via_playback_multi_pcm_prepare, 431317314379SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 431417314379SLydia Wang .close = via_pcm_open_close 4315d949cac1SHarald Welte }, 4316d949cac1SHarald Welte }; 4317d949cac1SHarald Welte 4318d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_analog_capture = { 4319d949cac1SHarald Welte .substreams = 3, 4320d949cac1SHarald Welte .channels_min = 2, 4321d949cac1SHarald Welte .channels_max = 2, 4322d949cac1SHarald Welte .nid = 0x12, /* NID to query formats and rates */ 4323d949cac1SHarald Welte .ops = { 432417314379SLydia Wang .open = via_pcm_open_close, 4325d949cac1SHarald Welte .prepare = via_capture_pcm_prepare, 432617314379SLydia Wang .cleanup = via_capture_pcm_cleanup, 432717314379SLydia Wang .close = via_pcm_open_close 4328d949cac1SHarald Welte }, 4329d949cac1SHarald Welte }; 4330d949cac1SHarald Welte 4331d949cac1SHarald Welte static struct hda_pcm_stream vt1702_pcm_digital_playback = { 43325691ec7fSHarald Welte .substreams = 2, 4333d949cac1SHarald Welte .channels_min = 2, 4334d949cac1SHarald Welte .channels_max = 2, 4335d949cac1SHarald Welte /* NID is set in via_build_pcms */ 4336d949cac1SHarald Welte .ops = { 4337d949cac1SHarald Welte .open = via_dig_playback_pcm_open, 4338d949cac1SHarald Welte .close = via_dig_playback_pcm_close, 43399da29271STakashi Iwai .prepare = via_dig_playback_pcm_prepare, 43409da29271STakashi Iwai .cleanup = via_dig_playback_pcm_cleanup 4341d949cac1SHarald Welte }, 4342d949cac1SHarald Welte }; 4343d949cac1SHarald Welte 4344d949cac1SHarald Welte /* fill in the dac_nids table from the parsed pin configuration */ 4345d949cac1SHarald Welte static int vt1702_auto_fill_dac_nids(struct via_spec *spec, 4346d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4347d949cac1SHarald Welte { 4348d949cac1SHarald Welte spec->multiout.num_dacs = 1; 4349d949cac1SHarald Welte spec->multiout.dac_nids = spec->private_dac_nids; 4350d949cac1SHarald Welte 4351d949cac1SHarald Welte if (cfg->line_out_pins[0]) { 4352d949cac1SHarald Welte /* config dac list */ 4353d949cac1SHarald Welte spec->multiout.dac_nids[0] = 0x10; 4354d949cac1SHarald Welte } 4355d949cac1SHarald Welte 4356d949cac1SHarald Welte return 0; 4357d949cac1SHarald Welte } 4358d949cac1SHarald Welte 4359d949cac1SHarald Welte /* add playback controls from the parsed DAC table */ 4360d949cac1SHarald Welte static int vt1702_auto_create_line_out_ctls(struct via_spec *spec, 4361d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4362d949cac1SHarald Welte { 4363d949cac1SHarald Welte int err; 4364d949cac1SHarald Welte 4365d949cac1SHarald Welte if (!cfg->line_out_pins[0]) 4366d949cac1SHarald Welte return -1; 4367d949cac1SHarald Welte 4368d949cac1SHarald Welte /* add control to mixer index 0 */ 4369d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4370d949cac1SHarald Welte "Master Front Playback Volume", 4371d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4372d949cac1SHarald Welte if (err < 0) 4373d949cac1SHarald Welte return err; 4374d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4375d949cac1SHarald Welte "Master Front Playback Switch", 4376d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1A, 3, 0, HDA_INPUT)); 4377d949cac1SHarald Welte if (err < 0) 4378d949cac1SHarald Welte return err; 4379d949cac1SHarald Welte 4380d949cac1SHarald Welte /* Front */ 4381d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4382d949cac1SHarald Welte "Front Playback Volume", 4383d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x10, 3, 0, HDA_OUTPUT)); 4384d949cac1SHarald Welte if (err < 0) 4385d949cac1SHarald Welte return err; 4386d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4387d949cac1SHarald Welte "Front Playback Switch", 4388d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_OUTPUT)); 4389d949cac1SHarald Welte if (err < 0) 4390d949cac1SHarald Welte return err; 4391d949cac1SHarald Welte 4392d949cac1SHarald Welte return 0; 4393d949cac1SHarald Welte } 4394d949cac1SHarald Welte 4395d949cac1SHarald Welte static int vt1702_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4396d949cac1SHarald Welte { 43970713efebSLydia Wang int err, i; 43980713efebSLydia Wang struct hda_input_mux *imux; 43990713efebSLydia Wang static const char *texts[] = { "ON", "OFF", NULL}; 4400d949cac1SHarald Welte if (!pin) 4401d949cac1SHarald Welte return 0; 4402d949cac1SHarald Welte spec->multiout.hp_nid = 0x1D; 4403cdc1784dSLydia Wang spec->hp_independent_mode_index = 0; 4404d949cac1SHarald Welte 4405d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4406d949cac1SHarald Welte "Headphone Playback Volume", 4407d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(0x1D, 3, 0, HDA_OUTPUT)); 4408d949cac1SHarald Welte if (err < 0) 4409d949cac1SHarald Welte return err; 4410d949cac1SHarald Welte 4411d949cac1SHarald Welte err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4412d949cac1SHarald Welte "Headphone Playback Switch", 4413d949cac1SHarald Welte HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4414d949cac1SHarald Welte if (err < 0) 4415d949cac1SHarald Welte return err; 4416d949cac1SHarald Welte 44170713efebSLydia Wang imux = &spec->private_imux[1]; 44180aa62aefSHarald Welte 44190713efebSLydia Wang /* for hp mode select */ 44200713efebSLydia Wang i = 0; 44210713efebSLydia Wang while (texts[i] != NULL) { 44220713efebSLydia Wang imux->items[imux->num_items].label = texts[i]; 44230713efebSLydia Wang imux->items[imux->num_items].index = i; 44240713efebSLydia Wang imux->num_items++; 44250713efebSLydia Wang i++; 44260713efebSLydia Wang } 44270713efebSLydia Wang 44280713efebSLydia Wang spec->hp_mux = &spec->private_imux[1]; 4429d949cac1SHarald Welte return 0; 4430d949cac1SHarald Welte } 4431d949cac1SHarald Welte 4432d949cac1SHarald Welte /* create playback/capture controls for input pins */ 4433d949cac1SHarald Welte static int vt1702_auto_create_analog_input_ctls(struct via_spec *spec, 4434d949cac1SHarald Welte const struct auto_pin_cfg *cfg) 4435d949cac1SHarald Welte { 4436d949cac1SHarald Welte static char *labels[] = { 4437d949cac1SHarald Welte "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4438d949cac1SHarald Welte }; 44390aa62aefSHarald Welte struct hda_input_mux *imux = &spec->private_imux[0]; 4440d949cac1SHarald Welte int i, err, idx = 0; 4441d949cac1SHarald Welte 4442d949cac1SHarald Welte /* for internal loopback recording select */ 4443d949cac1SHarald Welte imux->items[imux->num_items].label = "Stereo Mixer"; 4444d949cac1SHarald Welte imux->items[imux->num_items].index = 3; 4445d949cac1SHarald Welte imux->num_items++; 4446d949cac1SHarald Welte 4447d949cac1SHarald Welte for (i = 0; i < AUTO_PIN_LAST; i++) { 4448d949cac1SHarald Welte if (!cfg->input_pins[i]) 4449d949cac1SHarald Welte continue; 4450d949cac1SHarald Welte 4451d949cac1SHarald Welte switch (cfg->input_pins[i]) { 4452d949cac1SHarald Welte case 0x14: /* Mic */ 4453d949cac1SHarald Welte idx = 1; 4454d949cac1SHarald Welte break; 4455d949cac1SHarald Welte 4456d949cac1SHarald Welte case 0x15: /* Line In */ 4457d949cac1SHarald Welte idx = 2; 4458d949cac1SHarald Welte break; 4459d949cac1SHarald Welte 4460d949cac1SHarald Welte case 0x18: /* Front Mic */ 4461d949cac1SHarald Welte idx = 3; 4462d949cac1SHarald Welte break; 4463d949cac1SHarald Welte } 44649510e8ddSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x1A); 4465d949cac1SHarald Welte if (err < 0) 4466d949cac1SHarald Welte return err; 4467d949cac1SHarald Welte imux->items[imux->num_items].label = labels[i]; 4468d949cac1SHarald Welte imux->items[imux->num_items].index = idx-1; 4469d949cac1SHarald Welte imux->num_items++; 4470d949cac1SHarald Welte } 4471d949cac1SHarald Welte return 0; 4472d949cac1SHarald Welte } 4473d949cac1SHarald Welte 4474d949cac1SHarald Welte static int vt1702_parse_auto_config(struct hda_codec *codec) 4475d949cac1SHarald Welte { 4476d949cac1SHarald Welte struct via_spec *spec = codec->spec; 4477d949cac1SHarald Welte int err; 4478d949cac1SHarald Welte 44799da29271STakashi Iwai err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4480d949cac1SHarald Welte if (err < 0) 4481d949cac1SHarald Welte return err; 4482d949cac1SHarald Welte err = vt1702_auto_fill_dac_nids(spec, &spec->autocfg); 4483d949cac1SHarald Welte if (err < 0) 4484d949cac1SHarald Welte return err; 4485d949cac1SHarald Welte if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4486d949cac1SHarald Welte return 0; /* can't find valid BIOS pin config */ 4487d949cac1SHarald Welte 4488d949cac1SHarald Welte err = vt1702_auto_create_line_out_ctls(spec, &spec->autocfg); 4489d949cac1SHarald Welte if (err < 0) 4490d949cac1SHarald Welte return err; 4491d949cac1SHarald Welte err = vt1702_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4492d949cac1SHarald Welte if (err < 0) 4493d949cac1SHarald Welte return err; 4494c2c02ea3SLydia Wang /* limit AA path volume to 0 dB */ 4495c2c02ea3SLydia Wang snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT, 4496c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_OFFSET_SHIFT) | 4497c2c02ea3SLydia Wang (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | 4498c2c02ea3SLydia Wang (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) | 4499c2c02ea3SLydia Wang (1 << AC_AMPCAP_MUTE_SHIFT)); 4500d949cac1SHarald Welte err = vt1702_auto_create_analog_input_ctls(spec, &spec->autocfg); 4501d949cac1SHarald Welte if (err < 0) 4502d949cac1SHarald Welte return err; 4503d949cac1SHarald Welte 4504d949cac1SHarald Welte spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4505d949cac1SHarald Welte 45069da29271STakashi Iwai fill_dig_outs(codec); 450798aa34c0SHarald Welte 4508603c4019STakashi Iwai if (spec->kctls.list) 4509603c4019STakashi Iwai spec->mixers[spec->num_mixers++] = spec->kctls.list; 4510d949cac1SHarald Welte 45110aa62aefSHarald Welte spec->input_mux = &spec->private_imux[0]; 45120aa62aefSHarald Welte 4513f8fdd495SHarald Welte if (spec->hp_mux) 45145b0cb1d8SJaroslav Kysela via_hp_build(spec); 4515d949cac1SHarald Welte 4516d949cac1SHarald Welte return 1; 4517d949cac1SHarald Welte } 4518d949cac1SHarald Welte 4519d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4520d949cac1SHarald Welte static struct hda_amp_list vt1702_loopbacks[] = { 4521d949cac1SHarald Welte { 0x1A, HDA_INPUT, 1 }, 4522d949cac1SHarald Welte { 0x1A, HDA_INPUT, 2 }, 4523d949cac1SHarald Welte { 0x1A, HDA_INPUT, 3 }, 4524d949cac1SHarald Welte { 0x1A, HDA_INPUT, 4 }, 4525d949cac1SHarald Welte { } /* end */ 4526d949cac1SHarald Welte }; 4527d949cac1SHarald Welte #endif 4528d949cac1SHarald Welte 4529d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec) 4530d949cac1SHarald Welte { 4531d949cac1SHarald Welte struct via_spec *spec; 4532d949cac1SHarald Welte int err; 4533d949cac1SHarald Welte 4534d949cac1SHarald Welte /* create a codec specific record */ 45355b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4536d949cac1SHarald Welte if (spec == NULL) 4537d949cac1SHarald Welte return -ENOMEM; 4538d949cac1SHarald Welte 4539d949cac1SHarald Welte /* automatic parse from the BIOS config */ 4540d949cac1SHarald Welte err = vt1702_parse_auto_config(codec); 4541d949cac1SHarald Welte if (err < 0) { 4542d949cac1SHarald Welte via_free(codec); 4543d949cac1SHarald Welte return err; 4544d949cac1SHarald Welte } else if (!err) { 4545d949cac1SHarald Welte printk(KERN_INFO "hda_codec: Cannot set up configuration " 4546d949cac1SHarald Welte "from BIOS. Using genenic mode...\n"); 4547d949cac1SHarald Welte } 4548d949cac1SHarald Welte 454969e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_volume_init_verbs; 455069e52a80SHarald Welte spec->init_verbs[spec->num_iverbs++] = vt1702_uniwill_init_verbs; 4551d949cac1SHarald Welte 4552d949cac1SHarald Welte spec->stream_name_analog = "VT1702 Analog"; 4553d949cac1SHarald Welte spec->stream_analog_playback = &vt1702_pcm_analog_playback; 4554d949cac1SHarald Welte spec->stream_analog_capture = &vt1702_pcm_analog_capture; 4555d949cac1SHarald Welte 4556d949cac1SHarald Welte spec->stream_name_digital = "VT1702 Digital"; 4557d949cac1SHarald Welte spec->stream_digital_playback = &vt1702_pcm_digital_playback; 4558d949cac1SHarald Welte 4559d949cac1SHarald Welte if (!spec->adc_nids && spec->input_mux) { 4560d949cac1SHarald Welte spec->adc_nids = vt1702_adc_nids; 4561d949cac1SHarald Welte spec->num_adc_nids = ARRAY_SIZE(vt1702_adc_nids); 4562337b9d02STakashi Iwai get_mux_nids(codec); 4563d949cac1SHarald Welte spec->mixers[spec->num_mixers] = vt1702_capture_mixer; 4564d949cac1SHarald Welte spec->num_mixers++; 4565d949cac1SHarald Welte } 4566d949cac1SHarald Welte 4567d949cac1SHarald Welte codec->patch_ops = via_patch_ops; 4568d949cac1SHarald Welte 4569d949cac1SHarald Welte codec->patch_ops.init = via_auto_init; 457069e52a80SHarald Welte codec->patch_ops.unsol_event = via_unsol_event; 4571d949cac1SHarald Welte #ifdef CONFIG_SND_HDA_POWER_SAVE 4572d949cac1SHarald Welte spec->loopback.amplist = vt1702_loopbacks; 4573d949cac1SHarald Welte #endif 4574d949cac1SHarald Welte 4575d949cac1SHarald Welte return 0; 4576d949cac1SHarald Welte } 4577d949cac1SHarald Welte 4578eb7188caSLydia Wang /* Patch for VT1718S */ 4579eb7188caSLydia Wang 4580eb7188caSLydia Wang /* capture mixer elements */ 4581eb7188caSLydia Wang static struct snd_kcontrol_new vt1718S_capture_mixer[] = { 4582eb7188caSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 4583eb7188caSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 4584eb7188caSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 4585eb7188caSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 4586eb7188caSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 4587eb7188caSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 4588eb7188caSLydia Wang HDA_INPUT), 4589eb7188caSLydia Wang { 4590eb7188caSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 4591eb7188caSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 4592eb7188caSLydia Wang * So call somewhat different.. 4593eb7188caSLydia Wang */ 4594eb7188caSLydia Wang .name = "Input Source", 4595eb7188caSLydia Wang .count = 2, 4596eb7188caSLydia Wang .info = via_mux_enum_info, 4597eb7188caSLydia Wang .get = via_mux_enum_get, 4598eb7188caSLydia Wang .put = via_mux_enum_put, 4599eb7188caSLydia Wang }, 4600eb7188caSLydia Wang { } /* end */ 4601eb7188caSLydia Wang }; 4602eb7188caSLydia Wang 4603eb7188caSLydia Wang static struct hda_verb vt1718S_volume_init_verbs[] = { 4604eb7188caSLydia Wang /* 4605eb7188caSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 4606eb7188caSLydia Wang */ 4607eb7188caSLydia Wang {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4608eb7188caSLydia Wang {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4609eb7188caSLydia Wang 4610eb7188caSLydia Wang 4611eb7188caSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 4612eb7188caSLydia Wang * mixer widget 4613eb7188caSLydia Wang */ 4614eb7188caSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 4615eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 4616eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4617eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 4618eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 4619eb7188caSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, 4620eb7188caSLydia Wang 4621eb7188caSLydia Wang /* Setup default input of Front HP to MW9 */ 4622eb7188caSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 4623eb7188caSLydia Wang /* PW9 PW10 Output enable */ 4624eb7188caSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4625eb7188caSLydia Wang {0x2e, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 4626eb7188caSLydia Wang /* PW11 Input enable */ 4627eb7188caSLydia Wang {0x2f, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_IN_EN}, 4628eb7188caSLydia Wang /* Enable Boost Volume backdoor */ 4629eb7188caSLydia Wang {0x1, 0xf88, 0x8}, 4630eb7188caSLydia Wang /* MW0/1/2/3/4: un-mute index 0 (AOWx), mute index 1 (MW9) */ 4631eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4632eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4633eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4634eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4635eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4636eb7188caSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4637eb7188caSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4638eb7188caSLydia Wang {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4639eb7188caSLydia Wang {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 4640eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 4641eb7188caSLydia Wang /* set MUX1 = 2 (AOW4), MUX2 = 1 (AOW3) */ 4642eb7188caSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0x2}, 4643eb7188caSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0x1}, 4644eb7188caSLydia Wang /* Unmute MW4's index 0 */ 4645eb7188caSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 4646eb7188caSLydia Wang { } 4647eb7188caSLydia Wang }; 4648eb7188caSLydia Wang 4649eb7188caSLydia Wang 4650eb7188caSLydia Wang static struct hda_verb vt1718S_uniwill_init_verbs[] = { 4651eb7188caSLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 4652eb7188caSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 4653eb7188caSLydia Wang {0x24, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4654eb7188caSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4655eb7188caSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4656eb7188caSLydia Wang {0x27, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4657eb7188caSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4658eb7188caSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4659eb7188caSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 4660eb7188caSLydia Wang { } 4661eb7188caSLydia Wang }; 4662eb7188caSLydia Wang 4663eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_playback = { 4664eb7188caSLydia Wang .substreams = 2, 4665eb7188caSLydia Wang .channels_min = 2, 4666eb7188caSLydia Wang .channels_max = 10, 4667eb7188caSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 4668eb7188caSLydia Wang .ops = { 4669eb7188caSLydia Wang .open = via_playback_pcm_open, 4670eb7188caSLydia Wang .prepare = via_playback_multi_pcm_prepare, 4671eb7188caSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 4672eb7188caSLydia Wang .close = via_pcm_open_close, 4673eb7188caSLydia Wang }, 4674eb7188caSLydia Wang }; 4675eb7188caSLydia Wang 4676eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_analog_capture = { 4677eb7188caSLydia Wang .substreams = 2, 4678eb7188caSLydia Wang .channels_min = 2, 4679eb7188caSLydia Wang .channels_max = 2, 4680eb7188caSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 4681eb7188caSLydia Wang .ops = { 4682eb7188caSLydia Wang .open = via_pcm_open_close, 4683eb7188caSLydia Wang .prepare = via_capture_pcm_prepare, 4684eb7188caSLydia Wang .cleanup = via_capture_pcm_cleanup, 4685eb7188caSLydia Wang .close = via_pcm_open_close, 4686eb7188caSLydia Wang }, 4687eb7188caSLydia Wang }; 4688eb7188caSLydia Wang 4689eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_playback = { 4690eb7188caSLydia Wang .substreams = 2, 4691eb7188caSLydia Wang .channels_min = 2, 4692eb7188caSLydia Wang .channels_max = 2, 4693eb7188caSLydia Wang /* NID is set in via_build_pcms */ 4694eb7188caSLydia Wang .ops = { 4695eb7188caSLydia Wang .open = via_dig_playback_pcm_open, 4696eb7188caSLydia Wang .close = via_dig_playback_pcm_close, 4697eb7188caSLydia Wang .prepare = via_dig_playback_pcm_prepare, 4698eb7188caSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 4699eb7188caSLydia Wang }, 4700eb7188caSLydia Wang }; 4701eb7188caSLydia Wang 4702eb7188caSLydia Wang static struct hda_pcm_stream vt1718S_pcm_digital_capture = { 4703eb7188caSLydia Wang .substreams = 1, 4704eb7188caSLydia Wang .channels_min = 2, 4705eb7188caSLydia Wang .channels_max = 2, 4706eb7188caSLydia Wang }; 4707eb7188caSLydia Wang 4708eb7188caSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 4709eb7188caSLydia Wang static int vt1718S_auto_fill_dac_nids(struct via_spec *spec, 4710eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4711eb7188caSLydia Wang { 4712eb7188caSLydia Wang int i; 4713eb7188caSLydia Wang hda_nid_t nid; 4714eb7188caSLydia Wang 4715eb7188caSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 4716eb7188caSLydia Wang 4717eb7188caSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 4718eb7188caSLydia Wang 4719eb7188caSLydia Wang for (i = 0; i < 4; i++) { 4720eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4721eb7188caSLydia Wang if (nid) { 4722eb7188caSLydia Wang /* config dac list */ 4723eb7188caSLydia Wang switch (i) { 4724eb7188caSLydia Wang case AUTO_SEQ_FRONT: 4725eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x8; 4726eb7188caSLydia Wang break; 4727eb7188caSLydia Wang case AUTO_SEQ_CENLFE: 4728eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xa; 4729eb7188caSLydia Wang break; 4730eb7188caSLydia Wang case AUTO_SEQ_SURROUND: 4731eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0x9; 4732eb7188caSLydia Wang break; 4733eb7188caSLydia Wang case AUTO_SEQ_SIDE: 4734eb7188caSLydia Wang spec->multiout.dac_nids[i] = 0xb; 4735eb7188caSLydia Wang break; 4736eb7188caSLydia Wang } 4737eb7188caSLydia Wang } 4738eb7188caSLydia Wang } 4739eb7188caSLydia Wang 4740eb7188caSLydia Wang return 0; 4741eb7188caSLydia Wang } 4742eb7188caSLydia Wang 4743eb7188caSLydia Wang /* add playback controls from the parsed DAC table */ 4744eb7188caSLydia Wang static int vt1718S_auto_create_multi_out_ctls(struct via_spec *spec, 4745eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4746eb7188caSLydia Wang { 4747eb7188caSLydia Wang char name[32]; 4748eb7188caSLydia Wang static const char *chname[4] = { "Front", "Surround", "C/LFE", "Side" }; 4749eb7188caSLydia Wang hda_nid_t nid_vols[] = {0x8, 0x9, 0xa, 0xb}; 4750eb7188caSLydia Wang hda_nid_t nid_mutes[] = {0x24, 0x25, 0x26, 0x27}; 4751eb7188caSLydia Wang hda_nid_t nid, nid_vol, nid_mute = 0; 4752eb7188caSLydia Wang int i, err; 4753eb7188caSLydia Wang 4754eb7188caSLydia Wang for (i = 0; i <= AUTO_SEQ_SIDE; i++) { 4755eb7188caSLydia Wang nid = cfg->line_out_pins[i]; 4756eb7188caSLydia Wang 4757eb7188caSLydia Wang if (!nid) 4758eb7188caSLydia Wang continue; 4759eb7188caSLydia Wang nid_vol = nid_vols[i]; 4760eb7188caSLydia Wang nid_mute = nid_mutes[i]; 4761eb7188caSLydia Wang 4762eb7188caSLydia Wang if (i == AUTO_SEQ_CENLFE) { 4763eb7188caSLydia Wang /* Center/LFE */ 4764eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4765eb7188caSLydia Wang "Center Playback Volume", 4766eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, 4767eb7188caSLydia Wang HDA_OUTPUT)); 4768eb7188caSLydia Wang if (err < 0) 4769eb7188caSLydia Wang return err; 4770eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4771eb7188caSLydia Wang "LFE Playback Volume", 4772eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, 4773eb7188caSLydia Wang HDA_OUTPUT)); 4774eb7188caSLydia Wang if (err < 0) 4775eb7188caSLydia Wang return err; 4776eb7188caSLydia Wang err = via_add_control( 4777eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4778eb7188caSLydia Wang "Center Playback Switch", 4779eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 4780eb7188caSLydia Wang HDA_OUTPUT)); 4781eb7188caSLydia Wang if (err < 0) 4782eb7188caSLydia Wang return err; 4783eb7188caSLydia Wang err = via_add_control( 4784eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 4785eb7188caSLydia Wang "LFE Playback Switch", 4786eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 4787eb7188caSLydia Wang HDA_OUTPUT)); 4788eb7188caSLydia Wang if (err < 0) 4789eb7188caSLydia Wang return err; 4790eb7188caSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 4791eb7188caSLydia Wang /* Front */ 4792eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4793eb7188caSLydia Wang err = via_add_control( 4794eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4795eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4796eb7188caSLydia Wang if (err < 0) 4797eb7188caSLydia Wang return err; 4798eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4799eb7188caSLydia Wang err = via_add_control( 4800eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4801eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4802eb7188caSLydia Wang HDA_OUTPUT)); 4803eb7188caSLydia Wang if (err < 0) 4804eb7188caSLydia Wang return err; 4805eb7188caSLydia Wang } else { 4806eb7188caSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 4807eb7188caSLydia Wang err = via_add_control( 4808eb7188caSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 4809eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 4810eb7188caSLydia Wang if (err < 0) 4811eb7188caSLydia Wang return err; 4812eb7188caSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 4813eb7188caSLydia Wang err = via_add_control( 4814eb7188caSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 4815eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 4816eb7188caSLydia Wang HDA_OUTPUT)); 4817eb7188caSLydia Wang if (err < 0) 4818eb7188caSLydia Wang return err; 4819eb7188caSLydia Wang } 4820eb7188caSLydia Wang } 4821eb7188caSLydia Wang return 0; 4822eb7188caSLydia Wang } 4823eb7188caSLydia Wang 4824eb7188caSLydia Wang static int vt1718S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 4825eb7188caSLydia Wang { 4826eb7188caSLydia Wang int err; 4827eb7188caSLydia Wang 4828eb7188caSLydia Wang if (!pin) 4829eb7188caSLydia Wang return 0; 4830eb7188caSLydia Wang 4831eb7188caSLydia Wang spec->multiout.hp_nid = 0xc; /* AOW4 */ 4832eb7188caSLydia Wang spec->hp_independent_mode_index = 1; 4833eb7188caSLydia Wang 4834eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 4835eb7188caSLydia Wang "Headphone Playback Volume", 4836eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(0xc, 3, 0, HDA_OUTPUT)); 4837eb7188caSLydia Wang if (err < 0) 4838eb7188caSLydia Wang return err; 4839eb7188caSLydia Wang 4840eb7188caSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 4841eb7188caSLydia Wang "Headphone Playback Switch", 4842eb7188caSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 4843eb7188caSLydia Wang if (err < 0) 4844eb7188caSLydia Wang return err; 4845eb7188caSLydia Wang 4846eb7188caSLydia Wang create_hp_imux(spec); 4847eb7188caSLydia Wang return 0; 4848eb7188caSLydia Wang } 4849eb7188caSLydia Wang 4850eb7188caSLydia Wang /* create playback/capture controls for input pins */ 4851eb7188caSLydia Wang static int vt1718S_auto_create_analog_input_ctls(struct via_spec *spec, 4852eb7188caSLydia Wang const struct auto_pin_cfg *cfg) 4853eb7188caSLydia Wang { 4854eb7188caSLydia Wang static char *labels[] = { 4855eb7188caSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 4856eb7188caSLydia Wang }; 4857eb7188caSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 4858eb7188caSLydia Wang int i, err, idx = 0; 4859eb7188caSLydia Wang 4860eb7188caSLydia Wang /* for internal loopback recording select */ 4861eb7188caSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 4862eb7188caSLydia Wang imux->items[imux->num_items].index = 5; 4863eb7188caSLydia Wang imux->num_items++; 4864eb7188caSLydia Wang 4865eb7188caSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 4866eb7188caSLydia Wang if (!cfg->input_pins[i]) 4867eb7188caSLydia Wang continue; 4868eb7188caSLydia Wang 4869eb7188caSLydia Wang switch (cfg->input_pins[i]) { 4870eb7188caSLydia Wang case 0x2b: /* Mic */ 4871eb7188caSLydia Wang idx = 1; 4872eb7188caSLydia Wang break; 4873eb7188caSLydia Wang 4874eb7188caSLydia Wang case 0x2a: /* Line In */ 4875eb7188caSLydia Wang idx = 2; 4876eb7188caSLydia Wang break; 4877eb7188caSLydia Wang 4878eb7188caSLydia Wang case 0x29: /* Front Mic */ 4879eb7188caSLydia Wang idx = 3; 4880eb7188caSLydia Wang break; 4881eb7188caSLydia Wang 4882eb7188caSLydia Wang case 0x2c: /* CD */ 4883eb7188caSLydia Wang idx = 0; 4884eb7188caSLydia Wang break; 4885eb7188caSLydia Wang } 4886eb7188caSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 4887eb7188caSLydia Wang if (err < 0) 4888eb7188caSLydia Wang return err; 4889eb7188caSLydia Wang imux->items[imux->num_items].label = labels[i]; 4890eb7188caSLydia Wang imux->items[imux->num_items].index = idx; 4891eb7188caSLydia Wang imux->num_items++; 4892eb7188caSLydia Wang } 4893eb7188caSLydia Wang return 0; 4894eb7188caSLydia Wang } 4895eb7188caSLydia Wang 4896eb7188caSLydia Wang static int vt1718S_parse_auto_config(struct hda_codec *codec) 4897eb7188caSLydia Wang { 4898eb7188caSLydia Wang struct via_spec *spec = codec->spec; 4899eb7188caSLydia Wang int err; 4900eb7188caSLydia Wang 4901eb7188caSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 4902eb7188caSLydia Wang 4903eb7188caSLydia Wang if (err < 0) 4904eb7188caSLydia Wang return err; 4905eb7188caSLydia Wang err = vt1718S_auto_fill_dac_nids(spec, &spec->autocfg); 4906eb7188caSLydia Wang if (err < 0) 4907eb7188caSLydia Wang return err; 4908eb7188caSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 4909eb7188caSLydia Wang return 0; /* can't find valid BIOS pin config */ 4910eb7188caSLydia Wang 4911eb7188caSLydia Wang err = vt1718S_auto_create_multi_out_ctls(spec, &spec->autocfg); 4912eb7188caSLydia Wang if (err < 0) 4913eb7188caSLydia Wang return err; 4914eb7188caSLydia Wang err = vt1718S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 4915eb7188caSLydia Wang if (err < 0) 4916eb7188caSLydia Wang return err; 4917eb7188caSLydia Wang err = vt1718S_auto_create_analog_input_ctls(spec, &spec->autocfg); 4918eb7188caSLydia Wang if (err < 0) 4919eb7188caSLydia Wang return err; 4920eb7188caSLydia Wang 4921eb7188caSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 4922eb7188caSLydia Wang 4923eb7188caSLydia Wang fill_dig_outs(codec); 4924eb7188caSLydia Wang 4925eb7188caSLydia Wang if (spec->autocfg.dig_in_pin && codec->vendor_id == 0x11060428) 4926eb7188caSLydia Wang spec->dig_in_nid = 0x13; 4927eb7188caSLydia Wang 4928eb7188caSLydia Wang if (spec->kctls.list) 4929eb7188caSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 4930eb7188caSLydia Wang 4931eb7188caSLydia Wang spec->input_mux = &spec->private_imux[0]; 4932eb7188caSLydia Wang 4933eb7188caSLydia Wang if (spec->hp_mux) 49345b0cb1d8SJaroslav Kysela via_hp_build(spec); 4935eb7188caSLydia Wang 49365b0cb1d8SJaroslav Kysela via_smart51_build(spec); 4937eb7188caSLydia Wang 4938eb7188caSLydia Wang return 1; 4939eb7188caSLydia Wang } 4940eb7188caSLydia Wang 4941eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 4942eb7188caSLydia Wang static struct hda_amp_list vt1718S_loopbacks[] = { 4943eb7188caSLydia Wang { 0x21, HDA_INPUT, 1 }, 4944eb7188caSLydia Wang { 0x21, HDA_INPUT, 2 }, 4945eb7188caSLydia Wang { 0x21, HDA_INPUT, 3 }, 4946eb7188caSLydia Wang { 0x21, HDA_INPUT, 4 }, 4947eb7188caSLydia Wang { } /* end */ 4948eb7188caSLydia Wang }; 4949eb7188caSLydia Wang #endif 4950eb7188caSLydia Wang 4951eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec) 4952eb7188caSLydia Wang { 4953eb7188caSLydia Wang struct via_spec *spec; 4954eb7188caSLydia Wang int err; 4955eb7188caSLydia Wang 4956eb7188caSLydia Wang /* create a codec specific record */ 49575b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 4958eb7188caSLydia Wang if (spec == NULL) 4959eb7188caSLydia Wang return -ENOMEM; 4960eb7188caSLydia Wang 4961eb7188caSLydia Wang /* automatic parse from the BIOS config */ 4962eb7188caSLydia Wang err = vt1718S_parse_auto_config(codec); 4963eb7188caSLydia Wang if (err < 0) { 4964eb7188caSLydia Wang via_free(codec); 4965eb7188caSLydia Wang return err; 4966eb7188caSLydia Wang } else if (!err) { 4967eb7188caSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 4968eb7188caSLydia Wang "from BIOS. Using genenic mode...\n"); 4969eb7188caSLydia Wang } 4970eb7188caSLydia Wang 4971eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_volume_init_verbs; 4972eb7188caSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1718S_uniwill_init_verbs; 4973eb7188caSLydia Wang 4974bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4975bb3c6bfcSLydia Wang spec->stream_name_analog = "VT2020 Analog"; 4976bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4977bb3c6bfcSLydia Wang spec->stream_name_analog = "VT1828S Analog"; 4978bb3c6bfcSLydia Wang else 4979eb7188caSLydia Wang spec->stream_name_analog = "VT1718S Analog"; 4980eb7188caSLydia Wang spec->stream_analog_playback = &vt1718S_pcm_analog_playback; 4981eb7188caSLydia Wang spec->stream_analog_capture = &vt1718S_pcm_analog_capture; 4982eb7188caSLydia Wang 4983bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060441) 4984bb3c6bfcSLydia Wang spec->stream_name_digital = "VT2020 Digital"; 4985bb3c6bfcSLydia Wang else if (codec->vendor_id == 0x11064441) 4986bb3c6bfcSLydia Wang spec->stream_name_digital = "VT1828S Digital"; 4987bb3c6bfcSLydia Wang else 4988eb7188caSLydia Wang spec->stream_name_digital = "VT1718S Digital"; 4989eb7188caSLydia Wang spec->stream_digital_playback = &vt1718S_pcm_digital_playback; 4990bb3c6bfcSLydia Wang if (codec->vendor_id == 0x11060428 || codec->vendor_id == 0x11060441) 4991eb7188caSLydia Wang spec->stream_digital_capture = &vt1718S_pcm_digital_capture; 4992eb7188caSLydia Wang 4993eb7188caSLydia Wang if (!spec->adc_nids && spec->input_mux) { 4994eb7188caSLydia Wang spec->adc_nids = vt1718S_adc_nids; 4995eb7188caSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1718S_adc_nids); 4996eb7188caSLydia Wang get_mux_nids(codec); 4997bb3c6bfcSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 4998bb3c6bfcSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 4999eb7188caSLydia Wang spec->mixers[spec->num_mixers] = vt1718S_capture_mixer; 5000eb7188caSLydia Wang spec->num_mixers++; 5001eb7188caSLydia Wang } 5002eb7188caSLydia Wang 5003eb7188caSLydia Wang codec->patch_ops = via_patch_ops; 5004eb7188caSLydia Wang 5005eb7188caSLydia Wang codec->patch_ops.init = via_auto_init; 50060f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5007eb7188caSLydia Wang 5008eb7188caSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5009eb7188caSLydia Wang spec->loopback.amplist = vt1718S_loopbacks; 5010eb7188caSLydia Wang #endif 5011eb7188caSLydia Wang 5012eb7188caSLydia Wang return 0; 5013eb7188caSLydia Wang } 5014f3db423dSLydia Wang 5015f3db423dSLydia Wang /* Patch for VT1716S */ 5016f3db423dSLydia Wang 5017f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol, 5018f3db423dSLydia Wang struct snd_ctl_elem_info *uinfo) 5019f3db423dSLydia Wang { 5020f3db423dSLydia Wang uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; 5021f3db423dSLydia Wang uinfo->count = 1; 5022f3db423dSLydia Wang uinfo->value.integer.min = 0; 5023f3db423dSLydia Wang uinfo->value.integer.max = 1; 5024f3db423dSLydia Wang return 0; 5025f3db423dSLydia Wang } 5026f3db423dSLydia Wang 5027f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol, 5028f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 5029f3db423dSLydia Wang { 5030f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5031f3db423dSLydia Wang int index = 0; 5032f3db423dSLydia Wang 5033f3db423dSLydia Wang index = snd_hda_codec_read(codec, 0x26, 0, 5034f3db423dSLydia Wang AC_VERB_GET_CONNECT_SEL, 0); 5035f3db423dSLydia Wang if (index != -1) 5036f3db423dSLydia Wang *ucontrol->value.integer.value = index; 5037f3db423dSLydia Wang 5038f3db423dSLydia Wang return 0; 5039f3db423dSLydia Wang } 5040f3db423dSLydia Wang 5041f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol, 5042f3db423dSLydia Wang struct snd_ctl_elem_value *ucontrol) 5043f3db423dSLydia Wang { 5044f3db423dSLydia Wang struct hda_codec *codec = snd_kcontrol_chip(kcontrol); 5045f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5046f3db423dSLydia Wang int index = *ucontrol->value.integer.value; 5047f3db423dSLydia Wang 5048f3db423dSLydia Wang snd_hda_codec_write(codec, 0x26, 0, 5049f3db423dSLydia Wang AC_VERB_SET_CONNECT_SEL, index); 5050f3db423dSLydia Wang spec->dmic_enabled = index; 5051f3db423dSLydia Wang set_jack_power_state(codec); 5052f3db423dSLydia Wang 5053f3db423dSLydia Wang return 1; 5054f3db423dSLydia Wang } 5055f3db423dSLydia Wang 5056f3db423dSLydia Wang /* capture mixer elements */ 5057f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_capture_mixer[] = { 5058f3db423dSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x13, 0x0, HDA_INPUT), 5059f3db423dSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x13, 0x0, HDA_INPUT), 5060f3db423dSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x14, 0x0, HDA_INPUT), 5061f3db423dSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x14, 0x0, HDA_INPUT), 5062f3db423dSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x1A, 0x0, HDA_INPUT), 5063f3db423dSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x1E, 0x0, 5064f3db423dSLydia Wang HDA_INPUT), 5065f3db423dSLydia Wang { 5066f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5067f3db423dSLydia Wang .name = "Input Source", 5068f3db423dSLydia Wang .count = 1, 5069f3db423dSLydia Wang .info = via_mux_enum_info, 5070f3db423dSLydia Wang .get = via_mux_enum_get, 5071f3db423dSLydia Wang .put = via_mux_enum_put, 5072f3db423dSLydia Wang }, 5073f3db423dSLydia Wang { } /* end */ 5074f3db423dSLydia Wang }; 5075f3db423dSLydia Wang 5076f3db423dSLydia Wang static struct snd_kcontrol_new vt1716s_dmic_mixer[] = { 5077f3db423dSLydia Wang HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT), 5078f3db423dSLydia Wang { 5079f3db423dSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5080f3db423dSLydia Wang .name = "Digital Mic Capture Switch", 50815b0cb1d8SJaroslav Kysela .subdevice = HDA_SUBDEV_NID_FLAG | 0x26, 5082f3db423dSLydia Wang .count = 1, 5083f3db423dSLydia Wang .info = vt1716s_dmic_info, 5084f3db423dSLydia Wang .get = vt1716s_dmic_get, 5085f3db423dSLydia Wang .put = vt1716s_dmic_put, 5086f3db423dSLydia Wang }, 5087f3db423dSLydia Wang {} /* end */ 5088f3db423dSLydia Wang }; 5089f3db423dSLydia Wang 5090f3db423dSLydia Wang 5091f3db423dSLydia Wang /* mono-out mixer elements */ 5092f3db423dSLydia Wang static struct snd_kcontrol_new vt1716S_mono_out_mixer[] = { 5093f3db423dSLydia Wang HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT), 5094f3db423dSLydia Wang { } /* end */ 5095f3db423dSLydia Wang }; 5096f3db423dSLydia Wang 5097f3db423dSLydia Wang static struct hda_verb vt1716S_volume_init_verbs[] = { 5098f3db423dSLydia Wang /* 5099f3db423dSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5100f3db423dSLydia Wang */ 5101f3db423dSLydia Wang {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5102f3db423dSLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5103f3db423dSLydia Wang 5104f3db423dSLydia Wang 5105f3db423dSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5106f3db423dSLydia Wang * mixer widget 5107f3db423dSLydia Wang */ 5108f3db423dSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5109f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5110f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5111f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5112f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5113f3db423dSLydia Wang {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5114f3db423dSLydia Wang 5115f3db423dSLydia Wang /* MUX Indices: Stereo Mixer = 5 */ 5116f3db423dSLydia Wang {0x17, AC_VERB_SET_CONNECT_SEL, 0x5}, 5117f3db423dSLydia Wang 5118f3db423dSLydia Wang /* Setup default input of PW4 to MW0 */ 5119f3db423dSLydia Wang {0x1d, AC_VERB_SET_CONNECT_SEL, 0x0}, 5120f3db423dSLydia Wang 5121f3db423dSLydia Wang /* Setup default input of SW1 as MW0 */ 5122f3db423dSLydia Wang {0x18, AC_VERB_SET_CONNECT_SEL, 0x1}, 5123f3db423dSLydia Wang 5124f3db423dSLydia Wang /* Setup default input of SW4 as AOW0 */ 5125f3db423dSLydia Wang {0x28, AC_VERB_SET_CONNECT_SEL, 0x1}, 5126f3db423dSLydia Wang 5127f3db423dSLydia Wang /* PW9 PW10 Output enable */ 5128f3db423dSLydia Wang {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5129f3db423dSLydia Wang {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5130f3db423dSLydia Wang 5131f3db423dSLydia Wang /* Unmute SW1, PW12 */ 5132f3db423dSLydia Wang {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5133f3db423dSLydia Wang {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, 5134f3db423dSLydia Wang /* PW12 Output enable */ 5135f3db423dSLydia Wang {0x2a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40}, 5136f3db423dSLydia Wang /* Enable Boost Volume backdoor */ 5137f3db423dSLydia Wang {0x1, 0xf8a, 0x80}, 5138f3db423dSLydia Wang /* don't bybass mixer */ 5139f3db423dSLydia Wang {0x1, 0xf88, 0xc0}, 5140f3db423dSLydia Wang /* Enable mono output */ 5141f3db423dSLydia Wang {0x1, 0xf90, 0x08}, 5142f3db423dSLydia Wang { } 5143f3db423dSLydia Wang }; 5144f3db423dSLydia Wang 5145f3db423dSLydia Wang 5146f3db423dSLydia Wang static struct hda_verb vt1716S_uniwill_init_verbs[] = { 5147f3db423dSLydia Wang {0x1d, AC_VERB_SET_UNSOLICITED_ENABLE, 5148f3db423dSLydia Wang AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT}, 5149f3db423dSLydia Wang {0x19, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5150f3db423dSLydia Wang {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5151f3db423dSLydia Wang {0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5152f3db423dSLydia Wang {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, 5153f3db423dSLydia Wang AC_USRSP_EN | VIA_MONO_EVENT | VIA_JACK_EVENT}, 5154f3db423dSLydia Wang {0x1e, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5155f3db423dSLydia Wang {0x23, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5156f3db423dSLydia Wang { } 5157f3db423dSLydia Wang }; 5158f3db423dSLydia Wang 5159f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_playback = { 5160f3db423dSLydia Wang .substreams = 2, 5161f3db423dSLydia Wang .channels_min = 2, 5162f3db423dSLydia Wang .channels_max = 6, 5163f3db423dSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5164f3db423dSLydia Wang .ops = { 5165f3db423dSLydia Wang .open = via_playback_pcm_open, 5166f3db423dSLydia Wang .prepare = via_playback_multi_pcm_prepare, 5167f3db423dSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5168f3db423dSLydia Wang .close = via_pcm_open_close, 5169f3db423dSLydia Wang }, 5170f3db423dSLydia Wang }; 5171f3db423dSLydia Wang 5172f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_analog_capture = { 5173f3db423dSLydia Wang .substreams = 2, 5174f3db423dSLydia Wang .channels_min = 2, 5175f3db423dSLydia Wang .channels_max = 2, 5176f3db423dSLydia Wang .nid = 0x13, /* NID to query formats and rates */ 5177f3db423dSLydia Wang .ops = { 5178f3db423dSLydia Wang .open = via_pcm_open_close, 5179f3db423dSLydia Wang .prepare = via_capture_pcm_prepare, 5180f3db423dSLydia Wang .cleanup = via_capture_pcm_cleanup, 5181f3db423dSLydia Wang .close = via_pcm_open_close, 5182f3db423dSLydia Wang }, 5183f3db423dSLydia Wang }; 5184f3db423dSLydia Wang 5185f3db423dSLydia Wang static struct hda_pcm_stream vt1716S_pcm_digital_playback = { 5186f3db423dSLydia Wang .substreams = 2, 5187f3db423dSLydia Wang .channels_min = 2, 5188f3db423dSLydia Wang .channels_max = 2, 5189f3db423dSLydia Wang /* NID is set in via_build_pcms */ 5190f3db423dSLydia Wang .ops = { 5191f3db423dSLydia Wang .open = via_dig_playback_pcm_open, 5192f3db423dSLydia Wang .close = via_dig_playback_pcm_close, 5193f3db423dSLydia Wang .prepare = via_dig_playback_pcm_prepare, 5194f3db423dSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5195f3db423dSLydia Wang }, 5196f3db423dSLydia Wang }; 5197f3db423dSLydia Wang 5198f3db423dSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5199f3db423dSLydia Wang static int vt1716S_auto_fill_dac_nids(struct via_spec *spec, 5200f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5201f3db423dSLydia Wang { int i; 5202f3db423dSLydia Wang hda_nid_t nid; 5203f3db423dSLydia Wang 5204f3db423dSLydia Wang spec->multiout.num_dacs = cfg->line_outs; 5205f3db423dSLydia Wang 5206f3db423dSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5207f3db423dSLydia Wang 5208f3db423dSLydia Wang for (i = 0; i < 3; i++) { 5209f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5210f3db423dSLydia Wang if (nid) { 5211f3db423dSLydia Wang /* config dac list */ 5212f3db423dSLydia Wang switch (i) { 5213f3db423dSLydia Wang case AUTO_SEQ_FRONT: 5214f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x10; 5215f3db423dSLydia Wang break; 5216f3db423dSLydia Wang case AUTO_SEQ_CENLFE: 5217f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x25; 5218f3db423dSLydia Wang break; 5219f3db423dSLydia Wang case AUTO_SEQ_SURROUND: 5220f3db423dSLydia Wang spec->multiout.dac_nids[i] = 0x11; 5221f3db423dSLydia Wang break; 5222f3db423dSLydia Wang } 5223f3db423dSLydia Wang } 5224f3db423dSLydia Wang } 5225f3db423dSLydia Wang 5226f3db423dSLydia Wang return 0; 5227f3db423dSLydia Wang } 5228f3db423dSLydia Wang 5229f3db423dSLydia Wang /* add playback controls from the parsed DAC table */ 5230f3db423dSLydia Wang static int vt1716S_auto_create_multi_out_ctls(struct via_spec *spec, 5231f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5232f3db423dSLydia Wang { 5233f3db423dSLydia Wang char name[32]; 5234f3db423dSLydia Wang static const char *chname[3] = { "Front", "Surround", "C/LFE" }; 5235f3db423dSLydia Wang hda_nid_t nid_vols[] = {0x10, 0x11, 0x25}; 5236f3db423dSLydia Wang hda_nid_t nid_mutes[] = {0x1C, 0x18, 0x27}; 5237f3db423dSLydia Wang hda_nid_t nid, nid_vol, nid_mute; 5238f3db423dSLydia Wang int i, err; 5239f3db423dSLydia Wang 5240f3db423dSLydia Wang for (i = 0; i <= AUTO_SEQ_CENLFE; i++) { 5241f3db423dSLydia Wang nid = cfg->line_out_pins[i]; 5242f3db423dSLydia Wang 5243f3db423dSLydia Wang if (!nid) 5244f3db423dSLydia Wang continue; 5245f3db423dSLydia Wang 5246f3db423dSLydia Wang nid_vol = nid_vols[i]; 5247f3db423dSLydia Wang nid_mute = nid_mutes[i]; 5248f3db423dSLydia Wang 5249f3db423dSLydia Wang if (i == AUTO_SEQ_CENLFE) { 5250f3db423dSLydia Wang err = via_add_control( 5251f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5252f3db423dSLydia Wang "Center Playback Volume", 5253f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 1, 0, HDA_OUTPUT)); 5254f3db423dSLydia Wang if (err < 0) 5255f3db423dSLydia Wang return err; 5256f3db423dSLydia Wang err = via_add_control( 5257f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5258f3db423dSLydia Wang "LFE Playback Volume", 5259f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 2, 0, HDA_OUTPUT)); 5260f3db423dSLydia Wang if (err < 0) 5261f3db423dSLydia Wang return err; 5262f3db423dSLydia Wang err = via_add_control( 5263f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5264f3db423dSLydia Wang "Center Playback Switch", 5265f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 1, 0, 5266f3db423dSLydia Wang HDA_OUTPUT)); 5267f3db423dSLydia Wang if (err < 0) 5268f3db423dSLydia Wang return err; 5269f3db423dSLydia Wang err = via_add_control( 5270f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5271f3db423dSLydia Wang "LFE Playback Switch", 5272f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 2, 0, 5273f3db423dSLydia Wang HDA_OUTPUT)); 5274f3db423dSLydia Wang if (err < 0) 5275f3db423dSLydia Wang return err; 5276f3db423dSLydia Wang } else if (i == AUTO_SEQ_FRONT) { 5277f3db423dSLydia Wang 5278f3db423dSLydia Wang err = via_add_control( 5279f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, 5280f3db423dSLydia Wang "Master Front Playback Volume", 5281f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5282f3db423dSLydia Wang if (err < 0) 5283f3db423dSLydia Wang return err; 5284f3db423dSLydia Wang err = via_add_control( 5285f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, 5286f3db423dSLydia Wang "Master Front Playback Switch", 5287f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x16, 3, 0, HDA_INPUT)); 5288f3db423dSLydia Wang if (err < 0) 5289f3db423dSLydia Wang return err; 5290f3db423dSLydia Wang 5291f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5292f3db423dSLydia Wang err = via_add_control( 5293f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5294f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5295f3db423dSLydia Wang if (err < 0) 5296f3db423dSLydia Wang return err; 5297f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5298f3db423dSLydia Wang err = via_add_control( 5299f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5300f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5301f3db423dSLydia Wang HDA_OUTPUT)); 5302f3db423dSLydia Wang if (err < 0) 5303f3db423dSLydia Wang return err; 5304f3db423dSLydia Wang } else { 5305f3db423dSLydia Wang sprintf(name, "%s Playback Volume", chname[i]); 5306f3db423dSLydia Wang err = via_add_control( 5307f3db423dSLydia Wang spec, VIA_CTL_WIDGET_VOL, name, 5308f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_vol, 3, 0, HDA_OUTPUT)); 5309f3db423dSLydia Wang if (err < 0) 5310f3db423dSLydia Wang return err; 5311f3db423dSLydia Wang sprintf(name, "%s Playback Switch", chname[i]); 5312f3db423dSLydia Wang err = via_add_control( 5313f3db423dSLydia Wang spec, VIA_CTL_WIDGET_MUTE, name, 5314f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(nid_mute, 3, 0, 5315f3db423dSLydia Wang HDA_OUTPUT)); 5316f3db423dSLydia Wang if (err < 0) 5317f3db423dSLydia Wang return err; 5318f3db423dSLydia Wang } 5319f3db423dSLydia Wang } 5320f3db423dSLydia Wang return 0; 5321f3db423dSLydia Wang } 5322f3db423dSLydia Wang 5323f3db423dSLydia Wang static int vt1716S_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 5324f3db423dSLydia Wang { 5325f3db423dSLydia Wang int err; 5326f3db423dSLydia Wang 5327f3db423dSLydia Wang if (!pin) 5328f3db423dSLydia Wang return 0; 5329f3db423dSLydia Wang 5330f3db423dSLydia Wang spec->multiout.hp_nid = 0x25; /* AOW3 */ 5331f3db423dSLydia Wang spec->hp_independent_mode_index = 1; 5332f3db423dSLydia Wang 5333f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 5334f3db423dSLydia Wang "Headphone Playback Volume", 5335f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 5336f3db423dSLydia Wang if (err < 0) 5337f3db423dSLydia Wang return err; 5338f3db423dSLydia Wang 5339f3db423dSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 5340f3db423dSLydia Wang "Headphone Playback Switch", 5341f3db423dSLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 5342f3db423dSLydia Wang if (err < 0) 5343f3db423dSLydia Wang return err; 5344f3db423dSLydia Wang 5345f3db423dSLydia Wang create_hp_imux(spec); 5346f3db423dSLydia Wang return 0; 5347f3db423dSLydia Wang } 5348f3db423dSLydia Wang 5349f3db423dSLydia Wang /* create playback/capture controls for input pins */ 5350f3db423dSLydia Wang static int vt1716S_auto_create_analog_input_ctls(struct via_spec *spec, 5351f3db423dSLydia Wang const struct auto_pin_cfg *cfg) 5352f3db423dSLydia Wang { 5353f3db423dSLydia Wang static char *labels[] = { 5354f3db423dSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 5355f3db423dSLydia Wang }; 5356f3db423dSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 5357f3db423dSLydia Wang int i, err, idx = 0; 5358f3db423dSLydia Wang 5359f3db423dSLydia Wang /* for internal loopback recording select */ 5360f3db423dSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 5361f3db423dSLydia Wang imux->items[imux->num_items].index = 5; 5362f3db423dSLydia Wang imux->num_items++; 5363f3db423dSLydia Wang 5364f3db423dSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 5365f3db423dSLydia Wang if (!cfg->input_pins[i]) 5366f3db423dSLydia Wang continue; 5367f3db423dSLydia Wang 5368f3db423dSLydia Wang switch (cfg->input_pins[i]) { 5369f3db423dSLydia Wang case 0x1a: /* Mic */ 5370f3db423dSLydia Wang idx = 2; 5371f3db423dSLydia Wang break; 5372f3db423dSLydia Wang 5373f3db423dSLydia Wang case 0x1b: /* Line In */ 5374f3db423dSLydia Wang idx = 3; 5375f3db423dSLydia Wang break; 5376f3db423dSLydia Wang 5377f3db423dSLydia Wang case 0x1e: /* Front Mic */ 5378f3db423dSLydia Wang idx = 4; 5379f3db423dSLydia Wang break; 5380f3db423dSLydia Wang 5381f3db423dSLydia Wang case 0x1f: /* CD */ 5382f3db423dSLydia Wang idx = 1; 5383f3db423dSLydia Wang break; 5384f3db423dSLydia Wang } 5385f3db423dSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x16); 5386f3db423dSLydia Wang if (err < 0) 5387f3db423dSLydia Wang return err; 5388f3db423dSLydia Wang imux->items[imux->num_items].label = labels[i]; 5389f3db423dSLydia Wang imux->items[imux->num_items].index = idx-1; 5390f3db423dSLydia Wang imux->num_items++; 5391f3db423dSLydia Wang } 5392f3db423dSLydia Wang return 0; 5393f3db423dSLydia Wang } 5394f3db423dSLydia Wang 5395f3db423dSLydia Wang static int vt1716S_parse_auto_config(struct hda_codec *codec) 5396f3db423dSLydia Wang { 5397f3db423dSLydia Wang struct via_spec *spec = codec->spec; 5398f3db423dSLydia Wang int err; 5399f3db423dSLydia Wang 5400f3db423dSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 5401f3db423dSLydia Wang if (err < 0) 5402f3db423dSLydia Wang return err; 5403f3db423dSLydia Wang err = vt1716S_auto_fill_dac_nids(spec, &spec->autocfg); 5404f3db423dSLydia Wang if (err < 0) 5405f3db423dSLydia Wang return err; 5406f3db423dSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 5407f3db423dSLydia Wang return 0; /* can't find valid BIOS pin config */ 5408f3db423dSLydia Wang 5409f3db423dSLydia Wang err = vt1716S_auto_create_multi_out_ctls(spec, &spec->autocfg); 5410f3db423dSLydia Wang if (err < 0) 5411f3db423dSLydia Wang return err; 5412f3db423dSLydia Wang err = vt1716S_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 5413f3db423dSLydia Wang if (err < 0) 5414f3db423dSLydia Wang return err; 5415f3db423dSLydia Wang err = vt1716S_auto_create_analog_input_ctls(spec, &spec->autocfg); 5416f3db423dSLydia Wang if (err < 0) 5417f3db423dSLydia Wang return err; 5418f3db423dSLydia Wang 5419f3db423dSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 5420f3db423dSLydia Wang 5421f3db423dSLydia Wang fill_dig_outs(codec); 5422f3db423dSLydia Wang 5423f3db423dSLydia Wang if (spec->kctls.list) 5424f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 5425f3db423dSLydia Wang 5426f3db423dSLydia Wang spec->input_mux = &spec->private_imux[0]; 5427f3db423dSLydia Wang 5428f3db423dSLydia Wang if (spec->hp_mux) 54295b0cb1d8SJaroslav Kysela via_hp_build(spec); 5430f3db423dSLydia Wang 54315b0cb1d8SJaroslav Kysela via_smart51_build(spec); 5432f3db423dSLydia Wang 5433f3db423dSLydia Wang return 1; 5434f3db423dSLydia Wang } 5435f3db423dSLydia Wang 5436f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5437f3db423dSLydia Wang static struct hda_amp_list vt1716S_loopbacks[] = { 5438f3db423dSLydia Wang { 0x16, HDA_INPUT, 1 }, 5439f3db423dSLydia Wang { 0x16, HDA_INPUT, 2 }, 5440f3db423dSLydia Wang { 0x16, HDA_INPUT, 3 }, 5441f3db423dSLydia Wang { 0x16, HDA_INPUT, 4 }, 5442f3db423dSLydia Wang { } /* end */ 5443f3db423dSLydia Wang }; 5444f3db423dSLydia Wang #endif 5445f3db423dSLydia Wang 5446f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec) 5447f3db423dSLydia Wang { 5448f3db423dSLydia Wang struct via_spec *spec; 5449f3db423dSLydia Wang int err; 5450f3db423dSLydia Wang 5451f3db423dSLydia Wang /* create a codec specific record */ 54525b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 5453f3db423dSLydia Wang if (spec == NULL) 5454f3db423dSLydia Wang return -ENOMEM; 5455f3db423dSLydia Wang 5456f3db423dSLydia Wang /* automatic parse from the BIOS config */ 5457f3db423dSLydia Wang err = vt1716S_parse_auto_config(codec); 5458f3db423dSLydia Wang if (err < 0) { 5459f3db423dSLydia Wang via_free(codec); 5460f3db423dSLydia Wang return err; 5461f3db423dSLydia Wang } else if (!err) { 5462f3db423dSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 5463f3db423dSLydia Wang "from BIOS. Using genenic mode...\n"); 5464f3db423dSLydia Wang } 5465f3db423dSLydia Wang 5466f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_volume_init_verbs; 5467f3db423dSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1716S_uniwill_init_verbs; 5468f3db423dSLydia Wang 5469f3db423dSLydia Wang spec->stream_name_analog = "VT1716S Analog"; 5470f3db423dSLydia Wang spec->stream_analog_playback = &vt1716S_pcm_analog_playback; 5471f3db423dSLydia Wang spec->stream_analog_capture = &vt1716S_pcm_analog_capture; 5472f3db423dSLydia Wang 5473f3db423dSLydia Wang spec->stream_name_digital = "VT1716S Digital"; 5474f3db423dSLydia Wang spec->stream_digital_playback = &vt1716S_pcm_digital_playback; 5475f3db423dSLydia Wang 5476f3db423dSLydia Wang if (!spec->adc_nids && spec->input_mux) { 5477f3db423dSLydia Wang spec->adc_nids = vt1716S_adc_nids; 5478f3db423dSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1716S_adc_nids); 5479f3db423dSLydia Wang get_mux_nids(codec); 5480f3db423dSLydia Wang override_mic_boost(codec, 0x1a, 0, 3, 40); 5481f3db423dSLydia Wang override_mic_boost(codec, 0x1e, 0, 3, 40); 5482f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716S_capture_mixer; 5483f3db423dSLydia Wang spec->num_mixers++; 5484f3db423dSLydia Wang } 5485f3db423dSLydia Wang 5486f3db423dSLydia Wang spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer; 5487f3db423dSLydia Wang spec->num_mixers++; 5488f3db423dSLydia Wang 5489f3db423dSLydia Wang spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer; 5490f3db423dSLydia Wang 5491f3db423dSLydia Wang codec->patch_ops = via_patch_ops; 5492f3db423dSLydia Wang 5493f3db423dSLydia Wang codec->patch_ops.init = via_auto_init; 54940f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 5495f3db423dSLydia Wang 5496f3db423dSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 5497f3db423dSLydia Wang spec->loopback.amplist = vt1716S_loopbacks; 5498f3db423dSLydia Wang #endif 5499f3db423dSLydia Wang 5500f3db423dSLydia Wang return 0; 5501f3db423dSLydia Wang } 550225eaba2fSLydia Wang 550325eaba2fSLydia Wang /* for vt2002P */ 550425eaba2fSLydia Wang 550525eaba2fSLydia Wang /* capture mixer elements */ 550625eaba2fSLydia Wang static struct snd_kcontrol_new vt2002P_capture_mixer[] = { 550725eaba2fSLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 550825eaba2fSLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 550925eaba2fSLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 551025eaba2fSLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 551125eaba2fSLydia Wang HDA_CODEC_VOLUME("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 551225eaba2fSLydia Wang HDA_CODEC_VOLUME("Front Mic Boost Capture Volume", 0x29, 0x0, 551325eaba2fSLydia Wang HDA_INPUT), 551425eaba2fSLydia Wang { 551525eaba2fSLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 551625eaba2fSLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 551725eaba2fSLydia Wang * So call somewhat different.. 551825eaba2fSLydia Wang */ 551925eaba2fSLydia Wang /* .name = "Capture Source", */ 552025eaba2fSLydia Wang .name = "Input Source", 552125eaba2fSLydia Wang .count = 2, 552225eaba2fSLydia Wang .info = via_mux_enum_info, 552325eaba2fSLydia Wang .get = via_mux_enum_get, 552425eaba2fSLydia Wang .put = via_mux_enum_put, 552525eaba2fSLydia Wang }, 552625eaba2fSLydia Wang { } /* end */ 552725eaba2fSLydia Wang }; 552825eaba2fSLydia Wang 552925eaba2fSLydia Wang static struct hda_verb vt2002P_volume_init_verbs[] = { 553025eaba2fSLydia Wang /* 553125eaba2fSLydia Wang * Unmute ADC0-1 and set the default input to mic-in 553225eaba2fSLydia Wang */ 553325eaba2fSLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 553425eaba2fSLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 553525eaba2fSLydia Wang 553625eaba2fSLydia Wang 553725eaba2fSLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 553825eaba2fSLydia Wang * mixer widget 553925eaba2fSLydia Wang */ 554025eaba2fSLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 554125eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 554225eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 554325eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 554425eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 554525eaba2fSLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 554625eaba2fSLydia Wang 554725eaba2fSLydia Wang /* MUX Indices: Mic = 0 */ 554825eaba2fSLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 554925eaba2fSLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 555025eaba2fSLydia Wang 555125eaba2fSLydia Wang /* PW9 Output enable */ 555225eaba2fSLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 555325eaba2fSLydia Wang 555425eaba2fSLydia Wang /* Enable Boost Volume backdoor */ 555525eaba2fSLydia Wang {0x1, 0xfb9, 0x24}, 555625eaba2fSLydia Wang 555725eaba2fSLydia Wang /* MW0/1/4/8: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 555825eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 555925eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 556025eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 556125eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 556225eaba2fSLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 556325eaba2fSLydia Wang {0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 556425eaba2fSLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 556525eaba2fSLydia Wang {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 556625eaba2fSLydia Wang 556725eaba2fSLydia Wang /* set MUX0/1/4/8 = 0 (AOW0) */ 556825eaba2fSLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 556925eaba2fSLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 557025eaba2fSLydia Wang {0x37, AC_VERB_SET_CONNECT_SEL, 0}, 557125eaba2fSLydia Wang {0x3b, AC_VERB_SET_CONNECT_SEL, 0}, 557225eaba2fSLydia Wang 557325eaba2fSLydia Wang /* set PW0 index=0 (MW0) */ 557425eaba2fSLydia Wang {0x24, AC_VERB_SET_CONNECT_SEL, 0}, 557525eaba2fSLydia Wang 557625eaba2fSLydia Wang /* Enable AOW0 to MW9 */ 557725eaba2fSLydia Wang {0x1, 0xfb8, 0x88}, 557825eaba2fSLydia Wang { } 557925eaba2fSLydia Wang }; 558025eaba2fSLydia Wang 558125eaba2fSLydia Wang 558225eaba2fSLydia Wang static struct hda_verb vt2002P_uniwill_init_verbs[] = { 558325eaba2fSLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, 558425eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 558525eaba2fSLydia Wang {0x26, AC_VERB_SET_UNSOLICITED_ENABLE, 558625eaba2fSLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 558725eaba2fSLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 558825eaba2fSLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 558925eaba2fSLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 559025eaba2fSLydia Wang { } 559125eaba2fSLydia Wang }; 559225eaba2fSLydia Wang 559325eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_playback = { 559425eaba2fSLydia Wang .substreams = 2, 559525eaba2fSLydia Wang .channels_min = 2, 559625eaba2fSLydia Wang .channels_max = 2, 559725eaba2fSLydia Wang .nid = 0x8, /* NID to query formats and rates */ 559825eaba2fSLydia Wang .ops = { 559925eaba2fSLydia Wang .open = via_playback_pcm_open, 560025eaba2fSLydia Wang .prepare = via_playback_multi_pcm_prepare, 560125eaba2fSLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 560225eaba2fSLydia Wang .close = via_pcm_open_close, 560325eaba2fSLydia Wang }, 560425eaba2fSLydia Wang }; 560525eaba2fSLydia Wang 560625eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_analog_capture = { 560725eaba2fSLydia Wang .substreams = 2, 560825eaba2fSLydia Wang .channels_min = 2, 560925eaba2fSLydia Wang .channels_max = 2, 561025eaba2fSLydia Wang .nid = 0x10, /* NID to query formats and rates */ 561125eaba2fSLydia Wang .ops = { 561225eaba2fSLydia Wang .open = via_pcm_open_close, 561325eaba2fSLydia Wang .prepare = via_capture_pcm_prepare, 561425eaba2fSLydia Wang .cleanup = via_capture_pcm_cleanup, 561525eaba2fSLydia Wang .close = via_pcm_open_close, 561625eaba2fSLydia Wang }, 561725eaba2fSLydia Wang }; 561825eaba2fSLydia Wang 561925eaba2fSLydia Wang static struct hda_pcm_stream vt2002P_pcm_digital_playback = { 562025eaba2fSLydia Wang .substreams = 1, 562125eaba2fSLydia Wang .channels_min = 2, 562225eaba2fSLydia Wang .channels_max = 2, 562325eaba2fSLydia Wang /* NID is set in via_build_pcms */ 562425eaba2fSLydia Wang .ops = { 562525eaba2fSLydia Wang .open = via_dig_playback_pcm_open, 562625eaba2fSLydia Wang .close = via_dig_playback_pcm_close, 562725eaba2fSLydia Wang .prepare = via_dig_playback_pcm_prepare, 562825eaba2fSLydia Wang .cleanup = via_dig_playback_pcm_cleanup 562925eaba2fSLydia Wang }, 563025eaba2fSLydia Wang }; 563125eaba2fSLydia Wang 563225eaba2fSLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 563325eaba2fSLydia Wang static int vt2002P_auto_fill_dac_nids(struct via_spec *spec, 563425eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 563525eaba2fSLydia Wang { 563625eaba2fSLydia Wang spec->multiout.num_dacs = 1; 563725eaba2fSLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 563825eaba2fSLydia Wang if (cfg->line_out_pins[0]) 563925eaba2fSLydia Wang spec->multiout.dac_nids[0] = 0x8; 564025eaba2fSLydia Wang return 0; 564125eaba2fSLydia Wang } 564225eaba2fSLydia Wang 564325eaba2fSLydia Wang /* add playback controls from the parsed DAC table */ 564425eaba2fSLydia Wang static int vt2002P_auto_create_multi_out_ctls(struct via_spec *spec, 564525eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 564625eaba2fSLydia Wang { 564725eaba2fSLydia Wang int err; 564825eaba2fSLydia Wang 564925eaba2fSLydia Wang if (!cfg->line_out_pins[0]) 565025eaba2fSLydia Wang return -1; 565125eaba2fSLydia Wang 565225eaba2fSLydia Wang 565325eaba2fSLydia Wang /* Line-Out: PortE */ 565425eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 565525eaba2fSLydia Wang "Master Front Playback Volume", 565625eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 565725eaba2fSLydia Wang if (err < 0) 565825eaba2fSLydia Wang return err; 565925eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 566025eaba2fSLydia Wang "Master Front Playback Switch", 566125eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x26, 3, 0, HDA_OUTPUT)); 566225eaba2fSLydia Wang if (err < 0) 566325eaba2fSLydia Wang return err; 566425eaba2fSLydia Wang 566525eaba2fSLydia Wang return 0; 566625eaba2fSLydia Wang } 566725eaba2fSLydia Wang 566825eaba2fSLydia Wang static int vt2002P_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 566925eaba2fSLydia Wang { 567025eaba2fSLydia Wang int err; 567125eaba2fSLydia Wang 567225eaba2fSLydia Wang if (!pin) 567325eaba2fSLydia Wang return 0; 567425eaba2fSLydia Wang 567525eaba2fSLydia Wang spec->multiout.hp_nid = 0x9; 567625eaba2fSLydia Wang spec->hp_independent_mode_index = 1; 567725eaba2fSLydia Wang 567825eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 567925eaba2fSLydia Wang "Headphone Playback Volume", 568025eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL( 568125eaba2fSLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 568225eaba2fSLydia Wang if (err < 0) 568325eaba2fSLydia Wang return err; 568425eaba2fSLydia Wang 568525eaba2fSLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 568625eaba2fSLydia Wang "Headphone Playback Switch", 568725eaba2fSLydia Wang HDA_COMPOSE_AMP_VAL(0x25, 3, 0, HDA_OUTPUT)); 568825eaba2fSLydia Wang if (err < 0) 568925eaba2fSLydia Wang return err; 569025eaba2fSLydia Wang 569125eaba2fSLydia Wang create_hp_imux(spec); 569225eaba2fSLydia Wang return 0; 569325eaba2fSLydia Wang } 569425eaba2fSLydia Wang 569525eaba2fSLydia Wang /* create playback/capture controls for input pins */ 569625eaba2fSLydia Wang static int vt2002P_auto_create_analog_input_ctls(struct via_spec *spec, 569725eaba2fSLydia Wang const struct auto_pin_cfg *cfg) 569825eaba2fSLydia Wang { 569925eaba2fSLydia Wang static char *labels[] = { 570025eaba2fSLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 570125eaba2fSLydia Wang }; 570225eaba2fSLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 570325eaba2fSLydia Wang int i, err, idx = 0; 570425eaba2fSLydia Wang 570525eaba2fSLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 570625eaba2fSLydia Wang if (!cfg->input_pins[i]) 570725eaba2fSLydia Wang continue; 570825eaba2fSLydia Wang 570925eaba2fSLydia Wang switch (cfg->input_pins[i]) { 571025eaba2fSLydia Wang case 0x2b: /* Mic */ 571125eaba2fSLydia Wang idx = 0; 571225eaba2fSLydia Wang break; 571325eaba2fSLydia Wang 571425eaba2fSLydia Wang case 0x2a: /* Line In */ 571525eaba2fSLydia Wang idx = 1; 571625eaba2fSLydia Wang break; 571725eaba2fSLydia Wang 571825eaba2fSLydia Wang case 0x29: /* Front Mic */ 571925eaba2fSLydia Wang idx = 2; 572025eaba2fSLydia Wang break; 572125eaba2fSLydia Wang } 572225eaba2fSLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 572325eaba2fSLydia Wang if (err < 0) 572425eaba2fSLydia Wang return err; 572525eaba2fSLydia Wang imux->items[imux->num_items].label = labels[i]; 572625eaba2fSLydia Wang imux->items[imux->num_items].index = idx; 572725eaba2fSLydia Wang imux->num_items++; 572825eaba2fSLydia Wang } 572925eaba2fSLydia Wang 573025eaba2fSLydia Wang /* build volume/mute control of loopback */ 573125eaba2fSLydia Wang err = via_new_analog_input(spec, "Stereo Mixer", 3, 0x21); 573225eaba2fSLydia Wang if (err < 0) 573325eaba2fSLydia Wang return err; 573425eaba2fSLydia Wang 573525eaba2fSLydia Wang /* for internal loopback recording select */ 573625eaba2fSLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 573725eaba2fSLydia Wang imux->items[imux->num_items].index = 3; 573825eaba2fSLydia Wang imux->num_items++; 573925eaba2fSLydia Wang 574025eaba2fSLydia Wang /* for digital mic select */ 574125eaba2fSLydia Wang imux->items[imux->num_items].label = "Digital Mic"; 574225eaba2fSLydia Wang imux->items[imux->num_items].index = 4; 574325eaba2fSLydia Wang imux->num_items++; 574425eaba2fSLydia Wang 574525eaba2fSLydia Wang return 0; 574625eaba2fSLydia Wang } 574725eaba2fSLydia Wang 574825eaba2fSLydia Wang static int vt2002P_parse_auto_config(struct hda_codec *codec) 574925eaba2fSLydia Wang { 575025eaba2fSLydia Wang struct via_spec *spec = codec->spec; 575125eaba2fSLydia Wang int err; 575225eaba2fSLydia Wang 575325eaba2fSLydia Wang 575425eaba2fSLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 575525eaba2fSLydia Wang if (err < 0) 575625eaba2fSLydia Wang return err; 575725eaba2fSLydia Wang 575825eaba2fSLydia Wang err = vt2002P_auto_fill_dac_nids(spec, &spec->autocfg); 575925eaba2fSLydia Wang if (err < 0) 576025eaba2fSLydia Wang return err; 576125eaba2fSLydia Wang 576225eaba2fSLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0]) 576325eaba2fSLydia Wang return 0; /* can't find valid BIOS pin config */ 576425eaba2fSLydia Wang 576525eaba2fSLydia Wang err = vt2002P_auto_create_multi_out_ctls(spec, &spec->autocfg); 576625eaba2fSLydia Wang if (err < 0) 576725eaba2fSLydia Wang return err; 576825eaba2fSLydia Wang err = vt2002P_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 576925eaba2fSLydia Wang if (err < 0) 577025eaba2fSLydia Wang return err; 577125eaba2fSLydia Wang err = vt2002P_auto_create_analog_input_ctls(spec, &spec->autocfg); 577225eaba2fSLydia Wang if (err < 0) 577325eaba2fSLydia Wang return err; 577425eaba2fSLydia Wang 577525eaba2fSLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 577625eaba2fSLydia Wang 577725eaba2fSLydia Wang fill_dig_outs(codec); 577825eaba2fSLydia Wang 577925eaba2fSLydia Wang if (spec->kctls.list) 578025eaba2fSLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 578125eaba2fSLydia Wang 578225eaba2fSLydia Wang spec->input_mux = &spec->private_imux[0]; 578325eaba2fSLydia Wang 578425eaba2fSLydia Wang if (spec->hp_mux) 57855b0cb1d8SJaroslav Kysela via_hp_build(spec); 578625eaba2fSLydia Wang 578725eaba2fSLydia Wang return 1; 578825eaba2fSLydia Wang } 578925eaba2fSLydia Wang 579025eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 579125eaba2fSLydia Wang static struct hda_amp_list vt2002P_loopbacks[] = { 579225eaba2fSLydia Wang { 0x21, HDA_INPUT, 0 }, 579325eaba2fSLydia Wang { 0x21, HDA_INPUT, 1 }, 579425eaba2fSLydia Wang { 0x21, HDA_INPUT, 2 }, 579525eaba2fSLydia Wang { } /* end */ 579625eaba2fSLydia Wang }; 579725eaba2fSLydia Wang #endif 579825eaba2fSLydia Wang 579925eaba2fSLydia Wang 580025eaba2fSLydia Wang /* patch for vt2002P */ 580125eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec) 580225eaba2fSLydia Wang { 580325eaba2fSLydia Wang struct via_spec *spec; 580425eaba2fSLydia Wang int err; 580525eaba2fSLydia Wang 580625eaba2fSLydia Wang /* create a codec specific record */ 58075b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 580825eaba2fSLydia Wang if (spec == NULL) 580925eaba2fSLydia Wang return -ENOMEM; 581025eaba2fSLydia Wang 581125eaba2fSLydia Wang /* automatic parse from the BIOS config */ 581225eaba2fSLydia Wang err = vt2002P_parse_auto_config(codec); 581325eaba2fSLydia Wang if (err < 0) { 581425eaba2fSLydia Wang via_free(codec); 581525eaba2fSLydia Wang return err; 581625eaba2fSLydia Wang } else if (!err) { 581725eaba2fSLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 581825eaba2fSLydia Wang "from BIOS. Using genenic mode...\n"); 581925eaba2fSLydia Wang } 582025eaba2fSLydia Wang 582125eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_volume_init_verbs; 582225eaba2fSLydia Wang spec->init_verbs[spec->num_iverbs++] = vt2002P_uniwill_init_verbs; 582325eaba2fSLydia Wang 582425eaba2fSLydia Wang spec->stream_name_analog = "VT2002P Analog"; 582525eaba2fSLydia Wang spec->stream_analog_playback = &vt2002P_pcm_analog_playback; 582625eaba2fSLydia Wang spec->stream_analog_capture = &vt2002P_pcm_analog_capture; 582725eaba2fSLydia Wang 582825eaba2fSLydia Wang spec->stream_name_digital = "VT2002P Digital"; 582925eaba2fSLydia Wang spec->stream_digital_playback = &vt2002P_pcm_digital_playback; 583025eaba2fSLydia Wang 583125eaba2fSLydia Wang if (!spec->adc_nids && spec->input_mux) { 583225eaba2fSLydia Wang spec->adc_nids = vt2002P_adc_nids; 583325eaba2fSLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt2002P_adc_nids); 583425eaba2fSLydia Wang get_mux_nids(codec); 583525eaba2fSLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 583625eaba2fSLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 583725eaba2fSLydia Wang spec->mixers[spec->num_mixers] = vt2002P_capture_mixer; 583825eaba2fSLydia Wang spec->num_mixers++; 583925eaba2fSLydia Wang } 584025eaba2fSLydia Wang 584125eaba2fSLydia Wang codec->patch_ops = via_patch_ops; 584225eaba2fSLydia Wang 584325eaba2fSLydia Wang codec->patch_ops.init = via_auto_init; 58440f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 584525eaba2fSLydia Wang 584625eaba2fSLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 584725eaba2fSLydia Wang spec->loopback.amplist = vt2002P_loopbacks; 584825eaba2fSLydia Wang #endif 584925eaba2fSLydia Wang 585025eaba2fSLydia Wang return 0; 585125eaba2fSLydia Wang } 5852ab6734e7SLydia Wang 5853ab6734e7SLydia Wang /* for vt1812 */ 5854ab6734e7SLydia Wang 5855ab6734e7SLydia Wang /* capture mixer elements */ 5856ab6734e7SLydia Wang static struct snd_kcontrol_new vt1812_capture_mixer[] = { 5857ab6734e7SLydia Wang HDA_CODEC_VOLUME("Capture Volume", 0x10, 0x0, HDA_INPUT), 5858ab6734e7SLydia Wang HDA_CODEC_MUTE("Capture Switch", 0x10, 0x0, HDA_INPUT), 5859ab6734e7SLydia Wang HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x11, 0x0, HDA_INPUT), 5860ab6734e7SLydia Wang HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x11, 0x0, HDA_INPUT), 5861ab6734e7SLydia Wang HDA_CODEC_MUTE("Mic Boost Capture Volume", 0x2b, 0x0, HDA_INPUT), 5862ab6734e7SLydia Wang HDA_CODEC_MUTE("Front Mic Boost Capture Volume", 0x29, 0x0, 5863ab6734e7SLydia Wang HDA_INPUT), 5864ab6734e7SLydia Wang { 5865ab6734e7SLydia Wang .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 5866ab6734e7SLydia Wang /* The multiple "Capture Source" controls confuse alsamixer 5867ab6734e7SLydia Wang * So call somewhat different.. 5868ab6734e7SLydia Wang */ 5869ab6734e7SLydia Wang .name = "Input Source", 5870ab6734e7SLydia Wang .count = 2, 5871ab6734e7SLydia Wang .info = via_mux_enum_info, 5872ab6734e7SLydia Wang .get = via_mux_enum_get, 5873ab6734e7SLydia Wang .put = via_mux_enum_put, 5874ab6734e7SLydia Wang }, 5875ab6734e7SLydia Wang { } /* end */ 5876ab6734e7SLydia Wang }; 5877ab6734e7SLydia Wang 5878ab6734e7SLydia Wang static struct hda_verb vt1812_volume_init_verbs[] = { 5879ab6734e7SLydia Wang /* 5880ab6734e7SLydia Wang * Unmute ADC0-1 and set the default input to mic-in 5881ab6734e7SLydia Wang */ 5882ab6734e7SLydia Wang {0x8, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5883ab6734e7SLydia Wang {0x9, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5884ab6734e7SLydia Wang 5885ab6734e7SLydia Wang 5886ab6734e7SLydia Wang /* Mute input amps (CD, Line In, Mic 1 & Mic 2) of the analog-loopback 5887ab6734e7SLydia Wang * mixer widget 5888ab6734e7SLydia Wang */ 5889ab6734e7SLydia Wang /* Amp Indices: CD = 1, Mic1 = 2, Line = 3, Mic2 = 4 */ 5890ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, 5891ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, 5892ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, 5893ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, 5894ab6734e7SLydia Wang {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, 5895ab6734e7SLydia Wang 5896ab6734e7SLydia Wang /* MUX Indices: Mic = 0 */ 5897ab6734e7SLydia Wang {0x1e, AC_VERB_SET_CONNECT_SEL, 0}, 5898ab6734e7SLydia Wang {0x1f, AC_VERB_SET_CONNECT_SEL, 0}, 5899ab6734e7SLydia Wang 5900ab6734e7SLydia Wang /* PW9 Output enable */ 5901ab6734e7SLydia Wang {0x2d, AC_VERB_SET_PIN_WIDGET_CONTROL, AC_PINCTL_OUT_EN}, 5902ab6734e7SLydia Wang 5903ab6734e7SLydia Wang /* Enable Boost Volume backdoor */ 5904ab6734e7SLydia Wang {0x1, 0xfb9, 0x24}, 5905ab6734e7SLydia Wang 5906ab6734e7SLydia Wang /* MW0/1/4/13/15: un-mute index 0 (MUXx), un-mute index 1 (MW9) */ 5907ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5908ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5909ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5910ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5911ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)}, 5912ab6734e7SLydia Wang {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5913ab6734e7SLydia Wang {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5914ab6734e7SLydia Wang {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5915ab6734e7SLydia Wang {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5916ab6734e7SLydia Wang {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, 5917ab6734e7SLydia Wang 5918ab6734e7SLydia Wang /* set MUX0/1/4/13/15 = 0 (AOW0) */ 5919ab6734e7SLydia Wang {0x34, AC_VERB_SET_CONNECT_SEL, 0}, 5920ab6734e7SLydia Wang {0x35, AC_VERB_SET_CONNECT_SEL, 0}, 5921ab6734e7SLydia Wang {0x38, AC_VERB_SET_CONNECT_SEL, 0}, 5922ab6734e7SLydia Wang {0x3c, AC_VERB_SET_CONNECT_SEL, 0}, 5923ab6734e7SLydia Wang {0x3d, AC_VERB_SET_CONNECT_SEL, 0}, 5924ab6734e7SLydia Wang 5925ab6734e7SLydia Wang /* Enable AOW0 to MW9 */ 5926ab6734e7SLydia Wang {0x1, 0xfb8, 0xa8}, 5927ab6734e7SLydia Wang { } 5928ab6734e7SLydia Wang }; 5929ab6734e7SLydia Wang 5930ab6734e7SLydia Wang 5931ab6734e7SLydia Wang static struct hda_verb vt1812_uniwill_init_verbs[] = { 5932ab6734e7SLydia Wang {0x33, AC_VERB_SET_UNSOLICITED_ENABLE, 5933ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5934ab6734e7SLydia Wang {0x25, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT }, 5935ab6734e7SLydia Wang {0x28, AC_VERB_SET_UNSOLICITED_ENABLE, 5936ab6734e7SLydia Wang AC_USRSP_EN | VIA_JACK_EVENT | VIA_BIND_HP_EVENT}, 5937ab6734e7SLydia Wang {0x29, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5938ab6734e7SLydia Wang {0x2a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5939ab6734e7SLydia Wang {0x2b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | VIA_JACK_EVENT}, 5940ab6734e7SLydia Wang { } 5941ab6734e7SLydia Wang }; 5942ab6734e7SLydia Wang 5943ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_playback = { 5944ab6734e7SLydia Wang .substreams = 2, 5945ab6734e7SLydia Wang .channels_min = 2, 5946ab6734e7SLydia Wang .channels_max = 2, 5947ab6734e7SLydia Wang .nid = 0x8, /* NID to query formats and rates */ 5948ab6734e7SLydia Wang .ops = { 5949ab6734e7SLydia Wang .open = via_playback_pcm_open, 5950ab6734e7SLydia Wang .prepare = via_playback_multi_pcm_prepare, 5951ab6734e7SLydia Wang .cleanup = via_playback_multi_pcm_cleanup, 5952ab6734e7SLydia Wang .close = via_pcm_open_close, 5953ab6734e7SLydia Wang }, 5954ab6734e7SLydia Wang }; 5955ab6734e7SLydia Wang 5956ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_analog_capture = { 5957ab6734e7SLydia Wang .substreams = 2, 5958ab6734e7SLydia Wang .channels_min = 2, 5959ab6734e7SLydia Wang .channels_max = 2, 5960ab6734e7SLydia Wang .nid = 0x10, /* NID to query formats and rates */ 5961ab6734e7SLydia Wang .ops = { 5962ab6734e7SLydia Wang .open = via_pcm_open_close, 5963ab6734e7SLydia Wang .prepare = via_capture_pcm_prepare, 5964ab6734e7SLydia Wang .cleanup = via_capture_pcm_cleanup, 5965ab6734e7SLydia Wang .close = via_pcm_open_close, 5966ab6734e7SLydia Wang }, 5967ab6734e7SLydia Wang }; 5968ab6734e7SLydia Wang 5969ab6734e7SLydia Wang static struct hda_pcm_stream vt1812_pcm_digital_playback = { 5970ab6734e7SLydia Wang .substreams = 1, 5971ab6734e7SLydia Wang .channels_min = 2, 5972ab6734e7SLydia Wang .channels_max = 2, 5973ab6734e7SLydia Wang /* NID is set in via_build_pcms */ 5974ab6734e7SLydia Wang .ops = { 5975ab6734e7SLydia Wang .open = via_dig_playback_pcm_open, 5976ab6734e7SLydia Wang .close = via_dig_playback_pcm_close, 5977ab6734e7SLydia Wang .prepare = via_dig_playback_pcm_prepare, 5978ab6734e7SLydia Wang .cleanup = via_dig_playback_pcm_cleanup 5979ab6734e7SLydia Wang }, 5980ab6734e7SLydia Wang }; 5981ab6734e7SLydia Wang /* fill in the dac_nids table from the parsed pin configuration */ 5982ab6734e7SLydia Wang static int vt1812_auto_fill_dac_nids(struct via_spec *spec, 5983ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5984ab6734e7SLydia Wang { 5985ab6734e7SLydia Wang spec->multiout.num_dacs = 1; 5986ab6734e7SLydia Wang spec->multiout.dac_nids = spec->private_dac_nids; 5987ab6734e7SLydia Wang if (cfg->line_out_pins[0]) 5988ab6734e7SLydia Wang spec->multiout.dac_nids[0] = 0x8; 5989ab6734e7SLydia Wang return 0; 5990ab6734e7SLydia Wang } 5991ab6734e7SLydia Wang 5992ab6734e7SLydia Wang 5993ab6734e7SLydia Wang /* add playback controls from the parsed DAC table */ 5994ab6734e7SLydia Wang static int vt1812_auto_create_multi_out_ctls(struct via_spec *spec, 5995ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 5996ab6734e7SLydia Wang { 5997ab6734e7SLydia Wang int err; 5998ab6734e7SLydia Wang 5999ab6734e7SLydia Wang if (!cfg->line_out_pins[0]) 6000ab6734e7SLydia Wang return -1; 6001ab6734e7SLydia Wang 6002ab6734e7SLydia Wang /* Line-Out: PortE */ 6003ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 6004ab6734e7SLydia Wang "Master Front Playback Volume", 6005ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x8, 3, 0, HDA_OUTPUT)); 6006ab6734e7SLydia Wang if (err < 0) 6007ab6734e7SLydia Wang return err; 6008ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_BIND_PIN_MUTE, 6009ab6734e7SLydia Wang "Master Front Playback Switch", 6010ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(0x28, 3, 0, HDA_OUTPUT)); 6011ab6734e7SLydia Wang if (err < 0) 6012ab6734e7SLydia Wang return err; 6013ab6734e7SLydia Wang 6014ab6734e7SLydia Wang return 0; 6015ab6734e7SLydia Wang } 6016ab6734e7SLydia Wang 6017ab6734e7SLydia Wang static int vt1812_auto_create_hp_ctls(struct via_spec *spec, hda_nid_t pin) 6018ab6734e7SLydia Wang { 6019ab6734e7SLydia Wang int err; 6020ab6734e7SLydia Wang 6021ab6734e7SLydia Wang if (!pin) 6022ab6734e7SLydia Wang return 0; 6023ab6734e7SLydia Wang 6024ab6734e7SLydia Wang spec->multiout.hp_nid = 0x9; 6025ab6734e7SLydia Wang spec->hp_independent_mode_index = 1; 6026ab6734e7SLydia Wang 6027ab6734e7SLydia Wang 6028ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_VOL, 6029ab6734e7SLydia Wang "Headphone Playback Volume", 6030ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL( 6031ab6734e7SLydia Wang spec->multiout.hp_nid, 3, 0, HDA_OUTPUT)); 6032ab6734e7SLydia Wang if (err < 0) 6033ab6734e7SLydia Wang return err; 6034ab6734e7SLydia Wang 6035ab6734e7SLydia Wang err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, 6036ab6734e7SLydia Wang "Headphone Playback Switch", 6037ab6734e7SLydia Wang HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_OUTPUT)); 6038ab6734e7SLydia Wang if (err < 0) 6039ab6734e7SLydia Wang return err; 6040ab6734e7SLydia Wang 6041ab6734e7SLydia Wang create_hp_imux(spec); 6042ab6734e7SLydia Wang return 0; 6043ab6734e7SLydia Wang } 6044ab6734e7SLydia Wang 6045ab6734e7SLydia Wang /* create playback/capture controls for input pins */ 6046ab6734e7SLydia Wang static int vt1812_auto_create_analog_input_ctls(struct via_spec *spec, 6047ab6734e7SLydia Wang const struct auto_pin_cfg *cfg) 6048ab6734e7SLydia Wang { 6049ab6734e7SLydia Wang static char *labels[] = { 6050ab6734e7SLydia Wang "Mic", "Front Mic", "Line", "Front Line", "CD", "Aux", NULL 6051ab6734e7SLydia Wang }; 6052ab6734e7SLydia Wang struct hda_input_mux *imux = &spec->private_imux[0]; 6053ab6734e7SLydia Wang int i, err, idx = 0; 6054ab6734e7SLydia Wang 6055ab6734e7SLydia Wang for (i = 0; i < AUTO_PIN_LAST; i++) { 6056ab6734e7SLydia Wang if (!cfg->input_pins[i]) 6057ab6734e7SLydia Wang continue; 6058ab6734e7SLydia Wang 6059ab6734e7SLydia Wang switch (cfg->input_pins[i]) { 6060ab6734e7SLydia Wang case 0x2b: /* Mic */ 6061ab6734e7SLydia Wang idx = 0; 6062ab6734e7SLydia Wang break; 6063ab6734e7SLydia Wang 6064ab6734e7SLydia Wang case 0x2a: /* Line In */ 6065ab6734e7SLydia Wang idx = 1; 6066ab6734e7SLydia Wang break; 6067ab6734e7SLydia Wang 6068ab6734e7SLydia Wang case 0x29: /* Front Mic */ 6069ab6734e7SLydia Wang idx = 2; 6070ab6734e7SLydia Wang break; 6071ab6734e7SLydia Wang } 6072ab6734e7SLydia Wang err = via_new_analog_input(spec, labels[i], idx, 0x21); 6073ab6734e7SLydia Wang if (err < 0) 6074ab6734e7SLydia Wang return err; 6075ab6734e7SLydia Wang imux->items[imux->num_items].label = labels[i]; 6076ab6734e7SLydia Wang imux->items[imux->num_items].index = idx; 6077ab6734e7SLydia Wang imux->num_items++; 6078ab6734e7SLydia Wang } 6079ab6734e7SLydia Wang /* build volume/mute control of loopback */ 6080ab6734e7SLydia Wang err = via_new_analog_input(spec, "Stereo Mixer", 5, 0x21); 6081ab6734e7SLydia Wang if (err < 0) 6082ab6734e7SLydia Wang return err; 6083ab6734e7SLydia Wang 6084ab6734e7SLydia Wang /* for internal loopback recording select */ 6085ab6734e7SLydia Wang imux->items[imux->num_items].label = "Stereo Mixer"; 6086ab6734e7SLydia Wang imux->items[imux->num_items].index = 5; 6087ab6734e7SLydia Wang imux->num_items++; 6088ab6734e7SLydia Wang 6089ab6734e7SLydia Wang /* for digital mic select */ 6090ab6734e7SLydia Wang imux->items[imux->num_items].label = "Digital Mic"; 6091ab6734e7SLydia Wang imux->items[imux->num_items].index = 6; 6092ab6734e7SLydia Wang imux->num_items++; 6093ab6734e7SLydia Wang 6094ab6734e7SLydia Wang return 0; 6095ab6734e7SLydia Wang } 6096ab6734e7SLydia Wang 6097ab6734e7SLydia Wang static int vt1812_parse_auto_config(struct hda_codec *codec) 6098ab6734e7SLydia Wang { 6099ab6734e7SLydia Wang struct via_spec *spec = codec->spec; 6100ab6734e7SLydia Wang int err; 6101ab6734e7SLydia Wang 6102ab6734e7SLydia Wang 6103ab6734e7SLydia Wang err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL); 6104ab6734e7SLydia Wang if (err < 0) 6105ab6734e7SLydia Wang return err; 6106ab6734e7SLydia Wang fill_dig_outs(codec); 6107ab6734e7SLydia Wang err = vt1812_auto_fill_dac_nids(spec, &spec->autocfg); 6108ab6734e7SLydia Wang if (err < 0) 6109ab6734e7SLydia Wang return err; 6110ab6734e7SLydia Wang 6111ab6734e7SLydia Wang if (!spec->autocfg.line_outs && !spec->autocfg.hp_outs) 6112ab6734e7SLydia Wang return 0; /* can't find valid BIOS pin config */ 6113ab6734e7SLydia Wang 6114ab6734e7SLydia Wang err = vt1812_auto_create_multi_out_ctls(spec, &spec->autocfg); 6115ab6734e7SLydia Wang if (err < 0) 6116ab6734e7SLydia Wang return err; 6117ab6734e7SLydia Wang err = vt1812_auto_create_hp_ctls(spec, spec->autocfg.hp_pins[0]); 6118ab6734e7SLydia Wang if (err < 0) 6119ab6734e7SLydia Wang return err; 6120ab6734e7SLydia Wang err = vt1812_auto_create_analog_input_ctls(spec, &spec->autocfg); 6121ab6734e7SLydia Wang if (err < 0) 6122ab6734e7SLydia Wang return err; 6123ab6734e7SLydia Wang 6124ab6734e7SLydia Wang spec->multiout.max_channels = spec->multiout.num_dacs * 2; 6125ab6734e7SLydia Wang 6126ab6734e7SLydia Wang fill_dig_outs(codec); 6127ab6734e7SLydia Wang 6128ab6734e7SLydia Wang if (spec->kctls.list) 6129ab6734e7SLydia Wang spec->mixers[spec->num_mixers++] = spec->kctls.list; 6130ab6734e7SLydia Wang 6131ab6734e7SLydia Wang spec->input_mux = &spec->private_imux[0]; 6132ab6734e7SLydia Wang 6133ab6734e7SLydia Wang if (spec->hp_mux) 61345b0cb1d8SJaroslav Kysela via_hp_build(spec); 6135ab6734e7SLydia Wang 6136ab6734e7SLydia Wang return 1; 6137ab6734e7SLydia Wang } 6138ab6734e7SLydia Wang 6139ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6140ab6734e7SLydia Wang static struct hda_amp_list vt1812_loopbacks[] = { 6141ab6734e7SLydia Wang { 0x21, HDA_INPUT, 0 }, 6142ab6734e7SLydia Wang { 0x21, HDA_INPUT, 1 }, 6143ab6734e7SLydia Wang { 0x21, HDA_INPUT, 2 }, 6144ab6734e7SLydia Wang { } /* end */ 6145ab6734e7SLydia Wang }; 6146ab6734e7SLydia Wang #endif 6147ab6734e7SLydia Wang 6148ab6734e7SLydia Wang 6149ab6734e7SLydia Wang /* patch for vt1812 */ 6150ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec) 6151ab6734e7SLydia Wang { 6152ab6734e7SLydia Wang struct via_spec *spec; 6153ab6734e7SLydia Wang int err; 6154ab6734e7SLydia Wang 6155ab6734e7SLydia Wang /* create a codec specific record */ 61565b0cb1d8SJaroslav Kysela spec = via_new_spec(codec); 6157ab6734e7SLydia Wang if (spec == NULL) 6158ab6734e7SLydia Wang return -ENOMEM; 6159ab6734e7SLydia Wang 6160ab6734e7SLydia Wang /* automatic parse from the BIOS config */ 6161ab6734e7SLydia Wang err = vt1812_parse_auto_config(codec); 6162ab6734e7SLydia Wang if (err < 0) { 6163ab6734e7SLydia Wang via_free(codec); 6164ab6734e7SLydia Wang return err; 6165ab6734e7SLydia Wang } else if (!err) { 6166ab6734e7SLydia Wang printk(KERN_INFO "hda_codec: Cannot set up configuration " 6167ab6734e7SLydia Wang "from BIOS. Using genenic mode...\n"); 6168ab6734e7SLydia Wang } 6169ab6734e7SLydia Wang 6170ab6734e7SLydia Wang 6171ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_volume_init_verbs; 6172ab6734e7SLydia Wang spec->init_verbs[spec->num_iverbs++] = vt1812_uniwill_init_verbs; 6173ab6734e7SLydia Wang 6174ab6734e7SLydia Wang spec->stream_name_analog = "VT1812 Analog"; 6175ab6734e7SLydia Wang spec->stream_analog_playback = &vt1812_pcm_analog_playback; 6176ab6734e7SLydia Wang spec->stream_analog_capture = &vt1812_pcm_analog_capture; 6177ab6734e7SLydia Wang 6178ab6734e7SLydia Wang spec->stream_name_digital = "VT1812 Digital"; 6179ab6734e7SLydia Wang spec->stream_digital_playback = &vt1812_pcm_digital_playback; 6180ab6734e7SLydia Wang 6181ab6734e7SLydia Wang 6182ab6734e7SLydia Wang if (!spec->adc_nids && spec->input_mux) { 6183ab6734e7SLydia Wang spec->adc_nids = vt1812_adc_nids; 6184ab6734e7SLydia Wang spec->num_adc_nids = ARRAY_SIZE(vt1812_adc_nids); 6185ab6734e7SLydia Wang get_mux_nids(codec); 6186ab6734e7SLydia Wang override_mic_boost(codec, 0x2b, 0, 3, 40); 6187ab6734e7SLydia Wang override_mic_boost(codec, 0x29, 0, 3, 40); 6188ab6734e7SLydia Wang spec->mixers[spec->num_mixers] = vt1812_capture_mixer; 6189ab6734e7SLydia Wang spec->num_mixers++; 6190ab6734e7SLydia Wang } 6191ab6734e7SLydia Wang 6192ab6734e7SLydia Wang codec->patch_ops = via_patch_ops; 6193ab6734e7SLydia Wang 6194ab6734e7SLydia Wang codec->patch_ops.init = via_auto_init; 61950f48327eSStephen Rothwell codec->patch_ops.unsol_event = via_unsol_event; 6196ab6734e7SLydia Wang 6197ab6734e7SLydia Wang #ifdef CONFIG_SND_HDA_POWER_SAVE 6198ab6734e7SLydia Wang spec->loopback.amplist = vt1812_loopbacks; 6199ab6734e7SLydia Wang #endif 6200ab6734e7SLydia Wang 6201ab6734e7SLydia Wang return 0; 6202ab6734e7SLydia Wang } 6203ab6734e7SLydia Wang 6204c577b8a1SJoseph Chan /* 6205c577b8a1SJoseph Chan * patch entries 6206c577b8a1SJoseph Chan */ 62071289e9e8STakashi Iwai static struct hda_codec_preset snd_hda_preset_via[] = { 62083218c178STakashi Iwai { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708}, 62093218c178STakashi Iwai { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708}, 62103218c178STakashi Iwai { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708}, 62113218c178STakashi Iwai { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708}, 62123218c178STakashi Iwai { .id = 0x1106e710, .name = "VT1709 10-Ch", 6213f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62143218c178STakashi Iwai { .id = 0x1106e711, .name = "VT1709 10-Ch", 6215f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62163218c178STakashi Iwai { .id = 0x1106e712, .name = "VT1709 10-Ch", 6217f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62183218c178STakashi Iwai { .id = 0x1106e713, .name = "VT1709 10-Ch", 6219f7278fd0SJosepch Chan .patch = patch_vt1709_10ch}, 62203218c178STakashi Iwai { .id = 0x1106e714, .name = "VT1709 6-Ch", 6221f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62223218c178STakashi Iwai { .id = 0x1106e715, .name = "VT1709 6-Ch", 6223f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62243218c178STakashi Iwai { .id = 0x1106e716, .name = "VT1709 6-Ch", 6225f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62263218c178STakashi Iwai { .id = 0x1106e717, .name = "VT1709 6-Ch", 6227f7278fd0SJosepch Chan .patch = patch_vt1709_6ch}, 62283218c178STakashi Iwai { .id = 0x1106e720, .name = "VT1708B 8-Ch", 6229f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62303218c178STakashi Iwai { .id = 0x1106e721, .name = "VT1708B 8-Ch", 6231f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62323218c178STakashi Iwai { .id = 0x1106e722, .name = "VT1708B 8-Ch", 6233f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62343218c178STakashi Iwai { .id = 0x1106e723, .name = "VT1708B 8-Ch", 6235f7278fd0SJosepch Chan .patch = patch_vt1708B_8ch}, 62363218c178STakashi Iwai { .id = 0x1106e724, .name = "VT1708B 4-Ch", 6237f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62383218c178STakashi Iwai { .id = 0x1106e725, .name = "VT1708B 4-Ch", 6239f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62403218c178STakashi Iwai { .id = 0x1106e726, .name = "VT1708B 4-Ch", 6241f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62423218c178STakashi Iwai { .id = 0x1106e727, .name = "VT1708B 4-Ch", 6243f7278fd0SJosepch Chan .patch = patch_vt1708B_4ch}, 62443218c178STakashi Iwai { .id = 0x11060397, .name = "VT1708S", 6245d949cac1SHarald Welte .patch = patch_vt1708S}, 62463218c178STakashi Iwai { .id = 0x11061397, .name = "VT1708S", 6247d949cac1SHarald Welte .patch = patch_vt1708S}, 62483218c178STakashi Iwai { .id = 0x11062397, .name = "VT1708S", 6249d949cac1SHarald Welte .patch = patch_vt1708S}, 62503218c178STakashi Iwai { .id = 0x11063397, .name = "VT1708S", 6251d949cac1SHarald Welte .patch = patch_vt1708S}, 62523218c178STakashi Iwai { .id = 0x11064397, .name = "VT1708S", 6253d949cac1SHarald Welte .patch = patch_vt1708S}, 62543218c178STakashi Iwai { .id = 0x11065397, .name = "VT1708S", 6255d949cac1SHarald Welte .patch = patch_vt1708S}, 62563218c178STakashi Iwai { .id = 0x11066397, .name = "VT1708S", 6257d949cac1SHarald Welte .patch = patch_vt1708S}, 62583218c178STakashi Iwai { .id = 0x11067397, .name = "VT1708S", 6259d949cac1SHarald Welte .patch = patch_vt1708S}, 62603218c178STakashi Iwai { .id = 0x11060398, .name = "VT1702", 6261d949cac1SHarald Welte .patch = patch_vt1702}, 62623218c178STakashi Iwai { .id = 0x11061398, .name = "VT1702", 6263d949cac1SHarald Welte .patch = patch_vt1702}, 62643218c178STakashi Iwai { .id = 0x11062398, .name = "VT1702", 6265d949cac1SHarald Welte .patch = patch_vt1702}, 62663218c178STakashi Iwai { .id = 0x11063398, .name = "VT1702", 6267d949cac1SHarald Welte .patch = patch_vt1702}, 62683218c178STakashi Iwai { .id = 0x11064398, .name = "VT1702", 6269d949cac1SHarald Welte .patch = patch_vt1702}, 62703218c178STakashi Iwai { .id = 0x11065398, .name = "VT1702", 6271d949cac1SHarald Welte .patch = patch_vt1702}, 62723218c178STakashi Iwai { .id = 0x11066398, .name = "VT1702", 6273d949cac1SHarald Welte .patch = patch_vt1702}, 62743218c178STakashi Iwai { .id = 0x11067398, .name = "VT1702", 6275d949cac1SHarald Welte .patch = patch_vt1702}, 6276eb7188caSLydia Wang { .id = 0x11060428, .name = "VT1718S", 6277eb7188caSLydia Wang .patch = patch_vt1718S}, 6278eb7188caSLydia Wang { .id = 0x11064428, .name = "VT1718S", 6279eb7188caSLydia Wang .patch = patch_vt1718S}, 6280bb3c6bfcSLydia Wang { .id = 0x11060441, .name = "VT2020", 6281bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6282bb3c6bfcSLydia Wang { .id = 0x11064441, .name = "VT1828S", 6283bb3c6bfcSLydia Wang .patch = patch_vt1718S}, 6284f3db423dSLydia Wang { .id = 0x11060433, .name = "VT1716S", 6285f3db423dSLydia Wang .patch = patch_vt1716S}, 6286f3db423dSLydia Wang { .id = 0x1106a721, .name = "VT1716S", 6287f3db423dSLydia Wang .patch = patch_vt1716S}, 628825eaba2fSLydia Wang { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P}, 628925eaba2fSLydia Wang { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P}, 6290ab6734e7SLydia Wang { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812}, 629136dd5c4aSLydia Wang { .id = 0x11060440, .name = "VT1818S", 629236dd5c4aSLydia Wang .patch = patch_vt1708S}, 6293c577b8a1SJoseph Chan {} /* terminator */ 6294c577b8a1SJoseph Chan }; 62951289e9e8STakashi Iwai 62961289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*"); 62971289e9e8STakashi Iwai 62981289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = { 62991289e9e8STakashi Iwai .preset = snd_hda_preset_via, 63001289e9e8STakashi Iwai .owner = THIS_MODULE, 63011289e9e8STakashi Iwai }; 63021289e9e8STakashi Iwai 63031289e9e8STakashi Iwai MODULE_LICENSE("GPL"); 63041289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec"); 63051289e9e8STakashi Iwai 63061289e9e8STakashi Iwai static int __init patch_via_init(void) 63071289e9e8STakashi Iwai { 63081289e9e8STakashi Iwai return snd_hda_add_codec_preset(&via_list); 63091289e9e8STakashi Iwai } 63101289e9e8STakashi Iwai 63111289e9e8STakashi Iwai static void __exit patch_via_exit(void) 63121289e9e8STakashi Iwai { 63131289e9e8STakashi Iwai snd_hda_delete_codec_preset(&via_list); 63141289e9e8STakashi Iwai } 63151289e9e8STakashi Iwai 63161289e9e8STakashi Iwai module_init(patch_via_init) 63171289e9e8STakashi Iwai module_exit(patch_via_exit) 6318